Merge branch 'integration_2025_w38' into 'develop'
Integration: `2025.w38` Closes #954 See merge request oai/openairinterface5g!3646 * !3639 Fix error ind unpack * !3643 Fix memory allocation issue in KPM RAN function sd parameter handling * !3568 Fix UE DCI monitoring after RRC setup * !3642 Check command line for unknown options also at gNB * !3647 Add nr-cuup to run_locally.sh * !3596 L3 measurements and measReport A2 * !3648 fix MSG3 with C-RNTI failure * !3649 CI: build images on Jetson (armv8) * !3334 N2 Handover
This commit is contained in:
@@ -363,6 +363,7 @@ add_library(ngap
|
||||
${NGAP_DIR}/ngap_gNB.c
|
||||
${NGAP_DIR}/ngap_common.c
|
||||
${NGAP_DIR}/ngap_gNB_context_management_procedures.c
|
||||
${NGAP_DIR}/ngap_gNB_mobility_management.c
|
||||
${NGAP_DIR}/ngap_gNB_decoder.c
|
||||
${NGAP_DIR}/ngap_gNB_encoder.c
|
||||
${NGAP_DIR}/ngap_gNB_handlers.c
|
||||
|
||||
@@ -175,6 +175,28 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ("Ubuntu-Jetson-Image-Builder") {
|
||||
steps {
|
||||
script {
|
||||
triggerSlaveJob ('RAN-Ubuntu-Jetson-Image-Builder', 'Ubuntu-Jetson-Image-Builder')
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
// Using a unique variable name for each test stage to avoid overwriting on a global variable
|
||||
// due to parallel-time concurrency
|
||||
ubuntuJetsonBuildStatus = finalizeSlaveJob('RAN-Ubuntu-Jetson-Image-Builder')
|
||||
}
|
||||
}
|
||||
failure {
|
||||
script {
|
||||
currentBuild.result = 'FAILURE'
|
||||
failingStages += ubuntuJetsonBuildStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage ("RHEL-Cluster-Image-Builder") {
|
||||
steps {
|
||||
script {
|
||||
|
||||
@@ -304,13 +304,18 @@ class Containerize():
|
||||
result = re.search('build_cross_arm64', self.imageKind)
|
||||
if result is not None:
|
||||
self.dockerfileprefix = '.ubuntu.cross-arm64'
|
||||
result = re.search('native_arm', self.imageKind)
|
||||
result = re.search('native_armv9', self.imageKind)
|
||||
if result is not None:
|
||||
imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', ''))
|
||||
imageNames.append(('ran-build-fhi72', 'build.fhi72.native_arm', 'ran-build-fhi72', ''))
|
||||
imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup', ''))
|
||||
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
|
||||
imageNames.append(('oai-gnb-aerial', 'gNB.aerial', 'oai-gnb-aerial', ''))
|
||||
result = re.search('native_armv8', self.imageKind)
|
||||
if result is not None:
|
||||
imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', ''))
|
||||
imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup', ''))
|
||||
imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', ''))
|
||||
|
||||
cmd.cd(lSourcePath)
|
||||
# if asterix, copy the entitlement and subscription manager configurations
|
||||
|
||||
@@ -15,7 +15,7 @@ gNBs =
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
|
||||
|
||||
@include "neighbour-config.conf"
|
||||
|
||||
nr_cellid = 12345678L;
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
Active_gNBs = ( "cu-cp-0");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
|
||||
# cell_type = "CELL_MACRO_GNB";
|
||||
|
||||
gNB_name = "cu-cp-0";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
|
||||
@include "neighbour-config.conf"
|
||||
|
||||
nr_cellid = 12345678L;
|
||||
|
||||
tr_s_preference = "f1";
|
||||
|
||||
local_s_address = "192.168.71.150";
|
||||
remote_s_address = "192.168.71.171";
|
||||
local_s_portd = 2153;
|
||||
remote_s_portd = 2153;
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ({ ipv4 = "192.168.71.132"; });
|
||||
|
||||
E1_INTERFACE =
|
||||
(
|
||||
{
|
||||
type = "cp";
|
||||
ipv4_cucp = "192.168.71.150";
|
||||
port_cucp = 38462;
|
||||
ipv4_cuup = "0.0.0.0"; # multiple CU-UPs
|
||||
port_cuup = 38462;
|
||||
}
|
||||
)
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config : {
|
||||
global_log_level = "info";
|
||||
pdcp_log_level = "info";
|
||||
rrc_log_level = "info";
|
||||
f1ap_log_level = "info";
|
||||
ngap_log_level = "info";
|
||||
};
|
||||
@@ -1,52 +1,51 @@
|
||||
neighbour_list = (
|
||||
{
|
||||
nr_cellid = 1;
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xb00;
|
||||
nr_cellid = 2;
|
||||
physical_cellId = 0x002;
|
||||
absoluteFrequencySSB = 641280 ;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
plmn = { mcc = 216; mnc = 03; mnc_length = 2};
|
||||
tracking_area_code = 2;
|
||||
},
|
||||
{
|
||||
gNB_ID = 0xa00;
|
||||
nr_cellid = 3;
|
||||
physical_cellId = 0x003;
|
||||
absoluteFrequencySSB = 641280 ;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
plmn = { mcc = 999; mnc = 99; mnc_length = 2};
|
||||
tracking_area_code = 3;
|
||||
},
|
||||
{
|
||||
gNB_ID = 0xc00;
|
||||
nr_cellid = 4;
|
||||
physical_cellId = 0x004;
|
||||
absoluteFrequencySSB = 641280 ;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
plmn = { mcc = 999; mnc = 99; mnc_length = 2};
|
||||
tracking_area_code = 4;
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
nr_cellid = 2;
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xe00;
|
||||
nr_cellid = 1;
|
||||
physical_cellId = 0x000;
|
||||
absoluteFrequencySSB = 641280 ;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
############################################################
|
||||
# gNB-to-gNB neighbour list + measurement configuration #
|
||||
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
|
||||
############################################################
|
||||
|
||||
neighbour_list = (
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 1) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 0; # Serving cell of gNB 0xe00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xb00;
|
||||
nr_cellid = 1; # Cell served by gNB 0xb00
|
||||
physical_cellId = 1;
|
||||
absoluteFrequencySSB= 621312;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 2) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 1; # Serving cell of gNB 0xb00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xe00;
|
||||
nr_cellid = 0; # Cell served by gNB 0xe00
|
||||
physical_cellId = 0;
|
||||
absoluteFrequencySSB= 641280;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
############################################################
|
||||
# Common NR measurement-event configuration #
|
||||
############################################################
|
||||
|
||||
nr_measurement_configuration = {
|
||||
Periodical = {
|
||||
|
||||
@@ -15,16 +15,18 @@ fi
|
||||
|
||||
# The script assumes you've build the following images:
|
||||
#
|
||||
# docker build . -f docker/Dockerfile.gNB.ubuntu22 -t oai-gnb
|
||||
# docker build . -f docker/Dockerfile.nrUE.ubuntu22 -t oai-nr-ue
|
||||
# docker build . -f docker/Dockerfile.gNB.ubuntu -t oai-gnb
|
||||
# docker build . -f docker/Dockerfile.nr-cuup.ubuntu -t oai-nr-cuup
|
||||
# docker build . -f docker/Dockerfile.nrUE.ubuntu -t oai-nr-ue
|
||||
#
|
||||
# The images above depend on the following images:
|
||||
#
|
||||
# docker build . -f docker/Dockerfile.build.ubuntu22 -t ran-build
|
||||
# dokcer build . -f docker/Dockerfile.base.ubuntu22 -t ran-base
|
||||
# docker build . -f docker/Dockerfile.build.ubuntu -t ran-build
|
||||
# docker build . -f docker/Dockerfile.base.ubuntu -t ran-base
|
||||
|
||||
docker tag oai-nr-ue oai-ci/oai-nr-ue:develop-${SHORT_COMMIT_SHA}
|
||||
docker tag oai-gnb oai-ci/oai-gnb:develop-${SHORT_COMMIT_SHA}
|
||||
docker tag oai-nr-cuup oai-ci/oai-nr-cuup:develop-${SHORT_COMMIT_SHA}
|
||||
|
||||
python3 main.py --mode=InitiateHtml --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \
|
||||
--ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<testCaseList>
|
||||
<htmlTabRef>rfsim-5gnr-f1-n2-ho</htmlTabRef>
|
||||
<htmlTabName>F1 SA TDD N2 Handover</htmlTabName>
|
||||
<htmlTabIcon>wrench</htmlTabIcon>
|
||||
<TestCaseRequestedList>
|
||||
111111
|
||||
800813
|
||||
000001
|
||||
000002
|
||||
000003
|
||||
020001
|
||||
020002
|
||||
030001
|
||||
030002
|
||||
000004
|
||||
040001
|
||||
050001
|
||||
020001
|
||||
020002
|
||||
050002
|
||||
030001
|
||||
030002
|
||||
050003
|
||||
020001
|
||||
020002
|
||||
050004
|
||||
050001
|
||||
020001
|
||||
020002
|
||||
050002
|
||||
100001
|
||||
222222
|
||||
</TestCaseRequestedList>
|
||||
<TestCaseExclusionList></TestCaseExclusionList>
|
||||
|
||||
<testCase id="111111">
|
||||
<class>Pull_Local_Registry</class>
|
||||
<desc>Pull Images from Local Registry</desc>
|
||||
<svr_id>0</svr_id>
|
||||
<images>oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan</images>
|
||||
</testCase>
|
||||
|
||||
<testCase id="800813">
|
||||
<class>Create_Workspace</class>
|
||||
<desc>Create new Workspace</desc>
|
||||
<eNB_instance>0</eNB_instance>
|
||||
<eNB_serverId>0</eNB_serverId>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000001">
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy OAI 5G CoreNetwork</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
|
||||
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
|
||||
<eNB_instance>0</eNB_instance>
|
||||
<eNB_serverId>0</eNB_serverId>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000002">
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy OAI 5G CU-CP/CU-UP/DU 0 + UE RFsim SA</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
|
||||
<services>oai-cucp-0 oai-cuup-0 oai-du-0 oai-nr-ue</services>
|
||||
<eNB_instance>0</eNB_instance>
|
||||
<eNB_serverId>0</eNB_serverId>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000003">
|
||||
<class>Attach_UE</class>
|
||||
<desc>Attach OAI UE (Wait for IP)</desc>
|
||||
<id>rfsim5g_ue</id>
|
||||
<nodes>acamas</nodes>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000004">
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy OAI 5G CU-CP/CU-UP/DU 1 RFsim SA</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
|
||||
<services>oai-cucp-1 oai-cuup-1 oai-du-1</services>
|
||||
<eNB_instance>0</eNB_instance>
|
||||
<eNB_serverId>0</eNB_serverId>
|
||||
</testCase>
|
||||
|
||||
<testCase id="020001">
|
||||
<class>Ping</class>
|
||||
<desc>Ping NR-UE from ext-dn</desc>
|
||||
<id>rfsim5g_ext_dn</id>
|
||||
<nodes>acamas</nodes>
|
||||
<svr_id>rfsim5g_ue</svr_id>
|
||||
<svr_node>acamas</svr_node>
|
||||
<ping_args>-c 100 -i 0.25</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase id="020002">
|
||||
<class>Ping</class>
|
||||
<desc>Ping ext-dn from NR-UE</desc>
|
||||
<id>rfsim5g_ue</id>
|
||||
<nodes>acamas</nodes>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>acamas</svr_node>
|
||||
<ping_args>-c 100 -i 0.25</ping_args>
|
||||
<ping_packetloss_threshold>10</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase id="030001">
|
||||
<class>Iperf</class>
|
||||
<desc>Iperf (DL/10Mbps/UDP)(30 sec)</desc>
|
||||
<iperf_args>-u -b 3M -t 20 -R</iperf_args>
|
||||
<id>rfsim5g_ue</id>
|
||||
<nodes>acamas</nodes>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>acamas</svr_node>
|
||||
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
|
||||
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase id="030002">
|
||||
<class>Iperf</class>
|
||||
<desc>Iperf (UL/4Mbps/UDP)(30 sec)</desc>
|
||||
<iperf_args>-u -b 1M -t 20</iperf_args>
|
||||
<id>rfsim5g_ue</id>
|
||||
<nodes>acamas</nodes>
|
||||
<svr_id>rfsim5g_ext_dn</svr_id>
|
||||
<svr_node>acamas</svr_node>
|
||||
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
|
||||
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase id="040001">
|
||||
<class>IdleSleep</class>
|
||||
<desc>Sleep</desc>
|
||||
<idle_sleep_time_in_sec>5</idle_sleep_time_in_sec>
|
||||
</testCase>
|
||||
|
||||
<testCase id="050001">
|
||||
<class>Custom_Command</class>
|
||||
<desc>Trigger N2 Handover</desc>
|
||||
<node>acamas</node>
|
||||
<command>echo ci trigger_n2_ho 1,1 | ncat 192.168.71.150 9090</command>
|
||||
<command_fail>yes</command_fail>
|
||||
</testCase>
|
||||
|
||||
<testCase id="050002">
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 1?</desc>
|
||||
<node>acamas</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.180 9090 | grep "1234"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase id="050003">
|
||||
<class>Custom_Command</class>
|
||||
<desc>Trigger N2 Handover</desc>
|
||||
<node>acamas</node>
|
||||
<command>echo ci trigger_n2_ho 0,1 | ncat 192.168.71.180 9090</command>
|
||||
<command_fail>yes</command_fail>
|
||||
</testCase>
|
||||
|
||||
<testCase id="050004">
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 0?</desc>
|
||||
<node>acamas</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase id="100001">
|
||||
<class>Undeploy_Object</class>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Undeploy all OAI 5G stack</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
|
||||
<eNB_instance>0</eNB_instance>
|
||||
<eNB_serverId>0</eNB_serverId>
|
||||
<d_retx_th>10,100,100,100</d_retx_th>
|
||||
<u_retx_th>10,100,100,100</u_retx_th>
|
||||
</testCase>
|
||||
|
||||
<testCase id="222222">
|
||||
<class>Clean_Test_Server_Images</class>
|
||||
<always_exec>true</always_exec>
|
||||
<desc>Clean Test Images on Test Server</desc>
|
||||
<svr_id>0</svr_id>
|
||||
<images>oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan</images>
|
||||
</testCase>
|
||||
|
||||
</testCaseList>
|
||||
@@ -40,7 +40,7 @@
|
||||
<class>Build_Image</class>
|
||||
<desc>Build all Images</desc>
|
||||
<node>gracehopper3-oai</node>
|
||||
<kind>native_arm</kind>
|
||||
<kind>native_armv9</kind>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000010">
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<testCaseList>
|
||||
<htmlTabRef>build-tab</htmlTabRef>
|
||||
<htmlTabName>Build Container Images for ARM</htmlTabName>
|
||||
<htmlTabIcon>wrench</htmlTabIcon>
|
||||
<TestCaseRequestedList>
|
||||
800813
|
||||
000001
|
||||
000010
|
||||
</TestCaseRequestedList>
|
||||
<TestCaseExclusionList></TestCaseExclusionList>
|
||||
|
||||
<testCase id="800813">
|
||||
<class>Create_Workspace</class>
|
||||
<desc>Create new Workspace for server 0</desc>
|
||||
<node>jetson3-oai</node>
|
||||
</testCase>
|
||||
<testCase id="000001">
|
||||
<class>Build_Image</class>
|
||||
<desc>Build all Images</desc>
|
||||
<node>jetson3-oai</node>
|
||||
<kind>native_armv8</kind>
|
||||
</testCase>
|
||||
|
||||
<testCase id="000010">
|
||||
<class>Push_Local_Registry</class>
|
||||
<desc>Push Images to Local Registry</desc>
|
||||
<node>jetson3-oai</node>
|
||||
<tag_prefix>armv8_</tag_prefix>
|
||||
</testCase>
|
||||
|
||||
</testCaseList>
|
||||
@@ -100,7 +100,6 @@ services:
|
||||
- SYS_NICE
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time
|
||||
--rfsimulator.options chanmod
|
||||
--gNBs.[0].remote_s_address 0.0.0.0
|
||||
--telnetsrv --telnetsrv.listenaddr 192.168.71.150
|
||||
--telnetsrv.shrmod ci
|
||||
@@ -111,6 +110,7 @@ services:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.150
|
||||
volumes:
|
||||
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
|
||||
- ../../conf_files/gnb-cu.sa.band78.106prb.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
services:
|
||||
mysql:
|
||||
container_name: "rfsim5g-mysql"
|
||||
image: mysql:8.0
|
||||
volumes:
|
||||
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
|
||||
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
- MYSQL_DATABASE=oai_db
|
||||
- MYSQL_USER=test
|
||||
- MYSQL_PASSWORD=test
|
||||
- MYSQL_ROOT_PASSWORD=linux
|
||||
healthcheck:
|
||||
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.131
|
||||
oai-amf:
|
||||
container_name: "rfsim5g-oai-amf"
|
||||
image: oaisoftwarealliance/oai-amf:ci-tmp-pr-372-26c14bcd
|
||||
environment:
|
||||
- TZ=Europe/paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.132
|
||||
oai-smf:
|
||||
container_name: "rfsim5g-oai-smf"
|
||||
image: oaisoftwarealliance/oai-smf:v2.1.9
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
|
||||
depends_on:
|
||||
- oai-amf
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.133
|
||||
oai-upf:
|
||||
container_name: "rfsim5g-oai-upf"
|
||||
image: oaisoftwarealliance/oai-upf:v2.1.9
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
volumes:
|
||||
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
|
||||
depends_on:
|
||||
- oai-smf
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_ADMIN
|
||||
cap_drop:
|
||||
- ALL
|
||||
privileged: true
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.134
|
||||
interface_name: eth0
|
||||
traffic_net:
|
||||
ipv4_address: 192.168.72.134
|
||||
interface_name: eth1
|
||||
oai-ext-dn:
|
||||
privileged: true
|
||||
container_name: rfsim5g-oai-ext-dn
|
||||
image: oaisoftwarealliance/trf-gen-cn5g:focal
|
||||
entrypoint: /bin/bash -c \
|
||||
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
|
||||
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
|
||||
depends_on:
|
||||
- oai-upf
|
||||
networks:
|
||||
traffic_net:
|
||||
ipv4_address: 192.168.72.135
|
||||
healthcheck:
|
||||
test: /bin/bash -c "ping -c 2 192.168.72.134"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-cucp-0:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-cucp-0
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --telnetsrv --telnetsrv.shrmod ci
|
||||
--telnetsrv.listenaddr 192.168.71.150
|
||||
--gNBs.[0].nr_cellid 0
|
||||
--log_config.global_log_options level,nocolor,time
|
||||
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
|
||||
depends_on:
|
||||
- oai-amf
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.150
|
||||
volumes:
|
||||
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
|
||||
- ../../conf_files/gnb-cucp.sa.e1-ho-n2.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-cuup-0:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-cuup-0
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.150
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.151
|
||||
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.151
|
||||
--gNBs.[0].local_s_address 192.168.71.151
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
depends_on:
|
||||
- oai-cucp-0
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.151
|
||||
volumes:
|
||||
- ../../conf_files/gnb-cuup.sa.f1.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-cuup"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
|
||||
oai-du-0:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-du-0
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --rfsim
|
||||
--log_config.global_log_options level,nocolor,time
|
||||
--gNBs.[0].nr_cellid 0
|
||||
--telnetsrv --telnetsrv.listenaddr 192.168.71.171
|
||||
--telnetsrv.shrmod ci
|
||||
--rfsimulator.serveraddr 192.168.71.190
|
||||
--gNBs.[0].servingCellConfigCommon.[0].ra_ResponseWindow 5
|
||||
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
|
||||
depends_on:
|
||||
- oai-cucp-0
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.171
|
||||
volumes:
|
||||
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
|
||||
- ../../conf_files/gnb-du.sa.band78.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-nr-ue:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-nr-ue
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_ADMIN # for interface bringup
|
||||
- NET_RAW # for ping
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --rfsim --rfsimulator.serveraddr server
|
||||
--band 78 -C 3619200000 -r 106 --numerology 1 --ssb 516
|
||||
--uicc0.imsi 208990100001100
|
||||
--log_config.global_log_options level,nocolor,time
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.190
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
volumes:
|
||||
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-uesoftmodem"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-cucp-1:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-cucp-1
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --telnetsrv --telnetsrv.shrmod ci
|
||||
--telnetsrv.listenaddr 192.168.71.180
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.180
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.181
|
||||
--gNBs.[0].local_s_address 192.168.71.180
|
||||
--gNBs.[0].remote_s_address 192.168.71.181
|
||||
--gNBs.[0].gNB_ID 0xb00
|
||||
--gNBs.[0].nr_cellid 1
|
||||
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NG_AMF 192.168.71.180
|
||||
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.180
|
||||
--log_config.global_log_options level,nocolor,time
|
||||
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.180
|
||||
volumes:
|
||||
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
|
||||
- ../../conf_files/gnb-cucp.sa.e1-ho-n2.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-cuup-1:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-cuup-1
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.180
|
||||
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.181
|
||||
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.181
|
||||
--gNBs.[0].local_s_address 192.168.71.181
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
depends_on:
|
||||
- oai-cucp-0
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.181
|
||||
volumes:
|
||||
- ../../conf_files/gnb-cuup.sa.f1.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-cuup"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
oai-du-1:
|
||||
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
|
||||
container_name: rfsim5g-oai-du-1
|
||||
cap_drop:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --rfsim
|
||||
--rfsimulator.serveraddr 192.168.71.190
|
||||
--log_config.global_log_options level,nocolor,time
|
||||
--gNBs.[0].gNB_DU_ID 1234
|
||||
--gNBs.[0].gNB_ID 0xb00
|
||||
--gNBs.[0].nr_cellid 1
|
||||
--gNBs.[0].servingCellConfigCommon.[0].physCellId 1
|
||||
--gNBs.[0].servingCellConfigCommon.[0].absoluteFrequencySSB 621312
|
||||
--gNBs.[0].servingCellConfigCommon.[0].dl_absoluteFrequencyPointA 620040
|
||||
--MACRLCs.[0].local_n_address 192.168.71.182
|
||||
--MACRLCs.[0].remote_n_address 192.168.71.180
|
||||
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
|
||||
depends_on:
|
||||
- oai-cucp-1
|
||||
networks:
|
||||
public_net:
|
||||
ipv4_address: 192.168.71.182
|
||||
volumes:
|
||||
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
|
||||
- ../../conf_files/gnb-du.sa.band78.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
healthcheck:
|
||||
test: /bin/bash -c "pgrep nr-softmodem"
|
||||
start_period: 10s
|
||||
start_interval: 500ms
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
networks:
|
||||
public_net:
|
||||
driver: bridge
|
||||
name: rfsim5g-oai-public-net
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 192.168.71.128/26
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: "rfsim5g-public"
|
||||
traffic_net:
|
||||
driver: bridge
|
||||
name: rfsim5g-oai-traffic-net
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 192.168.72.128/26
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: "rfsim5g-traffic"
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
ulimits:
|
||||
core: -1 # for core dumps
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --telnetsrv --telnetsrv.listenport 9090 --telnetsrv.shrmod ci --sa
|
||||
USE_ADDITIONAL_OPTIONS: --telnetsrv --telnetsrv.listenport 9090 --telnetsrv.shrmod ci
|
||||
--log_config.global_log_options level,nocolor,utc_time,line_num,function
|
||||
volumes:
|
||||
- ../../conf_files/gnb-cu.sa.f1.ho.conf:/opt/oai-gnb/etc/gnb.conf
|
||||
@@ -37,7 +37,7 @@ services:
|
||||
core: -1 # for core dumps
|
||||
environment:
|
||||
USE_B2XX: 'yes'
|
||||
USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=30AD30F --sa
|
||||
USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=30AD30F
|
||||
--telnetsrv --telnetsrv.listenport 9091 --telnetsrv.shrmod ci
|
||||
--log_config.global_log_options level,nocolor,utc_time,line_num,function
|
||||
devices:
|
||||
@@ -69,7 +69,7 @@ services:
|
||||
core: -1 # for core dumps
|
||||
environment:
|
||||
USE_B2XX: 'yes'
|
||||
USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=31F8010 --sa
|
||||
USE_ADDITIONAL_OPTIONS: --RUs.[0].sdr_addrs serial=31F8010
|
||||
--telnetsrv --telnetsrv.listenport 9091 --telnetsrv.shrmod ci
|
||||
--log_config.global_log_options level,nocolor,utc_time,line_num,function
|
||||
devices:
|
||||
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
container_name: oai-gnb
|
||||
environment:
|
||||
TZ: Europe/Paris
|
||||
USE_ADDITIONAL_OPTIONS: --sa --thread-pool 7,8,9,10,11,12
|
||||
USE_ADDITIONAL_OPTIONS: --thread-pool 7,8,9,10,11,12
|
||||
devices:
|
||||
- /dev/vfio:/dev/vfio/
|
||||
volumes:
|
||||
|
||||
@@ -88,6 +88,8 @@ static inline const char *rnti_types(nr_rnti_type_t rr)
|
||||
#define NR_NUMBER_OF_SYMBOLS_PER_SLOT_EXTENDED_CP 12
|
||||
#define NR_MAX_NB_LAYERS 4 // 8
|
||||
|
||||
#define BOUNDED_EVAL(a, b, c) (min(c, max(a, b)))
|
||||
|
||||
// Since the IQ samples are represented by SQ15 R+I (see https://en.wikipedia.org/wiki/Q_(number_format)) we need to compensate when
|
||||
// calcualting signal energy. Instead of shifting each sample right by 15, we can normalize the result in dB scale once its
|
||||
// calcualted. Signal energy is calculated using RMS^2, where each sample is squared before taking the average of the sum, therefore
|
||||
@@ -175,6 +177,17 @@ typedef struct {
|
||||
uint32_t step;
|
||||
} NR_timer_t;
|
||||
|
||||
typedef struct val_init {
|
||||
int val;
|
||||
bool init;
|
||||
} val_init_t;
|
||||
|
||||
typedef struct meas_s {
|
||||
uint16_t Nid_cell;
|
||||
val_init_t ss_rsrp_dBm;
|
||||
val_init_t csi_rsrp_dBm;
|
||||
} meas_t;
|
||||
|
||||
/**
|
||||
* @brief To start a timer
|
||||
* @param timer Timer to be started
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h"
|
||||
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
|
||||
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
|
||||
#include "openair2/RRC/NR/rrc_gNB_mobility.h"
|
||||
|
||||
#define TELNETSERVERCODE
|
||||
#include "telnetsrv.h"
|
||||
@@ -196,6 +197,53 @@ int rrc_gNB_trigger_f1_ho(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t rrc_ue_id);
|
||||
|
||||
/** @brief Trigger N2 handover for UE
|
||||
* @param buf: Neighbour PCI, SCell PCI, RRC UE ID
|
||||
* @param debug: Debug flag
|
||||
* @param prnt: Print function
|
||||
* @return 0 on success, -1 on failure */
|
||||
int rrc_gNB_trigger_n2_ho(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
{
|
||||
if (!RC.nrrrc)
|
||||
ERROR_MSG_RET("no RRC present, cannot list counts\n");
|
||||
|
||||
if (!buf) {
|
||||
ERROR_MSG_RET("Please provide neighbour cell id and ue id\n");
|
||||
} else {
|
||||
// Parse neighbour cell PCI
|
||||
char *token = strtok(buf, ",");
|
||||
if (!token) {
|
||||
ERROR_MSG_RET("Invalid input. Expected format: Neighbour PCI, ueId\n");
|
||||
}
|
||||
uint32_t neighbour_pci = strtol(token, NULL, 10);
|
||||
|
||||
// Parse ueId
|
||||
token = strtok(NULL, ",");
|
||||
if (!token) {
|
||||
ERROR_MSG_RET("Missing UE ID\n");
|
||||
}
|
||||
uint32_t ueId = strtol(token, NULL, 10);
|
||||
|
||||
// Retrieve UE context
|
||||
rrc_gNB_ue_context_t *ue_p = rrc_gNB_get_ue_context(RC.nrrrc[0], ueId);
|
||||
if (!ue_p) {
|
||||
ERROR_MSG_RET("UE with id %u not found\n", ueId);
|
||||
}
|
||||
gNB_RRC_UE_t *UE = &ue_p->ue_context;
|
||||
|
||||
// Trigger N2 handover
|
||||
nr_HO_N2_trigger_telnet(RC.nrrrc[0], neighbour_pci, UE->rrc_ue_id);
|
||||
|
||||
// Print success message
|
||||
prnt("RRC N2 handover triggered for UE %u with neighbour cell id %u\n",
|
||||
ueId,
|
||||
neighbour_pci);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int force_ul_failure(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
{
|
||||
if (!RC.nrmac)
|
||||
@@ -245,6 +293,7 @@ static telnetshell_cmddef_t cicmds[] = {
|
||||
{"trigger_f1_ho", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_f1_ho},
|
||||
{"fetch_du_by_ue_id", "[rrc_ue_id(int,opt)]", fetch_du_by_ue_id},
|
||||
{"get_current_bwp", "[rnti(hex,opt)]", get_current_bwp},
|
||||
{"trigger_n2_ho", "[neighbour_pci(uint32_t),ueId(uint32_t)]", rrc_gNB_trigger_n2_ho},
|
||||
{"", "", NULL},
|
||||
};
|
||||
|
||||
|
||||
+91
-2
@@ -210,7 +210,7 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
## Handover
|
||||
## Inter-DU Handover (F1)
|
||||
|
||||
The basic handover (HO) structure is as follows. In order to support various
|
||||
handover "message passing implementation" (F1AP, NGAP, XnAP), RRC employs
|
||||
@@ -264,6 +264,94 @@ sequenceDiagram
|
||||
sdu->>cucp: F1AP UE Context Release Complete
|
||||
Note over ue,tdu: UE active on target DU
|
||||
```
|
||||
## Inter-gNB Handover (N2)
|
||||
|
||||
This is an inter-NG-RAN procedure. The N2 handover specification is defined in the following documents:
|
||||
|
||||
* 3GPP TS 23.502, 4.9.1.3 Inter NG-RAN node N2 based handover:
|
||||
- Outlines detailed handover signaling flows for N2-based handovers.
|
||||
- Covers both intra-system (between 5G gNBs) and inter-system (between 5G and LTE eNBs) handovers.
|
||||
* 3GPP TS 38.300, section 9 Mobility and State Transitions:
|
||||
- describes mobility procedures at the NG-RAN level, depending on the RRC state.
|
||||
* 3GPP TS 38.413 (NGAP), section 8.4 UE Mobility Management Procedures:
|
||||
- Specifies the signaling procedures over the N2 interface.
|
||||
- Includes messages like Handover Request, Handover Command, and Handover Preparation.
|
||||
* 3GPP TS 38.331 (RRC): details the UE-level RRC procedures involved during handovers
|
||||
|
||||
### End-to-end flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ue as UE
|
||||
participant sdu as source DU
|
||||
participant scucp as source CU-CP
|
||||
participant scuup as source CU-UP
|
||||
participant tdu as target DU
|
||||
participant tcucp as target CU-CP
|
||||
participant tcuup as target CU-UP
|
||||
participant amf as AMF
|
||||
|
||||
Note over ue,sdu: UE active on source DU
|
||||
alt HO triggered through A3 event
|
||||
ue->>sdu: RRC Measurement Report
|
||||
sdu->>scucp: F1AP UL RRC Msg Transfer (RRC Measurement Report)
|
||||
Note over scucp: Handover decision (A3 event trigger)
|
||||
else Manual Trigger
|
||||
Note over scucp: Handover decision (e.g., telnet)
|
||||
end
|
||||
Note over scucp: nr_rrc_trigger_n2_ho() ("on source CU")
|
||||
scucp->>amf: HANDOVER REQUIRED
|
||||
amf->>tcucp: HANDOVER REQUEST
|
||||
Note over tcucp: rrc_gNB_process_Handover_Request
|
||||
Note over tcucp: trigger_bearer_setup
|
||||
tcucp->>tcuup: Bearer Context Setup Request
|
||||
tcuup->>tcucp: Bearer Context Setup Response
|
||||
Note over tcucp: rrc_gNB_process_e1_bearer_context_setup_resp
|
||||
Note over tcucp: nr_rrc_trigger_n2_ho_target() ("on target CU")
|
||||
Note over tcucp: nr_initiate_handover()
|
||||
tcucp->>tdu: F1AP UE Context Setup Req
|
||||
Note over tdu: Create UE context
|
||||
tdu->>tcucp: F1AP UE Context Setup Resp (incl. CellGroupConfig)
|
||||
Note over tcucp: rrc_CU_process_ue_context_setup_response() ("on target CU")
|
||||
Note over tcucp: e1_send_bearer_updates()
|
||||
tcucp->>tcuup: E1AP Bearer Context Modification Req
|
||||
tcuup->>tcucp: E1AP Bearer Context Modification Resp
|
||||
tcucp-->>tcucp: callback: ho_req_ack()
|
||||
Note over tcucp: nr_rrc_n2_ho_acknowledge() ("on target CU")
|
||||
tcucp->>amf: HANDOVER REQUEST ACKNOWLEDGE (data forwarding info)
|
||||
amf->>scucp: HANDOVER COMMAND
|
||||
scucp->>sdu: F1AP UE Context Modification Req (RRC Reconfiguration)
|
||||
sdu->>ue: RRC Reconfiguration
|
||||
sdu->>scucp: F1AP UE Context Modification Resp
|
||||
Note over sdu: Stop scheduling UE
|
||||
Note over scucp: rrc_CU_process_ue_context_modification_response()
|
||||
Note over scucp: e1_send_bearer_updates()
|
||||
scucp->>scuup: E1 Bearer Context Modification Req (PDCP Status Request)
|
||||
scuup->>scucp: E1 Bearer Context Modification Resp (PDCP Status Info)
|
||||
scucp->>amf: NG Uplink RAN Status Transfer
|
||||
amf->>tcucp: NG Downlink RAN Status Transfer
|
||||
tcucp->>tcuup: E1 Bearer Context Modification Req (PDCP Status Info)
|
||||
tcuup->>tcucp: E1 Bearer Context Modification Resp
|
||||
Note over ue: UE attachment to target DU
|
||||
Note over ue,tdu: RA (Msg1 + Msg2)
|
||||
ue->>tdu: RRC Reconfiguration Complete
|
||||
tdu->>tcucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
|
||||
tcucp-->>tcucp: callback: ho_success()
|
||||
Note over tcucp: nr_rrc_n2_ho_complete() ("on target CU")
|
||||
Note over tcucp: handle_rrcReconfigurationComplete() ("on target CU")
|
||||
tcucp->>amf: HANDOVER NOTIFY
|
||||
amf->>scucp: UE Context Release Command
|
||||
note over scucp: ngap_gNB_handle_ue_context_release_command
|
||||
note over scucp: rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND
|
||||
scucp->>scuup: E1 Bearer Context Release Command
|
||||
scuup->>scucp: E1 Bearer Context Release Complete
|
||||
Note over scucp: rrc_gNB_generate_RRCRelease
|
||||
scucp->>sdu: F1 UE Context Release Command
|
||||
sdu->>scucp: F1 UE Context Release Complete
|
||||
note over scucp: rrc_CU_process_ue_context_release_complete
|
||||
note over scucp: rrc_remove_ue
|
||||
Note over ue,tdu: UE active on target DU
|
||||
```
|
||||
|
||||
# Structures
|
||||
|
||||
@@ -308,7 +396,8 @@ corresponding CU.
|
||||
|
||||
`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
|
||||
handover cancel. `nr_ho_target_cu_t` contains function pointers `ho_req_ack`
|
||||
for handover request acknowledge and `ho_success` for handover success. As can
|
||||
for handover request acknowledge, `ho_success` for handover success,
|
||||
`ho_failure` for handover failure (N2 only).
|
||||
be seen in the sequence diagram above, either the "target CU" or "source CU"
|
||||
needs to do an operation, and a "switch" from target to source CU is done using
|
||||
these function pointers. For instance, in F1, the handover request acknowledge
|
||||
|
||||
+21
-1
@@ -99,6 +99,26 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
|
||||
|
||||
### Neighbor-gNB configuration
|
||||
|
||||
TBD
|
||||
#### What is a gNB neighbor?
|
||||
|
||||
Network continuity is a key aspect of 5G. In the 5G architecture, gNB neighbors play a central role in maintaining service continuity through mechanisms such as handover and load balancing. By definition, a gNB neighbor is another gNB that can be measured and linked by the UE. If the current serving gNB is no longer optimal, the UE may connect to a neighbor gNB.
|
||||
|
||||
To support this behavior, the network configuration specifies additional frequencies and cells that the UE should measure. The UE reports these measurements to the network, which then decides whether or not to initiate a handover.
|
||||
|
||||
Neighbor types include:
|
||||
- **Intra-gNB neighbors** - cells belonging to the same gNB
|
||||
- **Inter-gNB neighbors** - cells belonging to different gNBs
|
||||
- **Inter-RAT neighbors** - cells belonging to another RAT (e.g., LTE)
|
||||
|
||||
#### Required configuration parameters
|
||||
|
||||
To define a neighbor cell in the configuration file, the following parameters are typically needed:
|
||||
- **gNB ID** - unique identifier of the gNB
|
||||
- **Cell ID** - identifier of the cell within the gNB
|
||||
- **Physical Cell ID** - identifier of the cell’s synchronization signal (PCI)
|
||||
- **Absolute Frequency** - frequency used by the SSB (absoluteFrequencySSB)
|
||||
- **Subcarrier Spacing** - numerology (e.g., 15 kHz, 30 kHz)
|
||||
- **PLMN configuration** - MCC, MNC, and MNC length
|
||||
- **Tracking Area Code (TAC)** - identifier of the tracking area
|
||||
|
||||
Refer to the [handover tutorial](../handover-tutorial.md) for more information.
|
||||
|
||||
+2
-4
@@ -85,10 +85,8 @@ The standalone mode is the default mode.
|
||||
|
||||
Before tag `2024.w45`, the default mode was NSA. Thus, in the past, to run
|
||||
either the gNB or the UE in standalone mode, it was necessary to provide the
|
||||
`--sa` flag in the command line. This is not necessary anymore; if in doubt,
|
||||
you can provide `--sa` on the command line. The gNB will ignore this option if
|
||||
not relevant; the UE might complain that it does not know this option, which
|
||||
means that standalone is already active.
|
||||
`--sa` flag in the command line. This is not the case anymore. If provided
|
||||
the softmodem exits reporting that it does not know this option.
|
||||
|
||||
The default (SA) mode does the following:
|
||||
- At the gNB:
|
||||
|
||||
+10
-2
@@ -113,7 +113,7 @@ information on how the images are built.
|
||||
- [RAN-Ubuntu18-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu18-Image-Builder/)
|
||||
~BUILD-ONLY ~4G-LTE ~5G-NR ~nrUE
|
||||
- run formatting check from `ci-scripts/docker/Dockerfile.formatting.ubuntu`
|
||||
- obelix: Ubuntu 22 image build using docker (Note: builds U22 images while pipeline is named U18!)
|
||||
- obelix: Ubuntu image build using docker (Note: builds Ubuntu images of newer version while pipeline is named U18!)
|
||||
- base image from `Dockerfile.base.ubuntu`
|
||||
- build image from `Dockerfile.build.ubuntu`, followed by
|
||||
- target image from `Dockerfile.eNB.ubuntu`
|
||||
@@ -128,13 +128,21 @@ information on how the images are built.
|
||||
- build unit tests from `ci-scripts/docker/Dockerfile.unittest.ubuntu`, and run them
|
||||
- [RAN-Ubuntu-ARM-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu-ARM-Image-Builder/)
|
||||
~BUILD-ONLY ~4G-LTE ~5G-NR
|
||||
- gracehopper3-oai: ARM Ubuntu 22 image build using docker
|
||||
- gracehopper3-oai: ARM Ubuntu image build using docker
|
||||
- base image from `Dockerfile.base.ubuntu`
|
||||
- build image from `Dockerfile.build.ubuntu`, followed by
|
||||
- target image from `Dockerfile.gNB.ubuntu`
|
||||
- target image from `Dockerfile.nr-cuup.ubuntu`
|
||||
- target image from `Dockerfile.nrUE.ubuntu`
|
||||
- target image from `Dockerfile.gNB.aerial.ubuntu`
|
||||
- [RAN-Ubuntu-Jetson-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu-Jetson-Image-Builder/)
|
||||
~BUILD-ONLY ~4G-LTE ~5G-NR
|
||||
- jetson3-oai: ARMv8 Ubuntu image build using docker
|
||||
- base image from `Dockerfile.base.ubuntu`
|
||||
- build image from `Dockerfile.build.ubuntu`, followed by
|
||||
- target image from `Dockerfile.gNB.ubuntu`
|
||||
- target image from `Dockerfile.nr-cuup.ubuntu`
|
||||
- target image from `Dockerfile.nrUE.ubuntu`
|
||||
|
||||
#### Image Test pipelines
|
||||
|
||||
|
||||
@@ -207,6 +207,7 @@ neighbour_list = (
|
||||
physical_cellId = 1;
|
||||
absoluteFrequencySSB = 643296;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
band = 78;
|
||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
@@ -221,6 +222,7 @@ neighbour_list = (
|
||||
physical_cellId = 0;
|
||||
absoluteFrequencySSB = 643296;
|
||||
subcarrierSpacing = 1; #30 KHz
|
||||
band = 78;
|
||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
@@ -340,3 +342,147 @@ while true; do
|
||||
sleep 15
|
||||
done
|
||||
```
|
||||
|
||||
# N2 Handover
|
||||
|
||||
## Run the setup
|
||||
|
||||
An N2 handover involves the transfer of a UE from one gNB to another via the 5G core network. Unlike F1 handover, where the CU handles the process internally between its DUs, N2 handover requires signaling through the AMF, making it a core-network-based handover.
|
||||
|
||||
We assume:
|
||||
|
||||
* Two independent gNBs connected to the same 5GC via N2 interface.
|
||||
* A UE initially connected to gNB-PCI0, which will be handed over to gNB-PCI1.
|
||||
* Handover is triggered by either by decision based measurement event (e.g. A3) or telnet command.
|
||||
|
||||
## Steps to run N2 handover with OAI UE
|
||||
|
||||
1. Similarly to F1 handover, UE does not support any measurement reporting and handover is triggered by
|
||||
telnet command. Therefore, ensure that both gNBs and UE are built with telnet support:
|
||||
|
||||
```sh
|
||||
./build_oai --ninja --nrUE --gNB --build-lib telnetsrv
|
||||
```
|
||||
|
||||
Run the 5G Core Network if not already running. See [OAI CN5G tutorial](./NR_SA_Tutorial_OAI_CN5G.md).
|
||||
|
||||
2. Start the source gNB (gNB-PCI0) e.g.
|
||||
|
||||
```sh
|
||||
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.pci0.rfsim.conf --telnetsrv --telnetsrv.shrmod ci --gNBs.[0].min_rxtxtime 6 --rfsim --rfsimulator.serveraddr 127.0.0.1
|
||||
```
|
||||
|
||||
3. Start the UE e.g.
|
||||
|
||||
```sh
|
||||
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --uicc0.imsi 001010000000001 -O ../../../ci-scripts/conf_files/nrue.uicc.conf --rfsimulator.serveraddr server
|
||||
```
|
||||
|
||||
Ensure the UE successfully registers with the network.
|
||||
|
||||
4. Start the target gNB (gNB PCI1) e.g.
|
||||
|
||||
```sh
|
||||
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.pci1.rfsim.conf --sa --rfsim --telnetsrv --telnetsrv.shrmod ci --gNBs.[0].min_rxtxtime 6 --rfsimulator.serveraddr 127.0.0.1
|
||||
```
|
||||
|
||||
5. Trigger the N2 handover, e.g.
|
||||
|
||||
From gNB-PCI0, trigger handover on target gNB with PCI 1 for UE ID 1:
|
||||
|
||||
```sh
|
||||
echo ci trigger_n2_ho 1,1 | nc 127.0.0.1 9090 && echo
|
||||
```
|
||||
where the input parameters correspond to the PCI of the neighbor call and the RRC ID of the UE.
|
||||
|
||||
This will initiate the N2 handover on the source gNB.
|
||||
|
||||
## Neighbour list and measurement configuration
|
||||
|
||||
Make sure the configuration file contains a neighbour list and measurement configuration, e.g. [neighbour-config-rfsim.conf](../../ci-scripts/conf_files/neighbour-config.conf). This configuration can also be present in a different file and included in the gNB configuration file with `@include "neighbour-config-rfsim.conf"`.
|
||||
|
||||
For each gNB there is a `neighbour_cell_configuration` linked to its serving cell ID.
|
||||
|
||||
The measurement configuration is based on A2 and A3 measurement events in 5G NR. These events are used by the UE to report radio conditions to the gNB. The A2 Measurement Event indicates that the serving cell’s signal quality has degraded below a defined threshold and the UE shall initiate measurement of neighboring cells. The A3 Measurement Event indicates that a neighboring cell’s signal quality is better than that of the serving cell by a certain offset and the UE shall trigger handover to a stronger neighboring cell
|
||||
|
||||
This is an example with comments on how to use the configuration file:
|
||||
|
||||
```
|
||||
############################################################
|
||||
# gNB-to-gNB neighbour list + measurement configuration #
|
||||
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
|
||||
############################################################
|
||||
|
||||
neighbour_list = (
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 12345678L) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 12345678L; # Serving cell of gNB 0xe00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xb00;
|
||||
nr_cellid = 720898; # Cell served by gNB 0xb00
|
||||
physical_cellId = 1;
|
||||
absoluteFrequencySSB= 621312;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 720898) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 720898; # Serving cell of gNB 0xb00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xe00;
|
||||
nr_cellid = 12345678L; # Cell served by gNB 0xe00
|
||||
physical_cellId = 0;
|
||||
absoluteFrequencySSB= 641280;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
############################################################
|
||||
# Common NR measurement-event configuration #
|
||||
############################################################
|
||||
|
||||
nr_measurement_configuration = {
|
||||
Periodical = {
|
||||
enable = 1;
|
||||
includeBeamMeasurements = 1;
|
||||
maxNrofRS_IndexesToReport = 4;
|
||||
};
|
||||
|
||||
A2 = {
|
||||
enable = 1;
|
||||
threshold = 60;
|
||||
timeToTrigger = 1;
|
||||
};
|
||||
|
||||
A3 = (
|
||||
{
|
||||
cell_id = 720898; # neighbour of gNB 0xe00
|
||||
offset = 10;
|
||||
hysteresis = 0;
|
||||
timeToTrigger = 1;
|
||||
},
|
||||
{
|
||||
cell_id = 12345678; # neighbour of gNB 0xb00
|
||||
offset = 5;
|
||||
hysteresis = 1;
|
||||
timeToTrigger = 2;
|
||||
}
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
@@ -713,6 +713,11 @@ int main( int argc, char **argv ) {
|
||||
|
||||
// wait for end of program
|
||||
printf("TYPE <CTRL-C> TO TERMINATE\n");
|
||||
// Sleep a while before checking all parameters have been used
|
||||
// Some are used directly in external threads, asynchronously
|
||||
sleep(2);
|
||||
config_check_unknown_cmdlineopt(uniqCfg, CONFIG_CHECKALLSECTIONS);
|
||||
|
||||
itti_wait_tasks_end(NULL);
|
||||
printf("Returned from ITTI signal handler\n");
|
||||
|
||||
|
||||
@@ -1866,22 +1866,23 @@ uint8_t unpack_nr_stop_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void
|
||||
uint8_t pack_nr_error_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p4_p5_codec_config_t *config)
|
||||
{
|
||||
nfapi_nr_error_indication_scf_t *pNfapiMsg = (nfapi_nr_error_indication_scf_t *)msg;
|
||||
uint8_t retval = push16(pNfapiMsg->sfn, ppWritePackedMsg, end) != 0;
|
||||
retval &= push16(pNfapiMsg->slot, ppWritePackedMsg, end) != 0;
|
||||
retval &= push8(pNfapiMsg->message_id, ppWritePackedMsg, end);
|
||||
retval &= push8(pNfapiMsg->error_code, ppWritePackedMsg, end);
|
||||
|
||||
retval &= pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config);
|
||||
return retval;
|
||||
if (push16(pNfapiMsg->sfn, ppWritePackedMsg, end) && push16(pNfapiMsg->slot, ppWritePackedMsg, end)
|
||||
&& push8(pNfapiMsg->message_id, ppWritePackedMsg, end) && push8(pNfapiMsg->error_code, ppWritePackedMsg, end)
|
||||
&& pack_vendor_extension_tlv(pNfapiMsg->vendor_extension, ppWritePackedMsg, end, config)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t unpack_nr_error_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p4_p5_codec_config_t *config)
|
||||
{
|
||||
nfapi_nr_error_indication_scf_t *pNfapiMsg = (nfapi_nr_error_indication_scf_t *)msg;
|
||||
uint8_t retval = pull16(ppReadPackedMsg, &pNfapiMsg->sfn, end);
|
||||
retval &= pull16(ppReadPackedMsg, &pNfapiMsg->slot, end);
|
||||
retval &= pull8(ppReadPackedMsg, &pNfapiMsg->message_id, end);
|
||||
retval &= pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end);
|
||||
retval &= unpack_nr_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension));
|
||||
return retval;
|
||||
if (pull16(ppReadPackedMsg, &pNfapiMsg->sfn, end) && pull16(ppReadPackedMsg, &pNfapiMsg->slot, end)
|
||||
&& pull8(ppReadPackedMsg, &pNfapiMsg->message_id, end) && pull8(ppReadPackedMsg, &pNfapiMsg->error_code, end)
|
||||
&& unpack_nr_tlv_list(NULL, 0, ppReadPackedMsg, end, config, &(pNfapiMsg->vendor_extension))) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#define FAPI_NR_RX_PDU_TYPE_DLSCH 0x03
|
||||
#define FAPI_NR_DCI_IND 0x04
|
||||
#define FAPI_NR_RX_PDU_TYPE_RAR 0x05
|
||||
#define FAPI_NR_CSIRS_IND 0x06
|
||||
#define FAPI_NR_MEAS_IND 0x06
|
||||
|
||||
#define FAPI_NR_SIBS_MASK_SIB1 0x1
|
||||
|
||||
|
||||
@@ -49,16 +49,23 @@ typedef enum {
|
||||
NFAPI_NR_FORMAT_0_1_AND_1_1,
|
||||
} nfapi_nr_dci_formats_e;
|
||||
|
||||
typedef enum {
|
||||
NFAPI_NR_CSI_MEAS,
|
||||
NFAPI_NR_SS_MEAS
|
||||
} nfapi_nr_meas_type_e;
|
||||
|
||||
typedef struct {
|
||||
uint32_t rsrp;
|
||||
int rsrp_dBm;
|
||||
uint32_t gNB_index;
|
||||
uint16_t Nid_cell;
|
||||
nfapi_nr_meas_type_e meas_type;
|
||||
bool is_neighboring_cell;
|
||||
uint8_t rsrp_dBm;
|
||||
uint8_t rank_indicator;
|
||||
uint16_t i1;
|
||||
uint8_t i2;
|
||||
uint8_t cqi;
|
||||
rlm_t radiolink_monitoring;
|
||||
} fapi_nr_csirs_measurements_t;
|
||||
} fapi_nr_l1_measurements_t;
|
||||
|
||||
typedef struct {
|
||||
/// frequency_domain_resource;
|
||||
@@ -145,7 +152,7 @@ typedef struct {
|
||||
fapi_nr_pdsch_pdu_t pdsch_pdu;
|
||||
fapi_nr_ssb_pdu_t ssb_pdu;
|
||||
fapi_nr_sib_pdu_t sib_pdu;
|
||||
fapi_nr_csirs_measurements_t csirs_measurements;
|
||||
fapi_nr_l1_measurements_t l1_measurements;
|
||||
};
|
||||
} fapi_nr_rx_indication_body_t;
|
||||
|
||||
|
||||
@@ -707,18 +707,41 @@ typedef enum {
|
||||
NFAPI_NR_STOP_MSG_INVALID_STATE
|
||||
} nfapi_nr_stop_errors_e;
|
||||
|
||||
//3.3.5 PHY Notifications
|
||||
// 3.3.5 PHY Notifications
|
||||
|
||||
#define SCF_ERROR_LIST \
|
||||
X(NFAPI_NR_PHY_API_MSG_OK, 0x0) \
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_STATE, 0x1) \
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_CONFIG, 0x2) \
|
||||
X(NFAPI_NR_PHY_API_SFN_OUT_OF_SYNC, 0X3) \
|
||||
X(NFAPI_NR_PHY_API_MSG_SLOR_ERR, 0X4) \
|
||||
X(NFAPI_NR_PHY_API_MSG_BCH_MISSING, 0X5) \
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_SFN, 0X6) \
|
||||
X(NFAPI_NR_PHY_API_MSG_UL_DCI_ERR, 0X7) \
|
||||
X(NFAPI_NR_PHY_API_MSG_TX_ERR, 0X8)
|
||||
|
||||
#ifdef ENABLE_AERIAL
|
||||
#define AERIAL_ERROR_LIST \
|
||||
X(AERIAL_ERROR_CODE_FAPI_END, 0x32) \
|
||||
X(AERIAL_ERROR_CODE_L1_PROC_OBJ_UNAVAILABLE_ERR, 0x33) \
|
||||
X(AERIAL_ERROR_CODE_MSG_LATE_SLOT_ERR, 0x34) \
|
||||
X(AERIAL_ERROR_CODE_PARTIAL_SRS_IND_ERR, 0x35) \
|
||||
X(AERIAL_ERROR_CODE_L1_DL_CPLANE_TX_ERROR, 0x36) \
|
||||
X(AERIAL_ERROR_CODE_L1_UL_CPLANE_TX_ERROR, 0x37) \
|
||||
X(AERIAL_ERROR_CODE_L1_DL_GPU_ERROR, 0x38) \
|
||||
X(AERIAL_ERROR_CODE_L1_DL_CPU_TASK_ERROR, 0x39) \
|
||||
X(AERIAL_ERROR_CODE_L1_UL_CPU_TASK_ERROR, 0x3A) \
|
||||
X(AERIAL_ERROR_CODE_L1_P1_EXIT_ERROR, 0x3B) \
|
||||
X(AERIAL_ERROR_CODE_L1_P2_EXIT_ERROR, 0x3C) \
|
||||
X(AERIAL_ERROR_CODE_L1_DL_CH_ERROR, 0x3D) \
|
||||
X(AERIAL_ERROR_CODE_L1_UL_CH_ERROR, 0x3E)
|
||||
#else
|
||||
#define AERIAL_ERROR_LIST
|
||||
#endif
|
||||
|
||||
#define NFAPI_PHY_ERROR_LIST \
|
||||
X(NFAPI_NR_PHY_API_MSG_OK ,0x0)\
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_STATE ,0x1)\
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_CONFIG ,0x2)\
|
||||
X(NFAPI_NR_PHY_API_SFN_OUT_OF_SYNC ,0X3)\
|
||||
X(NFAPI_NR_PHY_API_MSG_SLOR_ERR ,0X4)\
|
||||
X(NFAPI_NR_PHY_API_MSG_BCH_MISSING ,0X5)\
|
||||
X(NFAPI_NR_PHY_API_MSG_INVALID_SFN ,0X6)\
|
||||
X(NFAPI_NR_PHY_API_MSG_UL_DCI_ERR ,0X7)\
|
||||
X(NFAPI_NR_PHY_API_MSG_TX_ERR ,0X8)
|
||||
SCF_ERROR_LIST \
|
||||
AERIAL_ERROR_LIST
|
||||
|
||||
typedef enum {
|
||||
#define X(name, value) name = value,
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "PHY/phy_extern_nr_ue.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "PHY/sse_intrin.h"
|
||||
#include "SCHED_NR_UE/defs.h"
|
||||
|
||||
#define K1 ((long long int) 512)
|
||||
#define K2 ((long long int) (1024-K1))
|
||||
@@ -254,6 +255,24 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
|
||||
ue->measurements.ssb_rsrp_dBm[ssb_index],
|
||||
rsrp_db_per_re,
|
||||
ue->measurements.ssb_sinr_dB[ssb_index]);
|
||||
|
||||
// Send SS measurements to MAC
|
||||
if (!ue->if_inst || !ue->if_inst->dl_indication)
|
||||
return;
|
||||
|
||||
fapi_nr_l1_measurements_t l1_measurements = {
|
||||
.gNB_index = proc->gNB_id,
|
||||
.meas_type = NFAPI_NR_SS_MEAS,
|
||||
.Nid_cell = ue->frame_parms.Nid_cell,
|
||||
.is_neighboring_cell = false,
|
||||
};
|
||||
int ssb_rsrp_dBm = ue->measurements.ssb_rsrp_dBm[ssb_index];
|
||||
l1_measurements.rsrp_dBm = BOUNDED_EVAL(16, ssb_rsrp_dBm + 157, 113); // TS 38.133 - Table 10.1.6.1-1
|
||||
nr_downlink_indication_t dl_indication = {0};
|
||||
fapi_nr_rx_indication_t rx_ind = {0};
|
||||
nr_fill_dl_indication(&dl_indication, NULL, &rx_ind, proc, ue, NULL);
|
||||
nr_fill_rx_indication(&rx_ind, FAPI_NR_MEAS_IND, ue, NULL, NULL, 1, proc, &l1_measurements, NULL);
|
||||
ue->if_inst->dl_indication(&dl_indication);
|
||||
}
|
||||
|
||||
// This function computes the received noise power
|
||||
|
||||
@@ -979,18 +979,24 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
|
||||
}
|
||||
|
||||
// Send CSI measurements to MAC
|
||||
fapi_nr_csirs_measurements_t csirs_measurements;
|
||||
csirs_measurements.rsrp = rsrp;
|
||||
csirs_measurements.rsrp_dBm = rsrp_dBm;
|
||||
csirs_measurements.rank_indicator = rank_indicator;
|
||||
csirs_measurements.i1 = *i1;
|
||||
csirs_measurements.i2 = *i2;
|
||||
csirs_measurements.cqi = cqi;
|
||||
csirs_measurements.radiolink_monitoring = RLM_no_monitoring; // TODO do be activated in case of RLM based on CSI-RS
|
||||
if (!ue->if_inst || !ue->if_inst->dl_indication)
|
||||
return;
|
||||
|
||||
fapi_nr_l1_measurements_t l1_measurements = {
|
||||
.gNB_index = proc->gNB_id,
|
||||
.meas_type = NFAPI_NR_CSI_MEAS,
|
||||
.Nid_cell = frame_parms->Nid_cell,
|
||||
.is_neighboring_cell = false,
|
||||
.rsrp_dBm = BOUNDED_EVAL(16, rsrp_dBm + 157, 113), // TS 38.133 - Table 10.1.6.1-1
|
||||
.rank_indicator = rank_indicator,
|
||||
.i1 = *i1,
|
||||
.i2 = *i2,
|
||||
.cqi = cqi,
|
||||
.radiolink_monitoring = RLM_no_monitoring, // TODO do be activated in case of RLM based on CSI-RS
|
||||
};
|
||||
nr_downlink_indication_t dl_indication;
|
||||
fapi_nr_rx_indication_t rx_ind = {0};
|
||||
nr_fill_dl_indication(&dl_indication, NULL, &rx_ind, proc, ue, NULL);
|
||||
nr_fill_rx_indication(&rx_ind, FAPI_NR_CSIRS_IND, ue, NULL, NULL, 1, proc, (void *)&csirs_measurements, NULL);
|
||||
if (ue->if_inst && ue->if_inst->dl_indication)
|
||||
ue->if_inst->dl_indication(&dl_indication);
|
||||
nr_fill_rx_indication(&rx_ind, FAPI_NR_MEAS_IND, ue, NULL, NULL, 1, proc, (void *)&l1_measurements, NULL);
|
||||
ue->if_inst->dl_indication(&dl_indication);
|
||||
}
|
||||
|
||||
@@ -186,8 +186,8 @@ void nr_fill_rx_indication(fapi_nr_rx_indication_t *rx_ind,
|
||||
rx->ssb_pdu.decoded_pdu = false;
|
||||
}
|
||||
} break;
|
||||
case FAPI_NR_CSIRS_IND:
|
||||
memcpy(&rx->csirs_measurements, typeSpecific, sizeof(fapi_nr_csirs_measurements_t));
|
||||
case FAPI_NR_MEAS_IND:
|
||||
memcpy(&rx->l1_measurements, typeSpecific, sizeof(fapi_nr_l1_measurements_t));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -178,3 +178,12 @@ void *rrc_nrue(void *notUsed)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void nr_mac_rrc_meas_ind_ue(module_id_t module_id,
|
||||
uint32_t gNB_index,
|
||||
uint16_t Nid_cell,
|
||||
bool csi_meas,
|
||||
bool is_neighboring_cell,
|
||||
uint8_t rsrp_dBm)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
define_property(TEST PROPERTY TEST_DESCRIPTION BRIEF_DOCS "A human-readable description of this test")
|
||||
define_property(TEST PROPERTY TEST_DESCRIPTION
|
||||
BRIEF_DOCS "A human-readable description of this test"
|
||||
FULL_DOCS "A human-readable description of this test")
|
||||
|
||||
# Define a macro to add tests for different physical simulators
|
||||
macro(add_physim_test test_name test_description test_exec)
|
||||
@@ -23,7 +25,9 @@ macro(add_physim_test test_name test_description test_exec)
|
||||
add_dependencies(tests ${test_exec})
|
||||
endmacro()
|
||||
|
||||
define_property(TEST PROPERTY CHECK_COUNT BRIEF_DOCS "helper property to enumerate checks in environment")
|
||||
define_property(TEST PROPERTY CHECK_COUNT
|
||||
BRIEF_DOCS "helper property to enumerate checks in environment"
|
||||
FULL_DOCS "the property counts the number of threshold checks, used to enumerate environment variables given to analyze-timing.sh")
|
||||
function(add_timed_physim_test test_name test_description test_exec)
|
||||
# catch all the arguments past the last expected arqument and store them in the options_list
|
||||
if (NOT TARGET ${test_exec})
|
||||
|
||||
@@ -283,6 +283,22 @@ typedef struct bearer_context_pdcp_config_s {
|
||||
bool pDCP_Reestablishment;
|
||||
} bearer_context_pdcp_config_t;
|
||||
|
||||
/* 9.3.1.35 3GPP TS 38.463 */
|
||||
typedef struct {
|
||||
// PDCP SN
|
||||
uint32_t sn;
|
||||
// HFN
|
||||
uint32_t hfn;
|
||||
} e1_pdcp_count_t;
|
||||
|
||||
/* 9.3.1.58 3GPP TS 38.463 */
|
||||
typedef struct {
|
||||
// UL Count Value
|
||||
e1_pdcp_count_t ul_count;
|
||||
// DL Count Value
|
||||
e1_pdcp_count_t dl_count;
|
||||
} e1_pdcp_status_info_t;
|
||||
|
||||
typedef struct drb_to_setup_s {
|
||||
long drbId;
|
||||
bearer_context_pdcp_config_t pdcp_config;
|
||||
@@ -384,6 +400,8 @@ typedef struct DRB_nGRAN_to_modify_s {
|
||||
bearer_context_pdcp_config_t *pdcp_config;
|
||||
// PDCP SN Status Request (O)
|
||||
bool pdcp_sn_status_requested;
|
||||
// PDCP SN Status Information
|
||||
e1_pdcp_status_info_t *pdcp_status;
|
||||
// DL UP Transport Layer Information (O) (clause 9.3.1.13, 9.3.2.1)
|
||||
int numDlUpParam;
|
||||
up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM];
|
||||
@@ -561,9 +579,8 @@ typedef struct DRB_nGRAN_modified_s {
|
||||
// Flow Failed List (O)
|
||||
int numQosFlowFailed;
|
||||
qos_flow_failed_t qosFlowsFailed[E1AP_MAX_NUM_QOS_FLOWS];
|
||||
// Old QoS Flow List (O)
|
||||
int numOldQosFlow;
|
||||
qos_flow_list_t oldQosFlows[E1AP_MAX_NUM_QOS_FLOWS];
|
||||
// PDCP SN Status Information (0)
|
||||
e1_pdcp_status_info_t *pdcp_status;
|
||||
} DRB_nGRAN_modified_t;
|
||||
|
||||
typedef struct DRB_nGRAN_failed_s {
|
||||
|
||||
@@ -62,3 +62,7 @@ MESSAGE_DEF(NR_RRC_MAC_SBCCH_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacSBc
|
||||
|
||||
// Message to GNB_APP to update SIB19 satellite position information
|
||||
MESSAGE_DEF(GNB_SAT_POSITION_UPDATE, MESSAGE_PRIORITY_MED, gnb_sat_position_update_t, gnb_sat_position_update)
|
||||
|
||||
// nrUE
|
||||
MESSAGE_DEF(NR_RRC_MAC_MEAS_DATA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacMeasDataInd, nr_rrc_mac_meas_data_ind)
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#define NR_RRC_MAC_BCCH_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_bcch_data_ind
|
||||
#define NR_RRC_MAC_SBCCH_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_sbcch_data_ind
|
||||
|
||||
#define NR_RRC_MAC_MEAS_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_meas_data_ind
|
||||
|
||||
#define RRC_MAC_BCCH_MBMS_DATA_REQ(mSGpTR) (mSGpTR)->ittiMsg.rrc_mac_bcch_mbms_data_req
|
||||
#define RRC_MAC_BCCH_MBMS_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.rrc_mac_bcch_mbms_data_ind
|
||||
|
||||
@@ -142,6 +144,14 @@ typedef struct NRRrcMacSBcchDataInd_s {
|
||||
uint8_t rsrp;
|
||||
} NRRrcMacSBcchDataInd;
|
||||
|
||||
typedef struct NRRrcMacMeasDataInd_s {
|
||||
uint8_t gnb_index;
|
||||
uint8_t rsrp_dBm;
|
||||
uint16_t Nid_cell;
|
||||
bool is_csi_meas;
|
||||
bool is_neighboring_cell;
|
||||
} NRRrcMacMeasDataInd;
|
||||
|
||||
typedef struct RrcMacBcchMbmsDataReq_s {
|
||||
uint32_t frame;
|
||||
uint32_t sdu_size;
|
||||
|
||||
@@ -71,6 +71,13 @@ MESSAGE_DEF(NGAP_UE_CTXT_MODIFICATION_FAIL , MESSAGE_PRIORITY_MED, ngap_ue_ctxt_
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_SETUP_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_setup_resp_t , ngap_pdusession_setup_resp)
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_resp_t , ngap_pdusession_modify_resp)
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_RESPONSE , MESSAGE_PRIORITY_MED, ngap_pdusession_release_resp_t , ngap_pdusession_release_resp)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_REQUIRED, MESSAGE_PRIORITY_MED, ngap_handover_required_t, ngap_handover_required)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_FAILURE, MESSAGE_PRIORITY_MED, ngap_handover_failure_t, ngap_handover_failure)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_REQUEST_ACKNOWLEDGE, MESSAGE_PRIORITY_MED, ngap_handover_request_ack_t, ngap_handover_request_ack)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_NOTIFY, MESSAGE_PRIORITY_MED, ngap_handover_notify_t, ngap_handover_notify)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, ngap_handover_cancel)
|
||||
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
|
||||
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
|
||||
|
||||
/* NGAP -> RRC messages */
|
||||
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )
|
||||
@@ -81,6 +88,9 @@ MESSAGE_DEF(NGAP_PDUSESSION_SETUP_REQ , MESSAGE_PRIORITY_MED, ngap_pd
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_REQ , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_req_t , ngap_pdusession_modify_req )
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_COMMAND , MESSAGE_PRIORITY_MED, ngap_pdusession_release_command_t , ngap_pdusession_release_command)
|
||||
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_COMMAND, MESSAGE_PRIORITY_MED, ngap_ue_release_command_t , ngap_ue_release_command)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_REQUEST, MESSAGE_PRIORITY_MED, ngap_handover_request_t, ngap_handover_request)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_COMMAND, MESSAGE_PRIORITY_MED, ngap_handover_command_t, ngap_handover_command)
|
||||
MESSAGE_DEF(NGAP_HANDOVER_CANCEL_ACK, MESSAGE_PRIORITY_MED, ngap_handover_cancel_ack_t, ngap_handover_cancel_ack)
|
||||
|
||||
/* NGAP <-> RRC messages (can be initiated either by MME or gNB) */
|
||||
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_REQ , MESSAGE_PRIORITY_MED, ngap_ue_release_req_t , ngap_ue_release_req)
|
||||
|
||||
@@ -65,11 +65,22 @@
|
||||
#define NGAP_PDUSESSION_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_setup_req
|
||||
#define NGAP_PDUSESSION_MODIFY_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_modify_req
|
||||
#define NGAP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.ngap_paging_ind
|
||||
#define NGAP_HANDOVER_REQUIRED(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_required
|
||||
#define NGAP_HANDOVER_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_failure
|
||||
#define NGAP_HANDOVER_REQUEST(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request
|
||||
#define NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request_ack
|
||||
#define NGAP_HANDOVER_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_command
|
||||
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
|
||||
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
|
||||
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack
|
||||
|
||||
#define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req
|
||||
#define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command
|
||||
#define NGAP_PDUSESSION_RELEASE_RESPONSE(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_resp
|
||||
|
||||
#define NGAP_UL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_ul_ran_status_transfer
|
||||
#define NGAP_DL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_dl_ran_status_transfer
|
||||
|
||||
//-------------------------------------------------------------------------------------------//
|
||||
|
||||
/* Length of the transport layer address string
|
||||
@@ -227,6 +238,15 @@ typedef struct ngap_mobility_restriction_s{
|
||||
plmn_id_t serving_plmn;
|
||||
}ngap_mobility_restriction_t;
|
||||
|
||||
/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
|
||||
typedef struct {
|
||||
uint8_t nb_qos;
|
||||
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
|
||||
pdu_session_type_t pdu_session_type;
|
||||
// UPF endpoint of the NG-U (N3) transport bearer
|
||||
gtpu_tunnel_t n3_incoming;
|
||||
} pdusession_transfer_t;
|
||||
|
||||
typedef enum pdusession_qosflow_mapping_ind_e{
|
||||
QOSFLOW_MAPPING_INDICATION_UL = 0,
|
||||
QOSFLOW_MAPPING_INDICATION_DL = 1,
|
||||
@@ -463,6 +483,244 @@ typedef struct ngap_uplink_nas_s {
|
||||
byte_array_t nas_pdu;
|
||||
} ngap_uplink_nas_t;
|
||||
|
||||
typedef struct target_cell_id_s {
|
||||
plmn_id_t plmn_identity;
|
||||
uint32_t nrCellIdentity;
|
||||
} cell_id_t;
|
||||
|
||||
typedef struct target_ran_node_id_s {
|
||||
uint32_t targetgNBId;
|
||||
plmn_id_t plmn_identity;
|
||||
uint32_t tac;
|
||||
} target_ran_node_id_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// QoS Flow Identifier
|
||||
uint8_t qfi;
|
||||
} qosflow_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// PDU Session ID
|
||||
uint8_t pdusession_id;
|
||||
// QoS Flow Information List
|
||||
uint8_t nb_of_qos_flow;
|
||||
qosflow_info_t qos_flow_info[QOSFLOW_MAX_VALUE];
|
||||
} pdusession_resource_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.97 */
|
||||
typedef struct {
|
||||
// Global Cell ID
|
||||
cell_id_t id;
|
||||
// Cell type
|
||||
uint8_t type;
|
||||
// Time UE Stayed in Cell
|
||||
long time_in_cell;
|
||||
// Cause
|
||||
ngap_cause_t *cause;
|
||||
} last_visited_ngran_cell_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// RRC Container: HandoverPreparationInformation message
|
||||
byte_array_t handoverInfo;
|
||||
// Target Cell ID
|
||||
cell_id_t targetCellId;
|
||||
// PDU Session Resource Information List
|
||||
uint16_t nb_pdu_session_resource;
|
||||
pdusession_resource_info_t pdu_session_resource[NGAP_MAX_PDU_SESSION];
|
||||
// UE History Information
|
||||
last_visited_ngran_cell_info_t ue_history_info;
|
||||
} source_to_target_transparent_container_t;
|
||||
|
||||
typedef enum {
|
||||
HANDOVER_TYPE_INTRA5GS, // Intra5GS: NG-RAN node to NG-RAN node
|
||||
HANDOVER_TYPE_5GSTOEPS, // 5GStoEPS: NG-RAN node to eNB
|
||||
HANDOVER_TYPE_EPSTO5GS, // EPSto5GS: eNB to NG-RAN node
|
||||
HANDOVER_TYPE_5GSTOUTRA, // 5GStoUTRA: NG-RAN node to UTRA
|
||||
} ho_type_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.1 */
|
||||
typedef struct {
|
||||
// PDU Session ID
|
||||
uint8_t pdusession_id;
|
||||
// Handover Required Transfer
|
||||
byte_array_t ho_required_transfer;
|
||||
} pdusession_resource_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.1 */
|
||||
typedef struct {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Handover Type
|
||||
ho_type_t handoverType;
|
||||
// Cause
|
||||
ngap_cause_t cause;
|
||||
// Target ID
|
||||
target_ran_node_id_t target_gnb_id;
|
||||
// PDU Session Resource List
|
||||
uint8_t nb_of_pdusessions;
|
||||
pdusession_resource_t pdusessions[NGAP_MAX_PDU_SESSION];
|
||||
// Source to Target Transparent Container
|
||||
source_to_target_transparent_container_t *source2target;
|
||||
} ngap_handover_required_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.6 */
|
||||
typedef struct {
|
||||
// AMF UE NGAP ID (M)
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Cause (M)
|
||||
ngap_cause_t cause;
|
||||
} ngap_handover_failure_t;
|
||||
|
||||
typedef struct {
|
||||
// Next-Hop NH
|
||||
uint8_t next_hop[SECURITY_KEY_LENGTH];
|
||||
// Next Hop Chaining Count
|
||||
uint8_t next_hop_chain_count;
|
||||
} ngap_security_context_t;
|
||||
|
||||
/* Handover Request (3GPP TS 38.413 9.2.3.4)
|
||||
PDU Session Resource Setup Item */
|
||||
typedef struct {
|
||||
// PDU Session ID
|
||||
uint8_t pdusession_id;
|
||||
pdu_session_type_t pdu_session_type;
|
||||
// S-NSSAI
|
||||
nssai_t nssai;
|
||||
// Handover Required Transfer
|
||||
pdusession_transfer_t pdusessionTransfer;
|
||||
} ho_request_pdusession_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.4 */
|
||||
typedef struct {
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Handover Type
|
||||
ho_type_t ho_type;
|
||||
// Cause
|
||||
ngap_cause_t cause;
|
||||
// UE Aggregate Maximum Bit Rate
|
||||
ngap_ambr_t ue_ambr;
|
||||
// UE Security Capabilities
|
||||
ngap_security_capabilities_t security_capabilities;
|
||||
// Security Context
|
||||
ngap_security_context_t security_context;
|
||||
// PDU Session Resource Setup List
|
||||
uint8_t nb_of_pdusessions;
|
||||
ho_request_pdusession_t pduSessionResourceSetupList[NGAP_MAX_PDU_SESSION];
|
||||
// Allowed NSSAI
|
||||
uint8_t nb_allowed_nssais;
|
||||
nssai_t allowed_nssai[8];
|
||||
// Source to Target Transparent Container contents
|
||||
uint64_t nr_cell_id;
|
||||
byte_array_t ue_ho_prep_info;
|
||||
byte_array_t ue_cap;
|
||||
// Mobility Restriction List
|
||||
ngap_mobility_restriction_t *mobility_restriction;
|
||||
// GUAMI
|
||||
nr_guami_t guami;
|
||||
} ngap_handover_request_t;
|
||||
|
||||
/* 9.3.4.11 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// QoS Flow Setup Response List
|
||||
uint8_t nb_of_qos_flow;
|
||||
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
|
||||
// DL NG-U UP TNL Information
|
||||
uint32_t gtp_teid;
|
||||
transport_layer_addr_t gNB_addr;
|
||||
} ho_request_ack_transfer_t;
|
||||
|
||||
/* 9.2.3.5 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// PDU Session ID (M)
|
||||
uint8_t pdu_session_id;
|
||||
// Handover Request Acknowledge Transfer (M)
|
||||
ho_request_ack_transfer_t ack_transfer;
|
||||
} pdu_session_resource_admitted_t;
|
||||
|
||||
typedef struct {
|
||||
// AMF UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// RAN UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// PDU Session Resource Admitted List
|
||||
pdu_session_resource_admitted_t pdusessions[NGAP_MAX_PDU_SESSION];
|
||||
uint8_t nb_of_pdusessions;
|
||||
// Target to Source Transparent Container
|
||||
byte_array_t target2source;
|
||||
} ngap_handover_request_ack_t;
|
||||
|
||||
/* 9.3.4.10 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// QoS Flow to be Forwarded List
|
||||
uint8_t nb_of_qos_flow;
|
||||
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
|
||||
// UL Forwarding UP TNL Information
|
||||
uint32_t gtp_teid;
|
||||
transport_layer_addr_t gNB_addr;
|
||||
} ho_command_transfer_t;
|
||||
|
||||
typedef struct {
|
||||
// PDU Session ID (M)
|
||||
uint8_t pdusession_id;
|
||||
// Handover Command Transfer (M)
|
||||
ho_command_transfer_t ho_command_transfer;
|
||||
} pdusession_resource_handover_t;
|
||||
|
||||
typedef struct {
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// Handover Type
|
||||
ho_type_t handoverType;
|
||||
// PDU Session Resource Handover List
|
||||
uint8_t nb_of_pdusessions;
|
||||
pdusession_resource_handover_t pdu_sessions[NGAP_MAX_PDU_SESSION];
|
||||
// Target to Source Transparent Container
|
||||
byte_array_t handoverCommand;
|
||||
} ngap_handover_command_t;
|
||||
|
||||
/* 9.3.1.16 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// NR user location information
|
||||
target_ran_node_id_t target_ng_ran;
|
||||
uint32_t nrCellIdentity;
|
||||
} user_location_information_t;
|
||||
|
||||
/* 9.2.3.7 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// User Location Information
|
||||
user_location_information_t user_info;
|
||||
} ngap_handover_notify_t;
|
||||
|
||||
/* 9.2.3.11 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Cause
|
||||
ngap_cause_t cause;
|
||||
} ngap_handover_cancel_t;
|
||||
|
||||
/* 9.2.3.12 3GPP TS 38.413 */
|
||||
typedef struct {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
} ngap_handover_cancel_ack_t;
|
||||
|
||||
typedef struct ngap_ue_cap_info_ind_s {
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
byte_array_t ue_radio_cap;
|
||||
@@ -533,15 +791,6 @@ typedef struct ngap_downlink_nas_s {
|
||||
byte_array_t nas_pdu;
|
||||
} ngap_downlink_nas_t;
|
||||
|
||||
/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
|
||||
typedef struct {
|
||||
uint8_t nb_qos;
|
||||
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
|
||||
pdu_session_type_t pdu_session_type;
|
||||
// UPF endpoint of the NG-U (N3) transport bearer
|
||||
gtpu_tunnel_t n3_incoming;
|
||||
} pdusession_transfer_t;
|
||||
|
||||
/* PDU Session Resource Setup/Modify Request Item */
|
||||
typedef struct {
|
||||
int pdusession_id;
|
||||
@@ -737,4 +986,47 @@ typedef struct ngap_pdusession_release_resp_s {
|
||||
|
||||
} ngap_pdusession_release_resp_t;
|
||||
|
||||
/** 9.2.3.14 Uplink RAN Status Transfer (3GPP TS 38.413)
|
||||
* COUNT value used for both UL and DL PDCP SN + HFN (12-bit or 18-bit SN) */
|
||||
|
||||
// Indicates PDCP SN length
|
||||
typedef enum { NGAP_SN_LENGTH_12 = 0, NGAP_SN_LENGTH_18 = 1 } ngap_sn_length_t;
|
||||
|
||||
typedef struct {
|
||||
// PDCP Sequence Number
|
||||
uint32_t pdcp_sn;
|
||||
// Hyper Frame Number
|
||||
uint32_t hfn;
|
||||
// SN length
|
||||
ngap_sn_length_t sn_len;
|
||||
} ngap_drb_count_value_t;
|
||||
|
||||
// DRBs Subject to Status Transfer Item
|
||||
typedef struct {
|
||||
// DRB ID
|
||||
uint8_t drb_id;
|
||||
// UL COUNT value
|
||||
ngap_drb_count_value_t ul_count;
|
||||
// DL COUNT value
|
||||
ngap_drb_count_value_t dl_count;
|
||||
} ngap_drb_status_t;
|
||||
|
||||
// RAN Status Transfer Transparent Container (9.3.1.108)
|
||||
typedef struct {
|
||||
// Number of DRBs in the list
|
||||
uint8_t nb_drb;
|
||||
// DRB Status List
|
||||
ngap_drb_status_t drb_status_list[MAX_DRBS_PER_UE];
|
||||
} ngap_ran_status_container_t;
|
||||
|
||||
// Uplink RAN Status Transfer message (9.2.3.14)
|
||||
typedef struct {
|
||||
// AMF UE NGAP ID (Mandatory)
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// RAN UE NGAP ID (Mandatory)
|
||||
uint32_t gnb_ue_ngap_id;
|
||||
// RAN Status Transfer Transparent Container (Mandatory)
|
||||
ngap_ran_status_container_t ran_status;
|
||||
} ngap_ran_status_transfer_t;
|
||||
|
||||
#endif /* NGAP_MESSAGES_TYPES_H_ */
|
||||
|
||||
@@ -360,6 +360,39 @@ static E1AP_UP_Parameters_t encode_dl_up_parameters(const int numUpParam, const
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
|
||||
static E1AP_PDCP_SN_Status_Information_t encode_pdcp_status_info(const e1_pdcp_status_info_t *in)
|
||||
{
|
||||
E1AP_PDCP_SN_Status_Information_t pdcp = {0};
|
||||
pdcp.pdcpStatusTransfer_DL.hFN = in->dl_count.hfn;
|
||||
pdcp.pdcpStatusTransfer_DL.pDCP_SN = in->dl_count.sn;
|
||||
pdcp.pdcpStatusTransfer_UL.countValue.hFN = in->ul_count.hfn;
|
||||
pdcp.pdcpStatusTransfer_UL.countValue.pDCP_SN = in->ul_count.sn;
|
||||
return pdcp;
|
||||
}
|
||||
|
||||
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
|
||||
static e1_pdcp_status_info_t decode_pdcp_status_info(const E1AP_PDCP_SN_Status_Information_t* in)
|
||||
{
|
||||
e1_pdcp_status_info_t out = {0};
|
||||
const E1AP_PDCP_Count_t *dl = &in->pdcpStatusTransfer_DL;
|
||||
const E1AP_PDCP_Count_t *ul = &in->pdcpStatusTransfer_UL.countValue;
|
||||
out.dl_count.hfn = dl->hFN;
|
||||
out.dl_count.sn = dl->pDCP_SN;
|
||||
out.ul_count.hfn = ul->hFN;
|
||||
out.ul_count.sn = ul->pDCP_SN;
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool eq_pdcp_info(const e1_pdcp_status_info_t *a, const e1_pdcp_status_info_t *b)
|
||||
{
|
||||
_E1_EQ_CHECK_INT(a->dl_count.hfn, b->dl_count.hfn);
|
||||
_E1_EQ_CHECK_INT(a->dl_count.sn, b->dl_count.sn);
|
||||
_E1_EQ_CHECK_INT(a->ul_count.hfn, b->ul_count.hfn);
|
||||
_E1_EQ_CHECK_INT(a->ul_count.sn, b->ul_count.sn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Equality check for DRB_nGRAN_to_setup_t
|
||||
*/
|
||||
@@ -1409,6 +1442,11 @@ static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_i
|
||||
asn1cCalloc(drb2Mod->pDCP_SN_Status_Request, pDCP_SN_Status_Request);
|
||||
*pDCP_SN_Status_Request = E1AP_PDCP_SN_Status_Request_requested;
|
||||
}
|
||||
// PDCP Status Information (O)
|
||||
if (j->pdcp_status) {
|
||||
asn1cCalloc(drb2Mod->pdcp_SN_Status_Information, pdcp);
|
||||
*pdcp = encode_pdcp_status_info(j->pdcp_status);
|
||||
}
|
||||
// DL UP TNL parameters (O)
|
||||
if (j->numDlUpParam > 0) {
|
||||
asn1cCalloc(drb2Mod->dL_UP_Parameters, DL_UP_Param_List);
|
||||
@@ -1658,7 +1696,7 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
|
||||
drb->id = drb2Mod->dRB_ID;
|
||||
// PDCP Config (O)
|
||||
if (drb2Mod->pDCP_Configuration) {
|
||||
drb->pdcp_config = malloc_or_fail(sizeof(*drb->pdcp_config));
|
||||
drb->pdcp_config = calloc_or_fail(1, sizeof(*drb->pdcp_config));
|
||||
CHECK_E1AP_DEC(e1_decode_pdcp_config(drb->pdcp_config, drb2Mod->pDCP_Configuration));
|
||||
}
|
||||
// SDAP Config (O)
|
||||
@@ -1667,9 +1705,14 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
|
||||
CHECK_E1AP_DEC(e1_decode_sdap_config(drb->sdap_config, drb2Mod->sDAP_Configuration));
|
||||
}
|
||||
// PDCP SN Status Request (O)
|
||||
if (drb2Mod->pDCP_SN_Status_Request) {
|
||||
if (drb2Mod->pDCP_SN_Status_Request && *drb2Mod->pDCP_SN_Status_Request == E1AP_PDCP_SN_Status_Request_requested) {
|
||||
drb->pdcp_sn_status_requested = true;
|
||||
}
|
||||
// PDCP SN Status Information (O)
|
||||
if (drb2Mod->pdcp_SN_Status_Information) {
|
||||
drb->pdcp_status = malloc_or_fail(sizeof(*drb->pdcp_status));
|
||||
*drb->pdcp_status = decode_pdcp_status_info(drb2Mod->pdcp_SN_Status_Information);
|
||||
}
|
||||
// DL UP TNL parameters (O)
|
||||
if (drb2Mod->dL_UP_Parameters) {
|
||||
E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters;
|
||||
@@ -1843,6 +1886,7 @@ static DRB_nGRAN_to_mod_t cp_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
|
||||
cp = *msg;
|
||||
_E1_CP_OPTIONAL_IE(&cp, msg, sdap_config);
|
||||
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_config);
|
||||
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_status);
|
||||
return cp;
|
||||
}
|
||||
|
||||
@@ -1904,12 +1948,15 @@ static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t
|
||||
_E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam);
|
||||
for (int i = 0; i < a->numDlUpParam; i++) {
|
||||
_E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id);
|
||||
eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
|
||||
result &= eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
|
||||
}
|
||||
if (a->pdcp_config && b->pdcp_config)
|
||||
result &= eq_pdcp_config(a->pdcp_config, b->pdcp_config);
|
||||
if (a->sdap_config && b->sdap_config)
|
||||
result &= eq_sdap_config(a->sdap_config, b->sdap_config);
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, pdcp_status);
|
||||
if (a->pdcp_status && b->pdcp_status)
|
||||
result &= eq_pdcp_info(a->pdcp_status, b->pdcp_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1984,6 +2031,7 @@ static void free_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
|
||||
{
|
||||
free(msg->pdcp_config);
|
||||
free(msg->sdap_config);
|
||||
free(msg->pdcp_status);
|
||||
}
|
||||
|
||||
/* Free PDU Session to modify item */
|
||||
@@ -2111,6 +2159,13 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo
|
||||
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));
|
||||
@@ -2310,6 +2365,11 @@ bool decode_E1_bearer_context_mod_response(e1ap_bearer_modif_resp_t *out, const
|
||||
drbMod->qosFlowsFailed[i] = decode_qos_flow_failed_item(drbIE->flow_Failed_List->list.array[i]);
|
||||
}
|
||||
}
|
||||
// PDCP Status Info (O)
|
||||
if (drbIE->pDCP_SN_Status_Information) {
|
||||
drbMod->pdcp_status = malloc_or_fail(sizeof(*drbMod->pdcp_status));
|
||||
*drbMod->pdcp_status = decode_pdcp_status_info(drbIE->pDCP_SN_Status_Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
// DRB Failed to Mod list
|
||||
@@ -2347,8 +2407,12 @@ e1ap_bearer_modif_resp_t cp_bearer_context_mod_response(const e1ap_bearer_modif_
|
||||
// Shallow copy
|
||||
cp = *msg;
|
||||
// Deep copy PDU sessions modified items
|
||||
for (int i = 0; i < msg->numPDUSessionsMod; i++)
|
||||
for (int i = 0; i < msg->numPDUSessionsMod; i++) {
|
||||
cp.pduSessionMod[i] = cp_pdu_session_mod_item(&msg->pduSessionMod[i]);
|
||||
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
|
||||
_E1_CP_OPTIONAL_IE(&cp.pduSessionMod[i].DRBnGRanModList[j], &msg->pduSessionMod[i].DRBnGRanModList[j], pdcp_status);
|
||||
}
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
@@ -2359,6 +2423,8 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
{
|
||||
if (!a || !b) return false; // Null-check both inputs
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Basic members
|
||||
_E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id);
|
||||
_E1_EQ_CHECK_INT(a->gNB_cu_up_ue_id, b->gNB_cu_up_ue_id);
|
||||
@@ -2375,7 +2441,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(pduA, pduB, ng_DL_UP_TL_info);
|
||||
if (pduA->ng_DL_UP_TL_info && pduB->ng_DL_UP_TL_info)
|
||||
eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
|
||||
result &= eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
|
||||
|
||||
// DRB Modified List
|
||||
_E1_EQ_CHECK_INT(pduA->numDRBModified, pduB->numDRBModified);
|
||||
@@ -2387,6 +2453,10 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
for (int j = 0; j < drbModA->numQosFlowSetup; j++) {
|
||||
_E1_EQ_CHECK_LONG(drbModA->qosFlows[j].qfi, drbModB->qosFlows[j].qfi);
|
||||
}
|
||||
_E1_EQ_CHECK_OPTIONAL_PTR(drbModA, drbModB, pdcp_status);
|
||||
if (drbModA->pdcp_status && drbModB->pdcp_status) {
|
||||
result &= eq_pdcp_info(drbModA->pdcp_status, drbModA->pdcp_status);
|
||||
}
|
||||
}
|
||||
|
||||
// DRB Failed to Modify List
|
||||
@@ -2420,7 +2490,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2432,6 +2502,9 @@ void free_e1ap_context_mod_response(const e1ap_bearer_modif_resp_t *msg)
|
||||
free(msg->pduSessionMod[i].confidentialityProtectionIndication);
|
||||
free(msg->pduSessionMod[i].integrityProtectionIndication);
|
||||
free(msg->pduSessionMod[i].ng_DL_UP_TL_info);
|
||||
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
|
||||
free(msg->pduSessionMod[i].DRBnGRanModList[j].pdcp_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +490,13 @@ static void test_bearer_context_modification_request(void)
|
||||
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
|
||||
};
|
||||
|
||||
const e1_pdcp_status_info_t dummy_pdcp_status = {
|
||||
.dl_count.hfn = 12,
|
||||
.dl_count.sn = 34,
|
||||
.ul_count.hfn = 56,
|
||||
.ul_count.sn = 78,
|
||||
};
|
||||
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = {
|
||||
.numDlUpParam = 1,
|
||||
.DlUpParamList[0].cell_group_id = MCG,
|
||||
@@ -497,10 +504,12 @@ static void test_bearer_context_modification_request(void)
|
||||
.id = 1,
|
||||
.pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)),
|
||||
.pdcp_sn_status_requested = true,
|
||||
.pdcp_status = malloc_or_fail(sizeof(*drb_to_mod.pdcp_status)),
|
||||
.numQosFlow2Setup = 1,
|
||||
.qosFlows[0] = dummy_qos_flows,
|
||||
};
|
||||
*drb_to_mod.pdcp_config = dummy_pdcp_config;
|
||||
*drb_to_mod.pdcp_status = dummy_pdcp_status;
|
||||
|
||||
pdu_session_to_mod_t pdusession_mod_item = {
|
||||
.sessionId = 1,
|
||||
@@ -592,12 +601,21 @@ const DRB_nGRAN_failed_t dummy_drb_failed = {
|
||||
*/
|
||||
static void test_bearer_context_modification_response(void)
|
||||
{
|
||||
const e1_pdcp_status_info_t dummy_pdcp_status = {
|
||||
.dl_count.hfn = 12,
|
||||
.dl_count.sn = 2,
|
||||
.ul_count.hfn = 12,
|
||||
.ul_count.sn = 3,
|
||||
};
|
||||
|
||||
// DRB Modified List
|
||||
DRB_nGRAN_modified_t drb_mod = {
|
||||
.id = 1,
|
||||
.numQosFlowSetup = 1,
|
||||
.qosFlows[0].qfi = 1,
|
||||
.pdcp_status = malloc_or_fail(sizeof(*drb_mod.pdcp_status)),
|
||||
};
|
||||
*drb_mod.pdcp_status = dummy_pdcp_status;
|
||||
|
||||
// DRB Setup List
|
||||
DRB_nGRAN_setup_t setup = {
|
||||
|
||||
@@ -170,7 +170,7 @@ static void capture_sst_sd(test_cond_value_t* test_cond_value, uint8_t *sst, uin
|
||||
DevAssert(test_cond_value->octet_string_value->len == 4);
|
||||
uint8_t *buf = test_cond_value->octet_string_value->buf;
|
||||
*sst = buf[0];
|
||||
*sd = malloc(**sd);
|
||||
*sd = malloc(sizeof(uint32_t));
|
||||
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
|
||||
}
|
||||
printf("[E2 AGENT][E2SM-KPM] Condition NSSAI (%d, %x).\n", *sst, (*sd) ? **sd : 0xffffff);
|
||||
|
||||
@@ -722,6 +722,8 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type)
|
||||
verify_section_notset(cfg, GNB_CONFIG_STRING_GNB_LIST ".[0]", GNB_CONFIG_STRING_AMF_IP_ADDRESS);
|
||||
verify_section_notset(cfg, NULL, CONFIG_STRING_SECURITY);
|
||||
verify_section_notset(cfg, NULL, CONFIG_STRING_NR_PDCP_LIST);
|
||||
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_NEIGHBOUR_LIST);
|
||||
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION);
|
||||
} // else nothing to be checked
|
||||
|
||||
/* other possible verifications: PNF, VNF, CU-CP, CU-UP, ...? */
|
||||
@@ -1858,21 +1860,21 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
|
||||
GNBNEIGHBOURCELLPARAMS_DESC,
|
||||
GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST,
|
||||
neighbourpath);
|
||||
LOG_D(GNB_APP, "HO LOG: For the Cell: %ld Neighbour Cell ELM NUM: %d\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
|
||||
LOG_I(GNB_APP, "Cell %ld has %d neighbours\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
|
||||
if (NeighbourCellParamList.numelt < 1)
|
||||
continue;
|
||||
|
||||
cell.neighbour_cells = malloc_or_fail(sizeof(seq_arr_t));
|
||||
seq_arr_init(cell.neighbour_cells, sizeof(nr_neighbour_cell_t));
|
||||
for (int l = 0; l < NeighbourCellParamList.numelt; ++l) {
|
||||
nr_neighbour_cell_t neighbourCell = {0};
|
||||
neighbourCell.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
|
||||
neighbourCell.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
|
||||
neighbourCell.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
|
||||
neighbourCell.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
|
||||
neighbourCell.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
|
||||
neighbourCell.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
|
||||
neighbourCell.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
|
||||
nr_neighbour_cell_t n = {0};
|
||||
n.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
|
||||
n.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
|
||||
n.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
|
||||
n.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
|
||||
n.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
|
||||
n.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
|
||||
n.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
|
||||
|
||||
char neighbour_plmn_path[CONFIG_MAXOPTLENGTH];
|
||||
snprintf(neighbour_plmn_path,
|
||||
@@ -1884,10 +1886,22 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
|
||||
GNB_CONFIG_STRING_NEIGHBOUR_PLMN);
|
||||
GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, neighbour_plmn_path);
|
||||
|
||||
neighbourCell.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
|
||||
neighbourCell.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
|
||||
neighbourCell.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
|
||||
seq_arr_push_back(cell.neighbour_cells, &neighbourCell, sizeof(neighbourCell));
|
||||
n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
|
||||
n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
|
||||
n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
|
||||
seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n));
|
||||
LOG_I(GNB_APP,
|
||||
" [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n",
|
||||
l,
|
||||
n.gNB_ID,
|
||||
n.nrcell_id,
|
||||
n.physicalCellId,
|
||||
n.subcarrierSpacing,
|
||||
n.absoluteFrequencySSB,
|
||||
n.tac,
|
||||
n.plmn.mcc,
|
||||
n.plmn.mnc_digit_length,
|
||||
n.plmn.mnc);
|
||||
}
|
||||
seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell));
|
||||
}
|
||||
|
||||
@@ -594,8 +594,8 @@ typedef struct NR_UE_MAC_INST_s {
|
||||
|
||||
nr_csi_report_t csi_report_template[MAX_CSI_REPORTCONFIG];
|
||||
|
||||
/// measurements from CSI-RS
|
||||
fapi_nr_csirs_measurements_t csirs_measurements;
|
||||
/// 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;
|
||||
|
||||
@@ -117,10 +117,8 @@ csi_payload_t nr_ue_aperiodic_csi_reporting(NR_UE_MAC_INST_t *mac, dci_field_t c
|
||||
|
||||
nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_dci_indication_pdu_t *dci);
|
||||
|
||||
int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac,
|
||||
frame_t frame,
|
||||
int slot,
|
||||
fapi_nr_csirs_measurements_t *csirs_measurements);
|
||||
void nr_ue_process_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements);
|
||||
|
||||
void nr_ue_aperiodic_srs_scheduling(NR_UE_MAC_INST_t *mac, long resource_trigger, int frame, int slot);
|
||||
|
||||
bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac,
|
||||
|
||||
@@ -302,7 +302,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->csirs_measurements, 0, sizeof(mac->csirs_measurements));
|
||||
memset(&mac->l1_measurements, 0, sizeof(mac->l1_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);
|
||||
|
||||
@@ -1193,7 +1193,9 @@ void nr_ra_contention_resolution_failed(NR_UE_MAC_INST_t *mac)
|
||||
ra->t_crnti = 0;
|
||||
// flush MSG3 buffer
|
||||
free_and_zero(ra->Msg3_buffer);
|
||||
nr_mac_rrc_msg3_ind(mac->ue_id, 0, true);
|
||||
// MSG3 with C-RNTI is a L2 procedure, we shouldn't send any indication to RRC
|
||||
if (!mac->msg3_C_RNTI)
|
||||
nr_mac_rrc_msg3_ind(mac->ue_id, 0, true);
|
||||
NR_PRACH_RESOURCES_t *prach_resources = &ra->prach_resources;
|
||||
prach_resources->preamble_tx_counter++;
|
||||
if (prach_resources->preamble_tx_counter == ra->preambleTransMax + 1) {
|
||||
|
||||
@@ -489,19 +489,42 @@ void ue_dci_configuration(NR_UE_MAC_INST_t *mac, fapi_nr_dl_config_request_t *dl
|
||||
config_dci_pdu(mac, dl_config, rnti_type, slot, ra_SS);
|
||||
}
|
||||
} else if (mac->state == UE_CONNECTED) {
|
||||
/*
|
||||
In RRC Connected state, the UE monitors for fallback DCI with C-RNTI in common and UE-specific SS.
|
||||
First the UE check all UE specific SS. If no fallback DCI is configured, it configures one of following
|
||||
common SS with the shown priority
|
||||
1. RA SS
|
||||
2. Other SI SS
|
||||
3. Paging SS
|
||||
4. SIB1 SS
|
||||
|
||||
There is a possibility that gNB could configure multiple UE-specific SS for fallback DCI or/and common SS,
|
||||
and in a given slot more than one SS become active. For the moment, we assume such configuration wouldn't happen
|
||||
and hence don't handle it for the sake of implementation simplicity.
|
||||
*/
|
||||
bool configured_fallback_dci = false;
|
||||
for (int i = 0; i < pdcch_config->list_SS.count; i++) {
|
||||
NR_SearchSpace_t *ss = pdcch_config->list_SS.array[i];
|
||||
if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ss))
|
||||
if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ss)) {
|
||||
config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, ss);
|
||||
configured_fallback_dci |= (ss->searchSpaceType->present == NR_SearchSpace__searchSpaceType_PR_common);
|
||||
}
|
||||
}
|
||||
const NR_SearchSpace_t *ra_SS = get_common_search_space(mac, pdcch_config->ra_SS_id);
|
||||
if (pdcch_config->list_SS.count == 0 && ra_SS) {
|
||||
// If the UE has not been provided a Type3-PDCCH CSS set or a USS set and
|
||||
// the UE has received a C-RNTI and has been provided a Type1-PDCCH CSS set,
|
||||
// the UE monitors PDCCH candidates for DCI format 0_0 and DCI format 1_0
|
||||
// with CRC scrambled by the C-RNTI in the Type1-PDCCH CSS set
|
||||
if (is_ss_monitor_occasion(frame, slot, slots_per_frame, ra_SS))
|
||||
config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, ra_SS);
|
||||
const NR_SearchSpace_t *css = NULL;
|
||||
if (!configured_fallback_dci) {
|
||||
#define CSS_VALID_CUR_SLOT(CSS) \
|
||||
((CSS > -1) && is_ss_monitor_occasion(frame, slot, slots_per_frame, get_common_search_space(mac, CSS)))
|
||||
if (CSS_VALID_CUR_SLOT(pdcch_config->ra_SS_id))
|
||||
css = get_common_search_space(mac, pdcch_config->ra_SS_id);
|
||||
else if (CSS_VALID_CUR_SLOT(pdcch_config->otherSI_SS_id))
|
||||
css = get_common_search_space(mac, pdcch_config->otherSI_SS_id);
|
||||
else if (CSS_VALID_CUR_SLOT(pdcch_config->paging_SS_id))
|
||||
css = get_common_search_space(mac, pdcch_config->paging_SS_id);
|
||||
#undef CSS_VALID_CUR_SLOT
|
||||
else
|
||||
css = mac->search_space_zero;
|
||||
AssertFatal(css, "Atleast one CSS should be present in connected state\n");
|
||||
config_dci_pdu(mac, dl_config, TYPE_C_RNTI_, slot, css);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1504,13 +1504,17 @@ nr_dci_format_t nr_ue_process_dci_indication_pdu(NR_UE_MAC_INST_t *mac, frame_t
|
||||
return format;
|
||||
}
|
||||
|
||||
int8_t nr_ue_process_csirs_measurements(NR_UE_MAC_INST_t *mac,
|
||||
frame_t frame,
|
||||
int slot,
|
||||
fapi_nr_csirs_measurements_t *csirs_measurements) {
|
||||
LOG_D(NR_MAC,"(%d.%d) Received CSI-RS measurements\n", frame, slot);
|
||||
memcpy(&mac->csirs_measurements, csirs_measurements, sizeof(*csirs_measurements));
|
||||
return 0;
|
||||
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));
|
||||
bool csi_meas = l1_measurements->meas_type == NFAPI_NR_CSI_MEAS;
|
||||
nr_mac_rrc_meas_ind_ue(mac->ue_id,
|
||||
l1_measurements->gNB_index,
|
||||
l1_measurements->Nid_cell,
|
||||
csi_meas,
|
||||
l1_measurements->is_neighboring_cell,
|
||||
l1_measurements->rsrp_dBm);
|
||||
}
|
||||
|
||||
static void set_harq_status(NR_UE_MAC_INST_t *mac,
|
||||
@@ -2993,9 +2997,9 @@ 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->csirs_measurements.rank_indicator];
|
||||
int pmi_x2_bitlen = csi_report->csi_meas_bitlen.pmi_x2_bitlen[mac->csirs_measurements.rank_indicator];
|
||||
int cqi_bitlen = csi_report->csi_meas_bitlen.cqi_bitlen[mac->csirs_measurements.rank_indicator];
|
||||
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];
|
||||
|
||||
if (get_softmodem_params()->emulate_l1) {
|
||||
static const uint8_t mcs_to_cqi[] = {0, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
|
||||
@@ -3003,9 +3007,9 @@ static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
CHECK_INDEX(nr_bler_data, NR_NUM_MCS - 1);
|
||||
int mcs = get_mcs_from_sinr(nr_bler_data, (mac->nr_ue_emul_l1.cqi - 640) * 0.1);
|
||||
CHECK_INDEX(mcs_to_cqi, mcs);
|
||||
mac->csirs_measurements.rank_indicator = mac->nr_ue_emul_l1.ri;
|
||||
mac->csirs_measurements.i1 = mac->nr_ue_emul_l1.pmi;
|
||||
mac->csirs_measurements.cqi = mcs_to_cqi[mcs];
|
||||
mac->l1_measurements.rank_indicator = mac->nr_ue_emul_l1.ri;
|
||||
mac->l1_measurements.i1 = mac->nr_ue_emul_l1.pmi;
|
||||
mac->l1_measurements.cqi = mcs_to_cqi[mcs];
|
||||
}
|
||||
|
||||
int padding_bitlen = 0;
|
||||
@@ -3014,19 +3018,19 @@ static csi_payload_t get_csirs_RI_PMI_CQI_payload(NR_UE_MAC_INST_t *mac,
|
||||
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->csirs_measurements.rank_indicator << cqi_bitlen) |
|
||||
(mac->csirs_measurements.cqi);
|
||||
temp_payload_2 = (mac->csirs_measurements.i1 << pmi_x2_bitlen) |
|
||||
mac->csirs_measurements.i2;
|
||||
(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;
|
||||
}
|
||||
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->csirs_measurements.rank_indicator << (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);
|
||||
(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);
|
||||
}
|
||||
|
||||
temp_payload_1 = reverse_bits(temp_payload_1, p1_bits);
|
||||
@@ -3085,17 +3089,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
|
||||
// TS 38.133 - Table 10.1.6.1-1
|
||||
int rsrp_dBm = mac->csirs_measurements.rsrp_dBm;
|
||||
if (rsrp_dBm < -140) {
|
||||
temp_payload = 16;
|
||||
} else if (rsrp_dBm > -44) {
|
||||
temp_payload = 113;
|
||||
} else {
|
||||
temp_payload = mac->csirs_measurements.rsrp_dBm + 157;
|
||||
}
|
||||
|
||||
temp_payload = reverse_bits(temp_payload, n_bits);
|
||||
temp_payload = reverse_bits(mac->l1_measurements.rsrp_dBm, n_bits); // rsrp_dBm as in TS 38.133 - Table 10.1.6.1-1
|
||||
|
||||
LOG_D(NR_MAC, "cri_ssbri_bitlen = %d\n", cri_ssbri_bitlen);
|
||||
LOG_D(NR_MAC, "rsrp_bitlen = %d\n", rsrp_bitlen);
|
||||
|
||||
@@ -3461,11 +3461,10 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot
|
||||
continue;
|
||||
|
||||
NR_timer_t *t = &UE->UE_sched_ctrl.transm_interrupt;
|
||||
interrupt_followup_action_t a = nr_timer_is_active(t) ? UE->interrupt_action : FOLLOW_INSYNC;
|
||||
// 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, a, mgc->mgl_slots);
|
||||
nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3549,7 +3548,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, interrupt_followup_action_t action, int slots)
|
||||
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
|
||||
{
|
||||
DevAssert(mac != NULL);
|
||||
DevAssert(UE != NULL);
|
||||
@@ -3557,7 +3556,6 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
|
||||
|
||||
nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1);
|
||||
nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt);
|
||||
UE->interrupt_action = action;
|
||||
|
||||
// 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
|
||||
@@ -3565,14 +3563,27 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
|
||||
// frames, after the inactivity of the UE.
|
||||
UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024;
|
||||
|
||||
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots) action %d\n", UE->rnti, slots, UE->interrupt_action);
|
||||
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nr_mac_ue_transmission_timeout(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
|
||||
{
|
||||
DevAssert(mac != NULL);
|
||||
DevAssert(UE != NULL);
|
||||
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
|
||||
|
||||
nr_timer_setup(&UE->UE_sched_ctrl.transm_timeout, slots, 1);
|
||||
nr_timer_start(&UE->UE_sched_ctrl.transm_timeout);
|
||||
|
||||
LOG_I(NR_MAC, "UE %04x: Timeout for UE transmission (%d slots)\n", UE->rnti, slots);
|
||||
return;
|
||||
}
|
||||
|
||||
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info)
|
||||
{
|
||||
int delay = nr_mac_get_reconfig_delay_slots(UE_info->current_DL_BWP.scs);
|
||||
nr_mac_interrupt_ue_transmission(mac, UE_info, FOLLOW_OUTOFSYNC, delay);
|
||||
nr_mac_ue_transmission_timeout(mac, UE_info, delay);
|
||||
LOG_I(NR_MAC, "gNB-DU received the TransmissionActionIndicator with Stop value for UE %04x\n", UE_info->rnti);
|
||||
return 0;
|
||||
}
|
||||
@@ -3641,9 +3652,11 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
|
||||
/* expired */
|
||||
nr_timer_stop(&sched_ctrl->transm_interrupt);
|
||||
if (UE->interrupt_action == FOLLOW_OUTOFSYNC)
|
||||
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
|
||||
/* else: default FOLLOW_INSYNC: nothing to do (UE is now active again) */
|
||||
}
|
||||
if (nr_timer_tick(&sched_ctrl->transm_timeout)) {
|
||||
nr_timer_stop(&sched_ctrl->transm_timeout);
|
||||
LOG_W(NR_MAC, "UE %04x UL failure after transmission timeout\n", UE->rnti);
|
||||
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ 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, interrupt_followup_action_t action, int slots);
|
||||
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,
|
||||
|
||||
@@ -688,6 +688,10 @@ typedef struct {
|
||||
/// Timer for RRC processing procedures and transmission activity
|
||||
NR_timer_t transm_interrupt;
|
||||
|
||||
/// Timer for timeout before UE is set to UL failure (e.g.,
|
||||
/// "TransmissionActionIndicator" handling
|
||||
NR_timer_t transm_timeout;
|
||||
|
||||
/// sri, ul_ri and tpmi based on SRS
|
||||
nr_srs_feedback_t srs_feedback;
|
||||
|
||||
@@ -762,8 +766,6 @@ typedef struct measgap_config {
|
||||
int mgl_slots;
|
||||
} measgap_config_t;
|
||||
|
||||
typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
|
||||
|
||||
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
|
||||
typedef struct {
|
||||
rnti_t rnti;
|
||||
@@ -780,7 +782,6 @@ typedef struct {
|
||||
/// in case of reestablishment, old spCellConfig to apply after
|
||||
/// reconfiguration
|
||||
NR_SpCellConfig_t *reconfigSpCellConfig;
|
||||
interrupt_followup_action_t interrupt_action;
|
||||
NR_UE_NR_Capability_t *capability;
|
||||
measgap_config_t measgap_config;
|
||||
// UE selected beam index
|
||||
|
||||
@@ -291,6 +291,32 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
||||
&security_parameters);
|
||||
}
|
||||
|
||||
// PDCP Status Information requested
|
||||
if (to_modif->pdcp_sn_status_requested) {
|
||||
modified->pdcp_status = malloc_or_fail(sizeof(*modified->pdcp_status));
|
||||
nr_pdcp_count_t dl_count = {0};
|
||||
nr_pdcp_count_t ul_count = {0};
|
||||
nr_pdcp_get_drb_count_values(req->gNB_cu_cp_ue_id, to_modif->id, &ul_count, &dl_count);
|
||||
modified->pdcp_status->dl_count.hfn = dl_count.hfn;
|
||||
modified->pdcp_status->dl_count.sn = dl_count.sn;
|
||||
modified->pdcp_status->ul_count.hfn = ul_count.hfn;
|
||||
modified->pdcp_status->ul_count.sn = ul_count.sn;
|
||||
}
|
||||
|
||||
// PDCP Status received (from source CU-UP)
|
||||
if (to_modif->pdcp_status) {
|
||||
DevAssert(to_modif->pdcp_config);
|
||||
DevAssert(to_modif->pdcp_config->pDCP_SN_Size_DL == to_modif->pdcp_config->pDCP_SN_Size_UL);
|
||||
bool is_sn_len_18 = to_modif->pdcp_config->pDCP_SN_Size_DL == NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits;
|
||||
nr_pdcp_count_t dl_count = { .hfn = to_modif->pdcp_status->dl_count.hfn, .sn = to_modif->pdcp_status->dl_count.sn};
|
||||
nr_pdcp_count_t ul_count = { .hfn = to_modif->pdcp_status->ul_count.hfn, .sn = to_modif->pdcp_status->ul_count.sn};
|
||||
nr_pdcp_count_update(req->gNB_cu_cp_ue_id,
|
||||
to_modif->id,
|
||||
dl_count,
|
||||
ul_count,
|
||||
is_sn_len_18 ? LONG_SN_SIZE : SHORT_SN_SIZE);
|
||||
}
|
||||
|
||||
if (f1inst < 0) // no F1-U?
|
||||
continue; // nothing to do
|
||||
|
||||
@@ -300,6 +326,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
|
||||
in_addr_t addr = to_modif->DlUpParamList[k].tl_info.tlAddress;
|
||||
GtpuUpdateTunnelOutgoingAddressAndTeid(f1inst, req->gNB_cu_cp_ue_id, to_modif->id, addr, to_modif->DlUpParamList[k].tl_info.teId);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -601,6 +601,94 @@ static void nr_pdcp_entity_get_stats(nr_pdcp_entity_t *entity,
|
||||
*out = entity->stats;
|
||||
}
|
||||
|
||||
/** @note: this function applies to the gNB only */
|
||||
static nr_pdcp_count_t nr_pdcp_entity_get_count_dl(nr_pdcp_entity_t *entity)
|
||||
{
|
||||
nr_pdcp_count_t count = {0};
|
||||
const int sn_bits = entity->sn_size;
|
||||
const uint32_t sn_mask = entity->sn_max;
|
||||
count.hfn = entity->rx_deliv >> sn_bits;
|
||||
count.sn = entity->rx_deliv & sn_mask;
|
||||
return count;
|
||||
}
|
||||
|
||||
/** @note: this function applies to the gNB only */
|
||||
static nr_pdcp_count_t nr_pdcp_entity_get_count_ul(nr_pdcp_entity_t *entity)
|
||||
{
|
||||
nr_pdcp_count_t count = {0};
|
||||
const int sn_bits = entity->sn_size;
|
||||
const uint32_t sn_mask = entity->sn_max;
|
||||
count.hfn = entity->tx_next >> sn_bits;
|
||||
count.sn = entity->tx_next & sn_mask;
|
||||
return count;
|
||||
}
|
||||
|
||||
/** @note: this function applies to the gNB only */
|
||||
static void nr_pdcp_entity_set_count_dl(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
|
||||
{
|
||||
// SN length in bits
|
||||
const uint32_t sn_mask = (1 << sn_size) - 1;
|
||||
|
||||
// Compose COUNT values for tx_next (DL) and rx_next (UL)
|
||||
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
|
||||
|
||||
// Apply the COUNT values
|
||||
entity->rx_next = count + 1;
|
||||
entity->rx_deliv = count;
|
||||
|
||||
LOG_I(PDCP,
|
||||
"DRB %d: Applied PDCP Count\n"
|
||||
" DL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
|
||||
" SN length: %s\n"
|
||||
" PDCP entity state after update:\n"
|
||||
" rx_next = %u\n"
|
||||
" rx_deliv = %u\n"
|
||||
" sn_size = %d\n"
|
||||
" sn_max = %d\n"
|
||||
" window = %d\n",
|
||||
entity->rb_id,
|
||||
pdcp_count.sn,
|
||||
pdcp_count.hfn,
|
||||
count,
|
||||
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
|
||||
entity->rx_next,
|
||||
entity->rx_deliv,
|
||||
entity->sn_size,
|
||||
entity->sn_max,
|
||||
entity->window_size);
|
||||
}
|
||||
|
||||
/** @note: this function applies to the gNB only */
|
||||
static void nr_pdcp_entity_set_count_ul(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
|
||||
{
|
||||
// SN length in bits
|
||||
const uint32_t sn_mask = (1 << sn_size) - 1;
|
||||
|
||||
// Compose COUNT values for tx_next (DL) and rx_next (UL)
|
||||
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
|
||||
|
||||
// Apply the COUNT values
|
||||
entity->tx_next = count;
|
||||
|
||||
LOG_I(PDCP,
|
||||
"DRB %d: Applied PDCP Count\n"
|
||||
" UL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
|
||||
" SN length: %s\n"
|
||||
" PDCP entity state after update:\n"
|
||||
" tx_next = %u\n"
|
||||
" sn_size = %d\n"
|
||||
" sn_max = %d\n"
|
||||
" window = %d\n",
|
||||
entity->rb_id,
|
||||
pdcp_count.sn,
|
||||
pdcp_count.hfn,
|
||||
count,
|
||||
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
|
||||
entity->tx_next,
|
||||
entity->sn_size,
|
||||
entity->sn_max,
|
||||
entity->window_size);
|
||||
}
|
||||
|
||||
nr_pdcp_entity_t *new_nr_pdcp_entity(
|
||||
nr_pdcp_entity_type_t type,
|
||||
@@ -675,5 +763,11 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
|
||||
|
||||
nr_pdcp_entity_set_security(ret, security_parameters);
|
||||
|
||||
/* PDCP Count API */
|
||||
ret->get_pdcp_count_dl = nr_pdcp_entity_get_count_dl;
|
||||
ret->get_pdcp_count_ul = nr_pdcp_entity_get_count_ul;
|
||||
ret->set_pdcp_count_dl = nr_pdcp_entity_set_count_dl;
|
||||
ret->set_pdcp_count_ul = nr_pdcp_entity_set_count_ul;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,11 @@ typedef struct {
|
||||
uint8_t ciphering_key[NR_K_KEY_SIZE];
|
||||
} nr_pdcp_entity_security_keys_and_algos_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sn;
|
||||
uint32_t hfn;
|
||||
} nr_pdcp_count_t;
|
||||
|
||||
typedef struct {
|
||||
//nr_pdcp_entity_type_t mode;
|
||||
/* PDU stats */
|
||||
@@ -87,7 +92,6 @@ typedef struct {
|
||||
uint8_t mode; /* 0: PDCP AM, 1: PDCP UM, 2: PDCP TM */
|
||||
} nr_pdcp_statistics_t;
|
||||
|
||||
|
||||
typedef struct nr_pdcp_entity_t {
|
||||
nr_pdcp_entity_type_t type;
|
||||
|
||||
@@ -180,6 +184,13 @@ typedef struct nr_pdcp_entity_t {
|
||||
|
||||
/* Keep tracks of whether the PDCP entity was suspended or not */
|
||||
bool entity_suspended;
|
||||
|
||||
/* PDCP Count API */
|
||||
nr_pdcp_count_t (*get_pdcp_count_dl)(struct nr_pdcp_entity_t *entity);
|
||||
nr_pdcp_count_t (*get_pdcp_count_ul)(struct nr_pdcp_entity_t *entity);
|
||||
void (*set_pdcp_count_dl)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
|
||||
void (*set_pdcp_count_ul)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
|
||||
|
||||
} nr_pdcp_entity_t;
|
||||
|
||||
nr_pdcp_entity_t *new_nr_pdcp_entity(
|
||||
|
||||
@@ -1072,3 +1072,57 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len)
|
||||
|
||||
return num_ues;
|
||||
}
|
||||
|
||||
void nr_pdcp_count_update(ue_id_t ue_id,
|
||||
rb_id_t drb_id,
|
||||
nr_pdcp_count_t dl_count,
|
||||
nr_pdcp_count_t ul_count,
|
||||
int sn_size)
|
||||
{
|
||||
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 == NULL) {
|
||||
LOG_E(PDCP, "UE %ld not found in PDCP manager\n", ue_id);
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, drb_id, false);
|
||||
if (entity == NULL) {
|
||||
LOG_E(PDCP, "No PDCP entity found for UE %ld DRB %ld\n", ue_id, drb_id);
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
entity->set_pdcp_count_dl(entity, dl_count, sn_size);
|
||||
entity->set_pdcp_count_ul(entity, ul_count, sn_size);
|
||||
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
}
|
||||
|
||||
/** * @brief Get PDCP COUNT values (PDCP-SN and HFN) for UL and DL from the PDCP entity
|
||||
* @param[in] ue_id Unique UE ID
|
||||
* @param[in] rb_id Radio Bearer ID
|
||||
* @param[out] ul_count UL PDCP count (SN, HFN)
|
||||
* @param[out] dl_count DL PDCP count (SN, HFN)
|
||||
*/
|
||||
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count)
|
||||
{
|
||||
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
|
||||
|
||||
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
|
||||
|
||||
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, rb_id, false);
|
||||
|
||||
if (!entity) {
|
||||
LOG_E(PDCP, "No entity found (DRB %ld)\n", rb_id);
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
return;
|
||||
}
|
||||
|
||||
*dl_count = entity->get_pdcp_count_dl(entity);
|
||||
*ul_count = entity->get_pdcp_count_ul(entity);
|
||||
|
||||
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
|
||||
}
|
||||
|
||||
@@ -125,4 +125,12 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len);
|
||||
|
||||
bool nr_pdcp_get_statistics(ue_id_t ue_id, int srb_flag, int rb_id, nr_pdcp_statistics_t *out);
|
||||
|
||||
void nr_pdcp_count_update(ue_id_t ue_id,
|
||||
rb_id_t drb_id,
|
||||
nr_pdcp_count_t dl_count,
|
||||
nr_pdcp_count_t ul_count,
|
||||
int sn_size);
|
||||
|
||||
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count);
|
||||
|
||||
#endif /* NR_PDCP_OAI_API_H */
|
||||
|
||||
@@ -1174,13 +1174,11 @@ static void handle_rlm(rlm_t rlm_result, int frame, NR_UE_MAC_INST_t *mac)
|
||||
nr_mac_rrc_sync_ind(mac->ue_id, frame, is_sync);
|
||||
}
|
||||
|
||||
static int8_t handle_csirs_measurements(NR_UE_MAC_INST_t *mac,
|
||||
frame_t frame,
|
||||
int slot,
|
||||
fapi_nr_csirs_measurements_t *csirs_measurements)
|
||||
static int8_t handle_l1_measurements(NR_UE_MAC_INST_t *mac, frame_t frame, int slot, fapi_nr_l1_measurements_t *l1_measurements)
|
||||
{
|
||||
handle_rlm(csirs_measurements->radiolink_monitoring, frame, mac);
|
||||
return nr_ue_process_csirs_measurements(mac, frame, slot, csirs_measurements);
|
||||
handle_rlm(l1_measurements->radiolink_monitoring, frame, mac);
|
||||
nr_ue_process_l1_measurements(mac, frame, slot, l1_measurements);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_harq_status(NR_UE_MAC_INST_t *mac, uint8_t harq_pid, uint8_t ack_nack)
|
||||
@@ -1316,11 +1314,11 @@ static uint32_t nr_ue_dl_processing(NR_UE_MAC_INST_t *mac, nr_downlink_indicatio
|
||||
}
|
||||
ret_mask |= (handle_dlsch(mac, dl_info, i)) << FAPI_NR_RX_PDU_TYPE_RAR;
|
||||
break;
|
||||
case FAPI_NR_CSIRS_IND:
|
||||
ret_mask |= (handle_csirs_measurements(mac,
|
||||
dl_info->frame,
|
||||
dl_info->slot,
|
||||
&rx_indication_body.csirs_measurements)) << FAPI_NR_CSIRS_IND;
|
||||
case FAPI_NR_MEAS_IND:
|
||||
ret_mask |= (handle_l1_measurements(mac,
|
||||
dl_info->frame,
|
||||
dl_info->slot,
|
||||
&rx_indication_body.l1_measurements)) << FAPI_NR_MEAS_IND;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -64,6 +64,10 @@
|
||||
#include "NR_LogicalChannelConfig.h"
|
||||
#include "NR_PDCP-Config.h"
|
||||
#include "NR_MAC-CellGroupConfig.h"
|
||||
#include "NR_HandoverPreparationInformation.h"
|
||||
#include "NR_HandoverCommand.h"
|
||||
#include "NR_HandoverCommand-IEs.h"
|
||||
#include "NR_AS-Config.h"
|
||||
#include "NR_SecurityModeCommand.h"
|
||||
#include "NR_CipheringAlgorithm.h"
|
||||
#include "NR_RRCReconfiguration-IEs.h"
|
||||
@@ -82,6 +86,8 @@
|
||||
#include "NR_UE-CapabilityRequestFilterNR.h"
|
||||
#include "NR_HandoverPreparationInformation.h"
|
||||
#include "NR_HandoverPreparationInformation-IEs.h"
|
||||
#include "NR_HandoverCommand.h"
|
||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#if defined(NR_Rel16)
|
||||
#include "NR_SCS-SpecificCarrier.h"
|
||||
@@ -197,6 +203,48 @@ int xer_nr_sprint(char *string, size_t string_size, asn_TYPE_descriptor_t *td, v
|
||||
return er.encoded;
|
||||
}
|
||||
|
||||
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (!buf || len == 0) return NULL;
|
||||
|
||||
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
|
||||
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList,
|
||||
(void **)&clist, buf, len, 0, 0);
|
||||
|
||||
if (rval.code != RC_OK) {
|
||||
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode container list\n", rnti);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NR_UE_NR_Capability_t *cap = decode_nr_ue_capability(rnti, clist);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist);
|
||||
return cap;
|
||||
}
|
||||
|
||||
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist)
|
||||
{
|
||||
if (!clist) return NULL;
|
||||
|
||||
for (int i = 0; i < clist->list.count; i++) {
|
||||
const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
|
||||
if (c->rat_Type != NR_RAT_Type_nr)
|
||||
continue;
|
||||
|
||||
NR_UE_NR_Capability_t *cap = NULL;
|
||||
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_NR_Capability,
|
||||
(void **)&cap,
|
||||
c->ue_CapabilityRAT_Container.buf,
|
||||
c->ue_CapabilityRAT_Container.size,
|
||||
0, 0);
|
||||
if (rval.code == RC_OK)
|
||||
return cap;
|
||||
|
||||
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode NR capability (%zu bytes)\n", rnti, rval.consumed);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc)
|
||||
@@ -564,6 +612,43 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
|
||||
return ie;
|
||||
}
|
||||
|
||||
static byte_array_t do_HO_RRCReconfiguration(nr_rrc_reconfig_param_t *params)
|
||||
{
|
||||
NR_RRCReconfiguration_IEs_t *ie = build_RRCReconfiguration_IEs(params);
|
||||
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||
|
||||
// Create the standalone RRCReconfiguration message
|
||||
NR_RRCReconfiguration_t rrcReconf = {0};
|
||||
rrcReconf.rrc_TransactionIdentifier = params->transaction_id;
|
||||
rrcReconf.criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration;
|
||||
rrcReconf.criticalExtensions.choice.rrcReconfiguration = ie;
|
||||
|
||||
// Encode the message
|
||||
int val = uper_encode_to_new_buffer(&asn_DEF_NR_RRCReconfiguration, NULL, &rrcReconf, (void **)&msg.buf);
|
||||
if (val <= 0) {
|
||||
LOG_E(NR_RRC, "ASN1 RRCReconfiguration message encoding failed\n");
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
|
||||
return msg;
|
||||
}
|
||||
msg.len = val;
|
||||
LOG_D(NR_RRC, "RRCReconfiguration: Encoded (%ld bytes)\n", msg.len);
|
||||
|
||||
// don't free what we did not allocate, so set fields with pointers to NULL
|
||||
// if memory comes from outside
|
||||
ie->measConfig = NULL;
|
||||
if (ie->radioBearerConfig) {
|
||||
ie->radioBearerConfig->srb3_ToRelease = NULL;
|
||||
ie->radioBearerConfig->drb_ToReleaseList = NULL;
|
||||
ie->radioBearerConfig->drb_ToAddModList = NULL;
|
||||
ie->radioBearerConfig->srb_ToAddModList = NULL;
|
||||
ie->radioBearerConfig->securityConfig = NULL;
|
||||
}
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
byte_array_t do_RRCReconfiguration(const nr_rrc_reconfig_param_t *params)
|
||||
{
|
||||
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||
@@ -785,6 +870,63 @@ int do_RRCSetupComplete(uint8_t *buffer,
|
||||
return((enc_rval.encoded+7)/8);
|
||||
}
|
||||
|
||||
// TODO: This function is only implemented for event A2
|
||||
int do_nrMeasurementReport_SA(NR_MeasurementReport_t *measurementReport,
|
||||
long trigger_to_measid,
|
||||
long trigger_quantity,
|
||||
long rs_type,
|
||||
uint16_t Nid_cell,
|
||||
int rsrp_dBm,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
asn_enc_rval_t enc_rval;
|
||||
NR_UL_DCCH_Message_t ul_dcch_msg;
|
||||
memset((void *)&ul_dcch_msg, 0, sizeof(NR_UL_DCCH_Message_t));
|
||||
|
||||
ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1;
|
||||
ul_dcch_msg.message.choice.c1 = CALLOC(1, sizeof(struct NR_UL_DCCH_MessageType__c1));
|
||||
ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_measurementReport;
|
||||
|
||||
memset(measurementReport, 0, sizeof(struct NR_MeasurementReport));
|
||||
ul_dcch_msg.message.choice.c1->choice.measurementReport = measurementReport;
|
||||
measurementReport->criticalExtensions.present = NR_MeasurementReport__criticalExtensions_PR_measurementReport;
|
||||
|
||||
NR_MeasurementReport_IEs_t *measurementReport_ie = CALLOC(1, sizeof(struct NR_MeasurementReport_IEs));
|
||||
measurementReport->criticalExtensions.choice.measurementReport = measurementReport_ie;
|
||||
measurementReport_ie->measResults.measId = trigger_to_measid;
|
||||
|
||||
NR_MeasResultServMO_t *measResultServMo = CALLOC(1, sizeof(struct NR_MeasResultServMO));
|
||||
|
||||
NR_MeasResultNR_t *measResultServingCell = &measResultServMo->measResultServingCell;
|
||||
measResultServingCell->physCellId = CALLOC(1, sizeof(NR_PhysCellId_t));
|
||||
*measResultServingCell->physCellId = Nid_cell;
|
||||
|
||||
struct NR_MeasQuantityResults *active_mq_res = CALLOC(1, sizeof(struct NR_MeasQuantityResults));
|
||||
|
||||
if (trigger_quantity == NR_MeasTriggerQuantityOffset_PR_rsrp) {
|
||||
active_mq_res->rsrp = CALLOC(1, sizeof(NR_RSRP_Range_t));
|
||||
|
||||
*active_mq_res->rsrp = rsrp_dBm + 157;
|
||||
if (rs_type == NR_NR_RS_Type_ssb)
|
||||
measResultServingCell->measResult.cellResults.resultsSSB_Cell = active_mq_res;
|
||||
else
|
||||
measResultServingCell->measResult.cellResults.resultsCSI_RS_Cell = active_mq_res;
|
||||
}
|
||||
|
||||
ASN_SEQUENCE_ADD(&measurementReport_ie->measResults.measResultServingMOList.list, measResultServMo);
|
||||
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, NULL, (void *)&ul_dcch_msg, buffer, buffer_size);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg);
|
||||
}
|
||||
|
||||
LOG_I(NR_RRC, "MeasurementReport Encoded %zd bits (%zd bytes)\n", enc_rval.encoded, (enc_rval.encoded + 7) / 8);
|
||||
|
||||
return ((enc_rval.encoded + 7) / 8);
|
||||
}
|
||||
|
||||
int do_NR_DLInformationTransfer(uint8_t *buffer,
|
||||
size_t buffer_len,
|
||||
uint8_t transaction_id,
|
||||
@@ -1101,6 +1243,192 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
return mc;
|
||||
}
|
||||
|
||||
/** @brief Prepares removal of measurement configurations related to the source gNB.
|
||||
* This function decodes the HandoverPreparationInformation received from the source gNB,
|
||||
* extracts the source MeasConfig, and marks those entries (measurement objects, report
|
||||
* configurations, and measurement IDs) for removal by populating the corresponding
|
||||
* ToRemoveList fields in the UE's current measConfig.
|
||||
* This ensures that obsolete or source-specific measurement configurations are removed
|
||||
* from the UE after handover and allows the target gNB to install its own valid configuration.
|
||||
* @param measConfig Target gNB's current measurement configuration to be updated.
|
||||
* @param prep_info Encoded HandoverPreparationInformation received from the source gNB.*/
|
||||
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_array_t prep_info)
|
||||
{
|
||||
if (currentMC == NULL) {
|
||||
LOG_E(NR_RRC, "HO LOG: UE's Measurement Configuration is NULL!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decodes the HandoverPreparationInformation message
|
||||
NR_HandoverPreparationInformation_t *hpi = NULL;
|
||||
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
|
||||
&asn_DEF_NR_HandoverPreparationInformation,
|
||||
(void **)&hpi,
|
||||
(uint8_t *)prep_info.buf,
|
||||
prep_info.len);
|
||||
|
||||
if (hoPrep_dec_rval.code != RC_OK || hoPrep_dec_rval.consumed < 0) {
|
||||
LOG_E(NR_RRC, "Handover Prep Info decode error while removing source gnb measurement configuration!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decodes the RRCReconfiguration message within the HandoverPreparationInformation
|
||||
and extract the measConfig provided by the source gNB */
|
||||
if (!hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig) {
|
||||
LOG_W(NR_RRC, "Missing sourceConfig: in source gNB rrcReconfiguration\n");
|
||||
return;
|
||||
}
|
||||
NR_RRCReconfiguration_t *rrcReconf = NULL;
|
||||
NR_AS_Config_t *sourceConfig = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig;
|
||||
asn_dec_rval_t rrcReconf_dec_rval = uper_decode_complete(NULL,
|
||||
&asn_DEF_NR_RRCReconfiguration,
|
||||
(void **)&rrcReconf,
|
||||
(uint8_t *)sourceConfig->rrcReconfiguration.buf,
|
||||
sourceConfig->rrcReconfiguration.size);
|
||||
if (rrcReconf_dec_rval.code != RC_OK || rrcReconf_dec_rval.consumed < 0) {
|
||||
LOG_E(NR_RRC, "Failed to decode source gNB rrcReconfiguration!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL)
|
||||
return;
|
||||
|
||||
NR_MeasConfig_t *sourceMC = rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig;
|
||||
|
||||
/* Add measurement objects, report configurations, and measurement IDs
|
||||
to the UE's measConfig removal lists, and update UE->measConfig */
|
||||
if (sourceMC->measObjectToAddModList->list.count > 0) {
|
||||
currentMC->measObjectToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measObjectToRemoveList));
|
||||
for (int i = 0; i < sourceMC->measObjectToAddModList->list.count; i++) {
|
||||
NR_MeasObjectId_t *measObjId = calloc_or_fail(1, sizeof(NR_MeasObjectId_t));
|
||||
*measObjId = sourceMC->measObjectToAddModList->list.array[i]->measObjectId;
|
||||
asn1cSeqAdd(¤tMC->measObjectToRemoveList->list, measObjId);
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceMC->reportConfigToAddModList->list.count > 0) {
|
||||
currentMC->reportConfigToRemoveList = calloc_or_fail(1, sizeof(*currentMC->reportConfigToRemoveList));
|
||||
for (int i = 0; i < sourceMC->reportConfigToAddModList->list.count; i++) {
|
||||
NR_ReportConfigId_t *reportConfigId = calloc_or_fail(1, sizeof(NR_ReportConfigId_t));
|
||||
*reportConfigId = sourceMC->reportConfigToAddModList->list.array[i]->reportConfigId;
|
||||
asn1cSeqAdd(¤tMC->reportConfigToRemoveList->list, reportConfigId);
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceMC->measIdToAddModList->list.count > 0) {
|
||||
currentMC->measIdToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measIdToRemoveList));
|
||||
for (int i = 0; i < sourceMC->measIdToAddModList->list.count; i++) {
|
||||
NR_MeasId_t *measId = calloc_or_fail(1, sizeof(NR_MeasId_t));
|
||||
*measId = sourceMC->measIdToAddModList->list.array[i]->measId;
|
||||
asn1cSeqAdd(¤tMC->measIdToRemoveList->list, measId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand)
|
||||
{
|
||||
// Decode Handover Command
|
||||
NR_HandoverCommand_t *hoCommand = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverCommand, (void **)&hoCommand, handoverCommand.buf, handoverCommand.len);
|
||||
|
||||
if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
|
||||
LOG_E(NR_RRC, "Can not decode Handover Command!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Prepare DL DCCH message for RRCReconfiguration
|
||||
NR_DL_DCCH_Message_t dl_dcch_msg = {0};
|
||||
asn_enc_rval_t enc_rval;
|
||||
dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1;
|
||||
asn1cCalloc(dl_dcch_msg.message.choice.c1, c1);
|
||||
c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
|
||||
asn1cCalloc(c1->choice.rrcReconfiguration, rrcReconf);
|
||||
|
||||
// Decode RRCReconfiguration from handoverCommandMessage
|
||||
OCTET_STRING_t *ho = &hoCommand->criticalExtensions.choice.c1->choice.handoverCommand->handoverCommandMessage;
|
||||
uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size);
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg);
|
||||
|
||||
// Encode DL DCCH message in buffer
|
||||
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, ba->buf, ba->len);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
|
||||
|
||||
return ((enc_rval.encoded + 7) / 8);
|
||||
}
|
||||
|
||||
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params)
|
||||
{
|
||||
// Encode RRCReconfiguration to buffer
|
||||
byte_array_t msg = do_HO_RRCReconfiguration(params);
|
||||
|
||||
// Add RRCReconfiguration to handoverCommand
|
||||
NR_HandoverCommand_t HoCommand = {0};
|
||||
HoCommand.criticalExtensions.present = NR_HandoverCommand__criticalExtensions_PR_c1;
|
||||
asn1cCalloc(HoCommand.criticalExtensions.choice.c1, c1);
|
||||
c1->present = NR_HandoverCommand__criticalExtensions__c1_PR_handoverCommand;
|
||||
asn1cCalloc(HoCommand.criticalExtensions.choice.c1->choice.handoverCommand, hc);
|
||||
OCTET_STRING_fromBuf(&hc->handoverCommandMessage, (const char *)msg.buf, msg.len);
|
||||
free_byte_array(msg);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_HandoverCommand, (void *)&HoCommand);
|
||||
|
||||
// Encode handoverCommand to buffer
|
||||
byte_array_t buffer = {.len = -1, .buf = NULL};
|
||||
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverCommand, NULL, (void *)&HoCommand, (void **)&buffer.buf);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverCommand, &HoCommand);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/** @brief HandoverPreparationInformation message (11.2.2 of TS 38.331)
|
||||
* 1) generate RRCReconfiguration to be transferred
|
||||
* 2) encodes UE Capabilities from UE Context
|
||||
* 3) encodes AS context
|
||||
* 4) generates HO Preparation Info */
|
||||
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci)
|
||||
{
|
||||
// Buffer to return
|
||||
byte_array_t buffer = {.buf = NULL, .len = 0};
|
||||
|
||||
/* Prepare handoverPreparationInformation IEs */
|
||||
NR_HandoverPreparationInformation_t hoPrepInfo = {0};
|
||||
hoPrepInfo.criticalExtensions.present = NR_HandoverPreparationInformation__criticalExtensions_PR_c1;
|
||||
asn1cCalloc(hoPrepInfo.criticalExtensions.choice.c1, c1);
|
||||
c1->present = NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation;
|
||||
asn1cCalloc(c1->choice.handoverPreparationInformation, hpi);
|
||||
|
||||
/* Decode stored NR_UE_CapabilityRAT_ContainerList and add to IEs */
|
||||
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
|
||||
asn_dec_rval_t dec_rval =
|
||||
uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&clist, params->ue_cap.buf, params->ue_cap.len, 0, 0);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
LOG_W(NR_RRC, "Failed to decode UE capability container list for HandoverPreparationInformation, ignoring capabilities\n");
|
||||
return buffer;
|
||||
}
|
||||
hpi->ue_CapabilityRAT_List = *clist;
|
||||
free(clist);
|
||||
|
||||
/* sourceConfig: Encode RRCReconfiguration as used in the source cell */
|
||||
byte_array_t msg = do_HO_RRCReconfiguration(params);
|
||||
asn1cCalloc(hpi->sourceConfig, sourceConfig);
|
||||
OCTET_STRING_fromBuf(&sourceConfig->rrcReconfiguration, (const char *)msg.buf, msg.len);
|
||||
free_byte_array(msg);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, (void **)&hoPrepInfo);
|
||||
|
||||
/* encode */
|
||||
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverPreparationInformation, NULL, (void *)&hoPrepInfo, (void *)&buffer.buf);
|
||||
AssertFatal(buffer.len > 0, "ASN1 message encoding failed (%ld)!\n", buffer.len);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverPreparationInformation, &hoPrepInfo);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void free_MeasConfig(NR_MeasConfig_t *mc)
|
||||
{
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, mc);
|
||||
|
||||
@@ -50,11 +50,14 @@
|
||||
#include "NR_ReestablishmentCause.h"
|
||||
#include "NR_SRB-ToAddModList.h"
|
||||
#include "NR_SecurityConfig.h"
|
||||
#include "NR_MeasurementReport.h"
|
||||
#include "NR_MeasurementTimingConfiguration.h"
|
||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||
#include "ds/seq_arr.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "rrc_messages_types.h"
|
||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
struct asn_TYPE_descriptor_s;
|
||||
|
||||
typedef struct {
|
||||
@@ -69,6 +72,7 @@ typedef struct {
|
||||
NR_CellGroupConfig_t *cell_group_config;
|
||||
bool masterKeyUpdate;
|
||||
int nextHopChainingCount;
|
||||
byte_array_t ue_cap;
|
||||
} nr_rrc_reconfig_param_t;
|
||||
|
||||
/*
|
||||
@@ -127,6 +131,15 @@ int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration
|
||||
|
||||
int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv, uint64_t fiveG_S_TMSI_part1);
|
||||
|
||||
int do_nrMeasurementReport_SA(NR_MeasurementReport_t *measurementReport,
|
||||
long trigger_to_measid,
|
||||
long trigger_quantity,
|
||||
long rs_type,
|
||||
uint16_t Nid_cell,
|
||||
int rsrp_dBm,
|
||||
uint8_t *buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id);
|
||||
|
||||
int do_NR_RRCReconfigurationComplete(uint8_t *buffer, size_t buffer_size, const uint8_t Transaction_id);
|
||||
@@ -161,4 +174,12 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
void free_MeasConfig(NR_MeasConfig_t *mc);
|
||||
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
|
||||
|
||||
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci);
|
||||
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
|
||||
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
|
||||
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand);
|
||||
|
||||
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len);
|
||||
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist);
|
||||
|
||||
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
|
||||
|
||||
@@ -315,7 +315,7 @@ typedef struct {
|
||||
int absoluteFrequencySSB;
|
||||
int subcarrierSpacing;
|
||||
int band;
|
||||
plmn_identity_t plmn;
|
||||
plmn_id_t plmn;
|
||||
uint32_t tac;
|
||||
bool isIntraFrequencyNeighbour;
|
||||
} nr_neighbour_cell_t;
|
||||
|
||||
@@ -125,4 +125,9 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
|
||||
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
|
||||
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
|
||||
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration);
|
||||
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);
|
||||
void activate_srb(gNB_RRC_UE_t *UE, int srb_id);
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status);
|
||||
|
||||
#endif
|
||||
|
||||
+111
-40
@@ -98,6 +98,7 @@
|
||||
#include "NR_DL-DCCH-Message.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "alg/find.h"
|
||||
#include "NR_HandoverCommand.h"
|
||||
|
||||
#ifdef E2_AGENT
|
||||
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
||||
@@ -307,7 +308,7 @@ unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
|
||||
* for the radio bearer changes, with some expections for SRB1 (i.e. when resuming
|
||||
* an RRC connection, or at the first reconfiguration after RRC connection
|
||||
* reestablishment in NR, do not re-establish PDCP) */
|
||||
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
|
||||
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
|
||||
{
|
||||
if (!ue->Srb[SRB1].Active) {
|
||||
LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
|
||||
@@ -328,7 +329,7 @@ static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablis
|
||||
return list;
|
||||
}
|
||||
|
||||
static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
|
||||
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
|
||||
{
|
||||
NR_DRB_ToAddMod_t *DRB_config = NULL;
|
||||
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
|
||||
@@ -349,12 +350,12 @@ static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
|
||||
return DRB_configList;
|
||||
}
|
||||
|
||||
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
|
||||
void freeSRBlist(NR_SRB_ToAddModList_t *l)
|
||||
{
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, l);
|
||||
}
|
||||
|
||||
static void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
|
||||
void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
|
||||
{
|
||||
AssertFatal(srb_id == 1 || srb_id == 2, "handling only SRB 1 or 2\n");
|
||||
if (UE->Srb[srb_id].Active == 1) {
|
||||
@@ -1383,6 +1384,7 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
|
||||
int servingCellRSRP = 0;
|
||||
int neighbourCellRSRP = 0;
|
||||
int scell_pci = -1;
|
||||
int best_rsrp = -10000;
|
||||
|
||||
switch (event_triggered->eventId.present) {
|
||||
case NR_EventTriggerConfig__eventId_PR_eventA2:
|
||||
@@ -1443,14 +1445,20 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
|
||||
// No F1 connection but static neighbour configuration is available
|
||||
const nr_a3_event_t *a3_event_configuration = get_a3_configuration(rrc, neighbour->physicalCellId);
|
||||
// Additional check - This part can be modified according to additional cell specific Handover Margin
|
||||
// a3-Offset: The actual value is field value * 0.5 dB.
|
||||
if (a3_event_configuration
|
||||
&& ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis)
|
||||
&& ((a3_event_configuration->a3_offset * 0.5 + a3_event_configuration->hysteresis)
|
||||
< (neighbourCellRSRP - servingCellRSRP))) {
|
||||
if (neighbourCellRSRP > best_rsrp) {
|
||||
// UE can send multiple neighbour cells A3 event report in 1 Meas Report. So, we need to find the best neighbour
|
||||
best_rsrp = neighbourCellRSRP;
|
||||
LOG_I(NR_RRC, "HO LOG: Serving Cell RSRP: %d - Best Neighbor RSRP: %d ! Trigger N2 HO\n", servingCellRSRP, best_rsrp);
|
||||
nr_rrc_trigger_n2_ho(rrc, ue, scell_pci, neighbour);
|
||||
}
|
||||
LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id);
|
||||
}
|
||||
} else if (neigh_cell && neighbour) {
|
||||
/* we know the cell and are connected to the DU! */
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[0];
|
||||
nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid);
|
||||
DevAssert(source_du);
|
||||
nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid);
|
||||
@@ -1459,7 +1467,6 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
|
||||
LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbour_pci);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
default:
|
||||
LOG_D(NR_RRC, "NR_EventTriggerConfig__eventId_PR_NOTHING or Other event report\n");
|
||||
@@ -1600,21 +1607,14 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
|
||||
UE->UE_Capability_nr = 0;
|
||||
}
|
||||
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL,
|
||||
&asn_DEF_NR_UE_NR_Capability,
|
||||
(void **)&UE->UE_Capability_nr,
|
||||
ue_cap_container->ue_CapabilityRAT_Container.buf,
|
||||
ue_cap_container->ue_CapabilityRAT_Container.size,
|
||||
0,
|
||||
0);
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
|
||||
UE->UE_Capability_nr = decode_nr_ue_capability(UE->rnti, ue_CapabilityRAT_ContainerList);
|
||||
if (!UE->UE_Capability_nr) {
|
||||
LOG_E(NR_RRC, "UE %d: NR capability decoding failed\n", UE->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
|
||||
LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
|
||||
UE->UE_Capability_nr = 0;
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
|
||||
}
|
||||
|
||||
const NR_RedCapParameters_r17_t *redcap_p = get_redcapparam_r17(UE->UE_Capability_nr);
|
||||
@@ -2076,9 +2076,28 @@ static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_
|
||||
}
|
||||
}
|
||||
|
||||
/* \brief use list of DRBs and send the corresponding bearer update message via
|
||||
* E1 to the CU of this UE. Also updates TEID info internally */
|
||||
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
|
||||
static void fill_e1_bearer_modif_pdcp_status(gNB_RRC_UE_t *UE,
|
||||
DRB_nGRAN_to_mod_t *drb_to_mod,
|
||||
const int drb_id,
|
||||
const bool um_on_default_drb,
|
||||
const ngap_drb_status_t *drb_status)
|
||||
{
|
||||
DevAssert(drb_status);
|
||||
drb_to_mod->id = drb_id;
|
||||
drb_to_mod->pdcp_sn_status_requested = false;
|
||||
// PDCP SN Status Information
|
||||
drb_to_mod->pdcp_config = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_config));
|
||||
set_bearer_context_pdcp_config(drb_to_mod->pdcp_config, get_drb(UE, drb_id), um_on_default_drb);
|
||||
drb_to_mod->pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_status));
|
||||
drb_to_mod->pdcp_status->dl_count.hfn = drb_status->dl_count.hfn;
|
||||
drb_to_mod->pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn;
|
||||
drb_to_mod->pdcp_status->ul_count.hfn = drb_status->ul_count.hfn;
|
||||
drb_to_mod->pdcp_status->ul_count.sn = drb_status->ul_count.pdcp_sn;
|
||||
}
|
||||
|
||||
/** @brief Fill and send Bearer Context Modification Request with:
|
||||
* (1) from F1 UE Context DRB to setup list, or (2) with PDCP Status Request */
|
||||
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status)
|
||||
{
|
||||
// Quit bearer updates if no CU-UP is associated
|
||||
if (!is_cuup_associated(rrc)) {
|
||||
@@ -2091,20 +2110,37 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f
|
||||
.gNB_cu_up_ue_id = UE->rrc_ue_id,
|
||||
};
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
|
||||
rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->id);
|
||||
if (pdu_ue == NULL) {
|
||||
LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %d\n", UE->rrc_ue_id, drb_f1->id);
|
||||
continue;
|
||||
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
|
||||
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
|
||||
int drb_id = i + 1;
|
||||
DRB_nGRAN_to_mod_t drb_to_mod = {0};
|
||||
if (n > 0) {
|
||||
/* DRBs to Setup in F1 UE Context Modifcation Response */
|
||||
if (i == n)
|
||||
break;
|
||||
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
|
||||
drb_id = drb_f1->id;
|
||||
fill_e1_bearer_modif(&drb_to_mod, drb_f1);
|
||||
} else if (is_inter_cu_ho) {
|
||||
/* On-going handover: send PDCP Status Request */
|
||||
if (UE->established_drbs[i].status == DRB_INACTIVE)
|
||||
continue;
|
||||
drb_to_mod.id = drb_id;
|
||||
// PDCP SN Status Request
|
||||
drb_to_mod.pdcp_sn_status_requested = true;
|
||||
LOG_I(NR_RRC, "PDCP Status requested (drb_id=%d)\n", drb_id);
|
||||
} else if (drb_status) {
|
||||
/* On-going handover: send PDCP Status */
|
||||
fill_e1_bearer_modif_pdcp_status(UE, &drb_to_mod, drb_id, rrc->configuration.um_on_default_drb, drb_status);
|
||||
}
|
||||
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
|
||||
DevAssert(pdu_e1 != NULL);
|
||||
pdu_e1->sessionId = pdu_ue->param.pdusession_id;
|
||||
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
|
||||
/* Fill E1 bearer context modification */
|
||||
fill_e1_bearer_modif(drb_e1, drb_f1);
|
||||
pdu_e1->numDRB2Modify += 1;
|
||||
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(UE, drb_id);
|
||||
if (!pdu)
|
||||
continue;
|
||||
pdu_session_to_mod_t *to_mod = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
|
||||
DevAssert(to_mod);
|
||||
to_mod->sessionId = pdu->param.pdusession_id;
|
||||
// Fill E1 DRB to Modify item
|
||||
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
|
||||
}
|
||||
DevAssert(req.numPDUSessionsMod > 0);
|
||||
DevAssert(req.numPDUSessions == 0);
|
||||
@@ -2169,7 +2205,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
|
||||
AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
|
||||
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
|
||||
if (num_drb == 0)
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
|
||||
else
|
||||
cuup_notify_reestablishment(rrc, UE);
|
||||
}
|
||||
@@ -2300,9 +2336,10 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
|
||||
}
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
if (resp->drbs_len > 0) {
|
||||
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
|
||||
if (resp->drbs_len > 0 || is_inter_cu_ho) {
|
||||
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
|
||||
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
|
||||
}
|
||||
|
||||
if (resp->du_to_cu_rrc_info) {
|
||||
@@ -2550,6 +2587,13 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
|
||||
}
|
||||
}
|
||||
|
||||
// If HO Preparation Info is stored, N2 handover is ongoing
|
||||
if (UE->ho_context) {
|
||||
LOG_I(NR_RRC, "Received Bearer Context Setup Response for UE %d with valid HO Context\n", UE->rrc_ue_id);
|
||||
UE->ho_context->target->ho_trigger(rrc, UE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UE->f1_ue_context_active)
|
||||
rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp);
|
||||
else
|
||||
@@ -2578,10 +2622,23 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
|
||||
return;
|
||||
}
|
||||
|
||||
// there is not really anything to do here as of now
|
||||
int n_drb_mod = 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];
|
||||
LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
|
||||
for (int j = 0; j < pdu->numDRBModified; j++) {
|
||||
// Trigger UL RAN Status Transfer
|
||||
if (pdu->DRBnGRanModList[j].pdcp_status) {
|
||||
pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
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, pdcp_status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2819,6 +2876,10 @@ void *rrc_gnb_task(void *args_p) {
|
||||
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
|
||||
break;
|
||||
|
||||
case NGAP_DL_RAN_STATUS_TRANSFER:
|
||||
rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(msg_p, instance);
|
||||
break;
|
||||
|
||||
/* Messages from F1AP task */
|
||||
case F1AP_SETUP_REQ:
|
||||
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
|
||||
@@ -2938,6 +2999,16 @@ void *rrc_gnb_task(void *args_p) {
|
||||
rrc_gNB_process_PAGING_IND(msg_p, instance);
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_REQUEST:
|
||||
rrc_gNB_process_Handover_Request(RC.nrrrc[instance], instance, &NGAP_HANDOVER_REQUEST(msg_p));
|
||||
rrc_gNB_free_Handover_Request(&NGAP_HANDOVER_REQUEST(msg_p)); // Free transfered NG message
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_COMMAND:
|
||||
rrc_gNB_process_HandoverCommand(RC.nrrrc[instance], &NGAP_HANDOVER_COMMAND(msg_p));
|
||||
rrc_gNB_free_Handover_Command(&NGAP_HANDOVER_COMMAND(msg_p)); // Free transfered NG message
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
|
||||
break;
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
#include <arpa/inet.h>
|
||||
#include "E1AP_ConfidentialityProtectionIndication.h"
|
||||
#include "E1AP_IntegrityProtectionIndication.h"
|
||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||
#include "NR_HandoverCommand.h"
|
||||
#include "NR_HandoverCommand-IEs.h"
|
||||
#include "NGAP_CauseRadioNetwork.h"
|
||||
#include "NGAP_Dynamic5QIDescriptor.h"
|
||||
#include "NGAP_GTPTunnel.h"
|
||||
@@ -49,6 +52,7 @@
|
||||
#include "NGAP_QosFlowSetupRequestItem.h"
|
||||
#include "NGAP_asn_constant.h"
|
||||
#include "NGAP_ProtocolIE-Field.h"
|
||||
#include "NGAP_CellSize.h"
|
||||
#include "NR_UE-NR-Capability.h"
|
||||
#include "NR_UERadioAccessCapabilityInformation.h"
|
||||
#include "MAC/mac.h"
|
||||
@@ -82,6 +86,8 @@
|
||||
#include "rrc_messages_types.h"
|
||||
#include "s1ap_messages_types.h"
|
||||
#include "uper_encoder.h"
|
||||
#include "rrc_gNB_mobility.h"
|
||||
#include "rrc_gNB_du.h"
|
||||
|
||||
#ifdef E2_AGENT
|
||||
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
||||
@@ -162,18 +168,24 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
|
||||
return guami;
|
||||
}
|
||||
|
||||
/** @brief Copy NGAP PDU Session Transfer item to RRC pdusession_t struct */
|
||||
static void cp_pdusession_transfer_to_pdusession(pdusession_t *dst, const pdusession_transfer_t *src)
|
||||
{
|
||||
dst->nb_qos = src->nb_qos;
|
||||
for (uint8_t i = 0; i < src->nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
|
||||
dst->qos[i] = src->qos[i];
|
||||
}
|
||||
dst->pdu_session_type = src->pdu_session_type;
|
||||
dst->n3_incoming = src->n3_incoming;
|
||||
}
|
||||
|
||||
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct */
|
||||
static void cp_pdusession_resource_item_to_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
|
||||
{
|
||||
dst->pdusession_id = src->pdusession_id;
|
||||
dst->nas_pdu = src->nas_pdu;
|
||||
dst->nb_qos = src->pdusessionTransfer.nb_qos;
|
||||
for (uint8_t i = 0; i < src->pdusessionTransfer.nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
|
||||
dst->qos[i] = src->pdusessionTransfer.qos[i];
|
||||
}
|
||||
dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type;
|
||||
dst->n3_incoming = src->pdusessionTransfer.n3_incoming;
|
||||
dst->nssai = src->nssai;
|
||||
cp_pdusession_transfer_to_pdusession(dst, &src->pdusessionTransfer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1062,7 +1074,168 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** @brief Sends the NG Handover Failure from the Target NG-RAN to the AMF */
|
||||
void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg)
|
||||
{
|
||||
LOG_I(NR_RRC, "Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", msg->amf_ue_ngap_id, msg->cause.value);
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_FAILURE);
|
||||
NGAP_HANDOVER_FAILURE(msg_p) = *msg;
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
/** @brief Process NG Handover Request message (8.4.2.2 3GPP TS 38.413) */
|
||||
int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg)
|
||||
{
|
||||
LOG_I(NR_RRC, "Received Handover Request (on PCI=%lu) \n", msg->nr_cell_id);
|
||||
struct nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, msg->nr_cell_id);
|
||||
if (du == NULL) {
|
||||
/* Cell Not Found! Return HO Request Failure*/
|
||||
LOG_E(RRC, "Failed to process Handover Request: no DU found with PCI=%lu \n", msg->nr_cell_id);
|
||||
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_RADIO_RESOURCES_NOT_AVAILABLE,
|
||||
};
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create UE context
|
||||
sctp_assoc_t curr_assoc_id = du->assoc_id;
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_create_ue_context(curr_assoc_id, UINT16_MAX, rrc, UINT64_MAX, UINT32_MAX);
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
// allocate context for target
|
||||
if (UE->ho_context != NULL) {
|
||||
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", UE->rrc_ue_id);
|
||||
return -1;
|
||||
}
|
||||
UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET);
|
||||
UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target;
|
||||
|
||||
// Store IDs in UE context
|
||||
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
|
||||
UE->ue_guami = msg->guami;
|
||||
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);
|
||||
UE->ue_cap_buffer = copy_byte_array(msg->ue_cap);
|
||||
|
||||
/* store the received Security Context in the UE context
|
||||
and take it into use as defined in TS 33.501 */
|
||||
set_UE_security_algos(rrc, UE, &msg->security_capabilities);
|
||||
UE->nh_ncc = msg->security_context.next_hop_chain_count;
|
||||
memcpy(UE->nh, msg->security_context.next_hop, SECURITY_KEY_LENGTH);
|
||||
// Reset KgNB
|
||||
memset(UE->kgnb, 0, SECURITY_KEY_LENGTH);
|
||||
// Derive KgNB*
|
||||
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
|
||||
uint32_t ssb_arfcn = get_ssb_arfcn(du);
|
||||
nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, UE->nh, UE->kgnb);
|
||||
UE->as_security_active = true;
|
||||
// Activate SRBs
|
||||
activate_srb(UE, SRB1);
|
||||
activate_srb(UE, SRB2);
|
||||
nr_rrc_pdcp_config_security(UE, false);
|
||||
|
||||
// Copy PDU Session Resource Setup item to RRC struct and do PDU Session Resource Setup procedure
|
||||
pdusession_t to_setup = {
|
||||
.nssai = msg->pduSessionResourceSetupList->nssai,
|
||||
.pdu_session_type = msg->pduSessionResourceSetupList->pdu_session_type ,
|
||||
.pdusession_id = msg->pduSessionResourceSetupList->pdusession_id,
|
||||
};
|
||||
cp_pdusession_transfer_to_pdusession(&to_setup, &msg->pduSessionResourceSetupList->pdusessionTransfer);
|
||||
if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, &to_setup, msg->ue_ambr.br_dl)) {
|
||||
LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\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_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
|
||||
};
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg)
|
||||
{
|
||||
free_byte_array(msg->ue_cap);
|
||||
free_byte_array(msg->ue_ho_prep_info);
|
||||
free(msg->mobility_restriction);
|
||||
}
|
||||
|
||||
/** @brief Send NG Uplink RAN Status Transfer message (8.4.6 3GPP TS 38.413)
|
||||
* Direction: source NG-RAN node -> AMF */
|
||||
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status)
|
||||
{
|
||||
AssertFatal(UE != NULL, "UE context is NULL\n");
|
||||
|
||||
LOG_I(NR_RRC,
|
||||
"Sending NGAP Uplink RAN Status Transfer (AMF_UE_NGAP_ID=%" PRIu64 ", GNB_UE_NGAP_ID=%u)\n",
|
||||
UE->amf_ue_ngap_id,
|
||||
UE->rrc_ue_id);
|
||||
|
||||
ngap_ran_status_transfer_t msg = {
|
||||
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
|
||||
.gnb_ue_ngap_id = UE->rrc_ue_id,
|
||||
};
|
||||
|
||||
// Loop through DRBs and extract COUNT values
|
||||
for (int i = 0; i < n_to_mod; ++i) {
|
||||
drb_t *drb = &UE->established_drbs[i];
|
||||
if (!drb->status)
|
||||
continue;
|
||||
|
||||
bool sn_length_18 = rrc->pdcp_config.drb.sn_size == 18;
|
||||
|
||||
ngap_drb_status_t *item = &msg.ran_status.drb_status_list[msg.ran_status.nb_drb++];
|
||||
item->drb_id = drb->drb_id;
|
||||
|
||||
const e1_pdcp_count_t *ul_pdcp = &pdcp_status[i].ul_count;
|
||||
const e1_pdcp_count_t *dl_pdcp = &pdcp_status[i].dl_count;
|
||||
|
||||
item->ul_count.pdcp_sn = ul_pdcp->sn;
|
||||
item->ul_count.hfn = ul_pdcp->hfn;
|
||||
item->ul_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
|
||||
|
||||
item->dl_count.pdcp_sn = dl_pdcp->sn;
|
||||
item->dl_count.hfn = dl_pdcp->hfn;
|
||||
item->dl_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
|
||||
}
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UL_RAN_STATUS_TRANSFER);
|
||||
NGAP_UL_RAN_STATUS_TRANSFER(msg_p) = msg;
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Process NG Handover Command on Source gNB */
|
||||
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg)
|
||||
{
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, msg->gNB_ue_ngap_id);
|
||||
|
||||
if (ue_context_p == NULL) {
|
||||
LOG_W(NR_RRC, "Unknown UE context associated to gNB_ue_ngap_id (%u)\n", msg->gNB_ue_ngap_id);
|
||||
return;
|
||||
}
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
uint8_t buffer[NR_RRC_BUF_SIZE];
|
||||
byte_array_t ba = {.buf = buffer, .len = sizeof(buffer)};
|
||||
int enc = doRRCReconfiguration_from_HandoverCommand(&ba, msg->handoverCommand);
|
||||
DevAssert(enc > 0);
|
||||
|
||||
rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer, enc);
|
||||
LOG_A(NR_RRC, "Send reconfiguration (HO Command) to UE %u/RNTI %04x\n", UE->rrc_ue_id, UE->rnti);
|
||||
}
|
||||
|
||||
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg)
|
||||
{
|
||||
free_byte_array(msg->handoverCommand);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the NG command NGAP_UE_CONTEXT_RELEASE_COMMAND, sent by AMF.
|
||||
* The gNB should remove all pdu session, NG context, and other context of the UE.
|
||||
@@ -1172,6 +1345,88 @@ void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
|
||||
LOG_I(NR_RRC,"Send message to ngap: NGAP_UE_CAPABILITIES_IND\n");
|
||||
}
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command)
|
||||
{
|
||||
LOG_D(NR_RRC, "Sending Handover Request Acknowledge\n");
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUEST_ACKNOWLEDGE);
|
||||
ngap_handover_request_ack_t *msg = &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(msg_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
// RAN UE NGAP ID
|
||||
msg->gNB_ue_ngap_id = UE->rrc_ue_id;
|
||||
// AMF UE NGAP ID
|
||||
msg->amf_ue_ngap_id = UE->amf_ue_ngap_id;
|
||||
// PDU Session Resource Admitted List
|
||||
msg->nb_of_pdusessions = UE->nb_of_pdusessions;
|
||||
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
|
||||
rrc_pdu_session_param_t *session = &UE->pduSession[i];
|
||||
session->status = PDU_SESSION_STATUS_ESTABLISHED;
|
||||
// PDU Session ID
|
||||
msg->pdusessions[i].pdu_session_id = session->param.pdusession_id;
|
||||
// Handover Request Acknowledge Transfer
|
||||
ho_request_ack_transfer_t *transfer = &msg->pdusessions[i].ack_transfer;
|
||||
transfer->gtp_teid = session->param.n3_outgoing.teid;
|
||||
memcpy(transfer->gNB_addr.buffer, session->param.n3_outgoing.addr.buffer, session->param.n3_outgoing.addr.length);
|
||||
transfer->gNB_addr.length = session->param.n3_outgoing.addr.length;
|
||||
transfer->nb_of_qos_flow = session->param.nb_qos;
|
||||
for (int q = 0; q < transfer->nb_of_qos_flow; q++) {
|
||||
transfer->qos_setup_list[q].qfi = session->param.qos[q].qfi;
|
||||
transfer->qos_setup_list[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
|
||||
}
|
||||
}
|
||||
// Target to Source Transparent Container
|
||||
msg->target2source = copy_byte_array(ho_command);
|
||||
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
/** @brief Prepare NG Handover Notify message and inform NGAP */
|
||||
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n");
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY);
|
||||
ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p);
|
||||
memset(ho_notify, 0, sizeof(*ho_notify));
|
||||
nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid);
|
||||
|
||||
ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id;
|
||||
ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id;
|
||||
ho_notify->user_info.nrCellIdentity = rrc->nr_cellid;
|
||||
ho_notify->user_info.target_ng_ran.tac = *du->setup_req->cell->info.tac;
|
||||
ho_notify->user_info.target_ng_ran.targetgNBId = rrc->node_id;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mcc = du->setup_req->cell->info.plmn.mcc;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mnc = du->setup_req->cell->info.plmn.mnc;
|
||||
ho_notify->user_info.target_ng_ran.plmn_identity.mnc_digit_length = du->setup_req->cell->info.plmn.mnc_digit_length;
|
||||
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
/** @brief Prepare NG Handover Cancel (source NG-RAN) and inform NGAP (N2-based HO only)
|
||||
* 3GPP TS 38.413 §8.4.5 (Handover Cancellation) - source NG-RAN -> AMF
|
||||
* 3GPP TS 38.413 §9.2.3.11 (HANDOVER CANCEL)
|
||||
* @param rrc CU/RRC instance
|
||||
* @param UE UE context
|
||||
* @param cause cancellation cause */
|
||||
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause)
|
||||
{
|
||||
DevAssert(UE != NULL);
|
||||
|
||||
LOG_I(NR_RRC, "Triggering NGAP Handover Cancel (gNB_ue_ngap_id=%d, amf_ue_ngap_id=%ld)\n", UE->rrc_ue_id, UE->amf_ue_ngap_id);
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_CANCEL);
|
||||
ngap_handover_cancel_t *ho_cancel = &NGAP_HANDOVER_CANCEL(msg_p);
|
||||
memset(ho_cancel, 0, sizeof(*ho_cancel));
|
||||
|
||||
/* Mandatory IEs (38.413 §9.2.3.11) */
|
||||
ho_cancel->gNB_ue_ngap_id = UE->rrc_ue_id;
|
||||
ho_cancel->amf_ue_ngap_id = UE->amf_ue_ngap_id;
|
||||
ho_cancel->cause = cause;
|
||||
|
||||
itti_send_msg_to_task(TASK_NGAP, module_id, msg_p);
|
||||
}
|
||||
|
||||
void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
|
||||
{
|
||||
int pdu_sessions_released = 0;
|
||||
@@ -1297,3 +1552,119 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Callback for NGAP Handover Required message (3GPP TS 38.413 9.2.3.1)
|
||||
* Direction: source gNB -> AMF */
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *UE,
|
||||
const nr_neighbour_cell_t *neighbour,
|
||||
const byte_array_t hoPrepInfo)
|
||||
{
|
||||
LOG_I(NR_RRC, "Handover Preparation: send Handover Required (target gNB ID=%d, PCI=%d)\n", neighbour->gNB_ID, neighbour->physicalCellId);
|
||||
|
||||
const plmn_id_t plmn = {.mcc = neighbour->plmn.mnc,
|
||||
.mnc = neighbour->plmn.mnc,
|
||||
.mnc_digit_length = neighbour->plmn.mnc_digit_length};
|
||||
|
||||
const target_ran_node_id_t target = {
|
||||
.plmn_identity = plmn,
|
||||
.tac = neighbour->tac,
|
||||
.targetgNBId = neighbour->gNB_ID,
|
||||
};
|
||||
|
||||
ngap_handover_required_t msg = {
|
||||
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
|
||||
.gNB_ue_ngap_id = UE->rrc_ue_id,
|
||||
.nb_of_pdusessions = UE->nb_of_pdusessions,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON,
|
||||
.handoverType = HANDOVER_TYPE_INTRA5GS,
|
||||
.target_gnb_id = target,
|
||||
};
|
||||
|
||||
cell_id_t target_cell = {
|
||||
.nrCellIdentity = neighbour->nrcell_id,
|
||||
.plmn_identity = plmn,
|
||||
};
|
||||
|
||||
// Source to Target Transparent Container (M)
|
||||
msg.source2target = calloc_or_fail(1, sizeof(*msg.source2target));
|
||||
// Target Cell ID (M)
|
||||
msg.source2target->targetCellId = target_cell;
|
||||
// RRC Container (M)
|
||||
msg.source2target->handoverInfo = copy_byte_array(hoPrepInfo);
|
||||
// UE History Info (M)
|
||||
msg.source2target->ue_history_info.cause = malloc_or_fail(sizeof(*msg.source2target->ue_history_info.cause));
|
||||
msg.source2target->ue_history_info.cause->type = NGAP_CAUSE_RADIO_NETWORK;
|
||||
msg.source2target->ue_history_info.cause->value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON;
|
||||
msg.source2target->ue_history_info.type = NGAP_CellSize_small;
|
||||
msg.source2target->ue_history_info.time_in_cell = 500; // dummy number for now
|
||||
msg.source2target->ue_history_info.id = target_cell;
|
||||
|
||||
/* Fill both PDU Session Resource List IE (M) in Handover Required
|
||||
and PDU Session Resource Information List IE (O) in the Source NG-RAN
|
||||
Node to Target NG-RAN Node Transparent Container */
|
||||
for (int i = 0; i < UE->nb_of_pdusessions; ++i) {
|
||||
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE || UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED) {
|
||||
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
|
||||
if (!pduSession)
|
||||
continue;
|
||||
// Handover Required Transfer (M)
|
||||
uint8_t ho_required_transfer[128] = {0};
|
||||
msg.pdusessions[i].ho_required_transfer.buf = ho_required_transfer;
|
||||
msg.pdusessions[i].ho_required_transfer.len = sizeof(ho_required_transfer);
|
||||
// PDU Session ID
|
||||
msg.pdusessions[i].pdusession_id = pduSession->param.pdusession_id;
|
||||
|
||||
// PDU Session Resource Information List (O)
|
||||
msg.source2target->pdu_session_resource[i].nb_of_qos_flow = pduSession->param.nb_qos;
|
||||
for (int j = 0; j < pduSession->param.nb_qos; ++j) {
|
||||
msg.source2target->pdu_session_resource[i].qos_flow_info[j].qfi = pduSession->param.qos[j].qfi;
|
||||
}
|
||||
msg.source2target->nb_pdu_session_resource++;
|
||||
}
|
||||
}
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUIRED);
|
||||
NGAP_HANDOVER_REQUIRED(msg_p) = msg;
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance)
|
||||
{
|
||||
const ngap_ran_status_transfer_t *cmd = &NGAP_DL_RAN_STATUS_TRANSFER(msg_p);
|
||||
gNB_RRC_INST *rrc = RC.nrrrc[instance];
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, cmd->gnb_ue_ngap_id);
|
||||
|
||||
if (!ue_context_p) {
|
||||
LOG_E(NR_RRC, "[gNB %ld] No UE context for gNB_ue_ngap_id %u\n", instance, cmd->gnb_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
LOG_I(NR_RRC,
|
||||
"[gNB %ld] DL RAN Status Transfer for gNB_ue_ngap_id %u AMF_UE_NGAP_ID %lu\n",
|
||||
instance,
|
||||
cmd->gnb_ue_ngap_id,
|
||||
cmd->amf_ue_ngap_id);
|
||||
|
||||
for (int i = 0; i < cmd->ran_status.nb_drb; ++i) {
|
||||
const ngap_drb_status_t *s = &cmd->ran_status.drb_status_list[i];
|
||||
LOG_I(NR_RRC,
|
||||
"DL RAN Status Transfer - DRB ID %d:\n"
|
||||
" UL COUNT: PDCP SN = %u, HFN = %u (%s)\n"
|
||||
" DL COUNT: PDCP SN = %u, HFN = %u (%s)\n",
|
||||
s->drb_id,
|
||||
s->ul_count.pdcp_sn,
|
||||
s->ul_count.hfn,
|
||||
s->ul_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit",
|
||||
s->dl_count.pdcp_sn,
|
||||
s->dl_count.hfn,
|
||||
s->dl_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit");
|
||||
|
||||
// Send to PDCP layer
|
||||
e1_send_bearer_updates(rrc, UE, 0, NULL, s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -89,4 +89,26 @@ void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering);
|
||||
|
||||
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance);
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *UE,
|
||||
const nr_neighbour_cell_t *neighbourCellConfiguration,
|
||||
const byte_array_t hoPrepInfo);
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg);
|
||||
|
||||
int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg);
|
||||
void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg);
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command);
|
||||
|
||||
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg);
|
||||
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg);
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE);
|
||||
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause);
|
||||
|
||||
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
|
||||
|
||||
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
|
||||
#endif
|
||||
|
||||
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
|
||||
static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
|
||||
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
|
||||
{
|
||||
nr_handover_context_t *ho_ctx = calloc_or_fail(1, sizeof(*ho_ctx));
|
||||
if (type == HO_CTX_SOURCE || type == HO_CTX_BOTH) {
|
||||
@@ -58,6 +57,8 @@ static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
|
||||
static void free_ho_ctx(nr_handover_context_t *ho_ctx)
|
||||
{
|
||||
free(ho_ctx->source);
|
||||
if (ho_ctx->target)
|
||||
FREE_AND_ZERO_BYTE_ARRAY(ho_ctx->target->ue_ho_prep_info);
|
||||
free(ho_ctx->target);
|
||||
free(ho_ctx);
|
||||
}
|
||||
@@ -121,18 +122,16 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
byte_array_t *ho_prep_info,
|
||||
ho_req_ack_t ack,
|
||||
ho_success_t success,
|
||||
ho_cancel_t cancel)
|
||||
ho_cancel_t cancel,
|
||||
ho_failure_t failure)
|
||||
{
|
||||
DevAssert(rrc != NULL);
|
||||
DevAssert(ue != NULL);
|
||||
DevAssert(target_du != NULL);
|
||||
// source_du might be NULL -> inter-CU handover
|
||||
DevAssert(ho_prep_info->buf != NULL && ho_prep_info->len > 0);
|
||||
DevAssert(ue->ho_context);
|
||||
|
||||
if (ue->ho_context != NULL) {
|
||||
LOG_E(NR_RRC, "handover for UE %u ongoing, cannot trigger new\n", ue->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
// if any reconfiguration is ongoing, abort handover request
|
||||
for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
|
||||
if (ue->xids[i] != RRC_ACTION_NONE) {
|
||||
@@ -141,15 +140,13 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
}
|
||||
}
|
||||
|
||||
// if we know the source DU (F1 handover), allocate the context for it,
|
||||
// otherwise only for the target DU (N2, Xn)
|
||||
ho_ctx_type_t ctx_type = source_du != NULL ? HO_CTX_BOTH : HO_CTX_TARGET;
|
||||
nr_handover_context_t *ho_ctx = alloc_ho_ctx(ctx_type);
|
||||
nr_handover_context_t *ho_ctx = ue->ho_context;
|
||||
ho_ctx->target->du = target_du;
|
||||
// we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup
|
||||
// response
|
||||
ho_ctx->target->ho_req_ack = ack;
|
||||
ho_ctx->target->ho_success = success;
|
||||
ho_ctx->target->ho_failure = failure;
|
||||
|
||||
const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
|
||||
if (source_du != NULL) {
|
||||
@@ -164,7 +161,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
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");
|
||||
}
|
||||
ue->ho_context = ho_ctx;
|
||||
|
||||
LOG_A(NR_RRC,
|
||||
"Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n",
|
||||
ue->rrc_ue_id,
|
||||
@@ -222,27 +219,12 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
free_ue_context_setup_req(&ue_context_setup_req);
|
||||
}
|
||||
|
||||
/*
|
||||
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, uint32_t *ho_prep_len, void *pdu_session)
|
||||
{
|
||||
// call from outside (via NGAP message), with the request for handover for a
|
||||
// (any) UE. This handler should, in order:
|
||||
//
|
||||
// 1. look up the target DU =du= via get_du_by_cell_id(nr_cgi)
|
||||
// (and reject the request if it can't find the DU)
|
||||
// 2. create a context for a new UE =UE=, based on pdu_session information
|
||||
// 3. call nr_initiate_handover(rrc, UE, NULL, du, ho_prep_info, ho_prep_len);
|
||||
//
|
||||
// after success of the DU, send the result via =success_ptr= to the source
|
||||
// CU.
|
||||
}
|
||||
*/
|
||||
|
||||
typedef struct deliver_ue_ctxt_modification_data_t {
|
||||
gNB_RRC_INST *rrc;
|
||||
f1ap_ue_context_mod_req_t *modification_req;
|
||||
sctp_assoc_t assoc_id;
|
||||
} deliver_ue_ctxt_modification_data_t;
|
||||
|
||||
static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
|
||||
{
|
||||
DevAssert(deliver_pdu_data != NULL);
|
||||
@@ -251,7 +233,8 @@ static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id,
|
||||
data->modification_req->rrc_container = &ba;
|
||||
data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
|
||||
}
|
||||
static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
|
||||
|
||||
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
|
||||
{
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
|
||||
|
||||
@@ -355,6 +338,13 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
|
||||
uint8_t buf[NR_RRC_BUF_SIZE];
|
||||
int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);
|
||||
|
||||
// Allocate handover context (both CU and DU)
|
||||
if (ue->ho_context != NULL) {
|
||||
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
ue->ho_context = alloc_ho_ctx(HO_CTX_BOTH);
|
||||
|
||||
// corresponds to a "handover request", 38.300 Sec 9.3.2.3
|
||||
// see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU
|
||||
// information (Source NG-RAN Node to Target NG-RAN Node Transparent Container)
|
||||
@@ -365,7 +355,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
|
||||
ho_success_t success = nr_rrc_f1_ho_complete;
|
||||
ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
|
||||
byte_array_t hpi = {.buf = buf, .len = size};
|
||||
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel);
|
||||
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL);
|
||||
}
|
||||
|
||||
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
|
||||
@@ -399,3 +389,224 @@ void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id)
|
||||
|
||||
nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
|
||||
}
|
||||
|
||||
/** @brief Generate the HandoverPreparationInformation to be carried
|
||||
* in the RRC Container (9.3.1.29 of 3GPP TS 38.413) of the Source
|
||||
* NG-RAN Node to Target NG-RAN Node Transparent Container IE */
|
||||
static byte_array_t rrc_gNB_generate_HandoverPreparationInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, int serving_pci)
|
||||
{
|
||||
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue, 0, false);
|
||||
params.ue_cap = ue->ue_cap_buffer;
|
||||
|
||||
byte_array_t hoPrepInfo = get_HandoverPreparationInformation(¶ms, serving_pci);
|
||||
free_RRCReconfiguration_params(params);
|
||||
|
||||
if (hoPrepInfo.len < 0) {
|
||||
LOG_E(NR_RRC, "HandoverPreparationInformation generation failed for UE %d\n", ue->rrc_ue_id);
|
||||
return hoPrepInfo;
|
||||
}
|
||||
|
||||
LOG_D(NR_RRC, "HO LOG: Handover Preparation for UE %lu Encoded (%zd bytes)\n", ue->amf_ue_ngap_id, hoPrepInfo.len);
|
||||
|
||||
return hoPrepInfo;
|
||||
}
|
||||
|
||||
static byte_array_t rrc_gNB_encode_HandoverCommand(gNB_RRC_UE_t *UE, gNB_RRC_INST *rrc)
|
||||
{
|
||||
DevAssert(UE->ho_context);
|
||||
DevAssert(UE->ho_context->target);
|
||||
|
||||
NR_SecurityConfig_t *sec = calloc_or_fail(1, sizeof(*sec));
|
||||
sec->keyToUse = calloc_or_fail(1, sizeof(*sec->keyToUse));
|
||||
*sec->keyToUse = NR_SecurityConfig__keyToUse_master;
|
||||
sec->securityAlgorithmConfig = calloc_or_fail(1, sizeof(*sec->securityAlgorithmConfig));
|
||||
struct NR_SecurityAlgorithmConfig *alg = sec->securityAlgorithmConfig;
|
||||
alg->cipheringAlgorithm = UE->ciphering_algorithm;
|
||||
alg->integrityProtAlgorithm = calloc_or_fail(1, sizeof(*alg->integrityProtAlgorithm));
|
||||
*alg->integrityProtAlgorithm = UE->integrity_algorithm;
|
||||
|
||||
// Mark source gNB's measurement configuration for removal in UE->measConfig
|
||||
if (UE->ho_context->target->ue_ho_prep_info.len) {
|
||||
fill_removal_lists_from_source_measConfig(UE->measConfig, UE->ho_context->target->ue_ho_prep_info);
|
||||
}
|
||||
|
||||
/* 3GPP TS 38.331 RadioBearerConfig: re-establish PDCP whenever
|
||||
the security key used for this radio bearer changes, e.g. for SRB2
|
||||
when receiving reconfiguration with sync [...]. Similarly, for DRBs
|
||||
PDCP re-establishment is necessary with derivation of new UP keys */
|
||||
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, UE, (1 << 1) | (1 << 2), true);
|
||||
UE->xids[params.transaction_id] = RRC_DEDICATED_RECONF;
|
||||
|
||||
params.security_config = sec;
|
||||
params.masterKeyUpdate = true;
|
||||
params.nextHopChainingCount = UE->nh_ncc;
|
||||
byte_array_t out = get_HandoverCommandMessage(¶ms);
|
||||
|
||||
// Free remove lists in UE->measConfig
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasIdToRemoveList, UE->measConfig->measIdToRemoveList);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_ReportConfigToRemoveList, UE->measConfig->reportConfigToRemoveList);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasObjectToRemoveList, UE->measConfig->measObjectToRemoveList);
|
||||
|
||||
free_RRCReconfiguration_params(params);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @brief This callback is used by the target gNB
|
||||
* to trigger the Handover Request Acknowledge towards the AMF */
|
||||
static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
AssertFatal(cu_exists_f1_ue_data(UE->rrc_ue_id), "No CU found for rrc_ue_id %d\n", UE->rrc_ue_id);
|
||||
|
||||
f1_ue_data_t previous_data = cu_get_f1_ue_data(UE->rrc_ue_id);
|
||||
/* this is the callback for N2 handover after F1 UE context setup response in
|
||||
* the handover case. Check that there was no associated DU, then set it */
|
||||
AssertFatal(previous_data.secondary_ue == -1, "there was already a DU present\n");
|
||||
previous_data.secondary_ue = UE->ho_context->target->du_ue_id;
|
||||
bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &previous_data);
|
||||
DevAssert(success);
|
||||
LOG_I(NR_RRC, "Updated CU F1AP Context for UE %d, DU Id : %u\n", UE->rrc_ue_id, UE->ho_context->target->du_ue_id);
|
||||
|
||||
byte_array_t hoCommand = rrc_gNB_encode_HandoverCommand(UE, rrc);
|
||||
if (hoCommand.len < 0) {
|
||||
LOG_E(NR_RRC, "ASN1 message encoding failed: failed to generate Handover Command Message\n");
|
||||
ngap_handover_failure_t fail = {.amf_ue_ngap_id = UE->amf_ue_ngap_id,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM};
|
||||
UE->ho_context->target->ho_failure(rrc, UE->rrc_ue_id, &fail);
|
||||
return;
|
||||
} else {
|
||||
LOG_D(NR_RRC, "HO LOG: Handover Command for UE %u Encoded (%ld bytes)\n", UE->rrc_ue_id, hoCommand.len);
|
||||
}
|
||||
|
||||
rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(rrc, UE, hoCommand);
|
||||
free_byte_array(hoCommand);
|
||||
}
|
||||
|
||||
/** @brief This callback is used by the target gNB
|
||||
* to trigger the Handover Notify towards the AMF */
|
||||
static void nr_rrc_n2_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
rrc_gNB_send_NGAP_HANDOVER_NOTIFY(rrc, UE);
|
||||
}
|
||||
|
||||
/** @brief This callback is used by the source gNB to inform the AMF
|
||||
* about the cancellation of an ongoing handover */
|
||||
static void nr_rrc_n2_ho_cancel(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
|
||||
{
|
||||
DevAssert(UE->ho_context);
|
||||
DevAssert(UE->ho_context->source);
|
||||
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_CANCELLED};
|
||||
rrc_gNB_send_NGAP_HANDOVER_CANCEL(rrc->module_id, UE, cause);
|
||||
}
|
||||
|
||||
/** @brief Callback function to trigger NG Handover Failure on the target gNB, to inform the AMF
|
||||
* that the preparation of resources has failed (e.g. unsatisfied criteria, gNB is already loaded).
|
||||
* This message represents an Unsuccessful Outcome of the Handover Resource Allocation */
|
||||
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg)
|
||||
{
|
||||
LOG_I(NR_RRC, "Triggering N2 Handover Failure\n");
|
||||
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, msg);
|
||||
LOG_I(NR_RRC, "Send UE Context Release for gnb_ue_id %d\n", gnb_ue_id);
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gnb_ue_id);
|
||||
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(rrc->module_id, ue_context_p, msg->cause);
|
||||
return;
|
||||
}
|
||||
|
||||
/** @brief Trigger N2 Handover on the target gNB:
|
||||
* 1) set up callbacks
|
||||
* 2) initiate N2 handover on target NG-RAN, which triggers the set up
|
||||
* of HO context and UE Context Setup procedures */
|
||||
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
|
||||
{
|
||||
ho_req_ack_t ack = nr_rrc_n2_ho_acknowledge;
|
||||
ho_success_t success = nr_rrc_n2_ho_complete;
|
||||
ho_failure_t failure = nr_rrc_n2_ho_failure;
|
||||
|
||||
const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id);
|
||||
nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure);
|
||||
FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info);
|
||||
|
||||
NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len);
|
||||
ue->UE_Capability_nr = ue_cap;
|
||||
ue->UE_Capability_size = ue->ue_cap_buffer.len;
|
||||
|
||||
}
|
||||
|
||||
/** @brief Trigger N2 handover on source gNB:
|
||||
* 1) Prepare RRC Container with HandoverPreparationInformation message
|
||||
* 2) send NGAP Handover Required message */
|
||||
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *ue,
|
||||
int serving_pci,
|
||||
const nr_neighbour_cell_t *neighbour_config)
|
||||
{
|
||||
byte_array_t hoPrepInfo = rrc_gNB_generate_HandoverPreparationInformation(rrc, ue, serving_pci);
|
||||
if (hoPrepInfo.len < 0) {
|
||||
free_byte_array(hoPrepInfo);
|
||||
LOG_E(NR_RRC, "Failed to trigger N2 handover on source gNB for UE %x\n", ue->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// allocate context for source
|
||||
if (ue->ho_context != NULL) {
|
||||
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ue->ho_context = alloc_ho_ctx(HO_CTX_SOURCE);
|
||||
ue->ho_context->source->du = get_du_for_ue(rrc, ue->rrc_ue_id);
|
||||
ue->ho_context->source->ho_status_transfer = rrc_gNB_send_NGAP_ul_ran_status_transfer;
|
||||
ue->ho_context->source->ho_cancel = nr_rrc_n2_ho_cancel;
|
||||
|
||||
rrc_gNB_send_NGAP_HANDOVER_REQUIRED(rrc, ue, neighbour_config, hoPrepInfo);
|
||||
free_byte_array(hoPrepInfo);
|
||||
}
|
||||
|
||||
extern const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci);
|
||||
extern const neighbour_cell_configuration_t *get_neighbour_cell_config(const gNB_RRC_INST *rrc, int cell_id);
|
||||
|
||||
void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t rrc_ue_id)
|
||||
{
|
||||
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id);
|
||||
if (ue_context_p == NULL) {
|
||||
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: cannot find UE context\n", rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
|
||||
|
||||
struct nr_rrc_du_container_t *du = get_du_for_ue(rrc, rrc_ue_id);
|
||||
if (du == NULL) {
|
||||
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: Unknown DU\n", rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
uint16_t scell_pci = du->setup_req->cell[0].info.nr_pci;
|
||||
|
||||
// Simulate handover on the same cell (testing purposes)
|
||||
if (neighbour_pci == scell_pci) {
|
||||
LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci);
|
||||
nr_neighbour_cell_t neighbourConfig = {
|
||||
.isIntraFrequencyNeighbour = true,
|
||||
.gNB_ID = du->setup_req->gNB_DU_id,
|
||||
.nrcell_id = du->setup_req->cell[0].info.nr_cellid,
|
||||
.physicalCellId = du->setup_req->cell[0].info.nr_pci,
|
||||
.plmn = du->setup_req->cell[0].info.plmn,
|
||||
.subcarrierSpacing = du->setup_req->cell[0].info.tdd.tbw.scs,
|
||||
};
|
||||
nr_rrc_trigger_n2_ho(rrc, UE, neighbour_pci, &neighbourConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
const f1ap_served_cell_info_t *scell_du = get_cell_information_by_phycellId(scell_pci);
|
||||
DevAssert(scell_du);
|
||||
LOG_I(NR_RRC, "UE %d: triggered N2 HO, source PCI=%d to target PCI=%d\n", rrc_ue_id, scell_pci, neighbour_pci);
|
||||
|
||||
const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, scell_du->nr_cellid);
|
||||
const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci);
|
||||
|
||||
if (neighbour == NULL) {
|
||||
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: could not find neighbour cell with PCI=%d\n", rrc_ue_id, neighbour_pci);
|
||||
return;
|
||||
}
|
||||
|
||||
nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "nr_rrc_defs.h"
|
||||
|
||||
/* forward declarations */
|
||||
typedef struct gNB_RRC_INST_s gNB_RRC_INST;
|
||||
@@ -32,6 +33,8 @@ typedef struct nr_rrc_du_container_t nr_rrc_du_container_t;
|
||||
typedef struct NR_CellGroupConfig NR_CellGroupConfig_t;
|
||||
|
||||
typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
typedef int (*ho_status_transfer_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
|
||||
|
||||
typedef struct nr_ho_source_cu {
|
||||
/// pointer to the (source) DU structure
|
||||
const nr_rrc_du_container_t *du;
|
||||
@@ -47,11 +50,16 @@ typedef struct nr_ho_source_cu {
|
||||
/// function pointer to announce the handover cancellation, e.g.,
|
||||
/// reestablishment
|
||||
ho_cancel_t ho_cancel;
|
||||
/// status transfer
|
||||
ho_status_transfer_t ho_status_transfer;
|
||||
} nr_ho_source_cu_t;
|
||||
|
||||
/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
|
||||
typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
|
||||
typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
|
||||
typedef struct nr_ho_target_cu {
|
||||
/// pointer to the (target) DU structure
|
||||
const nr_rrc_du_container_t *du;
|
||||
@@ -61,10 +69,16 @@ typedef struct nr_ho_target_cu {
|
||||
uint32_t du_ue_id;
|
||||
/// new (target) RNTI (as for du_ue_id)
|
||||
rnti_t new_rnti;
|
||||
/// Handover Preparation Buffer
|
||||
byte_array_t ue_ho_prep_info;
|
||||
/// function pointer to trigger handover on target gNB
|
||||
ho_trigger_t ho_trigger;
|
||||
/// function pointer to announce handover request acknowledgment
|
||||
ho_req_ack_t ho_req_ack;
|
||||
/// function pointer to announce handover success
|
||||
ho_success_t ho_success;
|
||||
/// function pointer to announce the handover failure
|
||||
ho_failure_t ho_failure;
|
||||
} nr_ho_target_cu_t;
|
||||
|
||||
typedef struct nr_handover_context_s {
|
||||
@@ -72,7 +86,20 @@ typedef struct nr_handover_context_s {
|
||||
nr_ho_target_cu_t *target;
|
||||
} nr_handover_context_t;
|
||||
|
||||
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
|
||||
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type);
|
||||
|
||||
void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du);
|
||||
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue);
|
||||
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
|
||||
|
||||
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *ue,
|
||||
int serving_pci,
|
||||
const nr_neighbour_cell_t *neighbour_config);
|
||||
|
||||
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len);
|
||||
|
||||
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
|
||||
|
||||
#endif /* RRC_GNB_MOBILITY_H_ */
|
||||
|
||||
@@ -37,6 +37,17 @@
|
||||
|
||||
typedef uint32_t channel_t;
|
||||
|
||||
void nr_mac_rrc_meas_ind_ue(module_id_t module_id, uint32_t gNB_index, uint16_t Nid_cell, bool csi_meas, bool is_neighboring_cell, uint8_t rsrp_dBm)
|
||||
{
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_MAC_UE, 0, NR_RRC_MAC_MEAS_DATA_IND);
|
||||
NR_RRC_MAC_MEAS_DATA_IND(message_p).gnb_index = gNB_index;
|
||||
NR_RRC_MAC_MEAS_DATA_IND(message_p).Nid_cell = Nid_cell;
|
||||
NR_RRC_MAC_MEAS_DATA_IND(message_p).is_csi_meas = csi_meas;
|
||||
NR_RRC_MAC_MEAS_DATA_IND(message_p).is_neighboring_cell = is_neighboring_cell;
|
||||
NR_RRC_MAC_MEAS_DATA_IND(message_p).rsrp_dBm = rsrp_dBm;
|
||||
itti_send_msg_to_task(TASK_RRC_NRUE, GNB_MODULE_ID_TO_INSTANCE(module_id), message_p);
|
||||
}
|
||||
|
||||
void nr_mac_rrc_sync_ind(const module_id_t module_id, const frame_t frame, const bool in_sync)
|
||||
{
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_MAC_UE, 0, NR_RRC_MAC_SYNC_IND);
|
||||
|
||||
@@ -37,6 +37,12 @@ void nr_mac_rrc_data_ind_ue(const module_id_t module_id,
|
||||
const channel_t channel,
|
||||
const uint8_t* pduP,
|
||||
const sdu_size_t pdu_len);
|
||||
void nr_mac_rrc_meas_ind_ue(module_id_t module_id,
|
||||
uint32_t gNB_index,
|
||||
uint16_t Nid_cell,
|
||||
bool csi_meas,
|
||||
bool is_neighboring_cell,
|
||||
uint8_t rsrp_dBm);
|
||||
void nr_mac_rrc_inactivity_timer_ind(const module_id_t mod_id);
|
||||
void nr_mac_rrc_msg3_ind(const module_id_t mod_id, const int rnti, bool prepare_payload);
|
||||
void nr_ue_rrc_timer_trigger(int instance, int frame, int gnb_id);
|
||||
|
||||
+282
-19
@@ -532,19 +532,74 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf
|
||||
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
|
||||
}
|
||||
|
||||
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
|
||||
static void nr_rrc_nh_update(NR_UE_RRC_INST_t *rrc, uint8_t *kamf, uint8_t *sync_input)
|
||||
{
|
||||
if (rec_1530->fullConfig) {
|
||||
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
|
||||
uint8_t nh[SECURITY_KEY_LEN] = {0};
|
||||
nr_derive_nh(kamf, sync_input, nh);
|
||||
log_hex_buffer("Sync input = stored NH", rrc->nh, SECURITY_KEY_LEN);
|
||||
memcpy(rrc->nh, nh, SECURITY_KEY_LEN);
|
||||
rrc->nhcc++; // Increase stored nextHopChainingCount
|
||||
}
|
||||
|
||||
/** @brief AS security key update procedure (5.3.5.7 3GPP TS 38.331) */
|
||||
void as_security_key_update(NR_UE_RRC_INST_t *rrc, NR_MasterKeyUpdate_t *mku)
|
||||
{
|
||||
if (mku->nas_Container) {
|
||||
LOG_E(NR_RRC, "forward the nas-Container to the upper layers: not implemented yet\n");
|
||||
}
|
||||
if (rec_1530->masterCellGroup)
|
||||
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
|
||||
if (rec_1530->masterKeyUpdate) {
|
||||
// TODO perform AS security key update procedure as specified in 5.3.5.7
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes masterKeyUpdate but this is not implemented yet\n");
|
||||
if (mku->keySetChangeIndicator) {
|
||||
LOG_E(NR_RRC, "derive or update the K gNB key based on the K AMF key, as specified in TS 33.501: not implemented yet\n");
|
||||
} else {
|
||||
/* derive or update the K gNB key based on the current K gNB key or the NH, using the nextHopChainingCount
|
||||
value indicated in the received masterKeyUpdate, as specified in 6.9.2.3.3 3GPP TS 33.501 */
|
||||
if (mku->nextHopChainingCount != rrc->nhcc) {
|
||||
// - If the UE received an NCC value that was different from the NCC associated with the currently active
|
||||
// K gNB/K eNB, the UE shall first synchronize the locally kept NH parameter by computing the function defined in
|
||||
// Annex A.10 iteratively (and increasing the NCC value until it matches the NCC value received from the source
|
||||
// ng-eNB/gNB via the HO command message.
|
||||
LOG_A(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount);
|
||||
/** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized
|
||||
* with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig
|
||||
* processing that triggers bearer modifications. Security configueration of bearers must
|
||||
* complete using the newly derived keys. As a workaround NAS is directly accessed here. */
|
||||
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
|
||||
uint8_t *kamf = nas->security.kamf;
|
||||
log_hex_buffer("Stored kamf", kamf, SECURITY_KEY_LEN);
|
||||
if (rrc->nhcc == 0) { // First derivation
|
||||
derive_kgnb(kamf, 0, rrc->kgnb);
|
||||
log_hex_buffer("Sync input = derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
|
||||
nr_rrc_nh_update(rrc, kamf, rrc->kgnb);
|
||||
}
|
||||
for (int i = rrc->nhcc; i < mku->nextHopChainingCount; i++) { // Following derivations
|
||||
LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i);
|
||||
nr_rrc_nh_update(rrc, kamf, rrc->nh);
|
||||
}
|
||||
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->nh, rrc->kgnb);
|
||||
// When the NCC values match, the UE shall compute the K NG-RAN *
|
||||
// from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL/EARFCN-DL using the
|
||||
// function defined in Annex A.11 and A.12.
|
||||
// The UE shall use the KNG-RAN * as the K gNB when communicating with the target gNB and as the KeNB when
|
||||
// communicating with the target ng-eNB.
|
||||
} else {
|
||||
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->kgnb);
|
||||
}
|
||||
log_hex_buffer("Derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
|
||||
}
|
||||
/* Check if there is dedicated NAS information to forward to NAS */
|
||||
}
|
||||
|
||||
static nr_pdcp_entity_security_keys_and_algos_t get_security_rrc_parameters(NR_UE_RRC_INST_t *ue, bool cp)
|
||||
{
|
||||
nr_pdcp_entity_security_keys_and_algos_t out = {0};
|
||||
out.ciphering_algorithm = ue->cipheringAlgorithm;
|
||||
out.integrity_algorithm = ue->integrityProtAlgorithm;
|
||||
nr_derive_key(cp ? RRC_ENC_ALG : UP_ENC_ALG, ue->cipheringAlgorithm, ue->kgnb, out.ciphering_key);
|
||||
nr_derive_key(cp ? RRC_INT_ALG : UP_INT_ALG, ue->integrityProtAlgorithm, ue->kgnb, out.integrity_key);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @brief Check if there is dedicated NAS information to forward to NAS */
|
||||
static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530)
|
||||
{
|
||||
if (rec_1530->dedicatedNAS_MessageList) {
|
||||
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *tmp = rec_1530->dedicatedNAS_MessageList;
|
||||
for (int i = 0; i < tmp->list.count; i++) {
|
||||
@@ -557,6 +612,21 @@ static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe
|
||||
}
|
||||
tmp->list.count = 0; // to prevent the automatic free by ASN1_FREE
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
|
||||
{
|
||||
if (rec_1530->fullConfig) {
|
||||
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
|
||||
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
|
||||
}
|
||||
if (rec_1530->masterCellGroup)
|
||||
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
|
||||
if (rec_1530->masterKeyUpdate) {
|
||||
as_security_key_update(rrc, rec_1530->masterKeyUpdate);
|
||||
nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true);
|
||||
nr_pdcp_config_set_security(rrc->ue_id, 1, true, &sp);
|
||||
}
|
||||
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
|
||||
if (rec_1530->dedicatedSIB1_Delivery) {
|
||||
NR_SIB1_t *sib1 = NULL;
|
||||
@@ -633,6 +703,9 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
|
||||
case NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration: {
|
||||
NR_RRCReconfiguration_IEs_t *ie = reconfiguration->criticalExtensions.choice.rrcReconfiguration;
|
||||
|
||||
if (ie->nonCriticalExtension)
|
||||
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
|
||||
|
||||
if (ie->radioBearerConfig) {
|
||||
LOG_I(NR_RRC, "RRCReconfiguration includes radio Bearer Configuration\n");
|
||||
nr_rrc_ue_process_RadioBearerConfig(rrc, ie->radioBearerConfig);
|
||||
@@ -640,8 +713,11 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
|
||||
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)ie->radioBearerConfig);
|
||||
}
|
||||
|
||||
/** @note This triggers PDU Session Establishment Accept which sets up the TUN interface.
|
||||
* SDAP entity is a pre-requisite, therefore the radioBearerConfig has to be processed
|
||||
* (in nr_rrc_ue_process_RadioBearerConfig, add_drb) early enough, or it may cause a race condition in SDAP. */
|
||||
if (ie->nonCriticalExtension)
|
||||
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
|
||||
nr_rrc_process_dedicatedNAS_MessageList(rrc, ie->nonCriticalExtension);
|
||||
|
||||
if (ie->secondaryCellGroup) {
|
||||
NR_CellGroupConfig_t *cellGroupConfig = NULL;
|
||||
@@ -807,6 +883,9 @@ NR_UE_RRC_INST_t* nr_rrc_init_ue(char* uecap_file, int instance_id, int num_ant_
|
||||
for (int i = 0; i < NB_CNX_UE; i++) {
|
||||
rrcPerNB_t *ptr = &rrc->perNB[i];
|
||||
ptr->SInfo = (NR_UE_RRC_SI_INFO){0};
|
||||
ptr->l3_measurements = (l3_measurements_t){0};
|
||||
ptr->l3_measurements.ssb_filter_coeff_rsrp = 1.0f;
|
||||
ptr->l3_measurements.csi_RS_filter_coeff_rsrp = 1.0f;
|
||||
init_SI_timers(&ptr->SInfo);
|
||||
}
|
||||
|
||||
@@ -1663,6 +1742,14 @@ static void handle_meas_reporting_remove(rrcPerNB_t *rrc, int id, NR_UE_Timers_C
|
||||
// TODO stop the periodical reporting timer or timer T321, whichever is running,
|
||||
// and reset the associated information (e.g. timeToTrigger) for this measId
|
||||
nr_timer_stop(&timers->T321);
|
||||
|
||||
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
|
||||
nr_timer_stop(&l3_measurements->TA2);
|
||||
nr_timer_stop(&l3_measurements->periodic_report_timer);
|
||||
|
||||
l3_measurements->reports_sent = 0;
|
||||
l3_measurements->max_reports = 0;
|
||||
l3_measurements->report_interval_ms = 0;
|
||||
}
|
||||
|
||||
static void handle_measobj_remove(rrcPerNB_t *rrc, struct NR_MeasObjectToRemoveList *remove_list, NR_UE_Timers_Constants_t *timers)
|
||||
@@ -1814,6 +1901,14 @@ static void handle_quantityconfig(rrcPerNB_t *rrc, NR_QuantityConfig_t *quantity
|
||||
if (!rrc->QuantityConfig[i])
|
||||
rrc->QuantityConfig[i] = calloc(1, sizeof(*rrc->QuantityConfig[i]));
|
||||
rrc->QuantityConfig[i]->quantityConfigCell = quantityNR->quantityConfigCell;
|
||||
// TODO: It remains to compute ssb_filter_coeff_rsrp and csi_RS_filter_coeff_rsrp for multiple quantityConfig
|
||||
// TS 38.331 - 5.5.3.2 Layer 3 filtering
|
||||
NR_QuantityConfigRS_t *qcc = &quantityNR->quantityConfigCell;
|
||||
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
|
||||
if (qcc->ssb_FilterConfig.filterCoefficientRSRP)
|
||||
l3_measurements->ssb_filter_coeff_rsrp = 1. / pow(2, (*qcc->ssb_FilterConfig.filterCoefficientRSRP) / 4);
|
||||
if (qcc->csi_RS_FilterConfig.filterCoefficientRSRP)
|
||||
l3_measurements->csi_RS_filter_coeff_rsrp = 1. / pow(2, (*qcc->csi_RS_FilterConfig.filterCoefficientRSRP) / 4);
|
||||
if (quantityNR->quantityConfigRS_Index)
|
||||
UPDATE_IE(rrc->QuantityConfig[i]->quantityConfigRS_Index, quantityNR->quantityConfigRS_Index, struct NR_QuantityConfigRS);
|
||||
}
|
||||
@@ -1940,14 +2035,8 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc,
|
||||
ue_rrc->integrityProtAlgorithm = *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm;
|
||||
}
|
||||
}
|
||||
security_rrc_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
|
||||
security_rrc_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
|
||||
nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_rrc_parameters.ciphering_key);
|
||||
nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_rrc_parameters.integrity_key);
|
||||
security_up_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
|
||||
security_up_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
|
||||
nr_derive_key(UP_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_up_parameters.ciphering_key);
|
||||
nr_derive_key(UP_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_up_parameters.integrity_key);
|
||||
security_rrc_parameters = get_security_rrc_parameters(ue_rrc, true);
|
||||
security_up_parameters = get_security_rrc_parameters(ue_rrc, false);
|
||||
}
|
||||
|
||||
if (radioBearerConfig->srb_ToAddModList != NULL) {
|
||||
@@ -2238,6 +2327,146 @@ static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apply_ema(val_init_t *vi_rsrp_dBm, float filter_coeff_rsrp, int rsrp_dBm)
|
||||
{
|
||||
int *quant = &vi_rsrp_dBm->val;
|
||||
bool *meas_init = &vi_rsrp_dBm->init;
|
||||
float coef = *meas_init ? filter_coeff_rsrp : 1.0f; // if not init, first measurement gets full weight
|
||||
*quant = (1.0f - coef) * (*quant) + coef * rsrp_dBm;
|
||||
*meas_init = true;
|
||||
}
|
||||
|
||||
void nr_ue_meas_filtering(rrcPerNB_t *rrc, bool is_neighboring_cell, uint16_t Nid_cell, bool csi_meas, int rsrp_dBm)
|
||||
{
|
||||
if (is_neighboring_cell)
|
||||
return;
|
||||
|
||||
l3_measurements_t *l3_measurements = &rrc->l3_measurements;
|
||||
meas_t *meas_cell = &l3_measurements->serving_cell;
|
||||
if (meas_cell->Nid_cell != Nid_cell) {
|
||||
meas_cell->ss_rsrp_dBm.init = false;
|
||||
meas_cell->csi_rsrp_dBm.init = false;
|
||||
}
|
||||
meas_cell->Nid_cell = Nid_cell;
|
||||
|
||||
if (csi_meas)
|
||||
apply_ema(&meas_cell->csi_rsrp_dBm, l3_measurements->csi_RS_filter_coeff_rsrp, rsrp_dBm);
|
||||
else
|
||||
apply_ema(&meas_cell->ss_rsrp_dBm, l3_measurements->ssb_filter_coeff_rsrp, rsrp_dBm);
|
||||
}
|
||||
|
||||
static long get_measurement_report_interval_ms(NR_ReportInterval_t interval)
|
||||
{
|
||||
switch (interval) {
|
||||
case NR_ReportInterval_ms120:
|
||||
return 120;
|
||||
case NR_ReportInterval_ms240:
|
||||
return 240;
|
||||
case NR_ReportInterval_ms480:
|
||||
return 480;
|
||||
case NR_ReportInterval_ms640:
|
||||
return 640;
|
||||
case NR_ReportInterval_ms1024:
|
||||
return 1024;
|
||||
case NR_ReportInterval_ms2048:
|
||||
return 2048;
|
||||
case NR_ReportInterval_ms5120:
|
||||
return 5120;
|
||||
case NR_ReportInterval_ms10240:
|
||||
return 10240;
|
||||
case NR_ReportInterval_min1:
|
||||
return 60000;
|
||||
case NR_ReportInterval_min6:
|
||||
return 360000;
|
||||
case NR_ReportInterval_min12:
|
||||
return 720000;
|
||||
case NR_ReportInterval_min30:
|
||||
return 1800000;
|
||||
default:
|
||||
return 1024;
|
||||
}
|
||||
}
|
||||
|
||||
static void nr_ue_check_meas_report(NR_UE_RRC_INST_t *rrc, const uint8_t gnb_index)
|
||||
{
|
||||
rrcPerNB_t *rrcNB = rrc->perNB + gnb_index;
|
||||
l3_measurements_t *l3_measurements = &rrcNB->l3_measurements;
|
||||
|
||||
for (int i = 0; i < MAX_MEAS_CONFIG; i++) {
|
||||
NR_ReportConfigToAddMod_t *report_config = rrcNB->ReportConfig[i];
|
||||
if (report_config == NULL)
|
||||
continue;
|
||||
|
||||
if (report_config->reportConfig.present != NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR)
|
||||
continue;
|
||||
|
||||
NR_ReportConfigNR_t *report_config_nr = report_config->reportConfig.choice.reportConfigNR;
|
||||
if (report_config_nr->reportType.present != NR_ReportConfigNR__reportType_PR_eventTriggered)
|
||||
continue;
|
||||
|
||||
NR_EventTriggerConfig_t *event_trigger_config = report_config_nr->reportType.choice.eventTriggered;
|
||||
if (event_trigger_config->eventId.present != NR_EventTriggerConfig__eventId_PR_eventA2)
|
||||
continue;
|
||||
|
||||
struct NR_EventTriggerConfig__eventId__eventA2 *event_A2 = event_trigger_config->eventId.choice.eventA2;
|
||||
if (event_A2->a2_Threshold.present != NR_MeasTriggerQuantity_PR_rsrp)
|
||||
continue;
|
||||
|
||||
meas_t *serving_cell = &l3_measurements->serving_cell;
|
||||
int serving_cell_rsrp = INT_MAX;
|
||||
if (serving_cell->ss_rsrp_dBm.init == true) {
|
||||
serving_cell_rsrp = serving_cell->ss_rsrp_dBm.val;
|
||||
} else if (serving_cell->csi_rsrp_dBm.init == true) {
|
||||
serving_cell_rsrp = serving_cell->csi_rsrp_dBm.val;
|
||||
} else {
|
||||
LOG_E(NR_RRC, "There are no RSRP measurements taken for the active cell\n");
|
||||
}
|
||||
|
||||
// TS 38.133 - Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping
|
||||
int rsrp_threshold = event_A2->a2_Threshold.choice.rsrp - 157;
|
||||
int rsrp_hysteresis = event_A2->hysteresis >> 1;
|
||||
|
||||
// TS 38.331 - 5.5.4.3 Event A2 (Serving becomes worse than threshold)
|
||||
if (serving_cell_rsrp + rsrp_hysteresis < rsrp_threshold) {
|
||||
if (!nr_timer_is_active(&l3_measurements->TA2) && (l3_measurements->reports_sent == 0)) {
|
||||
nr_timer_setup(&l3_measurements->TA2, get_A2_event_time_to_trigger(event_A2->timeToTrigger), 10);
|
||||
nr_timer_start(&l3_measurements->TA2);
|
||||
int report_config_id = report_config->reportConfigId;
|
||||
int meas_id = -1;
|
||||
for (int j = 0; j < MAX_MEAS_ID; j++) {
|
||||
NR_MeasIdToAddMod_t *meas_id_toAddMod = rrcNB->MeasId[j];
|
||||
if (meas_id_toAddMod == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (meas_id_toAddMod->reportConfigId == report_config_id) {
|
||||
meas_id = meas_id_toAddMod->measId;
|
||||
}
|
||||
}
|
||||
AssertFatal(meas_id > 0, "meas_id did not found for report_config_id %i\n", report_config_id);
|
||||
|
||||
l3_measurements->trigger_to_measid = meas_id;
|
||||
l3_measurements->trigger_quantity = event_A2->a2_Threshold.present;
|
||||
l3_measurements->rs_type = event_trigger_config->rsType;
|
||||
l3_measurements->reports_sent = 0;
|
||||
l3_measurements->max_reports = (event_trigger_config->reportAmount == NR_EventTriggerConfig__reportAmount_infinity)
|
||||
? INT_MAX
|
||||
: (1 << event_trigger_config->reportAmount);
|
||||
l3_measurements->report_interval_ms = get_measurement_report_interval_ms(event_trigger_config->reportInterval);
|
||||
|
||||
LOG_W(NR_RRC,
|
||||
"(active_cell_rsrp) %i + (rsrp_hysteresis) %i < (rsrp_threshold) %i\n",
|
||||
serving_cell_rsrp,
|
||||
rsrp_hysteresis,
|
||||
rsrp_threshold);
|
||||
}
|
||||
} else if (nr_timer_is_active(&l3_measurements->TA2) && (serving_cell_rsrp - rsrp_hysteresis > rsrp_threshold)) {
|
||||
nr_timer_stop(&l3_measurements->TA2);
|
||||
nr_timer_stop(&l3_measurements->periodic_report_timer);
|
||||
l3_measurements->reports_sent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nr_rrc_handle_ra_indication(NR_UE_RRC_INST_t *rrc, bool ra_succeeded)
|
||||
{
|
||||
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
|
||||
@@ -2354,6 +2583,21 @@ void *rrc_nrue(void *notUsed)
|
||||
|
||||
nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(rrc, sbcch->gnb_index,sbcch->frame, sbcch->slot, sbcch->sdu,
|
||||
sbcch->sdu_size, sbcch->rx_slss_id);
|
||||
case NR_RRC_MAC_MEAS_DATA_IND:
|
||||
|
||||
LOG_D(NR_RRC, "[%s][Nid_cell %i] Received %s measurements: RSRP = %i (dBm)\n",
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_neighboring_cell? "Neighboring cell" : "Active cell",
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).Nid_cell,
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_csi_meas ? "CSI meas" : "SSB meas",
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).rsrp_dBm - 157);
|
||||
|
||||
rrcPerNB_t *rrcNB = rrc->perNB + NR_RRC_MAC_MEAS_DATA_IND(msg_p).gnb_index;
|
||||
nr_ue_meas_filtering(rrcNB,
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_neighboring_cell,
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).Nid_cell,
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).is_csi_meas,
|
||||
NR_RRC_MAC_MEAS_DATA_IND(msg_p).rsrp_dBm - 157);
|
||||
nr_ue_check_meas_report(rrc, NR_RRC_MAC_MEAS_DATA_IND(msg_p).gnb_index);
|
||||
break;
|
||||
|
||||
case NR_RRC_MAC_CCCH_DATA_IND: {
|
||||
@@ -2992,3 +3236,22 @@ void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf)
|
||||
NR_UE_RRC_INST_t *rrc = get_NR_UE_rrc_inst(instance);
|
||||
rrc->mac_input_nf = mac_input_nf;
|
||||
}
|
||||
|
||||
void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id)
|
||||
{
|
||||
uint8_t buffer[NR_RRC_BUF_SIZE];
|
||||
NR_MeasurementReport_t measurementReport = {0};
|
||||
l3_measurements_t *l3m = &rrc->l3_measurements;
|
||||
int rsrp_dBm = l3m->rs_type == NR_NR_RS_Type_ssb ? l3m->serving_cell.ss_rsrp_dBm.val : l3m->serving_cell.csi_rsrp_dBm.val;
|
||||
uint8_t size = do_nrMeasurementReport_SA(&measurementReport,
|
||||
l3m->trigger_to_measid,
|
||||
l3m->trigger_quantity,
|
||||
l3m->rs_type,
|
||||
l3m->serving_cell.Nid_cell,
|
||||
rsrp_dBm,
|
||||
buffer,
|
||||
sizeof(buffer));
|
||||
|
||||
int srb_id = 1; // possibly TODO in SRB3 in some cases
|
||||
nr_pdcp_data_req_srb(ue_id, srb_id, 0, size, buffer, deliver_pdu_srb_rlc, NULL);
|
||||
}
|
||||
|
||||
@@ -192,6 +192,20 @@ typedef enum {
|
||||
|
||||
typedef enum { RB_NOT_PRESENT, RB_ESTABLISHED, RB_SUSPENDED } NR_RB_status_t;
|
||||
|
||||
typedef struct l3_measurements_s {
|
||||
float ssb_filter_coeff_rsrp;
|
||||
float csi_RS_filter_coeff_rsrp;
|
||||
meas_t serving_cell;
|
||||
long trigger_to_measid;
|
||||
long trigger_quantity;
|
||||
long rs_type;
|
||||
int reports_sent;
|
||||
int max_reports;
|
||||
long report_interval_ms;
|
||||
NR_timer_t TA2;
|
||||
NR_timer_t periodic_report_timer;
|
||||
} l3_measurements_t;
|
||||
|
||||
typedef struct rrcPerNB {
|
||||
NR_MeasObjectToAddMod_t *MeasObj[MAX_MEAS_OBJ];
|
||||
NR_ReportConfigToAddMod_t *ReportConfig[MAX_MEAS_CONFIG];
|
||||
@@ -201,6 +215,7 @@ typedef struct rrcPerNB {
|
||||
NR_MeasGapConfig_t *measGapConfig;
|
||||
NR_UE_RRC_SI_INFO SInfo;
|
||||
NR_RSRP_Range_t s_measure;
|
||||
l3_measurements_t l3_measurements;
|
||||
} rrcPerNB_t;
|
||||
|
||||
typedef struct NR_UE_RRC_INST_s {
|
||||
@@ -233,6 +248,10 @@ typedef struct NR_UE_RRC_INST_s {
|
||||
e_NR_IntegrityProtAlgorithm integrityProtAlgorithm;
|
||||
long keyToUse;
|
||||
bool as_security_activated;
|
||||
/// Next Hop Chaining Count
|
||||
uint8_t nh[32];
|
||||
uint64_t nhcc;
|
||||
|
||||
bool detach_after_release;
|
||||
NR_timer_t release_timer;
|
||||
NR_RRCRelease_t *RRCRelease;
|
||||
|
||||
@@ -55,6 +55,8 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc,
|
||||
|
||||
void handle_RRCRelease(NR_UE_RRC_INST_t *rrc);
|
||||
|
||||
void rrc_ue_generate_measurementReport(rrcPerNB_t *rrc, instance_t ue_id);
|
||||
|
||||
void set_rlf_sib1_timers_and_constants(NR_UE_Timers_Constants_t *tac, NR_UE_TimersAndConstants_t *ue_TimersAndConstants);
|
||||
|
||||
/**\brief RRC UE task.
|
||||
@@ -83,6 +85,7 @@ void nr_ue_rrc_timer_trigger(int module_id, int frame, int gnb_id);
|
||||
void handle_t300_expiry(NR_UE_RRC_INST_t *rrc);
|
||||
void handle_t430_expiry(NR_UE_RRC_INST_t *rrc);
|
||||
|
||||
int get_A2_event_time_to_trigger(long time_to_trigger);
|
||||
void reset_rlf_timers_and_constants(NR_UE_Timers_Constants_t *tac);
|
||||
void set_default_timers_and_constants(NR_UE_Timers_Constants_t *tac);
|
||||
void nr_rrc_set_sib1_timers_and_constants(NR_UE_Timers_Constants_t *tac, NR_SIB1_t *sib1);
|
||||
|
||||
@@ -122,6 +122,36 @@ void nr_rrc_SI_timers(NR_UE_RRC_SI_INFO *SInfo)
|
||||
}
|
||||
}
|
||||
|
||||
void handle_meas_timers(NR_UE_RRC_INST_t *rrc)
|
||||
{
|
||||
for (int i = 0; i < NB_CNX_UE; i++) {
|
||||
rrcPerNB_t *nb = &rrc->perNB[i];
|
||||
l3_measurements_t *l3_measurements = &nb->l3_measurements;
|
||||
|
||||
bool ta2_expired = nr_timer_tick(&l3_measurements->TA2);
|
||||
if (ta2_expired && l3_measurements->trigger_quantity > 0) {
|
||||
rrc_ue_generate_measurementReport(nb, rrc->ue_id);
|
||||
l3_measurements->reports_sent = 1;
|
||||
|
||||
if (l3_measurements->reports_sent < l3_measurements->max_reports) {
|
||||
nr_timer_setup(&l3_measurements->periodic_report_timer, l3_measurements->report_interval_ms, 10);
|
||||
nr_timer_start(&l3_measurements->periodic_report_timer);
|
||||
}
|
||||
}
|
||||
|
||||
bool periodic_expired = nr_timer_tick(&l3_measurements->periodic_report_timer);
|
||||
if (periodic_expired && l3_measurements->reports_sent < l3_measurements->max_reports) {
|
||||
rrc_ue_generate_measurementReport(nb, rrc->ue_id);
|
||||
l3_measurements->reports_sent++;
|
||||
|
||||
if (l3_measurements->reports_sent < l3_measurements->max_reports) {
|
||||
nr_timer_setup(&l3_measurements->periodic_report_timer, l3_measurements->report_interval_ms, 10);
|
||||
nr_timer_start(&l3_measurements->periodic_report_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nr_rrc_handle_timers(NR_UE_RRC_INST_t *rrc)
|
||||
{
|
||||
NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants;
|
||||
@@ -185,6 +215,8 @@ void nr_rrc_handle_timers(NR_UE_RRC_INST_t *rrc)
|
||||
// Spec 38.331 Section 5.2.2.6
|
||||
handle_t430_expiry(rrc);
|
||||
}
|
||||
|
||||
handle_meas_timers(rrc);
|
||||
}
|
||||
|
||||
int nr_rrc_get_T304(long t304)
|
||||
@@ -630,3 +662,43 @@ void reset_rlf_timers_and_constants(NR_UE_Timers_Constants_t *tac)
|
||||
tac->N310_cnt = 0;
|
||||
tac->N311_cnt = 0;
|
||||
}
|
||||
|
||||
int get_A2_event_time_to_trigger(long time_to_trigger)
|
||||
{
|
||||
switch (time_to_trigger) {
|
||||
case NR_TimeToTrigger_ms0:
|
||||
return 0;
|
||||
case NR_TimeToTrigger_ms40:
|
||||
return 40;
|
||||
case NR_TimeToTrigger_ms64:
|
||||
return 64;
|
||||
case NR_TimeToTrigger_ms80:
|
||||
return 80;
|
||||
case NR_TimeToTrigger_ms100:
|
||||
return 100;
|
||||
case NR_TimeToTrigger_ms128:
|
||||
return 128;
|
||||
case NR_TimeToTrigger_ms160:
|
||||
return 160;
|
||||
case NR_TimeToTrigger_ms256:
|
||||
return 256;
|
||||
case NR_TimeToTrigger_ms320:
|
||||
return 320;
|
||||
case NR_TimeToTrigger_ms480:
|
||||
return 480;
|
||||
case NR_TimeToTrigger_ms512:
|
||||
return 512;
|
||||
case NR_TimeToTrigger_ms640:
|
||||
return 640;
|
||||
case NR_TimeToTrigger_ms1024:
|
||||
return 1024;
|
||||
case NR_TimeToTrigger_ms1280:
|
||||
return 1280;
|
||||
case NR_TimeToTrigger_ms2560:
|
||||
return 2560;
|
||||
case NR_TimeToTrigger_ms5120:
|
||||
return 5120;
|
||||
default:
|
||||
AssertFatal(false, "Invalid TimeToTrigger %ld\n", time_to_trigger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,43 @@ void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in)
|
||||
}
|
||||
}
|
||||
|
||||
ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in)
|
||||
{
|
||||
ngap_cause_t out = {0};
|
||||
switch (in->present) {
|
||||
case NGAP_Cause_PR_radioNetwork:
|
||||
out.type = NGAP_CAUSE_RADIO_NETWORK;
|
||||
out.value = in->choice.radioNetwork;
|
||||
break;
|
||||
|
||||
case NGAP_Cause_PR_transport:
|
||||
out.type = NGAP_CAUSE_TRANSPORT;
|
||||
out.value = in->choice.transport;
|
||||
break;
|
||||
|
||||
case NGAP_Cause_PR_nas:
|
||||
out.type = NGAP_CAUSE_NAS;
|
||||
out.value = in->choice.nas;
|
||||
break;
|
||||
|
||||
case NGAP_Cause_PR_protocol:
|
||||
out.type = NGAP_CAUSE_PROTOCOL;
|
||||
out.value = in->choice.protocol;
|
||||
break;
|
||||
|
||||
case NGAP_Cause_PR_misc:
|
||||
out.type = NGAP_CAUSE_MISC;
|
||||
out.value = in->choice.misc;
|
||||
break;
|
||||
|
||||
default:
|
||||
out.type = NGAP_CAUSE_NOTHING;
|
||||
NGAP_ERROR("Unknown failure cause %d\n", in->present);
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in)
|
||||
{
|
||||
nr_guami_t out = {0};
|
||||
@@ -112,3 +149,125 @@ ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_Mobility
|
||||
TBCD_TO_MCC_MNC(&in->servingPLMN, out.serving_plmn.mcc, out.serving_plmn.mnc, out.serving_plmn.mnc_digit_length);
|
||||
return out;
|
||||
}
|
||||
|
||||
void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in)
|
||||
{
|
||||
out->id = NGAP_ProtocolIE_ID_id_TargetID;
|
||||
out->criticality = NGAP_Criticality_reject;
|
||||
out->value.present = NGAP_HandoverRequiredIEs__value_PR_TargetID;
|
||||
// CHOICE Target ID: NG-RAN (M)
|
||||
out->value.choice.TargetID.present = NGAP_TargetID_PR_targetRANNodeID;
|
||||
asn1cCalloc(out->value.choice.TargetID.choice.targetRANNodeID, targetRan);
|
||||
// Global RAN Node ID (M)
|
||||
targetRan->globalRANNodeID.present = NGAP_GlobalRANNodeID_PR_globalGNB_ID;
|
||||
asn1cCalloc(targetRan->globalRANNodeID.choice.globalGNB_ID, globalGnbId);
|
||||
globalGnbId->gNB_ID.present = NGAP_GNB_ID_PR_gNB_ID;
|
||||
MACRO_GNB_ID_TO_BIT_STRING(in->targetgNBId, &globalGnbId->gNB_ID.choice.gNB_ID);
|
||||
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc, in->plmn_identity.mnc, in->plmn_identity.mnc_digit_length, &globalGnbId->pLMNIdentity);
|
||||
// Selected TAI (M)
|
||||
INT24_TO_OCTET_STRING(in->tac, &targetRan->selectedTAI.tAC);
|
||||
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc,
|
||||
in->plmn_identity.mnc,
|
||||
in->plmn_identity.mnc_digit_length,
|
||||
&targetRan->selectedTAI.pLMNIdentity);
|
||||
}
|
||||
|
||||
void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id)
|
||||
{
|
||||
out->iE_Extensions = NULL;
|
||||
MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &out->pLMNIdentity);
|
||||
NR_CELL_ID_TO_BIT_STRING(cell_id, &out->nRCellIdentity);
|
||||
}
|
||||
|
||||
pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
|
||||
{
|
||||
pdusession_level_qos_parameter_t out = {0};
|
||||
// QFI
|
||||
out.qfi = qfi;
|
||||
AssertFatal(params != NULL, "QoS parameters are NULL\n");
|
||||
// QosCharacteristics
|
||||
const NGAP_QosCharacteristics_t *qosChar = ¶ms->qosCharacteristics;
|
||||
AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n");
|
||||
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
|
||||
AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n");
|
||||
out.fiveQI_type = NON_DYNAMIC;
|
||||
out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI;
|
||||
} else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) {
|
||||
AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n");
|
||||
out.fiveQI_type = DYNAMIC;
|
||||
out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI;
|
||||
} else {
|
||||
AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present);
|
||||
}
|
||||
// Allocation and Retention Priority
|
||||
const NGAP_AllocationAndRetentionPriority_t *arp = ¶ms->allocationAndRetentionPriority;
|
||||
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
|
||||
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
|
||||
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p)
|
||||
{
|
||||
gtpu_tunnel_t out = {0};
|
||||
// Transport layer address
|
||||
memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size);
|
||||
out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused;
|
||||
// GTP tunnel endpoint ID
|
||||
OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid);
|
||||
return out;
|
||||
}
|
||||
|
||||
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf)
|
||||
{
|
||||
void *decoded = NULL;
|
||||
asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000};
|
||||
asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0);
|
||||
if (rval.code != RC_OK) {
|
||||
NGAP_ERROR("Decoding failed for %s\n", td->name);
|
||||
return NULL;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
|
||||
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in)
|
||||
{
|
||||
void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in);
|
||||
if (!decoded) {
|
||||
LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded;
|
||||
for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) {
|
||||
NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i];
|
||||
switch (pdusessionTransfer_ies->id) {
|
||||
// UL NG-U UP TNL Information (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation:
|
||||
out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel);
|
||||
break;
|
||||
|
||||
// PDU Session Type (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_PDUSessionType:
|
||||
out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType;
|
||||
break;
|
||||
|
||||
// QoS Flow Setup Request List (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList:
|
||||
out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count;
|
||||
for (int i = 0; i < out->nb_qos; i++) {
|
||||
NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i];
|
||||
out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id);
|
||||
}
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,10 +83,16 @@ typedef int (*ngap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t str
|
||||
|
||||
void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in);
|
||||
nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in);
|
||||
ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in);
|
||||
ngap_ambr_t decode_ngap_UEAggregateMaximumBitRate(const NGAP_UEAggregateMaximumBitRate_t *in);
|
||||
nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in);
|
||||
ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in);
|
||||
ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_MobilityRestrictionList_t *in);
|
||||
void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in);
|
||||
void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id);
|
||||
pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params);
|
||||
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf);
|
||||
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in);
|
||||
|
||||
/** @}*/
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_nas_procedures.h"
|
||||
#include "ngap_messages_types.h"
|
||||
#include "ngap_gNB_mobility_management.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "openair3/SECU/kdf.h"
|
||||
#include "queue.h"
|
||||
@@ -275,12 +277,273 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
|
||||
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
||||
}
|
||||
|
||||
/** @brief UE Mobility Management: callback for Handover Required */
|
||||
int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
NGAP_DEBUG("Triggered Handover Required\n");
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
ngap_gNB_ue_context_t *ue_context_p = NULL;
|
||||
if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id)) {
|
||||
NGAP_ERROR("ue_context_p->gNB_ue_ngap_id %d does not match msg->gNB_ue_ngap_id %d\n",
|
||||
ue_context_p->gNB_ue_ngap_id,
|
||||
msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_required(msg);
|
||||
if (!pdu) {
|
||||
NGAP_ERROR("Failed to encode Handover Required\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
|
||||
byte_array_t out = { .buf = NULL, .len = 0 };
|
||||
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
NGAP_ERROR("Failed to encode Handover Required message\n");
|
||||
return -1;
|
||||
}
|
||||
free(pdu);
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
out.buf,
|
||||
out.len,
|
||||
ue_context_p->tx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngap_gNB_init(void) {
|
||||
NGAP_DEBUG("Starting NGAP layer\n");
|
||||
ngap_gNB_prepare_internal_data();
|
||||
itti_mark_task_ready(TASK_NGAP);
|
||||
}
|
||||
|
||||
static int ngap_gNB_handover_failure(instance_t instance, const ngap_handover_failure_t *msg)
|
||||
{
|
||||
ngap_gNB_ue_context_t *ue_context_p = NULL;
|
||||
uint8_t *buffer = NULL;
|
||||
uint32_t length;
|
||||
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(msg != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_failure(msg);
|
||||
if (!pdu) {
|
||||
NGAP_ERROR("Failed to encode NG Handover Failure\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(pdu, &buffer, &length) < 0) {
|
||||
NGAP_ERROR("Failed to encode HANDOVER FAILURE MESSAGE\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_amf_data_t *amf = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p);
|
||||
if ((ue_context_p = ngap_get_ue_context_from_amf_ue_ngap_id(msg->amf_ue_ngap_id)) == NULL) {
|
||||
//The context for this gNB ue ngap id doesn't exist in the map of gNB UEs
|
||||
NGAP_WARN("Failed to find UE context associated with amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
|
||||
/*In this case there is no ue context since HO REQ might be failed at preprocessing stage itself.
|
||||
* so sctp message send to AMF based on its assoc_id */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, 0);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, amf->nextstream);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ngap_gNB_handover_request_acknowledge(instance_t instance, ngap_handover_request_ack_t *msg)
|
||||
{
|
||||
byte_array_t ba = { .buf = NULL, .len = 0 };
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_t *ngap = ngap_gNB_get_instance(instance);
|
||||
DevAssert(msg != NULL);
|
||||
DevAssert(ngap != NULL);
|
||||
|
||||
// First, create and store NGAP UE context
|
||||
ngap_gNB_ue_context_t ue_context_p = {
|
||||
.amf_ref = ngap_gNB_get_AMF_from_instance(ngap),
|
||||
.gNB_ue_ngap_id = msg->gNB_ue_ngap_id,
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.gNB_instance = ngap,
|
||||
.ue_state = NGAP_UE_CONNECTED,
|
||||
};
|
||||
if (ue_context_p.amf_ref == NULL) {
|
||||
NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n");
|
||||
return -1;
|
||||
}
|
||||
ngap_store_ue_context(&ue_context_p);
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_request_ack(msg);
|
||||
if (!pdu) {
|
||||
NGAP_ERROR("Failed to encode NG Handover Request Acknowledge\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
|
||||
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
|
||||
NGAP_ERROR("Failed to encode Handover Request Acknowledge message\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap->instance, ue_context_p.amf_ref->assoc_id, ba.buf, ba.len, ue_context_p.tx_stream);
|
||||
NGAP_INFO("Sent Handover Request Acknowledge to AMF\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ngap_gNB_handover_notify(instance_t instance, const ngap_handover_notify_t *msg)
|
||||
{
|
||||
LOG_D(NGAP, "Encode NGAP Handover Notify\n");
|
||||
ngap_gNB_ue_context_t *ue_context_p = NULL;
|
||||
byte_array_t ba = { .buf = NULL, .len = 0};
|
||||
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(msg != NULL);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) {
|
||||
NGAP_ERROR("Failed to encode Handover Notify: no ue context associated with gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id) {
|
||||
NGAP_ERROR("Failed to encode Handover Notify: unknown gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ue_context_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
|
||||
NGAP_ERROR("Failed to encode Handover Notify: unknown amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_notify(msg);
|
||||
|
||||
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
|
||||
NGAP_ERROR("Failed to encode NG Handover Notify message\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
ba.buf,
|
||||
ba.len,
|
||||
ue_context_p->tx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief NGAP: Handover Cancel callback */
|
||||
static int ngap_gNB_handover_cancel(instance_t instance, const ngap_handover_cancel_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
byte_array_t ba = {.buf = NULL, .len = 0};
|
||||
|
||||
// Retrieve NGAP gNB instance
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
// we must have the UE context by RAN UE NGAP ID
|
||||
ngap_gNB_ue_context_t *ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
|
||||
if (ue_context_p == NULL) {
|
||||
NGAP_ERROR("Failed to encode Handover Cancel: no UE context for gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id) {
|
||||
NGAP_ERROR("Failed to encode Handover Cancel: mismatched gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ue_context_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
|
||||
NGAP_ERROR("Failed to encode Handover Cancel: mismatched amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Encode NGAP PDU */
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_cancel(msg);
|
||||
if (pdu == NULL) {
|
||||
NGAP_ERROR("Failed to encode NG Handover Cancel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
|
||||
NGAP_ERROR("Failed to encode NG Handover Cancel message PDU\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
|
||||
/* UE-associated signalling -> use the UE's allocated SCTP stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
ba.buf,
|
||||
ba.len,
|
||||
ue_context_p->tx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngap_gNB_handle_ul_ran_status_transfer(instance_t instance, const ngap_ran_status_transfer_t *msg)
|
||||
{
|
||||
NGAP_DEBUG("Triggered UL RAN Status Transfer\n");
|
||||
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
ngap_gNB_ue_context_t *ue_context_p = ngap_get_ue_context(msg->gnb_ue_ngap_id);
|
||||
if (!ue_context_p || ue_context_p->gNB_ue_ngap_id != msg->gnb_ue_ngap_id) {
|
||||
NGAP_ERROR("Could not find UE context for gNB_ue_ngap_id %d\n", msg->gnb_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_ul_ran_status_transfer(msg);
|
||||
if (!pdu) {
|
||||
NGAP_ERROR("Failed to encode UL RAN Status Transfer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
byte_array_t out = {.buf = NULL, .len = 0};
|
||||
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
|
||||
NGAP_ERROR("UL RAN Status Transfer encoding failure\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_itti_send_sctp_data_req(instance, ue_context_p->amf_ref->assoc_id, out.buf, out.len, ue_context_p->tx_stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ngap_gNB_process_itti_msg(void *notUsed) {
|
||||
MessageDef *received_msg = NULL;
|
||||
int result;
|
||||
@@ -356,6 +619,33 @@ void *ngap_gNB_process_itti_msg(void *notUsed) {
|
||||
ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_REQUIRED:
|
||||
if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) {
|
||||
NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_FAILURE:
|
||||
ngap_gNB_handover_failure(instance, &NGAP_HANDOVER_FAILURE(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_REQUEST_ACKNOWLEDGE:
|
||||
ngap_gNB_handover_request_acknowledge(instance, &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg));
|
||||
free_ng_handover_req_ack(&NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_NOTIFY:
|
||||
ngap_gNB_handover_notify(instance, &NGAP_HANDOVER_NOTIFY(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_CANCEL:
|
||||
ngap_gNB_handover_cancel(instance, &NGAP_HANDOVER_CANCEL(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_UL_RAN_STATUS_TRANSFER:
|
||||
ngap_gNB_handle_ul_ran_status_transfer(instance, &NGAP_UL_RAN_STATUS_TRANSFER(received_msg));
|
||||
break;
|
||||
|
||||
default:
|
||||
NGAP_ERROR("Received unhandled message: %d:%s\n", ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
|
||||
break;
|
||||
|
||||
@@ -70,6 +70,12 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
|
||||
NGAP_INFO("PDUSESSIONSetup initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_HandoverPreparation:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
NGAP_INFO("Handover Preparation initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_PDUSessionResourceModify:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
@@ -88,6 +94,18 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
|
||||
NGAP_INFO("TODO ErrorIndication initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_HandoverResourceAllocation:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
NGAP_INFO("Handover Resource Allocation initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_DownlinkRANStatusTransfer:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
NGAP_INFO("DL RAN Status Transfer initiating message\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
NGAP_ERROR("Unknown procedure ID (%d) for initiating message\n",
|
||||
(int)pdu->choice.initiatingMessage->procedureCode);
|
||||
@@ -119,6 +137,10 @@ static int ngap_gNB_decode_successful_outcome(NGAP_NGAP_PDU_t *pdu) {
|
||||
free(res.buffer);
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_HandoverPreparation:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
NGAP_ERROR("Unknown procedure ID (%d) for successfull outcome message\n",
|
||||
|
||||
@@ -52,7 +52,10 @@ static inline int ngap_gNB_encode_initiating(NGAP_NGAP_PDU_t *pdu, uint8_t **buf
|
||||
NGAP_ProcedureCode_id_NASNonDeliveryIndication,
|
||||
NGAP_ProcedureCode_id_UEContextReleaseRequest,
|
||||
NGAP_ProcedureCode_id_PathSwitchRequest,
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication};
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication,
|
||||
NGAP_ProcedureCode_id_UplinkRANStatusTransfer,
|
||||
NGAP_ProcedureCode_id_HandoverPreparation,
|
||||
NGAP_ProcedureCode_id_HandoverNotification};
|
||||
int i;
|
||||
for (i = 0; i < sizeofArray(tmp); i++)
|
||||
if (pdu->choice.initiatingMessage->procedureCode == tmp[i])
|
||||
@@ -76,7 +79,8 @@ static inline int ngap_gNB_encode_successfull_outcome(NGAP_NGAP_PDU_t *pdu, uint
|
||||
NGAP_ProcedureCode_id_UEContextRelease,
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceSetup,
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceModify,
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceRelease};
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceRelease,
|
||||
NGAP_ProcedureCode_id_HandoverResourceAllocation};
|
||||
int i;
|
||||
for (i = 0; i < sizeofArray(tmp); i++)
|
||||
if (pdu->choice.successfulOutcome->procedureCode == tmp[i])
|
||||
@@ -97,8 +101,17 @@ static inline int ngap_gNB_encode_unsuccessfull_outcome(NGAP_NGAP_PDU_t *pdu, ui
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
if (pdu->choice.unsuccessfulOutcome->procedureCode != NGAP_ProcedureCode_id_InitialContextSetup) {
|
||||
NGAP_DEBUG("Unknown procedure ID (%d) for unsuccessfull outcome message\n", (int)pdu->choice.unsuccessfulOutcome->procedureCode);
|
||||
const NGAP_ProcedureCode_t id[] = {NGAP_ProcedureCode_id_InitialContextSetup, NGAP_ProcedureCode_id_HandoverResourceAllocation};
|
||||
|
||||
int i;
|
||||
for (i = 0; i < sizeofArray(id); i++) {
|
||||
if (pdu->choice.unsuccessfulOutcome->procedureCode == id[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == sizeofArray(id)) {
|
||||
NGAP_WARN("Unknown procedure ID (%ld) for successful outcome message\n", pdu->choice.successfulOutcome->procedureCode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_gNB_decoder.h"
|
||||
#include "ngap_gNB_defs.h"
|
||||
#include "ngap_gNB_nnsf.h"
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_mobility_management.h"
|
||||
#include "ngap_gNB_nas_procedures.h"
|
||||
#include "ngap_gNB_trace.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
@@ -693,97 +695,40 @@ static int ngap_gNB_handle_error_indication(sctp_assoc_t assoc_id, uint32_t stre
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf)
|
||||
/** @brief Handler for NGAP Handover Request message (AMF -> target NG-RAN)
|
||||
* Decode the NGAP message and transfer to RRC */
|
||||
static int ngap_gNB_handle_handover_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
void *decoded = NULL;
|
||||
asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000};
|
||||
asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0);
|
||||
if (rval.code != RC_OK) {
|
||||
NGAP_ERROR("Decoding failed for %s\n", td->name);
|
||||
return NULL;
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
static pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
|
||||
{
|
||||
pdusession_level_qos_parameter_t out = {0};
|
||||
// QFI
|
||||
out.qfi = qfi;
|
||||
AssertFatal(params != NULL, "QoS parameters are NULL\n");
|
||||
// QosCharacteristics
|
||||
const NGAP_QosCharacteristics_t *qosChar = ¶ms->qosCharacteristics;
|
||||
AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n");
|
||||
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
|
||||
AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n");
|
||||
out.fiveQI_type = NON_DYNAMIC;
|
||||
out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI;
|
||||
} else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) {
|
||||
AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n");
|
||||
out.fiveQI_type = DYNAMIC;
|
||||
out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI;
|
||||
} else {
|
||||
AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present);
|
||||
}
|
||||
// Allocation and Retention Priority
|
||||
const NGAP_AllocationAndRetentionPriority_t *arp = ¶ms->allocationAndRetentionPriority;
|
||||
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
|
||||
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
|
||||
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p)
|
||||
{
|
||||
gtpu_tunnel_t out = {0};
|
||||
// Transport layer address
|
||||
memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size);
|
||||
out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused;
|
||||
// GTP tunnel endpoint ID
|
||||
OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
|
||||
static bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in)
|
||||
{
|
||||
void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in);
|
||||
if (!decoded) {
|
||||
LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n");
|
||||
return false;
|
||||
ngap_gNB_amf_data_t *amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
|
||||
if (amf_desc_p == NULL) {
|
||||
NGAP_ERROR("Received Handover Request for non existing AMF context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded;
|
||||
for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) {
|
||||
NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i];
|
||||
switch (pdusessionTransfer_ies->id) {
|
||||
// UL NG-U UP TNL Information (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation:
|
||||
out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel);
|
||||
break;
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_REQUEST);
|
||||
ngap_handover_request_t *msg = &NGAP_HANDOVER_REQUEST(message_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
// PDU Session Type (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_PDUSessionType:
|
||||
out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType;
|
||||
break;
|
||||
|
||||
// QoS Flow Setup Request List (Mandatory)
|
||||
case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList:
|
||||
out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count;
|
||||
for (int i = 0; i < out->nb_qos; i++) {
|
||||
NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i];
|
||||
out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id);
|
||||
}
|
||||
if (decode_ng_handover_request(msg, pdu) < 0) {
|
||||
NGAP_ERROR("Failed to decode NG Handover Request\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_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
|
||||
};
|
||||
NGAP_INFO("Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", fail.amf_ue_ngap_id, fail.cause.value);
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_FAILURE);
|
||||
NGAP_HANDOVER_FAILURE(msg_p) = fail;
|
||||
itti_send_msg_to_task(TASK_NGAP, amf_desc_p->ngap_gNB_instance->instance, msg_p);
|
||||
return -1;
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer);
|
||||
|
||||
return true;
|
||||
NGAP_INFO("Received NG Handover Request from AMF %s (ID=%lu)\n", amf_desc_p->amf_name, msg->amf_ue_ngap_id);
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
@@ -1052,6 +997,100 @@ static int ngap_gNB_handle_pdusession_setup_request(sctp_assoc_t assoc_id, uint3
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Handler for the NG Handover Command
|
||||
* AMF -> source NG-RAN */
|
||||
static int ngap_gNB_handle_handover_command(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
NGAP_INFO("Received NG Handover Command\n");
|
||||
ngap_gNB_amf_data_t *amf_desc_p = NULL;
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) {
|
||||
NGAP_ERROR(
|
||||
"[SCTP %u] Received Handover Command for non "
|
||||
"existing AMF context\n",
|
||||
assoc_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_COMMAND);
|
||||
ngap_handover_command_t *msg = &NGAP_HANDOVER_COMMAND(message_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
if (decode_ng_handover_command(msg, pdu) < 0) {
|
||||
NGAP_ERROR("Failed to decode NG Handover Command");
|
||||
free_ng_handover_command(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
|
||||
if (!ue_desc_p) {
|
||||
NGAP_ERROR(
|
||||
"[SCTP %u] Received HO Command Msg for non existing UE context (gNB_ue_ngap_id %d) \n",
|
||||
assoc_id,
|
||||
msg->gNB_ue_ngap_id);
|
||||
free_ng_handover_command(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ue_desc_p->rx_stream = stream;
|
||||
|
||||
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
|
||||
NGAP_ERROR("UE context amf_ue_ngap_id is different form that of the message (%ld != %ld)",
|
||||
ue_desc_p->amf_ue_ngap_id,
|
||||
msg->amf_ue_ngap_id);
|
||||
free_ng_handover_command(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_DEBUG("Handover Command is parsed with a size of: %ld. Sending to RRC GNB!\n", msg->handoverCommand.len);
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Handler for NG Handover Cancel Acknowledge */
|
||||
static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
NGAP_INFO("Received NG Handover Cancel Acknowledge\n");
|
||||
ngap_gNB_amf_data_t *amf_desc_p = NULL;
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
|
||||
if (amf_desc_p == NULL) {
|
||||
NGAP_ERROR("[SCTP %u] Received Handover Cancel Ack for non-existing AMF context\n", assoc_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_CANCEL_ACK);
|
||||
ngap_handover_cancel_ack_t *msg = &NGAP_HANDOVER_CANCEL_ACK(message_p);
|
||||
|
||||
if (decode_ng_handover_cancel_ack(msg, pdu) < 0) {
|
||||
NGAP_ERROR("Failed to decode NG Handover Cancel Acknowledge\n");
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
|
||||
if (!ue_desc_p) {
|
||||
NGAP_ERROR("[SCTP %u] Handover Cancel Ack for unknown UE context (gNB_ue_ngap_id %d)\n", assoc_id, msg->gNB_ue_ngap_id);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
|
||||
NGAP_WARN("AMF UE NGAP ID mismatch in HO Cancel Ack (ctx %ld != msg %ld)", ue_desc_p->amf_ue_ngap_id, msg->amf_ue_ngap_id);
|
||||
}
|
||||
|
||||
ue_desc_p->rx_stream = stream;
|
||||
|
||||
// cancel was already handled at RRC level, do not forward
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
ngap_gNB_amf_data_t *amf_desc_p = NULL;
|
||||
@@ -1379,6 +1418,100 @@ static int ngap_gNB_handle_ng_ENDC_pdusession_modification_confirm(sctp_assoc_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ngap_gNB_handle_dl_ran_status_transfer(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
NGAP_DownlinkRANStatusTransfer_t *container = &pdu->choice.initiatingMessage->value.choice.DownlinkRANStatusTransfer;
|
||||
NGAP_DownlinkRANStatusTransferIEs_t *ie = NULL;
|
||||
|
||||
ngap_gNB_amf_data_t *amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
|
||||
if (!amf_desc_p) {
|
||||
NGAP_ERROR("[SCTP %u] Received DL RAN Status Transfer for unknown AMF\n", assoc_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id = 0;
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
|
||||
asn_INTEGER2ulong(&ie->value.choice.AMF_UE_NGAP_ID, &amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
|
||||
NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
|
||||
|
||||
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(gnb_ue_ngap_id);
|
||||
if (!ue_desc_p) {
|
||||
NGAP_ERROR("No UE context for gNB_ue_ngap_id %lu\n", gnb_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ue_desc_p->rx_stream = stream;
|
||||
if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id)
|
||||
NGAP_WARN("UE context mismatch: amf_ue_ngap_id (%lu != %lu)\n", ue_desc_p->amf_ue_ngap_id, amf_ue_ngap_id);
|
||||
|
||||
// Transparent container
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t,
|
||||
ie,
|
||||
container,
|
||||
NGAP_ProtocolIE_ID_id_RANStatusTransfer_TransparentContainer,
|
||||
true);
|
||||
NGAP_RANStatusTransfer_TransparentContainer_t *tcont = &ie->value.choice.RANStatusTransfer_TransparentContainer;
|
||||
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_DL_RAN_STATUS_TRANSFER);
|
||||
ngap_ran_status_transfer_t *msg = &NGAP_DL_RAN_STATUS_TRANSFER(message_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
msg->amf_ue_ngap_id = amf_ue_ngap_id;
|
||||
msg->gnb_ue_ngap_id = gnb_ue_ngap_id;
|
||||
|
||||
int nb = tcont->dRBsSubjectToStatusTransferList.list.count;
|
||||
for (int i = 0; i < nb; ++i) {
|
||||
NGAP_DRBsSubjectToStatusTransferItem_t *item = tcont->dRBsSubjectToStatusTransferList.list.array[i];
|
||||
ngap_drb_status_t *s = &msg->ran_status.drb_status_list[i];
|
||||
s->drb_id = item->dRB_ID;
|
||||
|
||||
// UL COUNT
|
||||
switch (item->dRBStatusUL.present) {
|
||||
case NGAP_DRBStatusUL_PR_dRBStatusUL18:
|
||||
s->ul_count.sn_len = NGAP_SN_LENGTH_18;
|
||||
s->ul_count.pdcp_sn = item->dRBStatusUL.choice.dRBStatusUL18->uL_COUNTValue.pDCP_SN18;
|
||||
s->ul_count.hfn = item->dRBStatusUL.choice.dRBStatusUL18->uL_COUNTValue.hFN_PDCP_SN18;
|
||||
break;
|
||||
case NGAP_DRBStatusUL_PR_dRBStatusUL12:
|
||||
s->ul_count.sn_len = NGAP_SN_LENGTH_12;
|
||||
s->ul_count.pdcp_sn = item->dRBStatusUL.choice.dRBStatusUL12->uL_COUNTValue.pDCP_SN12;
|
||||
s->ul_count.hfn = item->dRBStatusUL.choice.dRBStatusUL12->uL_COUNTValue.hFN_PDCP_SN12;
|
||||
break;
|
||||
default:
|
||||
NGAP_ERROR("Unknown dRBStatusUL.present=%d\n", item->dRBStatusUL.present);
|
||||
break;
|
||||
}
|
||||
|
||||
// DL COUNT
|
||||
switch (item->dRBStatusDL.present) {
|
||||
case NGAP_DRBStatusDL_PR_dRBStatusDL18:
|
||||
s->dl_count.sn_len = NGAP_SN_LENGTH_18;
|
||||
s->dl_count.pdcp_sn = item->dRBStatusDL.choice.dRBStatusDL18->dL_COUNTValue.pDCP_SN18;
|
||||
s->dl_count.hfn = item->dRBStatusDL.choice.dRBStatusDL18->dL_COUNTValue.hFN_PDCP_SN18;
|
||||
break;
|
||||
case NGAP_DRBStatusDL_PR_dRBStatusDL12:
|
||||
s->dl_count.sn_len = NGAP_SN_LENGTH_12;
|
||||
s->dl_count.pdcp_sn = item->dRBStatusDL.choice.dRBStatusDL12->dL_COUNTValue.pDCP_SN12;
|
||||
s->dl_count.hfn = item->dRBStatusDL.choice.dRBStatusDL12->dL_COUNTValue.hFN_PDCP_SN12;
|
||||
break;
|
||||
default:
|
||||
NGAP_ERROR("Unknown dRBStatusDL.present=%d\n", item->dRBStatusDL.present);
|
||||
break;
|
||||
}
|
||||
|
||||
msg->ran_status.nb_drb++;
|
||||
}
|
||||
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, ue_desc_p->gNB_instance->instance, message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handlers matrix. Only gNB related procedure present here */
|
||||
const ngap_message_decoded_callback ngap_messages_callback[][3] = {
|
||||
{0, 0, 0}, /* AMFConfigurationUpdate */
|
||||
@@ -1388,13 +1521,13 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = {
|
||||
{ngap_gNB_handle_nas_downlink, 0, 0}, /* DownlinkNASTransport */
|
||||
{0, 0, 0}, /* DownlinkNonUEAssociatedNRPPaTransport */
|
||||
{0, 0, 0}, /* DownlinkRANConfigurationTransfer */
|
||||
{0, 0, 0}, /* DownlinkRANStatusTransfer */
|
||||
{ngap_gNB_handle_dl_ran_status_transfer, 0, 0}, /* DownlinkRANStatusTransfer */
|
||||
{0, 0, 0}, /* DownlinkUEAssociatedNRPPaTransport */
|
||||
{ngap_gNB_handle_error_indication, 0, 0}, /* ErrorIndication */
|
||||
{0, 0, 0}, /* HandoverCancel */
|
||||
{0, ngap_gNB_handle_handover_cancel_ack, 0}, /* HandoverCancel */
|
||||
{0, 0, 0}, /* HandoverNotification */
|
||||
{0, 0, 0}, /* HandoverPreparation */
|
||||
{0, 0, 0}, /* HandoverResourceAllocation */
|
||||
{0, ngap_gNB_handle_handover_command, 0}, /* HandoverPreparation */
|
||||
{ngap_gNB_handle_handover_request, 0, 0}, /* HandoverResourceAllocation */
|
||||
{ngap_gNB_handle_initial_context_request, 0, 0}, /* InitialContextSetup */
|
||||
{0, 0, 0}, /* InitialUEMessage */
|
||||
{0, 0, 0}, /* LocationReportingControl */
|
||||
|
||||
@@ -200,3 +200,11 @@ void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in)
|
||||
out->bits_unused = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in)
|
||||
{
|
||||
if (in.size) {
|
||||
memcpy(out->buffer, in.buf, in.size);
|
||||
out->length = in.size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,4 +50,6 @@ void ngap_gNB_prepare_internal_data(void);
|
||||
|
||||
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in);
|
||||
|
||||
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in);
|
||||
|
||||
#endif /* NGAP_GNB_MANAGEMENT_PROCEDURES_H_ */
|
||||
|
||||
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "conversions.h"
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
#include "ngap_gNB_defs.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_encoder.h"
|
||||
#include "ngap_gNB_itti_messaging.h"
|
||||
#include "conversions.h"
|
||||
|
||||
/** @brief UE Mobility Management: encode Handover Required
|
||||
* (9.2.3.1 of 3GPP TS 38.413) NG-RAN node → AMF */
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverPreparation;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverRequired;
|
||||
NGAP_HandoverRequired_t *out = &head->value.choice.HandoverRequired;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_HandoverRequiredIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie2->criticality = NGAP_Criticality_reject;
|
||||
ie2->value.present = NGAP_HandoverRequiredIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
|
||||
// Handover Type (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_HandoverType;
|
||||
ie3->criticality = NGAP_Criticality_reject;
|
||||
ie3->value.present = NGAP_HandoverRequiredIEs__value_PR_HandoverType;
|
||||
ie3->value.choice.HandoverType = msg->handoverType;
|
||||
|
||||
// Cause (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie4);
|
||||
ie4->id = NGAP_ProtocolIE_ID_id_Cause;
|
||||
ie4->criticality = NGAP_Criticality_ignore;
|
||||
ie4->value.present = NGAP_HandoverRequiredIEs__value_PR_Cause;
|
||||
encode_ngap_cause(&ie4->value.choice.Cause, &msg->cause);
|
||||
|
||||
// Target ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie5);
|
||||
encode_ngap_target_id(ie5, &msg->target_gnb_id);
|
||||
|
||||
// PDU Session Resource List (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie6);
|
||||
ie6->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceListHORqd;
|
||||
ie6->criticality = NGAP_Criticality_reject;
|
||||
ie6->value.present = NGAP_HandoverRequiredIEs__value_PR_PDUSessionResourceListHORqd;
|
||||
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
asn1cSequenceAdd(ie6->value.choice.PDUSessionResourceListHORqd.list, NGAP_PDUSessionResourceItemHORqd_t, hoRequiredPduSession);
|
||||
// PDU Session ID (M)
|
||||
hoRequiredPduSession->pDUSessionID = msg->pdusessions[i].pdusession_id;
|
||||
// Handover Required Transfer (M)
|
||||
NGAP_HandoverRequiredTransfer_t hoRequiredTransfer = {0};
|
||||
uint8_t ho_req_transfer_transparent_container_buffer[128] = {0};
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
|
||||
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_HandoverRequiredTransfer,
|
||||
NULL,
|
||||
&hoRequiredTransfer,
|
||||
ho_req_transfer_transparent_container_buffer,
|
||||
128);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
hoRequiredPduSession->handoverRequiredTransfer.buf = CALLOC(1, (enc_rval.encoded + 7) / 8);
|
||||
memcpy(hoRequiredPduSession->handoverRequiredTransfer.buf,
|
||||
ho_req_transfer_transparent_container_buffer,
|
||||
(enc_rval.encoded + 7) / 8);
|
||||
hoRequiredPduSession->handoverRequiredTransfer.size = (enc_rval.encoded + 7) / 8;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
|
||||
}
|
||||
|
||||
// Source NG-RAN Node to Target NG-RAN Node Transparent Container (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverRequiredIEs__value_PR_SourceToTarget_TransparentContainer;
|
||||
|
||||
NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = calloc_or_fail(1, sizeof(*source2target));
|
||||
|
||||
// RRC Container (M) (HandoverPreparationInformation)
|
||||
source2target->rRCContainer.size = msg->source2target->handoverInfo.len;
|
||||
source2target->rRCContainer.buf = malloc_or_fail(msg->source2target->handoverInfo.len);
|
||||
memcpy(source2target->rRCContainer.buf, msg->source2target->handoverInfo.buf, source2target->rRCContainer.size);
|
||||
|
||||
// PDU Session Resource Information List (O)
|
||||
asn1cCalloc(source2target->pDUSessionResourceInformationList, pduSessionList);
|
||||
for (uint8_t i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
const pdusession_resource_t *pduSession = &msg->pdusessions[i];
|
||||
NGAP_DEBUG("Handover Required: preparing PDU Session Resource Information List for PDU Session ID %d\n",
|
||||
pduSession->pdusession_id);
|
||||
asn1cSequenceAdd(pduSessionList->list, NGAP_PDUSessionResourceInformationItem_t, item);
|
||||
// PDU Session ID (M)
|
||||
item->pDUSessionID = msg->source2target->pdu_session_resource[i].pdusession_id;
|
||||
// QoS Flow Information List (M)
|
||||
for (int q = 0; q < msg->source2target->pdu_session_resource[i].nb_of_qos_flow; ++q) {
|
||||
asn1cSequenceAdd(item->qosFlowInformationList.list, NGAP_QosFlowInformationItem_t, qosFlowInfo);
|
||||
qosFlowInfo->qosFlowIdentifier = msg->source2target->pdu_session_resource[i].qos_flow_info[q].qfi;
|
||||
}
|
||||
}
|
||||
|
||||
// Target Cell ID (NG-RAN CGI) (M)
|
||||
source2target->targetCell_ID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
|
||||
asn1cCalloc(source2target->targetCell_ID.choice.nR_CGI, tNrCGI);
|
||||
encode_ngap_nr_cgi(tNrCGI, &msg->target_gnb_id.plmn_identity, msg->source2target->targetCellId.nrCellIdentity);
|
||||
|
||||
// UE history Information (M)
|
||||
asn1cSequenceAdd(source2target->uEHistoryInformation.list, NGAP_LastVisitedCellItem_t, lastVisitedCell);
|
||||
lastVisitedCell->iE_Extensions = NULL;
|
||||
// Last Visited Cell Information (M)
|
||||
lastVisitedCell->lastVisitedCellInformation.present = NGAP_LastVisitedCellInformation_PR_nGRANCell;
|
||||
// CHOICE (M): NG-RAN Cell
|
||||
asn1cCalloc(lastVisitedCell->lastVisitedCellInformation.choice.nGRANCell, lastVisitedNR);
|
||||
// Cell Type (M)
|
||||
lastVisitedNR->cellType.cellSize = msg->source2target->ue_history_info.type;
|
||||
// Global Cell ID (M)
|
||||
lastVisitedNR->globalCellID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
|
||||
asn1cCalloc(lastVisitedNR->globalCellID.choice.nR_CGI, lastVisitedNrCGI);
|
||||
cell_id_t *cell = &msg->source2target->ue_history_info.id;
|
||||
encode_ngap_nr_cgi(lastVisitedNrCGI, &cell->plmn_identity, cell->nrCellIdentity);
|
||||
// HO Cause Value (O)
|
||||
if (msg->source2target->ue_history_info.cause) {
|
||||
asn1cCalloc(lastVisitedNR->hOCauseValue, lastVisitedCause);
|
||||
encode_ngap_cause(lastVisitedCause, msg->source2target->ue_history_info.cause);
|
||||
}
|
||||
// Time UE Stayed in Cell (M)
|
||||
lastVisitedNR->timeUEStayedInCell = msg->source2target->ue_history_info.time_in_cell;
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
|
||||
uint8_t source_to_target_transparent_container_buf[16384] = {0};
|
||||
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer,
|
||||
NULL,
|
||||
(void *)source2target,
|
||||
(void *)&source_to_target_transparent_container_buf,
|
||||
16384);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
if (enc_rval.encoded < 0) {
|
||||
AssertFatal(enc_rval.encoded > 0,
|
||||
"HO LOG: Source to Transparent ASN1 message encoding failed (%s, %lu)!\n",
|
||||
enc_rval.failed_type->name,
|
||||
enc_rval.encoded);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int total_bytes = (enc_rval.encoded + 7) / 8;
|
||||
int ret = OCTET_STRING_fromBuf(&ie->value.choice.SourceToTarget_TransparentContainer,
|
||||
(const char *)&source_to_target_transparent_container_buf,
|
||||
total_bytes);
|
||||
if (ret != 0) {
|
||||
LOG_E(NR_RRC, "HO LOG: Can not perform OCTET_STRING_fromBuf for the SourceToTarget_TransparentContainer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu->present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome;
|
||||
asn1cCalloc(pdu->choice.unsuccessfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_UnsuccessfulOutcome__value_PR_HandoverFailure;
|
||||
NGAP_HandoverFailure_t *out = &head->value.choice.HandoverFailure;
|
||||
|
||||
// AMF_UE_NGAP_ID (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverFailureIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
}
|
||||
|
||||
// Cause (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_Cause;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_HandoverFailureIEs__value_PR_Cause;
|
||||
encode_ngap_cause(&ie->value.choice.Cause, &msg->cause);
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
static void free_ng_handover_request(ngap_handover_request_t *msg)
|
||||
{
|
||||
free_byte_array(msg->ue_ho_prep_info);
|
||||
free_byte_array(msg->ue_cap);
|
||||
free(msg->mobility_restriction);
|
||||
}
|
||||
|
||||
int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
NGAP_HandoverRequest_t *container = &pdu->choice.initiatingMessage->value.choice.HandoverRequest;
|
||||
NGAP_HandoverRequestIEs_t *ie;
|
||||
|
||||
// Handover Type (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true);
|
||||
out->ho_type = ie->value.choice.HandoverType;
|
||||
if (out->ho_type != HANDOVER_TYPE_INTRA5GS) {
|
||||
NGAP_ERROR("Only Intra5GS Handover is supported at the moment!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
|
||||
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &out->amf_ue_ngap_id);
|
||||
|
||||
// GUAMI (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_GUAMI, true);
|
||||
out->guami = decode_ngap_guami(&ie->value.choice.GUAMI);
|
||||
|
||||
// UE Aggregate Maximum Bit Rate (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate, true);
|
||||
out->ue_ambr = decode_ngap_UEAggregateMaximumBitRate(&ie->value.choice.UEAggregateMaximumBitRate);
|
||||
|
||||
// Allowed NSSAI (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true);
|
||||
NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count);
|
||||
out->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count;
|
||||
for (int i = 0; i < out->nb_allowed_nssais; i++) {
|
||||
out->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI);
|
||||
}
|
||||
|
||||
// UE Security Capabilities (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UESecurityCapabilities, true);
|
||||
out->security_capabilities = decode_ngap_security_capabilities(&ie->value.choice.UESecurityCapabilities);
|
||||
|
||||
// Security Context (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true);
|
||||
NGAP_SecurityContext_t *sc = &ie->value.choice.SecurityContext;
|
||||
memcpy(&out->security_context.next_hop, sc->nextHopNH.buf, sc->nextHopNH.size);
|
||||
out->security_context.next_hop_chain_count = sc->nextHopChainingCount;
|
||||
|
||||
// Mobility Restriction List (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_MobilityRestrictionList, false);
|
||||
if (ie != NULL) {
|
||||
out->mobility_restriction = malloc_or_fail(sizeof(*out->mobility_restriction));
|
||||
*out->mobility_restriction = decode_ngap_mobility_restriction(&ie->value.choice.MobilityRestrictionList);
|
||||
}
|
||||
|
||||
// Cause (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_Cause, true);
|
||||
out->cause = decode_ngap_cause(&ie->value.choice.Cause);
|
||||
|
||||
// Source to Target Transparent Container
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t,
|
||||
ie,
|
||||
container,
|
||||
NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer,
|
||||
true);
|
||||
NGAP_SourceToTarget_TransparentContainer_t *choice = &ie->value.choice.SourceToTarget_TransparentContainer;
|
||||
byte_array_t sourceToTargetTransparentContainer = create_byte_array(choice->size, choice->buf);
|
||||
NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = NULL;
|
||||
asn_dec_rval_t dec_rval = aper_decode_complete(NULL,
|
||||
&asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer,
|
||||
(void **)&source2target,
|
||||
sourceToTargetTransparentContainer.buf,
|
||||
sourceToTargetTransparentContainer.len);
|
||||
|
||||
free_byte_array(sourceToTargetTransparentContainer);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
free_ng_handover_request(out);
|
||||
NGAP_ERROR("Failed to decode sourceToTargetTransparentContainer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
|
||||
// Extract Cell Identity
|
||||
BIT_STRING_TO_NR_CELL_IDENTITY(&source2target->targetCell_ID.choice.nR_CGI->nRCellIdentity, out->nr_cell_id);
|
||||
|
||||
// Handover Preparation Information: store and decode
|
||||
out->ue_ho_prep_info = create_byte_array(source2target->rRCContainer.size, source2target->rRCContainer.buf);
|
||||
NR_HandoverPreparationInformation_t *hoPrepInformation = NULL;
|
||||
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
|
||||
&asn_DEF_NR_HandoverPreparationInformation,
|
||||
(void **)&hoPrepInformation,
|
||||
source2target->rRCContainer.buf,
|
||||
source2target->rRCContainer.size);
|
||||
AssertFatal(hoPrep_dec_rval.code == RC_OK && hoPrep_dec_rval.consumed > 0, "Handover Prep Info decode error\n");
|
||||
if (hoPrep_dec_rval.code != RC_OK && !hoPrep_dec_rval.consumed) {
|
||||
NGAP_ERROR("Failed to decode HandoverPreparationInformation, abort Handover Request decoding\n");
|
||||
free_ng_handover_request(out);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation);
|
||||
|
||||
// Decode UE capabilities and store
|
||||
NR_HandoverPreparationInformation_IEs_t *hpi =
|
||||
hoPrepInformation->criticalExtensions.choice.c1->choice.handoverPreparationInformation;
|
||||
const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList = &hpi->ue_CapabilityRAT_List;
|
||||
out->ue_cap.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
|
||||
NULL,
|
||||
ue_CapabilityRAT_ContainerList,
|
||||
(void **)&out->ue_cap.buf);
|
||||
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation);
|
||||
|
||||
if (out->ue_cap.len <= 0) {
|
||||
free_ng_handover_request(out);
|
||||
NGAP_ERROR("could not encode UE-CapabilityRAT-ContainerList\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// PDU Session Resource Setup List (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t,
|
||||
ie,
|
||||
container,
|
||||
NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListHOReq,
|
||||
true);
|
||||
out->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSetupListHOReq.list.count;
|
||||
for (int pduSesIdx = 0; pduSesIdx < out->nb_of_pdusessions; ++pduSesIdx) {
|
||||
NGAP_PDUSessionResourceSetupItemHOReq_t *item_p = ie->value.choice.PDUSessionResourceSetupListHOReq.list.array[pduSesIdx];
|
||||
// PDU Session ID (M)
|
||||
ho_request_pdusession_t *setup = &out->pduSessionResourceSetupList[pduSesIdx];
|
||||
setup->pdusession_id = item_p->pDUSessionID;
|
||||
setup->pdu_session_type = PDUSessionType_ipv4;
|
||||
// S-NSSAI (M)
|
||||
setup->nssai = decode_ngap_nssai(&item_p->s_NSSAI);
|
||||
// Handover Request Transfer (M)
|
||||
bool ret = decodePDUSessionResourceSetup(&setup->pdusessionTransfer, item_p->handoverRequestTransfer);
|
||||
if (!ret) {
|
||||
free_ng_handover_request(out);
|
||||
NGAP_ERROR("Failed to decode pDUSessionResourceSetupRequestTransfer in NG Initial Context Setup Request\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
|
||||
pdu->present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
asn1cCalloc(pdu->choice.successfulOutcome, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_SuccessfulOutcome__value_PR_HandoverRequestAcknowledge;
|
||||
NGAP_HandoverRequestAcknowledge_t *out = &head->value.choice.HandoverRequestAcknowledge;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
}
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
// Target to Source Transparent Container (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_TargetToSource_TransparentContainer;
|
||||
NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *t2s = calloc_or_fail(1, sizeof(*t2s));
|
||||
// RRC Container (M)
|
||||
{
|
||||
// Encode Handover Command
|
||||
t2s->rRCContainer.size = msg->target2source.len;
|
||||
t2s->rRCContainer.buf = malloc_or_fail(msg->target2source.len);
|
||||
memcpy(t2s->rRCContainer.buf, msg->target2source.buf, msg->target2source.len);
|
||||
byte_array_t ba = { .buf = NULL, .len = 0 };
|
||||
ba.len = aper_encode_to_new_buffer(&asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, NULL, t2s, (void **)&ba.buf);
|
||||
OCTET_STRING_fromBuf(&ie->value.choice.TargetToSource_TransparentContainer, (const char *)ba.buf, ba.len);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, t2s);
|
||||
free_byte_array(ba);
|
||||
}
|
||||
}
|
||||
|
||||
// PDU Session Resource Admitted List (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceAdmittedList;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_PDUSessionResourceAdmittedList;
|
||||
|
||||
for (int pduSesIdx = 0; pduSesIdx < msg->nb_of_pdusessions; pduSesIdx++) {
|
||||
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceAdmittedList.list, NGAP_PDUSessionResourceAdmittedItem_t, item);
|
||||
item->pDUSessionID = msg->pdusessions[pduSesIdx].pdu_session_id;
|
||||
/* dLQosFlowPerTNLInformation */
|
||||
NGAP_HandoverRequestAcknowledgeTransfer_t transfer = {0};
|
||||
transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
|
||||
asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp);
|
||||
GTP_TEID_TO_ASN1(msg->pdusessions[pduSesIdx].ack_transfer.gtp_teid, &tmp->gTP_TEID);
|
||||
tnl_to_bitstring(&tmp->transportLayerAddress, msg->pdusessions[pduSesIdx].ack_transfer.gNB_addr);
|
||||
|
||||
for (int j = 0; j < msg->pdusessions[pduSesIdx].ack_transfer.nb_of_qos_flow; j++) {
|
||||
asn1cSequenceAdd(transfer.qosFlowSetupResponseList.list, NGAP_QosFlowItemWithDataForwarding_t, qosItem);
|
||||
qosItem->qosFlowIdentifier = msg->pdusessions[pduSesIdx].ack_transfer.qos_setup_list[j].qfi;
|
||||
qosItem->dataForwardingAccepted = malloc_or_fail(sizeof(*qosItem->dataForwardingAccepted));
|
||||
*qosItem->dataForwardingAccepted = NGAP_DataForwardingAccepted_data_forwarding_accepted;
|
||||
}
|
||||
|
||||
void *buf;
|
||||
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, NULL, &transfer, &buf);
|
||||
AssertFatal(encoded > 0, "ASN1 message encoding failed !\n");
|
||||
item->handoverRequestAcknowledgeTransfer.buf = buf;
|
||||
item->handoverRequestAcknowledgeTransfer.size = encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, &transfer);
|
||||
}
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg)
|
||||
{
|
||||
free_byte_array(msg->target2source);
|
||||
}
|
||||
|
||||
/** @brief Decoder for the NG Handover Command */
|
||||
int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
NGAP_HandoverCommandIEs_t *ie;
|
||||
NGAP_HandoverCommand_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCommand;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
|
||||
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
|
||||
msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
|
||||
|
||||
// Handover Type (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true);
|
||||
if (ie->value.choice.HandoverType != HANDOVER_TYPE_INTRA5GS) {
|
||||
NGAP_ERROR("Only Intra 5GS Handover is supported at the moment!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// PDU Session Resource Handover List (O)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, false);
|
||||
if (ie != NULL) {
|
||||
msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceHandoverList.list.count;
|
||||
|
||||
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
NGAP_PDUSessionResourceHandoverItem_t *item = ie->value.choice.PDUSessionResourceHandoverList.list.array[i];
|
||||
|
||||
// PDU Session ID (M)
|
||||
msg->pdu_sessions[i].pdusession_id = item->pDUSessionID;
|
||||
|
||||
// Handover Command Transfer (M)
|
||||
NGAP_HandoverCommandTransfer_t *hoCommandTransfer = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
|
||||
&asn_DEF_NGAP_HandoverCommandTransfer,
|
||||
(void **)&hoCommandTransfer,
|
||||
item->handoverCommandTransfer.buf,
|
||||
item->handoverCommandTransfer.size);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
NGAP_ERROR("Failed to decode handoverCommandTransfer\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// DL Forwarding UP TNL Information (O)
|
||||
if (hoCommandTransfer->dLForwardingUP_TNLInformation) {
|
||||
NGAP_UPTransportLayerInformation_t *up_tnl = hoCommandTransfer->dLForwardingUP_TNLInformation;
|
||||
if (hoCommandTransfer->dLForwardingUP_TNLInformation->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel) {
|
||||
OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), msg->pdu_sessions[i].ho_command_transfer.gtp_teid);
|
||||
bitstring_to_tnl(&msg->pdu_sessions[i].ho_command_transfer.gNB_addr, up_tnl->choice.gTPTunnel->transportLayerAddress);
|
||||
} else {
|
||||
NGAP_WARN("Missing DL Forwarding UP TNL Information\n");
|
||||
}
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_HandoverCommandTransfer, hoCommandTransfer);
|
||||
}
|
||||
}
|
||||
|
||||
// Target to Source Transparent Container (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t,
|
||||
ie,
|
||||
container,
|
||||
NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer,
|
||||
true);
|
||||
NGAP_TargetToSource_TransparentContainer_t *target2source = &(ie->value.choice.TargetToSource_TransparentContainer);
|
||||
NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *transparentContainer = NULL;
|
||||
asn_dec_rval_t dec_rval = aper_decode_complete(NULL,
|
||||
&asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer,
|
||||
(void **)&transparentContainer,
|
||||
target2source->buf,
|
||||
target2source->size);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
NGAP_ERROR("Failed to decode TargetToSource_TransparentContainer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->handoverCommand = create_byte_array(transparentContainer->rRCContainer.size, transparentContainer->rRCContainer.buf);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, transparentContainer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_ng_handover_command(ngap_handover_command_t *msg)
|
||||
{
|
||||
free_byte_array(msg->handoverCommand);
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
|
||||
// Message Type (M)
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverNotification;
|
||||
head->criticality = NGAP_Criticality_ignore;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverNotify;
|
||||
NGAP_HandoverNotify_t *out = &head->value.choice.HandoverNotify;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
}
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
}
|
||||
|
||||
// User Location Information (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_UserLocationInformation;
|
||||
ie->criticality = NGAP_Criticality_ignore;
|
||||
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_UserLocationInformation;
|
||||
ie->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR;
|
||||
asn1cCalloc(ie->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p);
|
||||
// NR User Location Information
|
||||
const target_ran_node_id_t *target = &msg->user_info.target_ng_ran;
|
||||
// CGI (M)
|
||||
MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity);
|
||||
MCC_MNC_TO_TBCD(target->plmn_identity.mcc,
|
||||
target->plmn_identity.mnc,
|
||||
target->plmn_identity.mnc_digit_length,
|
||||
&userinfo_nr_p->nR_CGI.pLMNIdentity);
|
||||
// TAI (M)
|
||||
INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC);
|
||||
MCC_MNC_TO_PLMNID(target->plmn_identity.mcc,
|
||||
target->plmn_identity.mnc,
|
||||
target->plmn_identity.mnc_digit_length,
|
||||
&userinfo_nr_p->tAI.pLMNIdentity);
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
/** @brief NGAP Handover Cancel encoding (9.2.3.11 3GPP TS 38.413 Handover Cancel)
|
||||
* 8.4.5 Handover Cancellation (source NG-RAN -> AMF) */
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
|
||||
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverCancel;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverCancel;
|
||||
NGAP_HandoverCancel_t *out = &head->value.choice.HandoverCancel;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_HandoverCancelIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie2->criticality = NGAP_Criticality_reject;
|
||||
ie2->value.present = NGAP_HandoverCancelIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
|
||||
// Cause (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_Cause;
|
||||
ie3->criticality = NGAP_Criticality_reject;
|
||||
ie3->value.present = NGAP_HandoverCancelIEs__value_PR_Cause;
|
||||
encode_ngap_cause(&ie3->value.choice.Cause, &msg->cause);
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
/** @brief Decode NGAP Handover Cancel Acknowledge (9.2.3.12 3GPP TS 38.413) */
|
||||
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(out != NULL);
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
NGAP_HandoverCancelAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCancelAcknowledge;
|
||||
NGAP_HandoverCancelAcknowledgeIEs_t *ie;
|
||||
|
||||
/* AMF UE NGAP ID (M) */
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
|
||||
asn_INTEGER2ulong(&ie->value.choice.AMF_UE_NGAP_ID, &out->amf_ue_ngap_id);
|
||||
|
||||
/* RAN UE NGAP ID (M) */
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
|
||||
out->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Encode NGAP UL RAN Status Transfer (9.2.3.14 of 3GPP TS 38.413) */
|
||||
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_UplinkRANStatusTransfer;
|
||||
head->criticality = NGAP_Criticality_ignore;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_UplinkRANStatusTransfer;
|
||||
NGAP_UplinkRANStatusTransfer_t *out = &head->value.choice.UplinkRANStatusTransfer;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie2->criticality = NGAP_Criticality_reject;
|
||||
ie2->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie2->value.choice.RAN_UE_NGAP_ID = msg->gnb_ue_ngap_id;
|
||||
|
||||
// RAN Status Transfer Transparent Container (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_RANStatusTransfer_TransparentContainer;
|
||||
ie3->criticality = NGAP_Criticality_reject;
|
||||
ie3->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_RANStatusTransfer_TransparentContainer;
|
||||
NGAP_RANStatusTransfer_TransparentContainer_t *container = &ie3->value.choice.RANStatusTransfer_TransparentContainer;
|
||||
|
||||
for (int i = 0; i < msg->ran_status.nb_drb; ++i) {
|
||||
const ngap_drb_status_t *s = &msg->ran_status.drb_status_list[i];
|
||||
asn1cSequenceAdd(container->dRBsSubjectToStatusTransferList.list, NGAP_DRBsSubjectToStatusTransferItem_t, drb_item);
|
||||
drb_item->dRB_ID = s->drb_id;
|
||||
|
||||
// UL COUNT
|
||||
if (s->ul_count.sn_len == NGAP_SN_LENGTH_18) {
|
||||
drb_item->dRBStatusUL.present = NGAP_DRBStatusUL_PR_dRBStatusUL18;
|
||||
asn1cCalloc(drb_item->dRBStatusUL.choice.dRBStatusUL18, ul18);
|
||||
ul18->uL_COUNTValue.pDCP_SN18 = s->ul_count.pdcp_sn;
|
||||
ul18->uL_COUNTValue.hFN_PDCP_SN18 = s->ul_count.hfn;
|
||||
} else {
|
||||
drb_item->dRBStatusUL.present = NGAP_DRBStatusUL_PR_dRBStatusUL12;
|
||||
asn1cCalloc(drb_item->dRBStatusUL.choice.dRBStatusUL12, ul12);
|
||||
ul12->uL_COUNTValue.pDCP_SN12 = s->ul_count.pdcp_sn;
|
||||
ul12->uL_COUNTValue.hFN_PDCP_SN12 = s->ul_count.hfn;
|
||||
}
|
||||
|
||||
// DL COUNT
|
||||
if (s->dl_count.sn_len == NGAP_SN_LENGTH_18) {
|
||||
drb_item->dRBStatusDL.present = NGAP_DRBStatusDL_PR_dRBStatusDL18;
|
||||
asn1cCalloc(drb_item->dRBStatusDL.choice.dRBStatusDL18, dl18);
|
||||
dl18->dL_COUNTValue.pDCP_SN18 = s->dl_count.pdcp_sn;
|
||||
dl18->dL_COUNTValue.hFN_PDCP_SN18 = s->dl_count.hfn;
|
||||
} else {
|
||||
drb_item->dRBStatusDL.present = NGAP_DRBStatusDL_PR_dRBStatusDL12;
|
||||
asn1cCalloc(drb_item->dRBStatusDL.choice.dRBStatusDL12, dl12);
|
||||
dl12->dL_COUNTValue.pDCP_SN12 = s->dl_count.pdcp_sn;
|
||||
dl12->dL_COUNTValue.hFN_PDCP_SN12 = s->dl_count.hfn;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_RANStatusTransfer_TransparentContainer, &container);
|
||||
|
||||
return pdu;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 NGAP_GNB_MOBILITY_MANAGEMENT_H_
|
||||
#define NGAP_GNB_MOBILITY_MANAGEMENT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/platform_types.h"
|
||||
#include "ngap_messages_types.h"
|
||||
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg);
|
||||
int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu);
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg);
|
||||
void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg);
|
||||
int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu);
|
||||
void free_ng_handover_command(ngap_handover_command_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg);
|
||||
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu);
|
||||
|
||||
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */
|
||||
@@ -76,6 +76,12 @@ struct ngap_gNB_ue_context_s *ngap_get_ue_context(uint32_t gNB_ue_ngap_id)
|
||||
return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp);
|
||||
}
|
||||
|
||||
ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id)
|
||||
{
|
||||
ngap_gNB_ue_context_t temp = {.amf_ue_ngap_id = amf_ue_ngap_id};
|
||||
return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp);
|
||||
}
|
||||
|
||||
struct ngap_gNB_ue_context_s *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id)
|
||||
{
|
||||
struct ngap_gNB_ue_context_s *tmp = ngap_get_ue_context(gNB_ue_ngap_id);
|
||||
|
||||
@@ -81,6 +81,7 @@ typedef struct ngap_gNB_ue_context_s {
|
||||
void ngap_store_ue_context(const ngap_gNB_ue_context_t *ue_desc_p);
|
||||
|
||||
ngap_gNB_ue_context_t *ngap_get_ue_context(uint32_t gNB_ue_ngap_id);
|
||||
ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id);
|
||||
ngap_gNB_ue_context_t *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id);
|
||||
|
||||
#endif /* NGAP_GNB_UE_CONTEXT_H_ */
|
||||
|
||||
@@ -78,5 +78,36 @@
|
||||
#include "NGAP_Dynamic5QIDescriptor.h"
|
||||
#include "NGAP_PDUSessionResourceModifyRequestTransfer.h"
|
||||
#include "NGAP_QosFlowAddOrModifyRequestItem.h"
|
||||
#include "NGAP_PDUSessionResourceItemHORqd.h"
|
||||
#include "NGAP_PDUSessionResourceInformationItem.h"
|
||||
#include "NGAP_QosFlowInformationItem.h"
|
||||
#include "NGAP_LastVisitedCellItem.h"
|
||||
#include "NGAP_LastVisitedNGRANCellInformation.h"
|
||||
#include "NGAP_PDUSessionResourceInformationList.h"
|
||||
#include "NGAP_HandoverRequiredTransfer.h"
|
||||
#include "NGAP_SourceNGRANNode-ToTargetNGRANNode-TransparentContainer.h"
|
||||
#include "NGAP_PDUSessionResourceSetupItemHOReq.h"
|
||||
#include "NGAP_HandoverCommand.h"
|
||||
#include "NGAP_HandoverCommandTransfer.h"
|
||||
#include "NGAP_TargetNGRANNode-ToSourceNGRANNode-TransparentContainer.h"
|
||||
#include "NGAP_TargetToSource-TransparentContainer.h"
|
||||
#include "NGAP_UEHistoryInformation.h"
|
||||
#include "NGAP_Cause.h"
|
||||
#include "NGAP_PDUSessionResourceHandoverItem.h"
|
||||
#include "NGAP_SourceToTarget-TransparentContainer.h"
|
||||
#include "NGAP_HandoverRequest.h"
|
||||
#include "NGAP_PDUSessionResourceAdmittedList.h"
|
||||
#include "NGAP_PDUSessionResourceAdmittedItem.h"
|
||||
#include "NGAP_HandoverRequestAcknowledgeTransfer.h"
|
||||
#include "NGAP_QosFlowItemWithDataForwarding.h"
|
||||
#include "NR_HandoverPreparationInformation.h"
|
||||
#include "NGAP_HandoverNotify.h"
|
||||
#include "NGAP_DRBsSubjectToStatusTransferItem.h"
|
||||
#include "NGAP_DRBStatusUL.h"
|
||||
#include "NGAP_DRBStatusUL18.h"
|
||||
#include "NGAP_DRBStatusUL12.h"
|
||||
#include "NGAP_DRBStatusDL.h"
|
||||
#include "NGAP_DRBStatusDL18.h"
|
||||
#include "NGAP_DRBStatusDL12.h"
|
||||
|
||||
#endif // NGAP_MSG_INCLUDES_H
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
|
||||
@include "neighbour-config-rfsim.conf"
|
||||
|
||||
nr_cellid = 12345678L;
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
do_CSIRS = 1;
|
||||
do_SRS = 1;
|
||||
|
||||
#uess_agg_levels = [0,1,2,2,1]
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
|
||||
physCellId = 0;
|
||||
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
|
||||
absoluteFrequencySSB = 641280;
|
||||
dl_frequencyBand = 78;
|
||||
# this is 3600 MHz
|
||||
dl_absoluteFrequencyPointA = 640008;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=27,L=48 (275*(L-1))+RBstart
|
||||
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 28875;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 98;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 13;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
|
||||
#one (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
|
||||
msg3_DeltaPreamble = 1;
|
||||
p0_NominalWithGrant =-90;
|
||||
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
|
||||
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
|
||||
ssPBCH_BlockPower = -25;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ({ ipv4 = "192.168.70.132"; });
|
||||
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 150;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 120;
|
||||
pucch0_dtx_threshold = 100;
|
||||
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1
|
||||
nb_rx = 1
|
||||
att_tx = 12;
|
||||
att_rx = 12;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 114;
|
||||
eNB_instances = [0];
|
||||
clock_src = "internal";
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
rfsimulator :
|
||||
{
|
||||
serveraddr = "server";
|
||||
serverport = 4043;
|
||||
options = (); #("saviq"); or/and "chanmod"
|
||||
modelname = "AWGN";
|
||||
IQfile = "/tmp/rfsimulator.iqs";
|
||||
};
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
};
|
||||
|
||||
e2_agent = {
|
||||
near_ric_ip_addr = "127.0.0.1";
|
||||
#sm_dir = "/path/where/the/SMs/are/located/"
|
||||
sm_dir = "/usr/local/lib/flexric/"
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xb00;
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
|
||||
@include "neighbour-config-rfsim.conf"
|
||||
|
||||
nr_cellid = 720898;
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
do_CSIRS = 1;
|
||||
do_SRS = 1;
|
||||
|
||||
#uess_agg_levels = [0,1,2,2,1]
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
|
||||
physCellId = 1;
|
||||
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
|
||||
absoluteFrequencySSB = 621312;
|
||||
dl_frequencyBand = 78;
|
||||
# this is 3600 MHz
|
||||
dl_absoluteFrequencyPointA = 620040;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=27,L=48 (275*(L-1))+RBstart
|
||||
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 28875;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 98;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 13;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
|
||||
#one (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
|
||||
msg3_DeltaPreamble = 1;
|
||||
p0_NominalWithGrant =-90;
|
||||
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
|
||||
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
|
||||
ssPBCH_BlockPower = -25;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ({ ipv4 = "192.168.70.132"; });
|
||||
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 150;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 120;
|
||||
pucch0_dtx_threshold = 100;
|
||||
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1
|
||||
nb_rx = 1
|
||||
att_tx = 12;
|
||||
att_rx = 12;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 114;
|
||||
eNB_instances = [0];
|
||||
clock_src = "internal";
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
rfsimulator :
|
||||
{
|
||||
serveraddr = "server";
|
||||
serverport = 4043;
|
||||
options = (); #("saviq"); or/and "chanmod"
|
||||
modelname = "AWGN";
|
||||
IQfile = "/tmp/rfsimulator.iqs";
|
||||
};
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
};
|
||||
|
||||
e2_agent = {
|
||||
near_ric_ip_addr = "127.0.0.1";
|
||||
#sm_dir = "/path/where/the/SMs/are/located/"
|
||||
sm_dir = "/usr/local/lib/flexric/"
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
############################################################
|
||||
# gNB-to-gNB neighbour list + measurement configuration #
|
||||
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
|
||||
############################################################
|
||||
|
||||
neighbour_list = (
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 12345678L) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 12345678L; # Serving cell of gNB 0xe00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xb00;
|
||||
nr_cellid = 720898; # Cell served by gNB 0xb00
|
||||
physical_cellId = 1;
|
||||
absoluteFrequencySSB= 621312;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
##########################################################
|
||||
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 720898) #
|
||||
##########################################################
|
||||
{
|
||||
nr_cellid = 720898; # Serving cell of gNB 0xb00
|
||||
neighbour_cell_configuration = (
|
||||
{
|
||||
gNB_ID = 0xe00;
|
||||
nr_cellid = 12345678L; # Cell served by gNB 0xe00
|
||||
physical_cellId = 0;
|
||||
absoluteFrequencySSB= 641280;
|
||||
subcarrierSpacing = 1; # 30 kHz
|
||||
band = 78;
|
||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
|
||||
tracking_area_code = 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
############################################################
|
||||
# Common NR measurement-event configuration #
|
||||
############################################################
|
||||
|
||||
nr_measurement_configuration = {
|
||||
Periodical = {
|
||||
enable = 1;
|
||||
includeBeamMeasurements = 1;
|
||||
maxNrofRS_IndexesToReport = 4;
|
||||
};
|
||||
|
||||
A2 = {
|
||||
enable = 1;
|
||||
threshold = 60;
|
||||
timeToTrigger = 1;
|
||||
};
|
||||
|
||||
A3 = (
|
||||
{
|
||||
cell_id = 720898; # neighbour of gNB 0xe00
|
||||
offset = 10;
|
||||
hysteresis = 0;
|
||||
timeToTrigger = 1;
|
||||
},
|
||||
{
|
||||
cell_id = 12345678; # neighbour of gNB 0xb00
|
||||
offset = 5;
|
||||
hysteresis = 1;
|
||||
timeToTrigger = 2;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user