Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Michael Black
OpenXG-RAN
Commits
55384420
Commit
55384420
authored
Jul 06, 2020
by
MaheshK1995
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
slot indication changes
parent
0bfdeebd
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
124 additions
and
54 deletions
+124
-54
executables/main-ocp.c
executables/main-ocp.c
+1
-0
executables/nr-ru.c
executables/nr-ru.c
+1
-0
nfapi/oai_integration/nfapi_pnf.c
nfapi/oai_integration/nfapi_pnf.c
+6
-5
nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h
nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h
+7
-1
nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
+2
-1
nfapi/open-nFAPI/pnf/inc/pnf_p7.h
nfapi/open-nFAPI/pnf/inc/pnf_p7.h
+7
-1
nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h
nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h
+17
-0
nfapi/open-nFAPI/pnf/src/pnf_p7.c
nfapi/open-nFAPI/pnf/src/pnf_p7.c
+83
-46
No files found.
executables/main-ocp.c
View file @
55384420
...
...
@@ -74,6 +74,7 @@ int config_sync_var=-1;
volatile
int
oai_exit
=
0
;
double
cpuf
;
uint16_t
sf_ahead
=
4
;
uint16_t
slot_ahead
=
6
;
int
otg_enabled
;
uint64_t
downlink_frequency
[
MAX_NUM_CCs
][
4
];
int32_t
uplink_frequency_offset
[
MAX_NUM_CCs
][
4
];
...
...
executables/nr-ru.c
View file @
55384420
...
...
@@ -113,6 +113,7 @@ int attach_rru(RU_t *ru);
int
connect_rau
(
RU_t
*
ru
);
uint16_t
sf_ahead
;
uint16_t
slot_ahead
;
uint16_t
sl_ahead
;
extern
int
emulate_rf
;
...
...
nfapi/oai_integration/nfapi_pnf.c
View file @
55384420
...
...
@@ -1227,6 +1227,7 @@ int start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nfap
p7_config
->
trace
=
&
pnf_nfapi_trace
;
phy
->
user_data
=
p7_config
;
p7_config
->
subframe_buffer_size
=
phy_info
->
timing_window
;
p7_config
->
slot_buffer_size
=
phy_info
->
timing_window
;
// TODO: check if correct for NR
printf
(
"subframe_buffer_size configured using phy_info->timing_window:%d
\n
"
,
phy_info
->
timing_window
);
if
(
phy_info
->
timing_info_mode
&
0x1
)
{
...
...
@@ -1322,7 +1323,6 @@ int start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nfap
nfapi_send_pnf_start_resp
(
config
,
p7_config
->
phy_id
);
printf
(
"[PNF] Sending first P7 slot indication
\n
"
);
#if 1
//TODO: Change to nfapi_pnf_p7_slot_ind
nfapi_pnf_p7_slot_ind
(
p7_config
,
p7_config
->
phy_id
,
0
,
0
);
printf
(
"[PNF] Sent first P7 slot ind
\n
"
);
#else
...
...
@@ -1669,7 +1669,8 @@ void oai_subframe_ind(uint16_t sfn, uint16_t sf) {
void
oai_slot_ind
(
uint16_t
sfn
,
uint16_t
slot
)
{
#if 1 // Put the NR code here
//LOG_D(PHY,"%s(sfn:%d, sf:%d)\n", __FUNCTION__, sfn, sf);
LOG_I
(
PHY
,
"%s(sfn:%d, slot:%d)
\n
"
,
__FUNCTION__
,
sfn
,
slot
);
//TODO FIXME - HACK - DJP - using a global to bodge it in
if
(
p7_config_g
!=
NULL
&&
sync_var
==
0
)
{
...
...
@@ -1681,7 +1682,7 @@ void oai_slot_ind(uint16_t sfn, uint16_t slot) {
NFAPI_TRACE
(
NFAPI_TRACE_INFO
,
"[PNF] %s %d.%d (sfn:%u slot:%u) SFN/SLOT(TX):%u
\n
"
,
__FUNCTION__
,
ts
.
tv_sec
,
ts
.
tv_nsec
,
sfn
,
slot
,
NFAPI_SFNSLOT2DEC
(
sfn_slot_tx
));
}
//
int subframe_ret = nfapi_pnf_p7_slot_ind(p7_config_g, p7_config_g->phy_id, 0, 0);
//
TODO: send p7_config instead of p7_config_g
int
slot_ret
=
nfapi_pnf_p7_slot_ind
(
p7_config_g
,
p7_config_g
->
phy_id
,
sfn
,
slot
);
// if (subframe_ret) {
...
...
nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h
View file @
55384420
...
...
@@ -121,7 +121,13 @@ typedef struct {
#define NFAPI_TAG_LENGTH_PACKED_LEN 4
// Convenience methods to convert between SFN/SLOT formats
#define NFAPI_SFNSLOT2DEC(_sfnslot) ((((_sfnslot) >> 6) * 10) + ((_sfnslot) & 0xF))
#define NFAPI_SFNSLOT2DEC(_sfn,_slot) ((((_sfn) >> 6) * 20) + ((_slot) & 0x3F)) // total count of slots
#define NFAPI_SFNSLOTDEC2SFNSLOT(_sfnslot_dec) ((((_sfnslot_dec) / 20) << 4) | (((_sfnslot_dec) - (((_sfnslot_dec) / 20) * 10)) & 0x3F))
#define NFAPI_SFNSLOT2SFN(_sfnslot) ((_sfnslot) >> 6)
#define NFAPI_SFNSLOT2SLOT(_sfnslot) ((_sfnslot) & 0x3F)
#define NFAPI_MAX_SFNSLOTDEC 1024*20 // 20 is for numerology 1
// Convenience methods to convert between SFN/SFN formats
#define NFAPI_SFNSF2DEC(_sfnsf) ((((_sfnsf) >> 4) * 10) + ((_sfnsf) & 0xF))
...
...
nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h
View file @
55384420
...
...
@@ -1005,6 +1005,7 @@ typedef struct {
uint16_t
SFN
;
/// Slot number (0-319)
uint16_t
Slot
;
uint16_t
sfn_slot
;
nfapi_nr_dl_tti_request_body_t
dl_tti_request_body
;
nfapi_vendor_extension_tlv_t
vendor_extension
;
}
nfapi_nr_dl_tti_request_t
;
...
...
nfapi/open-nFAPI/pnf/inc/pnf_p7.h
View file @
55384420
...
...
@@ -82,7 +82,7 @@ typedef struct {
pthread_mutex_t
pack_mutex
;
// should we allow the client to specifiy
nfapi_pnf_p7_subframe_buffer_t
subframe_buffer
[
30
/*NFAPI_MAX_TIMING_WINDOW_SIZE*/
];
nfapi_pnf_p7_slot_buffer_t
slot_buffer
[
30
/*NFAPI_MAX_TIMING_WINDOW_SIZE*/
];
uint32_t
sequence_number
;
uint16_t
max_num_segments
;
...
...
@@ -95,6 +95,12 @@ typedef struct {
uint32_t
sf_start_time_hr
;
int32_t
sfn_sf_shift
;
uint16_t
sfn
;
uint16_t
slot
;
uint16_t
sfn_slot
;
uint32_t
slot_start_time_hr
;
int32_t
sfn_slot_shift
;
uint8_t
timing_info_period_counter
;
uint8_t
timing_info_aperiodic_send
;
// 0:false 1:true
...
...
nfapi/open-nFAPI/pnf/public_inc/nfapi_pnf_interface.h
View file @
55384420
...
...
@@ -553,6 +553,20 @@ typedef struct
nfapi_ue_release_request_t
*
ue_release_req
;
}
nfapi_pnf_p7_subframe_buffer_t
;
typedef
struct
{
uint16_t
sfn_slot
;
//TODO: Change P7 structs to NR
nfapi_nr_dl_tti_request_t
dl_config_req
;
//nfapi_dl_config_request_t* dl_config_req;
nfapi_nr_ul_tti_request_t
ul_config_req
;
//nfapi_ul_config_request_t* ul_config_req;
nfapi_nr_ul_dci_request_t
hi_dci0_req
;
//nfapi_hi_dci0_request_t* hi_dci0_req;
nfapi_nr_tx_data_request_t
*
tx_req
;
//nfapi_tx_request_t* tx_req;
//TODO: check these two later
nfapi_lbt_dl_config_request_t
*
lbt_dl_config_req
;
nfapi_ue_release_request_t
*
ue_release_req
;
}
nfapi_pnf_p7_slot_buffer_t
;
typedef
struct
nfapi_pnf_p7_config
nfapi_pnf_p7_config_t
;
/*! The nfapi PNF PHY P7 configuration information created using the nfapi_pnf_p7_create function
...
...
@@ -606,6 +620,8 @@ typedef struct nfapi_pnf_p7_config
* are no 'valid' subframe messages */
nfapi_pnf_p7_subframe_buffer_t
dummy_subframe
;
nfapi_pnf_p7_slot_buffer_t
dummy_slot
;
// defining a slot equivalent for now
/*! Configuration options for the p7 pack unpack functions*/
nfapi_p7_codec_config_t
codec_config
;
...
...
@@ -614,6 +630,7 @@ typedef struct nfapi_pnf_p7_config
// tdb : if these should be public
uint16_t
subframe_buffer_size
;
uint16_t
slot_buffer_size
;
uint8_t
timing_info_mode_periodic
;
// 0:false 1:true
uint8_t
timing_info_mode_aperiodic
;
// 0:false 1:true
uint8_t
timing_info_period
;
// 1..225 in subframes
...
...
nfapi/open-nFAPI/pnf/src/pnf_p7.c
View file @
55384420
...
...
@@ -32,8 +32,19 @@
#define FAPI2_IP_DSCP 0
extern
uint16_t
sf_ahead
;
extern
uint16_t
slot_ahead
;
//uint16_t sf_ahead=4;
void
add_slot
(
uint16_t
*
frameP
,
uint16_t
*
slotP
,
int
offset
)
{
uint16_t
num_slots
=
20
;
// set based on numerlogy (fixing for 1)
*
frameP
=
(
*
frameP
+
((
*
slotP
+
offset
)
/
num_slots
))
%
1024
;
*
slotP
=
((
*
slotP
+
offset
)
%
num_slots
);
}
void
add_sf
(
uint16_t
*
frameP
,
uint16_t
*
subframeP
,
int
offset
)
{
*
frameP
=
(
*
frameP
+
((
*
subframeP
+
offset
)
/
10
))
%
1024
;
...
...
@@ -50,6 +61,22 @@ void subtract_sf(uint16_t *frameP, uint16_t *subframeP, int offset)
*
subframeP
=
(
*
subframeP
+
10
-
offset
)
%
10
;
}
uint32_t
sfnslot_add_slot
(
uint16_t
sfn
,
uint16_t
slot
,
int
offset
)
{
uint32_t
new_sfnslot
;
// uint16_t sfn = NFAPI_SFNSLOT2SFN(sfnslot);
// uint16_t slot = NFAPI_SFNSLOT2SLOT(sfnslot);
//printf("%s() sfn:%u sf:%u\n", __FUNCTION__, sfn, sf);
add_slot
(
&
sfn
,
&
slot
,
offset
);
new_sfnslot
=
sfn
<<
6
|
slot
;
//printf("%s() sfn:%u sf:%u offset:%d sfnsf:%d(DEC:%d) new:%d(DEC:%d)\n", __FUNCTION__, sfn, sf, offset, sfnsf, NFAPI_SFNSF2DEC(sfnsf), new_sfnsf, NFAPI_SFNSF2DEC(new_sfnsf));
return
new_sfnslot
;
}
uint16_t
sfnsf_add_sf
(
uint16_t
sfnsf
,
int
offset
)
{
uint16_t
new_sfnsf
;
...
...
@@ -614,65 +641,72 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl
// todo : consider a more efficent lock mechasium
#if 0
if
(
pthread_mutex_lock
(
&
(
pnf_p7
->
mutex
))
!=
0
)
{
NFAPI_TRACE
(
NFAPI_TRACE_INFO
,
"failed to lock mutex
\n
"
);
return
-
1
;
}
// save the curren time and sfn_sf
pnf_p7->sf_start_time_hr = pnf_get_current_time_hr();
pnf_p7->sfn_sf = sfn_sf;
// save the curren time, sfn and slot
pnf_p7
->
slot_start_time_hr
=
pnf_get_current_time_hr
();
pnf_p7
->
sfn
=
sfn
;
pnf_p7
->
slot
=
slot
;
uint32_t sfn_sf_tx = sfnsf_add_sf(sfn_sf, sf_ahead);
uint32_t tx_sfn_sf_dec = NFAPI_SFNSF2DEC(sfn_sf_tx);
uint32_t
sfn_slot_tx
=
sfnslot_add_slot
(
sfn
,
slot
,
slot_ahead
);
uint32_t
tx_sfn_slot_dec
=
20
*
sfn
+
slot
;
#if 0
uint32_t tx_sfn_slot_dec = NFAPI_SFNSLOT2DEC(sfn_slot_tx);
// If the subframe_buffer has been configured
if(pnf_p7->_public.s
ubframe_buffer_size != 0)
if(pnf_p7->_public.s
lot_buffer_size!= 0) // for now value is same as sf_buffer_size
{
// apply the shift to the incoming sfn_sf
if(pnf_p7->sfn_s
f_shift != 0)
if(pnf_p7->sfn_s
lot_shift != 0) // see in vnf_build_send_dl_node_sync
{
int32_t sfn_s
f_dec = NFAPI_SFNSF2DEC(sfn_sf
);
int32_t sfn_s
lot_dec = NFAPI_SFNSLOT2DEC(sfn_slot
);
int32_t shifted_sfn_s
f = sfn_sf_dec += pnf_p7->sfn_sf
_shift;
int32_t shifted_sfn_s
lot = sfn_slot_dec += pnf_p7->sfn_slot
_shift;
// adjust for wrap-around
if(shifted_sfn_s
f
< 0)
shifted_sfn_s
f += NFAPI_MAX_SFNSF
DEC;
else if(shifted_sfn_s
f > NFAPI_MAX_SFNSF
DEC)
shifted_sfn_s
f -= NFAPI_MAX_SFNSF
DEC;
if(shifted_sfn_s
lot
< 0)
shifted_sfn_s
lot += NFAPI_MAX_SFNSLOT
DEC;
else if(shifted_sfn_s
lot > NFAPI_MAX_SFNSLOT
DEC)
shifted_sfn_s
lot -= NFAPI_MAX_SFNSLOT
DEC;
NFAPI_TRACE(NFAPI_TRACE_INFO, "Applying shift %d to sfn/s
f (%d -> %d)\n", pnf_p7->sfn_sf_shift, NFAPI_SFNSF2DEC(sfn_sf), shifted_sfn_sf
);
sfn_s
f = shifted_sfn_sf
;
NFAPI_TRACE(NFAPI_TRACE_INFO, "Applying shift %d to sfn/s
lot (%d -> %d)\n", pnf_p7->sfn_slot_shift, NFAPI_SFNSF2DEC(sfn_slot), shifted_sfn_slot
);
sfn_s
lot = shifted_sfn_slot
;
//
// DJP - why does the shift not apply to pnf_p7->sfn_sf???
//
pnf_p7->sfn_s
f
_shift = 0;
pnf_p7->sfn_s
lot
_shift = 0;
}
uint32_t sfn_s
f_dec = NFAPI_SFNSF2DEC(sfn_sf
);
uint8_t buffer_index = sfn_s
f_dec % pnf_p7->_public.subframe
_buffer_size;
uint32_t sfn_s
lot_dec = NFAPI_SFNSLOT2DEC(sfn_slot
);
uint8_t buffer_index = sfn_s
lot_dec % pnf_p7->_public.slot
_buffer_size;
nfapi_pnf_p7_s
ubframe_buffer_t* subframe
_buffer = &(pnf_p7->subframe_buffer[buffer_index]);
nfapi_pnf_p7_s
lot_buffer_t* slot
_buffer = &(pnf_p7->subframe_buffer[buffer_index]);
uint8_t tx_buffer_index = tx_sfn_s
f_dec % pnf_p7->_public.subframe
_buffer_size;
nfapi_pnf_p7_s
ubframe_buffer_t* tx_subframe_buffer = &(pnf_p7->subframe
_buffer[tx_buffer_index]);
uint8_t tx_buffer_index = tx_sfn_s
lot_dec % pnf_p7->_public.slot
_buffer_size;
nfapi_pnf_p7_s
lot_buffer_t* tx_slot_buffer = &(pnf_p7->slot
_buffer[tx_buffer_index]);
//printf("sfn_sf_dec:%d tx_sfn_sf_dec:%d\n", sfn_sf_dec, tx_sfn_sf_dec);
if (0) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() shift:%d subframe_buffer->sfn_sf:%d tx_subframe_buffer->sfn_sf:%d sfn_sf:%d subframe_buffer[buffer_index:%u dl_config_req:%p tx_req:%p] "
if (0) NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() shift:%d slot_buffer->sfn_sf:%d tx_slot_buffer->sfn_slot:%d sfn_sf:%d subframe_buffer[buffer_index:%u dl_config_req:%p tx_req:%p] "
"TX:sfn_sf:%d:tx_buffer_index:%d[dl_config_req:%p tx_req:%p]\n",
__FUNCTION__,
pnf_p7->sfn_sf_shift,
NFAPI_SFNSF2DEC(subframe_buffer->sfn_sf),
NFAPI_SFNSF2DEC(tx_subframe_buffer->sfn_sf),
sfn_sf_dec, buffer_index, subframe_buffer->dl_config_req, subframe_buffer->tx_req,
tx_sfn_sf_dec, tx_buffer_index, tx_subframe_buffer->dl_config_req, tx_subframe_buffer->tx_req);
pnf_p7->sfn_slot_shift,
NFAPI_SFNSF2DEC(slot_buffer->sfn_slot),
NFAPI_SFNSF2DEC(tx_slot_buffer->sfn_slot),
sfn_slot_dec, buffer_index, slot_buffer->dl_config_req, slot_buffer->tx_req,
tx_sfn_slot_dec, tx_buffer_index, tx_slot_buffer->dl_config_req, tx_slot_buffer->tx_req);
//TODO: Change later if required
// if the subframe buffer sfn sf is set then we have atlease 1 message
// from the vnf.
...
...
@@ -681,39 +715,40 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl
//printf("tx_subframe_buffer->sfn_sf:%d sfn_sf_tx:%d\n", tx_subframe_buffer->sfn_sf, sfn_sf_tx);
//printf("subframe_buffer->sfn_sf:%d sfn_sf:%d\n", subframe_buffer->sfn_sf, sfn_sf);
if(tx_s
ubframe_buffer->sfn_sf == sfn_sf
_tx)
if(tx_s
lot_buffer->sfn_slot == sfn_slot
_tx)
{
if(tx_s
ubframe
_buffer->tx_req != 0)
if(tx_s
lot
_buffer->tx_req != 0)
{
if(pnf_p7->_public.tx_req)
(pnf_p7->_public.tx_req)(&(pnf_p7->_public), tx_s
ubframe
_buffer->tx_req);
(pnf_p7->_public.tx_req)(&(pnf_p7->_public), tx_s
lot
_buffer->tx_req);
//deallocate_nfapi_tx_request(s
ubframe
_buffer->tx_req, pnf_p7);
//deallocate_nfapi_tx_request(s
lot
_buffer->tx_req, pnf_p7);
}
else
{
// send dummy
if(pnf_p7->_public.tx_req && pnf_p7->_public.dummy_s
ubframe
.tx_req)
if(pnf_p7->_public.tx_req && pnf_p7->_public.dummy_s
lot
.tx_req)
{
pnf_p7->_public.dummy_subframe.tx_req->sfn_sf = sfn_sf_tx;
(pnf_p7->_public.tx_req)(&(pnf_p7->_public), pnf_p7->_public.dummy_subframe.tx_req);
pnf_p7->_public.dummy_slot.tx_req->sfn_sf = sfn_slot_tx; // TODO: change tx_req to nfapi_nr_tx_data_request_t
(pnf_p7->_public.tx_req)(&(pnf_p7->_public), pnf_p7->_public.dummy_slot.tx_req);
}
}
}
if(tx_s
ubframe
_buffer->dl_config_req != 0)
if(tx_s
lot
_buffer->dl_config_req != 0)
{
if(pnf_p7->_public.dl_config_req)
(pnf_p7->_public.dl_config_req)(NULL, &(pnf_p7->_public), tx_s
ubframe
_buffer->dl_config_req);
(pnf_p7->_public.dl_config_req)(NULL, &(pnf_p7->_public), tx_s
lot
_buffer->dl_config_req);
//deallocate_nfapi_dl_config_request(subframe_buffer->dl_config_req, pnf_p7);
}
else
{
// send dummy
if(pnf_p7->_public.dl_config_req && pnf_p7->_public.dummy_s
ubframe
.dl_config_req)
if(pnf_p7->_public.dl_config_req && pnf_p7->_public.dummy_s
lot
.dl_config_req)
{
pnf_p7->_public.dummy_s
ubframe.dl_config_req->sfn_sf = sfn_sf_tx;
(pnf_p7->_public.dl_config_req)(NULL, &(pnf_p7->_public), pnf_p7->_public.dummy_s
ubframe
.dl_config_req);
pnf_p7->_public.dummy_s
lot.dl_config_req->sfn_sf = sfn_slot_tx; // TODO: Change dl_config_req to nfapi_nr_dl_tti_request_t
(pnf_p7->_public.dl_config_req)(NULL, &(pnf_p7->_public), pnf_p7->_public.dummy_s
lot
.dl_config_req);
}
}
...
...
@@ -734,6 +769,7 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl
}
}
if(tx_subframe_buffer->ue_release_req != 0)
{
if(pnf_p7->_public.ue_release_req)
...
...
@@ -859,6 +895,7 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl
{
pnf_p7->timing_info_ms_counter++;
}
}
else
{
...
...
@@ -879,14 +916,14 @@ int pnf_p7_slot_ind(pnf_p7_t* pnf_p7, uint16_t phy_id, uint16_t sfn, uint16_t sl
memset(&pnf_p7->stats, 0, sizeof(pnf_p7->stats));
}
pnf_p7->tick++;
#endif
if
(
pthread_mutex_unlock
(
&
(
pnf_p7
->
mutex
))
!=
0
)
{
NFAPI_TRACE
(
NFAPI_TRACE_INFO
,
"failed to unlock mutex
\n
"
);
return
-
1
;
}
#endif
return
0
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment