diff --git a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt index 93dfe6f8a13ec82300cf56c7da32ec2557fe0987..47f7ae41545aae31438e74c4a814a2437451003e 100644 --- a/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt +++ b/openair2/E2AP/RAN_FUNCTION/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(e2_ran_func_cuup STATIC O-RAN/ran_func_kpm.c # this file should only contain PDCP-U/GTP; to be done in the future ../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented O-RAN/ran_func_rc.c # this file should only contain PDCP-U/GTP; to be done in the future + O-RAN/ran_func_rc_subs.c ../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented ) @@ -27,6 +28,7 @@ add_library(e2_ran_func_du_cucp_cuup STATIC ../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented O-RAN/ran_func_rc.c # this file should only contain RRC/PDCP-C; to be done in the future # when nr-softmodem is divided in separate executables + O-RAN/ran_func_rc_subs.c ../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented ../flexric/src/sm/rc_sm/ie/rc_data_ie.c CUSTOMIZED/ran_func_mac.c diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c index 90f8d1ae0025587b75ec9f5d2a793841afab5a0e..7cf3e94f64c994c1d1eae2212cff3f7e9337d37c 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.c @@ -20,22 +20,41 @@ */ #include "ran_func_rc.h" +#include "ran_func_rc_subs.h" #include "ran_func_rc_extern.h" #include "../../flexric/test/rnd/fill_rnd_data_rc.h" // this dependancy will be taken out once RAN Function Definition is implemented #include "../../flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h" #include "../../flexric/src/sm/rc_sm/ie/ir/ran_param_list.h" #include "../../flexric/src/agent/e2_agent_api.h" -#include <assert.h> #include <stdio.h> -#include <pthread.h> #include <unistd.h> -#include "common/utils/assertions.h" #include "common/ran_context.h" -static const int rrc_state_changed_to = 202; -static bool subs_rrc_state_change = false; -static uint32_t ric_req_id = 0; +static pthread_once_t once_rc_mutex = PTHREAD_ONCE_INIT; +static rc_subs_data_t rc_subs_data = {0}; +static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void init_once_rc(void) +{ + init_rc_subs_data(&rc_subs_data); +} + +void read_rc_setup_sm(void* data) +{ + assert(data != NULL); +// assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0); + rc_e2_setup_t* rc = (rc_e2_setup_t*)data; + rc->ran_func_def = fill_rc_ran_func_def(); + + // E2 Setup Request is sent periodically until the connection is established + // RC subscritpion data should be initialized only once + const int ret = pthread_once(&once_rc_mutex, init_once_rc); + DevAssert(ret == 0); +} + + +RB_PROTOTYPE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id); static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context) { @@ -65,11 +84,11 @@ static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context) ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[i] = rrc_ue_context->rrc_ue_id; } } else if (RC.nrrrc[0]->node_type == ngran_gNB_DU) { - assert(false && "Cannot access RRC layer in DU\n"); + assert(false && "RRC state change cannot be triggered from DU\n"); } #elif defined (NGRAN_GNB_CUUP) - assert(false && "Cannot access RRC layer in CU-UP\n"); + assert(false && "RRC state change cannot be triggered from CU-UP\n"); #endif @@ -88,7 +107,7 @@ static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc { seq_ran_param_t seq_ran_param = {0}; - seq_ran_param.ran_param_id = rrc_state_changed_to; + seq_ran_param.ran_param_id = RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID; seq_ran_param.ran_param_val.type = ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE; seq_ran_param.ran_param_val.flag_false = calloc(1, sizeof(ran_parameter_value_t)); assert(seq_ran_param.ran_param_val.flag_false != NULL && "Memory exhausted"); @@ -132,34 +151,70 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex return rc_ind; } - void signal_rrc_state_changed_to(const gNB_RRC_UE_t *rrc_ue_context, const rc_sm_rrc_state_e rrc_state) -{ - AssertFatal(subs_rrc_state_change == true, "xApp should be subscribed to E2 node before sending the RIC INDICATION Message\n"); +{ + pthread_mutex_lock(&rc_mutex); + if (rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID].rbh_root == NULL) { + pthread_mutex_unlock(&rc_mutex); + return; + } - rc_ind_data_t* rc_ind_data = fill_ue_rrc_state_change(rrc_ue_context, rrc_state); + struct ric_req_id_s *node; + RB_FOREACH(node, ric_id_2_param_id_trees, &rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID]) { + rc_ind_data_t* rc_ind_data = fill_ue_rrc_state_change(rrc_ue_context, rrc_state); - async_event_agent_api(ric_req_id, rc_ind_data); - printf("Event for RIC Req ID %u generated\n", ric_req_id); + // Needs review: memory ownership of the type rc_ind_data_t is transferred to the E2 Agent. Bad + async_event_agent_api(node->ric_req_id, rc_ind_data); + printf( "Event for RIC Req ID %u generated\n", node->ric_req_id); + } + + pthread_mutex_unlock(&rc_mutex); } -bool read_rc_sm(void* data) +static void free_aperiodic_subscription(uint32_t ric_req_id) { - assert(data != NULL); -// assert(data->type == RAN_CTRL_STATS_V1_03); - assert(0!=0 && "Not implemented"); - - return true; + remove_rc_subs_data(&rc_subs_data, ric_req_id); } -void read_rc_setup_sm(void* data) +sm_ag_if_ans_t write_subs_rc_sm(void const* src) { - assert(data != NULL); -// assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0); - rc_e2_setup_t* rc = (rc_e2_setup_t*)data; - rc->ran_func_def = fill_rc_ran_func_def(); + assert(src != NULL); // && src->type == RAN_CTRL_SUBS_V1_03); + + wr_rc_sub_data_t* wr_rc = (wr_rc_sub_data_t*)src; + + assert(wr_rc->rc.ad != NULL && "Cannot be NULL"); + + // 9.2.1.2 RIC ACTION DEFINITION IE + switch (wr_rc->rc.ad->format) { + case FORMAT_1_E2SM_RC_ACT_DEF: { + // Parameters to be Reported List + // [1-65535] + const uint32_t ric_req_id = wr_rc->ric_req_id; + arr_ran_param_id_t* arr_ran_param_id = calloc(1, sizeof(arr_ran_param_id_t)); + assert(arr_ran_param_id != NULL && "Memory exhausted"); + arr_ran_param_id->len = wr_rc->rc.ad->frmt_1.sz_param_report_def; + arr_ran_param_id->ran_param_id = calloc(arr_ran_param_id->len, sizeof(ran_param_id_e)); + + const size_t sz = arr_ran_param_id->len; + for(size_t i = 0; i < sz; i++) { + arr_ran_param_id->ran_param_id[i] = wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id; + } + insert_rc_subs_data(&rc_subs_data, ric_req_id, arr_ran_param_id); + break; + } + + default: + AssertFatal(wr_rc->rc.ad->format == FORMAT_1_E2SM_RC_ACT_DEF, "Action Definition Format %d not yet implemented", wr_rc->rc.ad->format); + } + + sm_ag_if_ans_t ans = {.type = SUBS_OUTCOME_SM_AG_IF_ANS_V0}; + ans.subs_out.type = APERIODIC_SUBSCRIPTION_FLRC; + ans.subs_out.aper.free_aper_subs = free_aperiodic_subscription; + + return ans; } + sm_ag_if_ans_t write_ctrl_rc_sm(void const* data) { assert(data != NULL); @@ -205,36 +260,12 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data) return ans; } -sm_ag_if_ans_t write_subs_rc_sm(void const* src) -{ - assert(src != NULL); // && src->type == RAN_CTRL_SUBS_V1_03); - - wr_rc_sub_data_t* wr_rc = (wr_rc_sub_data_t*)src; - - ric_req_id = wr_rc->ric_req_id; // store the ric_req_id to generate asynchronous event - - assert(wr_rc->rc.ad != NULL && "Cannot be NULL"); - - // 9.2.1.2 RIC ACTION DEFINITION IE - switch (wr_rc->rc.ad->format) { - case FORMAT_1_E2SM_RC_ACT_DEF: { - // Parameters to be Reported List - // [1-65535] - for(size_t i = 0; i < wr_rc->rc.ad->frmt_1.sz_param_report_def; i++) { - if (wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id == rrc_state_changed_to) { - subs_rrc_state_change = true; - } else { - AssertFatal(wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id == rrc_state_changed_to, "RAN Parameter ID %d not yet implemented\n", wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id); - } - } - break; - } - - default: - AssertFatal(wr_rc->rc.ad->format == FORMAT_1_E2SM_RC_ACT_DEF, "Action Definition Format %d not yet implemented", wr_rc->rc.ad->format); - } - sm_ag_if_ans_t ans = {0}; +bool read_rc_sm(void* data) +{ + assert(data != NULL); +// assert(data->type == RAN_CTRL_STATS_V1_03); + assert(0!=0 && "Not implemented"); - return ans; + return true; } diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h index ed877fea021431ac27a37263ba3fb5b58bc99d1e..efabe7caff497e57c0860ff5f8703d627257ad90 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc.h @@ -24,13 +24,12 @@ #include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h" -bool read_rc_sm(void *); - void read_rc_setup_sm(void* data); +sm_ag_if_ans_t write_subs_rc_sm(void const* src); + sm_ag_if_ans_t write_ctrl_rc_sm(void const* data); -sm_ag_if_ans_t write_subs_rc_sm(void const* src); +bool read_rc_sm(void *); #endif - diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c new file mode 100644 index 0000000000000000000000000000000000000000..6c7176eafb02f5a15eebd276c36831e96c463c1f --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.c @@ -0,0 +1,114 @@ +/* + * 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 "ran_func_rc_subs.h" +#include "common/utils/assertions.h" + +#include <assert.h> +#include <pthread.h> + +#define MAX_NUM_RIC_REQ_ID 64 + +static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER; + +int cmp_ric_req_id(struct ric_req_id_s *c1, struct ric_req_id_s *c2) +{ + if (c1->ric_req_id < c2->ric_req_id) + return -1; + + if (c1->ric_req_id > c2->ric_req_id) + return 1; + + return 0; +} + +RB_GENERATE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id); + +void init_rc_subs_data(rc_subs_data_t* rc_subs_data) +{ + pthread_mutex_lock(&rc_mutex); + + // Initialize hash table + DevAssert(rc_subs_data->htable == NULL); + + // Initialize RB trees + // 1 RB tree = 1 ran_param_id => many ric_req_id(s) + for (size_t i = 0; i < END_E2SM_RC_RAN_PARAM_ID; i++) { + RB_INIT(&rc_subs_data->rb[i]); + } + + rc_subs_data->htable = hashtable_create(MAX_NUM_RIC_REQ_ID, NULL, free); + assert(rc_subs_data->htable != NULL && "Memory exhausted"); + pthread_mutex_unlock(&rc_mutex); +} + +void insert_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id, arr_ran_param_id_t* arr_ran_param_id) +{ + pthread_mutex_lock(&rc_mutex); + + // Insert in hash table + DevAssert(rc_subs_data->htable != NULL); + uint64_t key = ric_req_id; + // Check if the subscription already exists + AssertFatal(hashtable_is_key_exists(rc_subs_data->htable, key) == HASH_TABLE_KEY_NOT_EXISTS, "RIC req ID %d already subscribed", ric_req_id); + arr_ran_param_id_t* data = malloc(sizeof(*data)); + assert(data != NULL); + *data = *arr_ran_param_id; + hashtable_rc_t ret = hashtable_insert(rc_subs_data->htable, key, data); + assert(ret == HASH_TABLE_OK && "Hash table not ok"); + + // Insert in RB trees + // 1 RB tree = 1 ran_param_id => many ric_req_id(s) + const size_t sz = arr_ran_param_id->len; + rb_ric_req_id_t *node = calloc(1, sizeof(*node)); + assert(node != NULL); + node->ric_req_id = ric_req_id; + for (size_t i = 0; i < sz; i++) { + RB_INSERT(ric_id_2_param_id_trees, &rc_subs_data->rb[arr_ran_param_id->ran_param_id[i]], node); + } + + pthread_mutex_unlock(&rc_mutex); +} + +void remove_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id) +{ + pthread_mutex_lock(&rc_mutex); + DevAssert(rc_subs_data->htable != NULL); + + uint64_t key = ric_req_id; + // Get the array of ran_param_id(s) + void *data = NULL; + hashtable_rc_t ret = hashtable_get(rc_subs_data->htable, key, &data); + AssertFatal(ret == HASH_TABLE_OK && data != NULL, "element for ue_id %d not found\n", ric_req_id); + arr_ran_param_id_t arr_ran_param_id = *(arr_ran_param_id_t *)data; + // Remove ric_req_id with its ran_param_id(s) from hash table + ret = hashtable_remove(rc_subs_data->htable, key); + + // Remove ric_req_id from each ran_param_id tree where subscribed + rb_ric_req_id_t *node = calloc(1, sizeof(*node)); + assert(node != NULL); + node->ric_req_id = ric_req_id; + for (size_t i = 0; i < arr_ran_param_id.len; i++) { + RB_REMOVE(ric_id_2_param_id_trees, &rc_subs_data->rb[arr_ran_param_id.ran_param_id[i]], node); + } + + pthread_mutex_unlock(&rc_mutex); +} diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h new file mode 100644 index 0000000000000000000000000000000000000000..1eb013c37c185c2a8d4de2810ba5edcc5bfa1cf1 --- /dev/null +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_subs.h @@ -0,0 +1,56 @@ +/* + * 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 RAN_FUNC_SM_RAN_CTRL_SUBSCRIPTION_AGENT_H +#define RAN_FUNC_SM_RAN_CTRL_SUBSCRIPTION_AGENT_H + +#include "common/utils/hashtable/hashtable.h" +#include "common/utils/collection/tree.h" + +typedef enum { + RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID = 202, // 8.2.4 RAN Parameters for Report Service Style 4 + + END_E2SM_RC_RAN_PARAM_ID +} ran_param_id_e; + +typedef struct{ + size_t len; + ran_param_id_e* ran_param_id; +} arr_ran_param_id_t; + +typedef struct ric_req_id_s { + RB_ENTRY(ric_req_id_s) entries; + uint32_t ric_req_id; +} rb_ric_req_id_t; + +typedef struct { + RB_HEAD(ric_id_2_param_id_trees, ric_req_id_s) rb[END_E2SM_RC_RAN_PARAM_ID]; // 1 RB tree = (1 RAN Parameter ID) : (n RIC Request ID) => m RB tree = (m RAN Parameter ID) : (n RIC Request ID) + hash_table_t* htable; // 1 Hash table = (n RIC Request ID) : (m RAN Parameter ID) +} rc_subs_data_t; + + +int cmp_ric_req_id(struct ric_req_id_s *c1, struct ric_req_id_s *c2); + +void init_rc_subs_data(rc_subs_data_t* rc_subs_data); +void insert_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id, arr_ran_param_id_t* arr_ran_param_id); +void remove_rc_subs_data(rc_subs_data_t* rc_subs_data, uint32_t ric_req_id); + +#endif