Commit fa4617c0 authored by Melissa Elkadi's avatar Melissa Elkadi

Fixing MR based on OAI feedback

The following was changed in this commit:
 - A new struct in the mac instance was created
   for all emulated L1 fields.
 - NR UE RRC state bug was fixed
 - Checking the RAR rnti based on the MAC instance
   ra.ra_rnti and not hardcoding to 0x10b
 - Modified the name of the new emulate_l2 flag
   to make sense: emulate_l1
parent 85e06605
...@@ -269,12 +269,12 @@ static void process_queued_nr_nfapi_msgs(NR_UE_MAC_INST_t *mac, int sfn_slot) ...@@ -269,12 +269,12 @@ static void process_queued_nr_nfapi_msgs(NR_UE_MAC_INST_t *mac, int sfn_slot)
nfapi_nr_ul_dci_request_t *ul_dci_request = get_queue(&nr_ul_dci_req_queue); nfapi_nr_ul_dci_request_t *ul_dci_request = get_queue(&nr_ul_dci_req_queue);
LOG_D(NR_MAC, "Try to get a ul_tti_req for sfn/slot %d %d from queue with %lu items\n", LOG_D(NR_MAC, "Try to get a ul_tti_req for sfn/slot %d %d from queue with %lu items\n",
NFAPI_SFNSLOT2SFN(mac->active_harq_sfn_slot),NFAPI_SFNSLOT2SLOT(mac->active_harq_sfn_slot), nr_ul_tti_req_queue.num_items); NFAPI_SFNSLOT2SFN(mac->nr_ue_emul_l1.active_harq_sfn_slot),NFAPI_SFNSLOT2SLOT(mac->nr_ue_emul_l1.active_harq_sfn_slot), nr_ul_tti_req_queue.num_items);
nfapi_nr_ul_tti_request_t *ul_tti_request = unqueue_matching(&nr_ul_tti_req_queue, MAX_QUEUE_SIZE, sfn_slot_matcher, &mac->active_harq_sfn_slot); nfapi_nr_ul_tti_request_t *ul_tti_request = unqueue_matching(&nr_ul_tti_req_queue, MAX_QUEUE_SIZE, sfn_slot_matcher, &mac->nr_ue_emul_l1.active_harq_sfn_slot);
if (!ul_tti_request) if (!ul_tti_request)
{ {
LOG_D(NR_MAC, "Try to get a ul_tti_req from seprate queue because dl_tti_req was late\n"); LOG_D(NR_MAC, "Try to get a ul_tti_req from seprate queue because dl_tti_req was late\n");
ul_tti_request = unqueue_matching(&nr_wait_ul_tti_req_queue, MAX_QUEUE_SIZE, sfn_slot_matcher, &mac->active_harq_sfn_slot); ul_tti_request = unqueue_matching(&nr_wait_ul_tti_req_queue, MAX_QUEUE_SIZE, sfn_slot_matcher, &mac->nr_ue_emul_l1.active_harq_sfn_slot);
} }
if (rach_ind && rach_ind->number_of_pdus > 0) if (rach_ind && rach_ind->number_of_pdus > 0)
......
...@@ -489,10 +489,10 @@ int main( int argc, char **argv ) { ...@@ -489,10 +489,10 @@ int main( int argc, char **argv ) {
if (get_softmodem_params()->sa) if (get_softmodem_params()->sa)
AssertFatal(get_softmodem_params()->phy_test == 0,"Standalone mode and phy_test are mutually exclusive\n"); AssertFatal(get_softmodem_params()->phy_test == 0,"Standalone mode and phy_test are mutually exclusive\n");
if (!get_softmodem_params()->nsa && get_softmodem_params()->emulate_l2) if (!get_softmodem_params()->nsa && get_softmodem_params()->emulate_l1)
start_oai_nrue_threads(); start_oai_nrue_threads();
if (!get_softmodem_params()->nsa && !get_softmodem_params()->emulate_l2) { if (!get_softmodem_params()->emulate_l1) {
for (int CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { for (int CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
PHY_vars_UE_g[0][CC_id] = (PHY_VARS_NR_UE *)malloc(sizeof(PHY_VARS_NR_UE)); PHY_vars_UE_g[0][CC_id] = (PHY_VARS_NR_UE *)malloc(sizeof(PHY_VARS_NR_UE));
UE[CC_id] = PHY_vars_UE_g[0][CC_id]; UE[CC_id] = PHY_vars_UE_g[0][CC_id];
......
...@@ -98,7 +98,7 @@ extern "C" ...@@ -98,7 +98,7 @@ extern "C"
#define CONFIG_HLP_DISABLNBIOT "disable nb-iot, even if defined in config\n" #define CONFIG_HLP_DISABLNBIOT "disable nb-iot, even if defined in config\n"
#define CONFIG_HLP_USRP_THREAD "having extra thead for usrp tx\n" #define CONFIG_HLP_USRP_THREAD "having extra thead for usrp tx\n"
#define CONFIG_HLP_NFAPI "Change the nFAPI mode for NR\n" #define CONFIG_HLP_NFAPI "Change the nFAPI mode for NR\n"
#define CONFIG_L2_EMULATOR "Run in L2 emulated mode (disable PHY layer)\n" #define CONFIG_L1_EMULATOR "Run in L1 emulated mode (disable PHY layer)\n"
/*-----------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------------------------------------------------------------------------*/
/* command line parameters common to eNodeB and UE */ /* command line parameters common to eNodeB and UE */
...@@ -126,7 +126,7 @@ extern "C" ...@@ -126,7 +126,7 @@ extern "C"
#define NSA softmodem_params.nsa #define NSA softmodem_params.nsa
#define NODE_NUMBER softmodem_params.node_number #define NODE_NUMBER softmodem_params.node_number
#define NON_STOP softmodem_params.non_stop #define NON_STOP softmodem_params.non_stop
#define EMULATE_L2 softmodem_params.emulate_l2 #define EMULATE_L1 softmodem_params.emulate_l1
#define DEFAULT_RFCONFIG_FILE "/usr/local/etc/syriq/ue.band7.tm1.PRB100.NR40.dat"; #define DEFAULT_RFCONFIG_FILE "/usr/local/etc/syriq/ue.band7.tm1.PRB100.NR40.dat";
...@@ -165,7 +165,7 @@ extern int usrp_tx_thread; ...@@ -165,7 +165,7 @@ extern int usrp_tx_thread;
{"do-prb-interpolation", CONFIG_HLP_PRBINTER, PARAMFLAG_BOOL, iptr:&PRB_INTERPOLATION, defintval:0, TYPE_INT, 0}, \ {"do-prb-interpolation", CONFIG_HLP_PRBINTER, PARAMFLAG_BOOL, iptr:&PRB_INTERPOLATION, defintval:0, TYPE_INT, 0}, \
{"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \ {"nfapi", CONFIG_HLP_NFAPI, 0, u8ptr:&nfapi_mode, defintval:0, TYPE_UINT8, 0}, \
{"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \ {"non-stop", CONFIG_HLP_NONSTOP, PARAMFLAG_BOOL, iptr:&NON_STOP, defintval:0, TYPE_INT, 0}, \
{"emulate-l2", CONFIG_L2_EMULATOR, PARAMFLAG_BOOL, iptr:&EMULATE_L2, defintval:0, TYPE_INT, 0}, \ {"emulate-l1", CONFIG_L1_EMULATOR, PARAMFLAG_BOOL, iptr:&EMULATE_L1, defintval:0, TYPE_INT, 0}, \
} }
#define CONFIG_HLP_NSA "Enable NSA mode \n" #define CONFIG_HLP_NSA "Enable NSA mode \n"
...@@ -261,7 +261,7 @@ typedef struct { ...@@ -261,7 +261,7 @@ typedef struct {
int nsa; int nsa;
uint16_t node_number; uint16_t node_number;
int non_stop; int non_stop;
int emulate_l2; int emulate_l1;
} softmodem_params_t; } softmodem_params_t;
extern uint64_t get_softmodem_optmask(void); extern uint64_t get_softmodem_optmask(void);
......
...@@ -264,6 +264,16 @@ typedef struct { ...@@ -264,6 +264,16 @@ typedef struct {
uint8_t nbits; uint8_t nbits;
} dci_field_t; } dci_field_t;
typedef struct {
bool expected_sib;
bool index_has_sib[16];
bool expected_rar;
bool index_has_rar[16];
bool expected_dci;
bool index_has_dci[16];
int active_harq_sfn_slot;
} nr_emulated_l1_t;
typedef struct { typedef struct {
uint8_t format_indicator; //1 bit uint8_t format_indicator; //1 bit
......
...@@ -662,7 +662,7 @@ int nr_rrc_mac_config_req_ue( ...@@ -662,7 +662,7 @@ int nr_rrc_mac_config_req_ue(
mac->ul_config_request = (fapi_nr_ul_config_request_t *)calloc(num_slots_ul, sizeof(fapi_nr_ul_config_request_t)); mac->ul_config_request = (fapi_nr_ul_config_request_t *)calloc(num_slots_ul, sizeof(fapi_nr_ul_config_request_t));
// Setup the SSB to Rach Occasions mapping according to the config // Setup the SSB to Rach Occasions mapping according to the config
build_ssb_to_ro_map(mac);//->scc, mac->phy_config.config_req.cell_config.frame_duplex_type); build_ssb_to_ro_map(mac);//->scc, mac->phy_config.config_req.cell_config.frame_duplex_type);
if (!get_softmodem_params()->emulate_l2) if (!get_softmodem_params()->emulate_l1)
mac->if_module->phy_config_request(&mac->phy_config); mac->if_module->phy_config_request(&mac->phy_config);
mac->common_configuration_complete = 1; mac->common_configuration_complete = 1;
} }
......
...@@ -433,13 +433,8 @@ typedef struct { ...@@ -433,13 +433,8 @@ typedef struct {
// Defined for abstracted mode // Defined for abstracted mode
nr_downlink_indication_t dl_info; nr_downlink_indication_t dl_info;
NR_UE_HARQ_STATUS_t dl_harq_info[16]; NR_UE_HARQ_STATUS_t dl_harq_info[16];
bool expected_sib;
bool index_has_sib[16]; nr_emulated_l1_t nr_ue_emul_l1;
bool expected_rar;
bool index_has_rar[16];
bool expected_dci;
bool index_has_dci[16];
int active_harq_sfn_slot;
pthread_mutex_t mutex_dl_info; pthread_mutex_t mutex_dl_info;
......
...@@ -597,7 +597,7 @@ void nr_get_prach_resources(module_id_t mod_id, ...@@ -597,7 +597,7 @@ void nr_get_prach_resources(module_id_t mod_id,
} else { } else {
/* TODO: This controls the tx_power of UE and the ramping procedure of RA of UE. Later we /* TODO: This controls the tx_power of UE and the ramping procedure of RA of UE. Later we
can abstract this, perhaps in the proxy. But for the time being lets leave it as below. */ can abstract this, perhaps in the proxy. But for the time being lets leave it as below. */
int16_t dl_pathloss = !get_softmodem_params()->emulate_l2 ? get_nr_PL(mod_id, CC_id, gNB_id) : 0; int16_t dl_pathloss = !get_softmodem_params()->emulate_l1 ? get_nr_PL(mod_id, CC_id, gNB_id) : 0;
ssb_rach_config(ra, prach_resources, nr_rach_ConfigCommon, prach_pdu); ssb_rach_config(ra, prach_resources, nr_rach_ConfigCommon, prach_pdu);
ra_preambles_config(prach_resources, mac, dl_pathloss); ra_preambles_config(prach_resources, mac, dl_pathloss);
LOG_D(MAC, "[RAPROC] - Selected RA preamble index %d for contention-based random access procedure... \n", prach_resources->ra_PreambleIndex); LOG_D(MAC, "[RAPROC] - Selected RA preamble index %d for contention-based random access procedure... \n", prach_resources->ra_PreambleIndex);
......
...@@ -1325,7 +1325,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac, ...@@ -1325,7 +1325,7 @@ void set_harq_status(NR_UE_MAC_INST_t *mac,
// FIXME k0 != 0 currently not taken into consideration // FIXME k0 != 0 currently not taken into consideration
current_harq->dl_frame = frame; current_harq->dl_frame = frame;
current_harq->dl_slot = slot; current_harq->dl_slot = slot;
mac->active_harq_sfn_slot = NFAPI_SFNSLOT2HEX(frame, (slot + data_toul_fb)); mac->nr_ue_emul_l1.active_harq_sfn_slot = NFAPI_SFNSLOT2HEX(frame, (slot + data_toul_fb));
LOG_D(NR_PHY,"Setting harq_status for harq_id %d, dl %d.%d, sched ul %d.%d\n", LOG_D(NR_PHY,"Setting harq_status for harq_id %d, dl %d.%d, sched ul %d.%d\n",
harq_id, frame, slot, frame, (slot + data_toul_fb)); harq_id, frame, slot, frame, (slot + data_toul_fb));
......
...@@ -285,12 +285,12 @@ static bool is_my_dci(NR_UE_MAC_INST_t *mac, nfapi_nr_dl_dci_pdu_t *received_pdu ...@@ -285,12 +285,12 @@ static bool is_my_dci(NR_UE_MAC_INST_t *mac, nfapi_nr_dl_dci_pdu_t *received_pdu
static void copy_dl_tti_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi_nr_dl_tti_request_t *dl_tti_request) static void copy_dl_tti_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi_nr_dl_tti_request_t *dl_tti_request)
{ {
NR_UE_MAC_INST_t *mac = get_mac_inst(dl_info->module_id); NR_UE_MAC_INST_t *mac = get_mac_inst(dl_info->module_id);
mac->expected_sib = false; mac->nr_ue_emul_l1.expected_sib = false;
memset(mac->index_has_sib, 0, sizeof(*mac->index_has_sib)); memset(mac->nr_ue_emul_l1.index_has_sib, 0, sizeof(*mac->nr_ue_emul_l1.index_has_sib));
mac->expected_rar = false; mac->nr_ue_emul_l1.expected_rar = false;
memset(mac->index_has_rar, 0, sizeof(*mac->index_has_rar)); memset(mac->nr_ue_emul_l1.index_has_rar, 0, sizeof(*mac->nr_ue_emul_l1.index_has_rar));
mac->expected_dci = false; mac->nr_ue_emul_l1.expected_dci = false;
memset(mac->index_has_dci, 0, sizeof(*mac->index_has_dci)); memset(mac->nr_ue_emul_l1.index_has_dci, 0, sizeof(*mac->nr_ue_emul_l1.index_has_dci));
int pdu_idx = 0; int pdu_idx = 0;
int num_pdus = dl_tti_request->dl_tti_request_body.nPDUs; int num_pdus = dl_tti_request->dl_tti_request_body.nPDUs;
...@@ -331,20 +331,20 @@ static void copy_dl_tti_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi_ ...@@ -331,20 +331,20 @@ static void copy_dl_tti_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi_
fill_dl_info_with_pdcch(dl_info->dci_ind, dci_pdu_list, pdu_idx); fill_dl_info_with_pdcch(dl_info->dci_ind, dci_pdu_list, pdu_idx);
if (dci_pdu_list->RNTI == 0xffff) if (dci_pdu_list->RNTI == 0xffff)
{ {
mac->expected_sib = true; mac->nr_ue_emul_l1.expected_sib = true;
mac->index_has_sib[j] = true; mac->nr_ue_emul_l1.index_has_sib[j] = true;
LOG_D(NR_MAC, "Setting index_has_sib[%d] = true\n", j); LOG_D(NR_MAC, "Setting index_has_sib[%d] = true\n", j);
} }
else if (dci_pdu_list->RNTI == 0x10b) else if (dci_pdu_list->RNTI == mac->ra.ra_rnti)
{ {
mac->expected_rar = true; mac->nr_ue_emul_l1.expected_rar = true;
mac->index_has_rar[j] = true; mac->nr_ue_emul_l1.index_has_rar[j] = true;
LOG_D(NR_MAC, "Setting index_has_rar[%d] = true\n", j); LOG_D(NR_MAC, "Setting index_has_rar[%d] = true\n", j);
} }
else else
{ {
mac->expected_dci = true; mac->nr_ue_emul_l1.expected_dci = true;
mac->index_has_dci[j] = true; mac->nr_ue_emul_l1.index_has_dci[j] = true;
LOG_D(NR_MAC, "Setting index_has_dci[%d] = true\n", j); LOG_D(NR_MAC, "Setting index_has_dci[%d] = true\n", j);
} }
pdu_idx++; pdu_idx++;
...@@ -425,24 +425,26 @@ static void copy_tx_data_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi ...@@ -425,24 +425,26 @@ static void copy_tx_data_req_to_dl_info(nr_downlink_indication_t *dl_info, nfapi
for (int i = 0; i < num_pdus; i++) for (int i = 0; i < num_pdus; i++)
{ {
nfapi_nr_pdu_t *pdu_list = &tx_data_request->pdu_list[i]; nfapi_nr_pdu_t *pdu_list = &tx_data_request->pdu_list[i];
if (mac->index_has_sib[i]) if (mac->nr_ue_emul_l1.index_has_sib[i])
{ {
AssertFatal(!get_softmodem_params()->nsa,
"Should not be processing SIB in NSA mode, something bad happened\n");
fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_SIB); fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_SIB);
pdu_idx++; pdu_idx++;
} }
else if (mac->index_has_rar[i]) else if (mac->nr_ue_emul_l1.index_has_rar[i])
{ {
fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_RAR); fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_RAR);
pdu_idx++; pdu_idx++;
} }
else if (mac->index_has_dci[i]) else if (mac->nr_ue_emul_l1.index_has_dci[i])
{ {
fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_DLSCH); fill_rx_ind(pdu_list, rx_ind, pdu_idx, FAPI_NR_RX_PDU_TYPE_DLSCH);
pdu_idx++; pdu_idx++;
} }
else else
{ {
LOG_D(NR_MAC, "mac->index_has_dci[%d] = 0, so this index contained a DCI for a different UE\n", i); LOG_D(NR_MAC, "mac->nr_ue_emul_l1.index_has_dci[%d] = 0, so this index contained a DCI for a different UE\n", i);
} }
} }
...@@ -656,7 +658,9 @@ void check_and_process_dci(nfapi_nr_dl_tti_request_t *dl_tti_request, ...@@ -656,7 +658,9 @@ void check_and_process_dci(nfapi_nr_dl_tti_request_t *dl_tti_request,
incoming tx_data_request is also destined for the current UE. If the incoming tx_data_request is also destined for the current UE. If the
RAR hasn't been processed yet, we do not want to be filtering the RAR hasn't been processed yet, we do not want to be filtering the
tx_data_requests. */ tx_data_requests. */
if (tx_data_request && (mac->expected_sib || mac->expected_rar || mac->expected_dci)) if (tx_data_request && (mac->nr_ue_emul_l1.expected_sib ||
mac->nr_ue_emul_l1.expected_rar ||
mac->nr_ue_emul_l1.expected_dci))
{ {
frame = tx_data_request->SFN; frame = tx_data_request->SFN;
slot = tx_data_request->Slot; slot = tx_data_request->Slot;
...@@ -1124,7 +1128,7 @@ nr_ue_if_module_t *nr_ue_if_module_init(uint32_t module_id){ ...@@ -1124,7 +1128,7 @@ nr_ue_if_module_t *nr_ue_if_module_init(uint32_t module_id){
nr_ue_if_module_inst[module_id]->current_frame = 0; nr_ue_if_module_inst[module_id]->current_frame = 0;
nr_ue_if_module_inst[module_id]->current_slot = 0; nr_ue_if_module_inst[module_id]->current_slot = 0;
nr_ue_if_module_inst[module_id]->phy_config_request = nr_ue_phy_config_request; nr_ue_if_module_inst[module_id]->phy_config_request = nr_ue_phy_config_request;
if (get_softmodem_params()->emulate_l2) if (get_softmodem_params()->emulate_l1)
nr_ue_if_module_inst[module_id]->scheduled_response = nr_ue_scheduled_response_stub; nr_ue_if_module_inst[module_id]->scheduled_response = nr_ue_scheduled_response_stub;
else else
nr_ue_if_module_inst[module_id]->scheduled_response = nr_ue_scheduled_response; nr_ue_if_module_inst[module_id]->scheduled_response = nr_ue_scheduled_response;
......
...@@ -1206,8 +1206,8 @@ int8_t nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(module_id_t module_id, ...@@ -1206,8 +1206,8 @@ int8_t nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message(module_id_t module_id,
check_requested_SI_List(module_id, NR_UE_rrc_inst[module_id].requested_SI_List, *sib1); check_requested_SI_List(module_id, NR_UE_rrc_inst[module_id].requested_SI_List, *sib1);
if( nr_rrc_get_state(module_id) <= RRC_STATE_IDLE_NR ) { if( nr_rrc_get_state(module_id) <= RRC_STATE_IDLE_NR ) {
NR_UE_rrc_inst[module_id].ra_trigger = INITIAL_ACCESS_FROM_RRC_IDLE; NR_UE_rrc_inst[module_id].ra_trigger = INITIAL_ACCESS_FROM_RRC_IDLE;
LOG_D(PHY,"Setting state to NR_RRC_SI_RECEIVED\n"); LOG_D(PHY,"Setting state to RRC_STATE_IDLE_NR\n");
nr_rrc_set_state (module_id, NR_RRC_SI_RECEIVED); nr_rrc_set_state (module_id, RRC_STATE_IDLE_NR);
} }
// take ServingCellConfigCommon and configure L1/L2 // take ServingCellConfigCommon and configure L1/L2
NR_UE_rrc_inst[module_id].servingCellConfigCommonSIB = sib1->servingCellConfigCommon; NR_UE_rrc_inst[module_id].servingCellConfigCommonSIB = sib1->servingCellConfigCommon;
...@@ -1763,13 +1763,8 @@ int8_t nr_rrc_ue_decode_ccch( const protocol_ctxt_t *const ctxt_pP, const NR_SRB ...@@ -1763,13 +1763,8 @@ int8_t nr_rrc_ue_decode_ccch( const protocol_ctxt_t *const ctxt_pP, const NR_SRB
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void nr_rrc_ue_generate_RRCSetupRequest(module_id_t module_id, const uint8_t gNB_index) { void nr_rrc_ue_generate_RRCSetupRequest(module_id_t module_id, const uint8_t gNB_index) {
uint8_t i=0,rv[6]; uint8_t i=0,rv[6];
/* TODO: Melissa, this is not a proper fix. The NAS layer should be
getting intialized and then the substate will not crash when AMF_MODE_ENABLED if(get_softmodem_params()->sa) {
is equal to 1. However, as a side note, when we keep the code below,
once the CBRA procedure is finished, the NAS layer is ran and the AMF_MODE_ENABLED
is switched to one and the substate assertion in the nr_rrc_set_sub_state()
does not happen. So show this to Raymond and maybe its okay? */
if(get_softmodem_params()->sa && !get_softmodem_params()->emulate_l2) {
AMF_MODE_ENABLED = 1; AMF_MODE_ENABLED = 1;
} }
if(NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size ==0) { if(NR_UE_rrc_inst[module_id].Srb0[gNB_index].Tx_buffer.payload_size ==0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment