Commit 4715308b authored by cig's avatar cig

Msg3 implementation

- extraction of resource allocation from Msg2
- configuration of Msg3 PDU
- Msg3 scheduling
- MAC/PHY interface adaptations
parent 8f09cc6c
......@@ -2974,7 +2974,7 @@ void nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, int eNB
void nr_process_rar(nr_downlink_indication_t *dl_info) {
module_id_t module_id = dl_info->module_id;
int cc_id = dl_info->cc_id, frame_rx = dl_info->proc->frame_rx, nr_tti_rx = dl_info->proc->nr_tti_rx, ta_command, k2, delta;
int cc_id = dl_info->cc_id, frame_rx = dl_info->proc->frame_rx, nr_tti_rx = dl_info->proc->nr_tti_rx, ta_command, delta;
uint8_t gNB_index = dl_info->gNB_index; // *rar;
//fapi_nr_dci_indication_t *dci_ind = dl_info->dci_ind;
PHY_VARS_NR_UE *ue = PHY_vars_UE_g[module_id][cc_id];
......@@ -3015,6 +3015,7 @@ void nr_process_rar(nr_downlink_indication_t *dl_info) {
ta_command = nr_ue_process_rar(ue->Mod_id,
cc_id,
frame_rx,
nr_tti_rx,
dlsch0->harq_processes[0]->b,
&ue->pdcch_vars[ue->current_thread_id[nr_tti_rx]][gNB_index]->pdcch_config[0].rnti,
prach_resources->ra_PreambleIndex,
......@@ -3031,13 +3032,6 @@ void nr_process_rar(nr_downlink_indication_t *dl_info) {
nr_process_timing_advance_rar(ue, dl_info->proc, ta_command);
if (ue->mode != debug_prach) {
ue->ulsch_Msg3_active[gNB_index] = 1;
// TS 38.213 ch 8.3 Msg3 PUSCH
// PUSCH time domain resource allocation A for normal CP
// TS 38.214 ch 6.1.2.1.1
k2 = table_6_1_2_1_1_2_time_dom_res_alloc_A[0][0];
sliv_S = table_6_1_2_1_1_2_time_dom_res_alloc_A[0][1];
sliv_L = table_6_1_2_1_1_2_time_dom_res_alloc_A[0][2];
switch (mu_pusch) {
case 0:
......@@ -3054,28 +3048,10 @@ void nr_process_rar(nr_downlink_indication_t *dl_info) {
break;
}
ue->Msg3_startSymbol[gNB_index] = sliv_S;
ue->Msg3_Length[gNB_index] = sliv_L;
ue->ulsch_Msg3_subframe[gNB_index] = (nr_tti_rx + k2 + delta) % slots_per_frame;
if (nr_tti_rx + k2 + delta > slots_per_frame){
ue->ulsch_Msg3_frame[gNB_index] = (frame_rx + 1) % 1024;
} else {
ue->ulsch_Msg3_frame[gNB_index] = frame_rx;
}
#ifdef DEBUG_RA
LOG_D(PHY,"[UE %d][RAPROC] Msg3 nr_tti_rx %d delta %d\n", ue->Mod_id, nr_tti_rx, delta);
#endif
LOG_D(PHY,"[UE %d][RAPROC] Got Msg3_alloc Frame %d subframe %d: Msg3_frame %d, Msg3_subframe %d\n",
ue->Mod_id,
frame_rx,
nr_tti_rx,
ue->ulsch_Msg3_frame[gNB_index],
ue->ulsch_Msg3_subframe[gNB_index]);
// harq_pid = subframe2harq_pid(&ue->frame_parms,
// ue->ulsch_Msg3_frame[gNB_index],
// ue->ulsch_Msg3_subframe[gNB_index]);
// ue->ulsch[gNB_index]->harq_processes[harq_pid]->round = 0;
// ue->Msg3_timer[gNB_index] = 10;
// ue->ulsch[gNB_index].power_offset = 6;
// ue->ulsch_no_allocation_counter[gNB_index] = 0;
ue->UE_mode[gNB_index] = RA_RESPONSE;
}
} else {
......
......@@ -221,6 +221,10 @@ typedef struct {
uint32_t RA_tx_frame;
/// Random-access variable for window calculation (subframe of last change in window counter)
uint8_t RA_tx_subframe;
/// Scheduled TX frame for RA Msg3
frame_t msg3_frame;
/// Scheduled TX slot for RA Msg3
slot_t msg3_slot;
/// Random-access variable for backoff (frame of last change in backoff counter)
uint32_t RA_backoff_frame;
/// Random-access variable for backoff (subframe of last change in backoff counter)
......
......@@ -197,6 +197,17 @@ and fills the PRACH PDU per each FD occasion.
*/
void nr_ue_prach_scheduler(module_id_t module_idP, frame_t frameP, sub_frame_t slotP);
/* \brief This function schedules the Msg3 transmission
@param
@param
@param
@returns void
*/
void nr_ue_msg3_scheduler(NR_UE_MAC_INST_t *mac,
frame_t current_frame,
sub_frame_t current_slot,
uint8_t Msg3_tda_id);
/* \brief Function called by PHY to process the received RAR and check that the preamble matches what was sent by the gNB. It provides the timing advance and t-CRNTI.
@param Mod_id Index of UE instance
@param CC_id Index to a component carrier
......@@ -212,6 +223,7 @@ random-access procedure
uint16_t nr_ue_process_rar(module_id_t mod_id,
int CC_id,
frame_t frameP,
sub_frame_t slotP,
uint8_t * dlsch_buffer,
rnti_t * t_crnti,
uint8_t preamble_index,
......
......@@ -895,7 +895,6 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in
scheduled_response.slot = rx_slot;
scheduled_response.ul_config->slot = ul_info->slot_tx;
scheduled_response.ul_config->number_pdus = 1;
scheduled_response.ul_config->ul_config_list[0].pdu_type = FAPI_NR_UL_CONFIG_TYPE_PUSCH;
scheduled_response.ul_config->ul_config_list[0].pusch_config_pdu.rnti = rnti;
scheduled_response.ul_config->ul_config_list[0].pusch_config_pdu.rb_size = rb_size;
......@@ -929,11 +928,122 @@ NR_UE_L2_STATE_t nr_ue_scheduler(nr_downlink_indication_t *dl_info, nr_uplink_in
// Note: Contention resolution is currently not active
if (mac->RA_contention_resolution_timer_active == 1)
ue_contention_resolution(mod_id, gNB_index, cc_id, ul_info->frame_tx);
} else if (get_softmodem_params()->do_ra){
NR_UE_MAC_INST_t *mac = get_mac_inst(ul_info->module_id);
if (ul_info->slot_tx == mac->msg3_slot && ul_info->frame_tx == mac->msg3_frame){
uint8_t ulsch_input_buffer[MAX_ULSCH_PAYLOAD_BYTES];
nr_scheduled_response_t scheduled_response;
scheduled_response.ul_config = &mac->ul_config_request;
scheduled_response.dl_config = NULL;
scheduled_response.ul_config->number_pdus++; //TBR fix
fapi_nr_ul_config_request_pdu_t *ul_config_list = &scheduled_response.ul_config->ul_config_list[scheduled_response.ul_config->number_pdus - 1];
fapi_nr_tx_request_t tx_req;
fapi_nr_tx_request_body_t tx_req_body;
uint16_t TBS_bytes = scheduled_response.ul_config->ul_config_list[scheduled_response.ul_config->number_pdus - 1].pusch_config_pdu.pusch_data.tb_size;
//if (IS_SOFTMODEM_NOS1){
// // Getting IP traffic to be transmitted
// data_existing = nr_ue_get_sdu(mod_id,
// cc_id,
// frame_tx,
// slot_tx,
// 0,
// ulsch_input_buffer,
// TBS_bytes,
// &access_mode);
//}
//Random traffic to be transmitted if there is no IP traffic available for this Tx opportunity
//if (!IS_SOFTMODEM_NOS1 || !data_existing) {
//Use zeros for the header bytes in noS1 mode, in order to make sure that the LCID is not valid
//and block this traffic from being forwarded to the upper layers at the gNB
printf("[DEBUG_MSG3] Random data to be tranmsitted (TBS_bytes %d, pdu_index %d ul_config (%p) list (%p): \n", TBS_bytes, scheduled_response.ul_config->number_pdus - 1, scheduled_response.ul_config, ul_config_list);
//Give the first byte a dummy value (a value not corresponding to any valid LCID based on 38.321, Table 6.2.1-2)
//in order to distinguish the PHY random packets at the MAC layer of the gNB receiver from the normal packets that should
//have a valid LCID (nr_process_mac_pdu function)
ulsch_input_buffer[0] = 0x31;
for (int i = 1; i < TBS_bytes; i++) {
ulsch_input_buffer[i] = (unsigned char) rand();
//printf(" input encoder a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
}
//}
LOG_D(MAC, "[UE %d] Frame %d, Subframe %d Adding Msg3 UL Config Request for rnti: %x\n",
ul_info->module_id,
ul_info->frame_tx,
ul_info->slot_tx,
mac->t_crnti);
// Config UL TX PDU
tx_req.slot = ul_info->slot_tx;
tx_req.sfn = ul_info->frame_tx;
tx_req.number_of_pdus = 1;
tx_req_body.pdu_length = TBS_bytes;
tx_req_body.pdu_index = 0;
tx_req_body.pdu = ulsch_input_buffer;
ul_config_list->pdu_type = FAPI_NR_UL_CONFIG_TYPE_PUSCH;
scheduled_response.tx_request = &tx_req;
scheduled_response.tx_request->tx_request_body = &tx_req_body;
scheduled_response.module_id = ul_info->module_id;
scheduled_response.CC_id = ul_info->cc_id;
scheduled_response.frame = ul_info->frame_rx;
scheduled_response.slot = ul_info->slot_rx;
if(mac->if_module != NULL && mac->if_module->scheduled_response != NULL){
mac->if_module->scheduled_response(&scheduled_response);
}
}
}
}
return UE_CONNECTION_OK;
}
// Scheduling the Msg3 transmission according to according to TS 38.213
// Note: Msg3 tx in the uplink symbols of mixed slot
void nr_ue_msg3_scheduler(NR_UE_MAC_INST_t *mac,
frame_t current_frame,
sub_frame_t current_slot,
uint8_t Msg3_tda_id){
int delta;
NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
int mu = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
struct NR_PUSCH_TimeDomainResourceAllocationList *pusch_TimeDomainAllocationList = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList;
// k2 as per 3GPP TS 38.214 version 15.9.0 Release 15 ch 6.1.2.1.1
// PUSCH time domain resource allocation is higher layer configured from uschTimeDomainAllocationList in either pusch-ConfigCommon
uint8_t k2 = *pusch_TimeDomainAllocationList->list.array[Msg3_tda_id]->k2;
switch (mu) {
case 0:
delta = 2;
break;
case 1:
delta = 3;
break;
case 2:
delta = 4;
break;
case 3:
delta = 6;
break;
}
mac->msg3_slot = (current_slot + k2 + delta) % nr_slots_per_frame[mu];
if (current_slot + k2 + delta > nr_slots_per_frame[mu])
mac->msg3_frame = (current_frame + 1) % 1024;
else
mac->msg3_frame = current_frame;
#ifdef DEBUG_MSG3
LOG_D(MAC, "[DEBUG_MSG3] current_slot %d k2 %d delta %d temp_slot %d mac->msg3_frame %d mac->msg3_slot %d \n", current_slot, k2, delta, current_slot + k2 + delta, mac->msg3_frame, mac->msg3_slot);
#endif
}
// This function schedules the PRACH according to prach_ConfigurationIndex and TS 38.211, tables 6.3.3.2.x
// It fills the PRACH PDU per each FD occasion.
// PRACH formats 9, 10, 11 are corresponding to dual PRACH format configurations A1/B1, A2/B2, A3/B3.
......
......@@ -46,7 +46,8 @@
#include "NR_MAC_COMMON/nr_mac_extern.h"
#include <common/utils/nr/nr_common.h>
#define DEBUG_RAR
// #define DEBUG_RAR
// #define DEBUG_MSG3
// table 7.2-1 TS 38.321
uint16_t table_7_2_1[16] = {
......@@ -66,6 +67,111 @@ uint16_t table_7_2_1[16] = {
1920, // row index 13
};
void nr_config_Msg3_pdu(NR_UE_MAC_INST_t *mac,
int Msg3_f_alloc,
uint8_t Msg3_t_alloc,
uint8_t mcs,
uint8_t freq_hopping){
int f_alloc, mask, StartSymbolIndex, NrOfSymbols;
uint16_t TBS_bytes;
fapi_nr_ul_config_request_t *ul_config = &mac->ul_config_request;
ul_config->number_pdus++;
nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu = &ul_config->ul_config_list[ul_config->number_pdus - 1].pusch_config_pdu;
NR_ServingCellConfigCommon_t *scc = mac->scc;
NR_BWP_Uplink_t *ubwp = mac->ULbwp[0];
int startSymbolAndLength = ubwp->bwp_Common->pusch_ConfigCommon->choice.setup->pusch_TimeDomainAllocationList->list.array[Msg3_t_alloc]->startSymbolAndLength;
#ifdef DEBUG_MSG3
printf("[DEBUG_MSG3] Configuring Msg3 PDU of %d UL pdus \n", ul_config->number_pdus);
#endif
// active BWP start
int abwp_start = NRRIV2PRBOFFSET(ubwp->bwp_Common->genericParameters.locationAndBandwidth, 275);
int abwp_size = NRRIV2BW(ubwp->bwp_Common->genericParameters.locationAndBandwidth, 275);
// initial BWP start
int ibwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, 275);
int ibwp_size = NRRIV2BW(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, 275);
//// Resource assignment from RAR
// Frequency domain allocation
if (abwp_size < 180)
mask = (1 << ((int) ceil(log2((abwp_size*(abwp_size+1))>>1)))) - 1;
else
mask = (1 << (28 - (int)(ceil(log2((abwp_size*(abwp_size+1))>>1))))) - 1;
f_alloc = Msg3_f_alloc & mask;
pusch_config_pdu->bwp_size = ibwp_size;
if ((ibwp_start < abwp_start) || (ibwp_size > abwp_size))
pusch_config_pdu->bwp_start = abwp_start;
else
pusch_config_pdu->bwp_start = ibwp_start;
nr_ue_process_dci_freq_dom_resource_assignment(pusch_config_pdu, NULL, ibwp_size, 0, f_alloc);
// virtual resource block to physical resource mapping for Msg3 PUSCH (6.3.1.7 in 38.211)
pusch_config_pdu->rb_start += ibwp_start - abwp_start;
// Time domain allocation
SLIV2SL(startSymbolAndLength, &StartSymbolIndex, &NrOfSymbols);
pusch_config_pdu->ul_dmrs_symb_pos = 1<<StartSymbolIndex;
pusch_config_pdu->start_symbol_index = StartSymbolIndex;
pusch_config_pdu->nr_of_symbols = NrOfSymbols;
#ifdef DEBUG_MSG3
printf("[DEBUG_MSG3] Freq assignment (RB start %d size %d) BWP (start %d, size %d), Time assignment (sliv_S %d sliv_L %d) \n",
pusch_config_pdu->rb_start,
pusch_config_pdu->rb_size,
pusch_config_pdu->bwp_start,
pusch_config_pdu->bwp_size,
StartSymbolIndex,
NrOfSymbols);
#endif
// MCS
pusch_config_pdu->mcs_index = mcs;
// Frequency hopping
pusch_config_pdu->frequency_hopping = freq_hopping;
// TC-RNTI
pusch_config_pdu->rnti = mac->t_crnti;
//// Completing PUSCH PDU
pusch_config_pdu->pdu_bit_map = PUSCH_PDU_BITMAP_PUSCH_DATA;
pusch_config_pdu->mcs_table = 0;
pusch_config_pdu->nrOfLayers = 0;
pusch_config_pdu->dmrs_config_type = 0;
// no data in dmrs symbols as in 6.2.2 in 38.214
pusch_config_pdu->num_dmrs_cdm_grps_no_data = 2; // TBR
pusch_config_pdu->cyclic_prefix = 0;
pusch_config_pdu->target_code_rate = nr_get_code_rate_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
pusch_config_pdu->qam_mod_order = nr_get_Qm_ul(pusch_config_pdu->mcs_index, pusch_config_pdu->mcs_table);
if (scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->msg3_transformPrecoder == NULL)
pusch_config_pdu->transform_precoding = 1;
else
pusch_config_pdu->transform_precoding = 0;
pusch_config_pdu->data_scrambling_id = *scc->physCellId;
pusch_config_pdu->ul_dmrs_scrambling_id = *scc->physCellId; //If provided and the PUSCH is not a msg3 PUSCH, otherwise, L2 should set this to physical cell id.
pusch_config_pdu->scid = 0; //DMRS sequence initialization [TS38.211, sec 6.4.1.1.1]. Should match what is sent in DCI 0_1, otherwise set to 0.
pusch_config_pdu->dmrs_ports = 1; // 6.2.2 in 38.214 only port 0 to be used
pusch_config_pdu->resource_alloc = 1; //type 1
pusch_config_pdu->subcarrier_spacing = ubwp->bwp_Common->genericParameters.subcarrierSpacing;
pusch_config_pdu->vrb_to_prb_mapping = 0;
pusch_config_pdu->uplink_frequency_shift_7p5khz = 0;
//Optional Data only included if indicated in pduBitmap
pusch_config_pdu->pusch_data.rv_index = 0; // 8.3 in 38.213
pusch_config_pdu->pusch_data.harq_process_id = 0;
pusch_config_pdu->pusch_data.new_data_indicator = 1; // new data
pusch_config_pdu->pusch_data.num_cb = 0;
TBS_bytes = nr_compute_tbs(pusch_config_pdu->qam_mod_order,
pusch_config_pdu->target_code_rate,
pusch_config_pdu->rb_size,
pusch_config_pdu->nr_of_symbols,
12, // TBR compute accordingly - nb dmrs set for no data in dmrs symbol
0, // nb_rb_oh
0, // to verify tb scaling
pusch_config_pdu->nrOfLayers = 1)/8;
pusch_config_pdu->pusch_data.tb_size = TBS_bytes;
}
/////////////////////////////////////
// Random Access Response PDU //
// TS 38.213 ch 8.2 //
......@@ -90,12 +196,13 @@ uint16_t table_7_2_1[16] = {
//| F_alloc |Time allocation|//
//| MCS | TPC |CSI|//
/////////////////////////////////////
// WIP todo:
// TbD WIP Msg3 development ongoing
// - apply UL grant freq alloc & time alloc as per 8.2 TS 38.213
// - apply tpc command, csi req, mcs
// - apply tpc command
uint16_t nr_ue_process_rar(module_id_t mod_id,
int CC_id,
frame_t frameP,
frame_t frame,
sub_frame_t slot,
uint8_t * dlsch_buffer,
rnti_t * t_crnti,
uint8_t preamble_index,
......@@ -106,11 +213,7 @@ uint16_t nr_ue_process_rar(module_id_t mod_id,
NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 1); // RAR subPDU pointer
uint8_t n_subPDUs = 0; // number of RAR payloads
uint8_t n_subheaders = 0; // number of MAC RAR subheaders
//uint8_t best_rx_rapid = -1; // the closest RAPID receive from all RARs
//unsigned char freq_hopping, msg3_t_alloc, mcs, tpc_command, csi_req; // WIP
//uint16_t ta_command = 0, msg3_f_alloc, bwp_size; // WIP
uint16_t ta_command = 0;
//int f_alloc, mask; // WIP
AssertFatal(CC_id == 0, "RAR reception on secondary CCs is not supported yet\n");
......@@ -139,17 +242,19 @@ uint16_t nr_ue_process_rar(module_id_t mod_id,
}
};
LOG_D(MAC, "number of RAR subheader %d; number of RAR pyloads %d\n", n_subheaders, n_subPDUs);
// LOG_I(MAC, "[UE %d][RAPROC] Frame %d Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n",
// mod_id, frameP, *(uint8_t *) rarh, rar[0], rar[1], rar[2], rar[3], rar[4], rar[5], rarh->RAPID, preamble_index);
#ifdef DEBUG_RAR
LOG_D(MAC, "[DEBUG_RAR] (%d,%d) number of RAR subheader %d; number of RAR pyloads %d\n", frame, slot, n_subheaders, n_subPDUs);
LOG_D(MAC, "[DEBUG_RAR] Received RAR (%02x|%02x.%02x.%02x.%02x.%02x.%02x) for preamble %d/%d\n", *(uint8_t *) rarh, rar[0], rar[1], rar[2], rar[3], rar[4], rar[5], rarh->RAPID, preamble_index);
#endif
#if 0 // TbD WIP Msg3 development ongoing
if (ue_mac->RA_RAPID_found) {
uint8_t freq_hopping, mcs, Msg3_t_alloc, Msg3_f_alloc;
unsigned char tpc_command, csi_req;
// TC-RNTI
*t_crnti = rar->TCRNTI_2 + (rar->TCRNTI_1 << 8);
ue_mac->t_crnti = *t_crnti;
ue_mac->rnti_type = NR_RNTI_TC;
// TA command
ta_command = rar->TA2 + (rar->TA1 << 5);
// CSI
......@@ -182,27 +287,36 @@ uint16_t nr_ue_process_rar(module_id_t mod_id,
ue_mac->Msg3_TPC = 8;
break;
}
//MCS
// MCS
mcs = (unsigned char) (rar->UL_GRANT_4 >> 4);
// time and frequency alloc
bwp_size = NRRIV2BW(ue_mac->ULbwp[0]->bwp_Common->genericParameters.locationAndBandwidth,275);
msg3_t_alloc = (unsigned char) (rar->UL_GRANT_3 & 0x07);
msg3_f_alloc = (uint16_t) ((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12));
// time alloc
Msg3_t_alloc = (unsigned char) (rar->UL_GRANT_3 & 0x07);
// frequency alloc
Msg3_f_alloc = (uint16_t) ((rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12));
// frequency hopping
freq_hopping = (unsigned char) (rar->UL_GRANT_1 >> 2);
if (bwp_size < 180)
mask = (1 << ((int) ceil(log2((bwp_size*(bwp_size+1))>>1)))) - 1;
else
mask = (1 << (28 - (int)(ceil(log2((bwp_size*(bwp_size+1))>>1))))) - 1;
#ifdef DEBUG_RAR
LOG_D(MAC, "[DEBUG_RAR] Received RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d csi_req %d t_crnti %x \n",
Msg3_t_alloc,
Msg3_f_alloc,
ta_command,
mcs,
freq_hopping,
tpc_command,
csi_req,
ue_mac->t_crnti);
#endif
f_alloc = msg3_f_alloc & mask;
// Config Msg3 PDU
nr_config_Msg3_pdu(ue_mac, Msg3_f_alloc, Msg3_t_alloc, mcs, freq_hopping);
// Schedule Msg3
nr_ue_msg3_scheduler(ue_mac, frame, slot, Msg3_t_alloc);
// frequency hopping flag
freq_hopping = (unsigned char) (rar->UL_GRANT_1 >> 2);
} else {
ue_mac->t_crnti = 0;
ta_command = (0xffff);
}
#endif
// move the selected RAR to the front of the RA_PDSCH buffer
memcpy((void *) (selected_rar_buffer + 0), (void *) rarh, 1);
......
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