Commit c1490bb8 authored by Rohit Gupta's avatar Rohit Gupta

Merge branch 'develop' into enhancement-10-harmony

parents 1db923d6 7203a521
This diff is collapsed.
...@@ -233,7 +233,7 @@ check_install_usrp_uhd_driver(){ ...@@ -233,7 +233,7 @@ check_install_usrp_uhd_driver(){
$SUDO add-apt-repository ppa:ettusresearch/uhd -y $SUDO add-apt-repository ppa:ettusresearch/uhd -y
$SUDO apt-get update $SUDO apt-get update
$SUDO apt-get -y install python python-tk libboost-all-dev libusb-1.0-0-dev $SUDO apt-get -y install python python-tk libboost-all-dev libusb-1.0-0-dev
$SUDO apt-get -y install libuhd-dev libuhd003 $SUDO apt-get -y install libuhd-dev libuhd003 uhd-host
} }
install_usrp_uhd_driver() { install_usrp_uhd_driver() {
...@@ -291,7 +291,7 @@ check_install_additional_tools (){ ...@@ -291,7 +291,7 @@ check_install_additional_tools (){
$SUDO pip install paramiko $SUDO pip install paramiko
$SUDO pip install pyroute2 $SUDO pip install pyroute2
$SUDO rm -fr /opt/ssh $SUDO rm -fr /opt/ssh
$SUDO GIT_SSL_NO_VERIFY=true git clone https://gist.github.com/2190472.git /opt/ssh $SUDO GIT_SSL_NO_VERIFY=true git clone https://gitlab.eurecom.fr/oai/ssh.git /opt/ssh
log_netiface=$OPENAIR_DIR/cmake_targets/log/netiface_install_log.txt log_netiface=$OPENAIR_DIR/cmake_targets/log/netiface_install_log.txt
echo_info "Installing Netinterfaces package. The logfile for installation is in $log_netiface" echo_info "Installing Netinterfaces package. The logfile for installation is in $log_netiface"
......
This diff is collapsed.
...@@ -621,7 +621,9 @@ typedef struct { ...@@ -621,7 +621,9 @@ typedef struct {
uint8_t DLSCH_dci_size_bits; uint8_t DLSCH_dci_size_bits;
/// DCI buffer for DLSCH /// DCI buffer for DLSCH
uint8_t DLSCH_DCI[8][(MAX_DCI_SIZE_BITS>>3)+1]; /* rounded to 32 bits unit (actual value should be 8 due to the logic
* of the function generate_dci0) */
uint8_t DLSCH_DCI[8][(((MAX_DCI_SIZE_BITS)+31)>>5)*4];
/// Number of Allocated RBs for DL after scheduling (prior to frequency allocation) /// Number of Allocated RBs for DL after scheduling (prior to frequency allocation)
uint16_t nb_rb[8]; // num_max_harq uint16_t nb_rb[8]; // num_max_harq
...@@ -645,7 +647,9 @@ typedef struct { ...@@ -645,7 +647,9 @@ typedef struct {
uint8_t assigned_mcs_ul; uint8_t assigned_mcs_ul;
/// DCI buffer for ULSCH /// DCI buffer for ULSCH
uint8_t ULSCH_DCI[8][(MAX_DCI_SIZE_BITS>>3)+1]; /* rounded to 32 bits unit (actual value should be 8 due to the logic
* of the function generate_dci0) */
uint8_t ULSCH_DCI[8][(((MAX_DCI_SIZE_BITS)+31)>>5)*4];
/// DL DAI /// DL DAI
uint8_t DAI; uint8_t DAI;
......
...@@ -577,7 +577,7 @@ nwGtpv1uProcessGpdu( NwGtpv1uStackT *thiz, ...@@ -577,7 +577,7 @@ nwGtpv1uProcessGpdu( NwGtpv1uStackT *thiz,
" G-PDU ltid %u size %u", " G-PDU ltid %u size %u",
tunnelEndPointKey.teid, tunnelEndPointKey.teid,
gpduLen); gpduLen);
GTPU_ERROR("Received T-PDU over non-existent tunnel end-point '%x' from "NW_IPV4_ADDR"\n", GTPU_DEBUG("Received T-PDU over non-existent tunnel end-point '%x' from "NW_IPV4_ADDR"\n",
ntohl(msgHdr->teid), NW_IPV4_ADDR_FORMAT((peerIp))); ntohl(msgHdr->teid), NW_IPV4_ADDR_FORMAT((peerIp)));
} }
......
...@@ -179,6 +179,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd ...@@ -179,6 +179,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd
int tmp; int tmp;
unsigned int user_args[4];
static unsigned int update_firmware_command; static unsigned int update_firmware_command;
static unsigned int update_firmware_address; static unsigned int update_firmware_address;
static unsigned int update_firmware_length; static unsigned int update_firmware_length;
...@@ -328,15 +330,38 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd ...@@ -328,15 +330,38 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd
acknowledge with no limit */ acknowledge with no limit */
#define MAX_IOCTL_ACK_CNT 500 #define MAX_IOCTL_ACK_CNT 500
update_firmware_command = *((unsigned int*)arg);
/* 'arg' is an unsigned long and is supposed to be big enough
* to hold an address - this hypothesis is okay for i386 and x86_64
* maybe not somewhere else
* this will probably fail with older kernels
* (works with 3.17 on x86_64)
*/
tmp = copy_from_user(&user_args, (void*)arg, 4*sizeof(unsigned int));
if (tmp) {
printk("[openair][IOCTL] UPDATE_FIRMWARE: error copying parameters to kernel-space (%d bytes uncopied).\n", tmp);
return -1;
}
update_firmware_command = user_args[0];
switch (update_firmware_command) { switch (update_firmware_command) {
case UPDATE_FIRMWARE_TRANSFER_BLOCK: case UPDATE_FIRMWARE_TRANSFER_BLOCK:
update_firmware_address = ((unsigned int*)arg)[1]; update_firmware_address = user_args[1];
update_firmware_length = ((unsigned int*)arg)[2]; update_firmware_length = user_args[2];
/* This is totally wrong on x86_64: a pointer is 64 bits and
* unsigned int is 32 bits. The userspace program has to ensure
* that its buffer address fits into 32 bits.
* If it proves a too strong requirement, we may change things
* in the future.
* The compiler emits a warning here. Do not remove this warning!
* This is to clearly remember this problem.
* If you require the compilation to work with zero warning,
* consider this one as an exception and find a proper workaround.
*/
update_firmware_ubuffer = user_args[3];
update_firmware_ubuffer = (unsigned int*)((unsigned int*)arg)[3];
update_firmware_kbuffer = (unsigned int*)kmalloc(update_firmware_length * 4 /* 4 because kmalloc expects bytes */, update_firmware_kbuffer = (unsigned int*)kmalloc(update_firmware_length * 4 /* 4 because kmalloc expects bytes */,
GFP_KERNEL); GFP_KERNEL);
...@@ -390,8 +415,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd ...@@ -390,8 +415,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd
case UPDATE_FIRMWARE_CLEAR_BSS: case UPDATE_FIRMWARE_CLEAR_BSS:
update_firmware_bss_address = ((unsigned int*)arg)[1]; update_firmware_bss_address = user_args[1];
update_firmware_bss_size = ((unsigned int*)arg)[2]; update_firmware_bss_size = user_args[2];
sparc_tmp_0 = update_firmware_bss_address; sparc_tmp_0 = update_firmware_bss_address;
sparc_tmp_1 = update_firmware_bss_size; sparc_tmp_1 = update_firmware_bss_size;
...@@ -411,8 +436,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd ...@@ -411,8 +436,8 @@ int openair_device_ioctl(struct inode *inode,struct file *filp, unsigned int cmd
case UPDATE_FIRMWARE_START_EXECUTION: case UPDATE_FIRMWARE_START_EXECUTION:
update_firmware_start_address = ((unsigned int*)arg)[1]; update_firmware_start_address = user_args[1];
update_firmware_stack_pointer = ((unsigned int*)arg)[2]; update_firmware_stack_pointer = user_args[2];
sparc_tmp_0 = update_firmware_start_address; sparc_tmp_0 = update_firmware_start_address;
sparc_tmp_1 = update_firmware_stack_pointer; sparc_tmp_1 = update_firmware_stack_pointer;
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <getopt.h> #include <getopt.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdint.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "openair_device.h" #include "openair_device.h"
...@@ -239,6 +240,18 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) { ...@@ -239,6 +240,18 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
/* Dynamically allocate a chunk of memory to store the section into. */ /* Dynamically allocate a chunk of memory to store the section into. */
section_content = (char*)malloc(elf_Shdr.sh_size); section_content = (char*)malloc(elf_Shdr.sh_size);
/* Fail if the address returned by malloc does not fit in 32 bits.
* The kernel driver gets this address as 32 bits and uses it to copy
* from userspace. If the address does not fit into 32 bits the kernel
* will copy garbage or fail to copy completely.
* To be reworked if things do not work on some setups.
*/
if (sizeof(char*) > 4 && (((uintptr_t)section_content)>>32)) {
fprintf(stderr, "FATAL ERROR: an internal serious problem has been encountered.\n");
fprintf(stderr, "Contact the authors so as to solve this issue.\n");
exit(-1);
}
if (!section_content) { if (!section_content) {
fprintf(stderr, "Error: could not dynamically allocate %d bytes for section %s.\n", fprintf(stderr, "Error: could not dynamically allocate %d bytes for section %s.\n",
elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name); elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
...@@ -281,6 +294,13 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) { ...@@ -281,6 +294,13 @@ void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
ioctl_params[0] = UPDATE_FIRMWARE_TRANSFER_BLOCK; ioctl_params[0] = UPDATE_FIRMWARE_TRANSFER_BLOCK;
ioctl_params[1] = elf_Shdr.sh_addr; ioctl_params[1] = elf_Shdr.sh_addr;
ioctl_params[2] = elf_Shdr.sh_size / 4; ioctl_params[2] = elf_Shdr.sh_size / 4;
/* This is wrong on x86_64 because a pointer is 64 bits and
* an unsigned int is only 32 bits.
* The compiler emits a warning, but the test
* above on the value of section_content makes it work
* (hopefully).
* To be changed if things do not work.
*/
ioctl_params[3] = (unsigned int)((unsigned int*)section_content); ioctl_params[3] = (unsigned int)((unsigned int*)section_content);
//invert4(ioctl_params[1]); //invert4(ioctl_params[1]);
//invert4(ioctl_params[2]); //invert4(ioctl_params[2]);
......
...@@ -50,7 +50,7 @@ eNBs = ...@@ -50,7 +50,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -33; pdsch_referenceSignalPower = -30;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -49,7 +49,7 @@ eNBs = ...@@ -49,7 +49,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -24; pdsch_referenceSignalPower = -23;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -50,7 +50,7 @@ eNBs = ...@@ -50,7 +50,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -30; pdsch_referenceSignalPower = -27;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -49,7 +49,7 @@ eNBs = ...@@ -49,7 +49,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -21; pdsch_referenceSignalPower = -19;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -50,7 +50,7 @@ eNBs = ...@@ -50,7 +50,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -27; pdsch_referenceSignalPower = -24;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -49,7 +49,7 @@ eNBs = ...@@ -49,7 +49,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -18; pdsch_referenceSignalPower = -16;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -49,7 +49,7 @@ eNBs = ...@@ -49,7 +49,7 @@ eNBs =
pucch_nRB_CQI = 1; pucch_nRB_CQI = 1;
pucch_nCS_AN = 0; pucch_nCS_AN = 0;
pucch_n1_AN = 32; pucch_n1_AN = 32;
pdsch_referenceSignalPower = -29; pdsch_referenceSignalPower = -24;
pdsch_p_b = 0; pdsch_p_b = 0;
pusch_n_SB = 1; pusch_n_SB = 1;
pusch_enable64QAM = "DISABLE"; pusch_enable64QAM = "DISABLE";
......
...@@ -38,6 +38,7 @@ eNBs = ...@@ -38,6 +38,7 @@ eNBs =
Nid_cell_mbsfn = 0; Nid_cell_mbsfn = 0;
nb_antennas_tx = 1; nb_antennas_tx = 1;
nb_antennas_rx = 1; nb_antennas_rx = 1;
nb_antenna_ports = 1;
tx_gain = 90; tx_gain = 90;
rx_gain = 125; rx_gain = 125;
prach_root = 0; prach_root = 0;
...@@ -100,7 +101,7 @@ eNBs = ...@@ -100,7 +101,7 @@ eNBs =
ue_TimersAndConstants_t311 = 10000; ue_TimersAndConstants_t311 = 10000;
ue_TimersAndConstants_n310 = 20; ue_TimersAndConstants_n310 = 20;
ue_TimersAndConstants_n311 = 1; ue_TimersAndConstants_n311 = 1;
ue_TransmissionMode = 1;
} }
); );
......
...@@ -38,6 +38,7 @@ eNBs = ...@@ -38,6 +38,7 @@ eNBs =
Nid_cell_mbsfn = 0; Nid_cell_mbsfn = 0;
nb_antennas_tx = 1; nb_antennas_tx = 1;
nb_antennas_rx = 1; nb_antennas_rx = 1;
nb_antenna_ports = 1;
tx_gain = 90; tx_gain = 90;
rx_gain = 125; rx_gain = 125;
prach_root = 0; prach_root = 0;
...@@ -100,7 +101,7 @@ eNBs = ...@@ -100,7 +101,7 @@ eNBs =
ue_TimersAndConstants_t311 = 10000; ue_TimersAndConstants_t311 = 10000;
ue_TimersAndConstants_n310 = 20; ue_TimersAndConstants_n310 = 20;
ue_TimersAndConstants_n311 = 1; ue_TimersAndConstants_n311 = 1;
ue_TransmissionMode = 1;
} }
); );
...@@ -161,7 +162,7 @@ eNBs = ...@@ -161,7 +162,7 @@ eNBs =
local_port = 50000; #for raw option local port must be the same to remote local_port = 50000; #for raw option local port must be the same to remote
remote_port = 50000; remote_port = 50000;
rrh_gw_active = "yes"; rrh_gw_active = "yes";
tr_preference = "raw_if4"; tr_preference = "raw_if4p5";
rf_preference = "usrp_b200"; rf_preference = "usrp_b200";
iq_txshift = 4; iq_txshift = 4;
tx_sample_advance = 80; tx_sample_advance = 80;
......
...@@ -1614,7 +1614,7 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst ...@@ -1614,7 +1614,7 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst
break; break;
case NGFI_RRU_IF4p5: case NGFI_RRU_IF4p5:
eNB->do_prach = do_prach; eNB->do_prach = do_prach;
eNB->fep = (single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full; eNB->fep = eNB_fep_full;//(single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full;
eNB->td = NULL; eNB->td = NULL;
eNB->te = NULL; eNB->te = NULL;
eNB->proc_uespec_rx = NULL; eNB->proc_uespec_rx = NULL;
...@@ -1643,7 +1643,7 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst ...@@ -1643,7 +1643,7 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst
break; break;
case eNodeB_3GPP: case eNodeB_3GPP:
eNB->do_prach = do_prach; eNB->do_prach = do_prach;
eNB->fep = (single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full; eNB->fep = eNB_fep_full;//(single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full;
eNB->td = ulsch_decoding_data;//(single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data; eNB->td = ulsch_decoding_data;//(single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data;
eNB->te = dlsch_encoding;//(single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding; eNB->te = dlsch_encoding;//(single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding;
eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX; eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX;
...@@ -1663,9 +1663,9 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst ...@@ -1663,9 +1663,9 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst
break; break;
case eNodeB_3GPP_BBU: case eNodeB_3GPP_BBU:
eNB->do_prach = do_prach; eNB->do_prach = do_prach;
eNB->fep = (single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full; eNB->fep = eNB_fep_full;//(single_thread_flag==1) ? eNB_fep_full_2thread : eNB_fep_full;
eNB->td = (single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data; eNB->td = ulsch_decoding_data;//(single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data;
eNB->te = (single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding; eNB->te = dlsch_encoding;//(single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding;
eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX; eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX;
eNB->proc_tx = proc_tx_full; eNB->proc_tx = proc_tx_full;
eNB->tx_fh = tx_fh_if5; eNB->tx_fh = tx_fh_if5;
...@@ -1688,8 +1688,8 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst ...@@ -1688,8 +1688,8 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst
case NGFI_RCC_IF4p5: case NGFI_RCC_IF4p5:
eNB->do_prach = do_prach; eNB->do_prach = do_prach;
eNB->fep = NULL; eNB->fep = NULL;
eNB->td = (single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data; eNB->td = ulsch_decoding_data;//(single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data;
eNB->te = (single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding; eNB->te = dlsch_encoding;//(single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding;
eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX; eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX;
eNB->proc_tx = proc_tx_high; eNB->proc_tx = proc_tx_high;
eNB->tx_fh = tx_fh_if4p5; eNB->tx_fh = tx_fh_if4p5;
...@@ -1712,8 +1712,8 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst ...@@ -1712,8 +1712,8 @@ void init_eNB(eNB_func_t node_function[], eNB_timing_t node_timing[],int nb_inst
eNB->do_prach = do_prach; eNB->do_prach = do_prach;
eNB->fep = NULL; eNB->fep = NULL;
eNB->td = (single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data; eNB->td = ulsch_decoding_data;//(single_thread_flag==1) ? ulsch_decoding_data_2thread : ulsch_decoding_data;
eNB->te = (single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding; eNB->te = dlsch_encoding;//(single_thread_flag==1) ? dlsch_encoding_2threads : dlsch_encoding;
eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX; eNB->proc_uespec_rx = phy_procedures_eNB_uespec_RX;
eNB->proc_tx = proc_tx_high; eNB->proc_tx = proc_tx_high;
eNB->tx_fh = tx_fh_if4p5; eNB->tx_fh = tx_fh_if4p5;
......
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