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
wangjie
OpenXG-RAN
Commits
3321a351
Commit
3321a351
authored
Oct 02, 2019
by
laurent
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add DL packet compression
parent
2aafa617
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
76 additions
and
24 deletions
+76
-24
executables/main-fs6.c
executables/main-fs6.c
+55
-13
executables/split_headers.h
executables/split_headers.h
+4
-4
openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c
openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c
+2
-2
openair1/SCHED_UE/phy_procedures_lte_ue.c
openair1/SCHED_UE/phy_procedures_lte_ue.c
+0
-1
openair2/LAYER2/MAC/pre_processor.c
openair2/LAYER2/MAC/pre_processor.c
+9
-2
openair2/UTIL/OPT/probe.c
openair2/UTIL/OPT/probe.c
+6
-2
No files found.
executables/main-fs6.c
View file @
3321a351
...
@@ -17,17 +17,54 @@
...
@@ -17,17 +17,54 @@
#include <openair1/PHY/LTE_ESTIMATION/lte_estimation.h>
#include <openair1/PHY/LTE_ESTIMATION/lte_estimation.h>
#include <executables/split_headers.h>
#include <executables/split_headers.h>
#include <openair1/PHY/CODING/coding_extern.h>
#include <openair1/PHY/CODING/coding_extern.h>
#include <emmintrin.h>
#define FS6_BUF_SIZE 100*1000
#define FS6_BUF_SIZE 100*1000
static
UDPsock_t
sockFS6
;
static
UDPsock_t
sockFS6
;
int
sum
(
uint8_t
*
b
,
int
s
)
{
int
sum
(
uint8_t
*
b
,
int
s
)
{
int
sum
=
0
;
int
sum
=
0
;
for
(
int
i
=
0
;
i
<
s
;
i
++
)
for
(
int
i
=
0
;
i
<
s
;
i
++
)
sum
+=
b
[
i
];
sum
+=
b
[
i
];
return
sum
;
return
sum
;
}
}
#define ceil16_bytes(a) ((((a+15)/16)*16)/8)
static
void
fs6Dlunpack
(
void
*
out
,
void
*
in
,
int
szUnpacked
)
{
static
uint64_t
*
lut
=
NULL
;
if
(
!
lut
)
{
lut
=
(
uint64_t
*
)
malloc
(
sizeof
(
*
lut
)
*
256
);
for
(
int
i
=
0
;
i
<
256
;
i
++
)
for
(
int
j
=
0
;
j
<
8
;
j
++
)
((
uint8_t
*
)(
lut
+
i
))[
7
-
j
]
=
(
i
>>
j
)
&
1
;
}
int64_t
*
out_64
=
(
int64_t
*
)
out
;
int
sz
=
ceil16_bytes
(
szUnpacked
);
for
(
int
i
=
0
;
i
<
sz
;
i
++
)
out_64
[
i
]
=
lut
[((
uint8_t
*
)
in
)[
i
]];
return
;
}
static
void
fs6Dlpack
(
void
*
out
,
void
*
in
,
int
szUnpacked
)
{
__m128i
zeros
=
_mm_set1_epi8
(
0
);
__m128i
shuffle
=
_mm_set_epi8
(
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
);
const
int
loop
=
ceil16_bytes
(
szUnpacked
)
/
sizeof
(
uint16_t
);
__m128i
*
iter
=
(
__m128i
*
)
in
;
for
(
int
i
=
0
;
i
<
loop
;
i
++
)
{
__m128i
tmp
=
_mm_shuffle_epi8
(
_mm_cmpgt_epi8
(
*
iter
++
,
zeros
),
shuffle
);
((
uint16_t
*
)
out
)[
i
]
=
(
uint16_t
)
_mm_movemask_epi8
(
tmp
);
}
}
void
prach_eNB_tosplit
(
uint8_t
*
bufferZone
,
int
bufSize
,
PHY_VARS_eNB
*
eNB
)
{
void
prach_eNB_tosplit
(
uint8_t
*
bufferZone
,
int
bufSize
,
PHY_VARS_eNB
*
eNB
)
{
fs6_ul_t
*
header
=
(
fs6_ul_t
*
)
commonUDPdata
(
bufferZone
);
fs6_ul_t
*
header
=
(
fs6_ul_t
*
)
commonUDPdata
(
bufferZone
);
...
@@ -729,7 +766,7 @@ void rcvFs6DL(uint8_t *bufferZone, int nbBlocks, PHY_VARS_eNB *eNB, int frame, i
...
@@ -729,7 +766,7 @@ void rcvFs6DL(uint8_t *bufferZone, int nbBlocks, PHY_VARS_eNB *eNB, int frame, i
dlsch0
->
i0
=
hDLUE
(
bufPtr
)
->
i0
;
dlsch0
->
i0
=
hDLUE
(
bufPtr
)
->
i0
;
dlsch0
->
sib1_br_flag
=
hDLUE
(
bufPtr
)
->
sib1_br_flag
;
dlsch0
->
sib1_br_flag
=
hDLUE
(
bufPtr
)
->
sib1_br_flag
;
#endif
#endif
memcpy
(
dlsch_harq
->
e
,
fs6Dlunpack
(
dlsch_harq
->
e
,
hDLUE
(
bufPtr
)
+
1
,
hDLUE
(
bufPtr
)
->
dataLen
);
hDLUE
(
bufPtr
)
+
1
,
hDLUE
(
bufPtr
)
->
dataLen
);
LOG_D
(
PHY
,
"received %d bits, in harq id: %di fsf: %d.%d, sum %d
\n
"
,
LOG_D
(
PHY
,
"received %d bits, in harq id: %di fsf: %d.%d, sum %d
\n
"
,
hDLUE
(
bufPtr
)
->
dataLen
,
hDLUE
(
bufPtr
)
->
harq_pid
,
frame
,
subframe
,
sum
(
dlsch_harq
->
e
,
hDLUE
(
bufPtr
)
->
dataLen
));
hDLUE
(
bufPtr
)
->
dataLen
,
hDLUE
(
bufPtr
)
->
harq_pid
,
frame
,
subframe
,
sum
(
dlsch_harq
->
e
,
hDLUE
(
bufPtr
)
->
dataLen
));
...
@@ -971,11 +1008,11 @@ void appendFs6DLUE(uint8_t *bufferZone, LTE_DL_FRAME_PARMS *fp, int UE_id, int8_
...
@@ -971,11 +1008,11 @@ void appendFs6DLUE(uint8_t *bufferZone, LTE_DL_FRAME_PARMS *fp, int UE_id, int8_
harqData
->
pdsch_start
,
harqData
->
pdsch_start
,
frame
,
subframe
,
frame
,
subframe
,
0
);
0
);
AssertFatal
(
firstFreeByte
+
UEdataLen
+
sizeof
(
fs6_dl_t
)
<=
bufferZone
+
FS6_BUF_SIZE
,
""
);
AssertFatal
(
firstFreeByte
+
ceil16_bytes
(
UEdataLen
)
+
sizeof
(
fs6_dl_t
)
<=
bufferZone
+
FS6_BUF_SIZE
,
""
);
commonUDP_t
*
newUDPheader
=
(
commonUDP_t
*
)
firstFreeByte
;
commonUDP_t
*
newUDPheader
=
(
commonUDP_t
*
)
firstFreeByte
;
FirstUDPheader
->
nbBlocks
++
;
FirstUDPheader
->
nbBlocks
++
;
newUDPheader
->
blockID
=
curBlock
;
newUDPheader
->
blockID
=
curBlock
;
newUDPheader
->
contentBytes
=
sizeof
(
fs6_dl_t
)
+
sizeof
(
fs6_dl_uespec_t
)
+
UEdataLen
;
newUDPheader
->
contentBytes
=
sizeof
(
fs6_dl_t
)
+
sizeof
(
fs6_dl_uespec_t
)
+
ceil16_bytes
(
UEdataLen
)
;
// We skip the fs6 DL header, that is populated by caller
// We skip the fs6 DL header, that is populated by caller
// This header will be duplicated during sending
// This header will be duplicated during sending
hDLUE
(
newUDPheader
)
->
type
=
fs6DlConfig
;
hDLUE
(
newUDPheader
)
->
type
=
fs6DlConfig
;
...
@@ -998,10 +1035,9 @@ void appendFs6DLUE(uint8_t *bufferZone, LTE_DL_FRAME_PARMS *fp, int UE_id, int8_
...
@@ -998,10 +1035,9 @@ void appendFs6DLUE(uint8_t *bufferZone, LTE_DL_FRAME_PARMS *fp, int UE_id, int8_
hDLUE
(
newUDPheader
)
->
sib1_br_flag
=
dlsch0
->
sib1_br_flag
;
hDLUE
(
newUDPheader
)
->
sib1_br_flag
=
dlsch0
->
sib1_br_flag
;
#endif
#endif
hDLUE
(
newUDPheader
)
->
dataLen
=
UEdataLen
;
hDLUE
(
newUDPheader
)
->
dataLen
=
UEdataLen
;
memcpy
(
hDLUE
(
newUDPheader
)
+
1
,
harqData
->
e
,
UEdataLen
);
fs6Dlpack
(
hDLUE
(
newUDPheader
)
+
1
,
harqData
->
e
,
UEdataLen
);
LOG_D
(
PHY
,
"sending %d bits, in harq id: %di fsf: %d.%d, sum %d
\n
"
,
LOG_D
(
PHY
,
"sending %d bits, in harq id: %di fsf: %d.%d, sum %d
\n
"
,
UEdataLen
,
harq_pid
,
frame
,
subframe
,
sum
(
harqData
->
e
,
UEdataLen
));
UEdataLen
,
harq_pid
,
frame
,
subframe
,
sum
(
harqData
->
e
,
UEdataLen
));
//for (int i=0; i < UEdataLen; i++)
//for (int i=0; i < UEdataLen; i++)
//LOG_D(PHY,"buffer ei[%d]:%hhx\n", i, ( (uint8_t *)(hDLUE(newUDPheader)+1) )[i]);
//LOG_D(PHY,"buffer ei[%d]:%hhx\n", i, ( (uint8_t *)(hDLUE(newUDPheader)+1) )[i]);
}
}
...
@@ -1112,7 +1148,13 @@ void phy_procedures_eNB_TX_tosplit(uint8_t *bufferZone, PHY_VARS_eNB *eNB, L1_rx
...
@@ -1112,7 +1148,13 @@ void phy_procedures_eNB_TX_tosplit(uint8_t *bufferZone, PHY_VARS_eNB *eNB, L1_rx
uint8_t
num_dci
=
eNB
->
pdcch_vars
[
subframe
&
1
].
num_dci
;
uint8_t
num_dci
=
eNB
->
pdcch_vars
[
subframe
&
1
].
num_dci
;
uint8_t
num_mdci
=
eNB
->
mpdcch_vars
[
subframe
&
1
].
num_dci
;
uint8_t
num_mdci
=
eNB
->
mpdcch_vars
[
subframe
&
1
].
num_dci
;
memcpy
(
hDL
(
bufferZone
)
->
pbch_pdu
,
eNB
->
pbch_pdu
,
4
);
memcpy
(
hDL
(
bufferZone
)
->
pbch_pdu
,
eNB
->
pbch_pdu
,
4
);
if
(
num_dci
<=
8
)
LOG_D
(
PHY
,
"num_pdcch_symbols %"
PRIu8
",number dci %"
PRIu8
"
\n
"
,
num_pdcch_symbols
,
num_dci
);
LOG_D
(
PHY
,
"num_pdcch_symbols %"
PRIu8
",number dci %"
PRIu8
"
\n
"
,
num_pdcch_symbols
,
num_dci
);
else
{
LOG_E
(
PHY
,
"Num dci too large for current FS6 implementation, reducing to 8 dci (was %d)
\n
"
,
num_dci
);
num_dci
=
8
;
}
if
(
NFAPI_MODE
==
NFAPI_MONOLITHIC
||
NFAPI_MODE
==
NFAPI_MODE_PNF
)
{
if
(
NFAPI_MODE
==
NFAPI_MONOLITHIC
||
NFAPI_MODE
==
NFAPI_MODE_PNF
)
{
hDL
(
bufferZone
)
->
num_pdcch_symbols
=
num_pdcch_symbols
;
hDL
(
bufferZone
)
->
num_pdcch_symbols
=
num_pdcch_symbols
;
...
...
executables/split_headers.h
View file @
3321a351
...
@@ -55,7 +55,7 @@ typedef struct {
...
@@ -55,7 +55,7 @@ typedef struct {
uint8_t
pbch_pdu
[
4
];
uint8_t
pbch_pdu
[
4
];
int
num_pdcch_symbols
;
int
num_pdcch_symbols
;
int
num_dci
;
int
num_dci
;
DCI_ALLOC_t
dci_alloc
[
32
];
DCI_ALLOC_t
dci_alloc
[
8
];
int
num_mdci
;
int
num_mdci
;
int
amp
;
int
amp
;
LTE_eNB_PHICH
phich_vars
;
LTE_eNB_PHICH
phich_vars
;
...
@@ -133,9 +133,9 @@ typedef struct {
...
@@ -133,9 +133,9 @@ typedef struct {
typedef
struct
{
typedef
struct
{
enum
pckType
type
:
8
;
enum
pckType
type
:
8
;
short
UE_id
;
short
UE_id
;
shor
t
harq_id
;
uint8_
t
harq_id
;
shor
t
segment
;
uint8_
t
segment
;
shor
t
segLen
;
in
t
segLen
;
int
ulsch_power
[
2
];
int
ulsch_power
[
2
];
}
fs6_ul_uespec_t
;
}
fs6_ul_uespec_t
;
...
...
openair1/PHY/LTE_TRANSPORT/ulsch_decoding.c
View file @
3321a351
...
@@ -666,9 +666,9 @@ int ulsch_decoding_data(PHY_VARS_eNB *eNB,int UE_id,int harq_pid,int llr8_flag)
...
@@ -666,9 +666,9 @@ int ulsch_decoding_data(PHY_VARS_eNB *eNB,int UE_id,int harq_pid,int llr8_flag)
&eNB->ulsch_tc_intl1_stats,
&eNB->ulsch_tc_intl1_stats,
&eNB->ulsch_tc_intl2_stats);
&eNB->ulsch_tc_intl2_stats);
LOG_D(PHY, "Cu should decode in %d iter\n",iter);
LOG_D(PHY, "Cu should decode in %d iter\n",iter);
if ( iter == 5 ) {
if (
1) { //
iter == 5 ) {
for (int i=0; i < Kr; i++ )
for (int i=0; i < Kr; i++ )
printf("%hx:", ulsch_harq->d[r][96+i]);
printf("%h
h
x:", ulsch_harq->d[r][96+i]);
printf("\n");
printf("\n");
}
}
*/
*/
...
...
openair1/SCHED_UE/phy_procedures_lte_ue.c
View file @
3321a351
...
@@ -3328,7 +3328,6 @@ void ue_dlsch_procedures(PHY_VARS_UE *ue,
...
@@ -3328,7 +3328,6 @@ void ue_dlsch_procedures(PHY_VARS_UE *ue,
pdsch
==
PDSCH
?
1
:
0
,
pdsch
==
PDSCH
?
1
:
0
,
dlsch0
->
harq_processes
[
harq_pid
]
->
TBS
>
256
?
1
:
0
);
dlsch0
->
harq_processes
[
harq_pid
]
->
TBS
>
256
?
1
:
0
);
LOG_D
(
PHY
,
"dlsch turbo decode in %d iter
\n
"
,
ret
);
if
(
LOG_DEBUGFLAG
(
UE_TIMING
))
{
if
(
LOG_DEBUGFLAG
(
UE_TIMING
))
{
stop_meas
(
&
ue
->
dlsch_decoding_stats
[
ue
->
current_thread_id
[
subframe_rx
]]);
stop_meas
(
&
ue
->
dlsch_decoding_stats
[
ue
->
current_thread_id
[
subframe_rx
]]);
LOG_I
(
PHY
,
" --> Unscrambling for CW0 %5.3f
\n
"
,
LOG_I
(
PHY
,
" --> Unscrambling for CW0 %5.3f
\n
"
,
...
...
openair2/LAYER2/MAC/pre_processor.c
View file @
3321a351
...
@@ -1799,7 +1799,7 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
...
@@ -1799,7 +1799,7 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
}
}
total_allocated_rbs
[
CC_id
]
+=
nb_allocated_rbs
[
CC_id
][
UE_id
];
total_allocated_rbs
[
CC_id
]
+=
nb_allocated_rbs
[
CC_id
][
UE_id
];
LOG_D
(
MAC
,
"In ulsch_preprocessor: assigning %d RBs for UE %d/%x CCid %d, harq_pid %d, nb_rb_ul %d, %d ,%d
\n
"
,
LOG_D
(
MAC
,
"In ulsch_preprocessor: assigning %d RBs for UE %d/%x CCid %d, harq_pid %d, nb_rb_ul %d, %d ,%d
, slice %d, round ul %d
\n
"
,
nb_allocated_rbs
[
CC_id
][
UE_id
],
nb_allocated_rbs
[
CC_id
][
UE_id
],
UE_id
,
UE_id
,
rntiTable
[
UE_id
],
rntiTable
[
UE_id
],
...
@@ -1807,7 +1807,9 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
...
@@ -1807,7 +1807,9 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
harq_pid
,
harq_pid
,
UE_list
->
UE_template
[
CC_id
][
UE_id
].
nb_rb_ul
[
harq_pid
],
UE_list
->
UE_template
[
CC_id
][
UE_id
].
nb_rb_ul
[
harq_pid
],
UE_list
->
UE_template
[
CC_id
][
UE_id
].
pre_allocated_nb_rb_ul
[
slice_idx
],
UE_list
->
UE_template
[
CC_id
][
UE_id
].
pre_allocated_nb_rb_ul
[
slice_idx
],
average_rbs_per_user
[
CC_id
]);
average_rbs_per_user
[
CC_id
],
slice_idx
,
UE_list
->
UE_sched_ctrl
[
UE_id
].
round_UL
[
CC_id
][
harq_pid
]);
}
}
}
}
...
@@ -1833,6 +1835,10 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
...
@@ -1833,6 +1835,10 @@ void ulsch_scheduler_pre_processor(module_id_t module_idP,
while
(
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]
>
0
&&
while
(
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]
>
0
&&
nb_allocated_rbs
[
CC_id
][
UE_id
]
<
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]
&&
nb_allocated_rbs
[
CC_id
][
UE_id
]
<
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]
&&
total_remaining_rbs
[
CC_id
]
>
0
)
{
total_remaining_rbs
[
CC_id
]
>
0
)
{
LOG_D
(
PHY
,
"in ulsch alloc: prealloc: %d, nb_allocated %d, total remain%d
\n
"
,
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
],
nb_allocated_rbs
[
CC_id
][
UE_id
],
total_remaining_rbs
[
CC_id
]);
nb_allocated_rbs
[
CC_id
][
UE_id
]
=
cmin
(
nb_allocated_rbs
[
CC_id
][
UE_id
]
+
1
,
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]);
nb_allocated_rbs
[
CC_id
][
UE_id
]
=
cmin
(
nb_allocated_rbs
[
CC_id
][
UE_id
]
+
1
,
UE_template
->
pre_allocated_nb_rb_ul
[
slice_idx
]);
total_remaining_rbs
[
CC_id
]
--
;
total_remaining_rbs
[
CC_id
]
--
;
total_allocated_rbs
[
CC_id
]
++
;
total_allocated_rbs
[
CC_id
]
++
;
...
@@ -1902,6 +1908,7 @@ assign_max_mcs_min_rb(module_id_t module_idP,
...
@@ -1902,6 +1908,7 @@ assign_max_mcs_min_rb(module_id_t module_idP,
ue_sched_ctl
->
max_rbs_allowed_slice_uplink
[
CC_id
][
slice_idx
]
=
nb_rbs_allowed_slice
(
sli
->
ul
[
slice_idx
].
pct
,
N_RB_UL
);
ue_sched_ctl
->
max_rbs_allowed_slice_uplink
[
CC_id
][
slice_idx
]
=
nb_rbs_allowed_slice
(
sli
->
ul
[
slice_idx
].
pct
,
N_RB_UL
);
int
bytes_to_schedule
=
UE_template
->
estimated_ul_buffer
-
UE_template
->
scheduled_ul_bytes
;
int
bytes_to_schedule
=
UE_template
->
estimated_ul_buffer
-
UE_template
->
scheduled_ul_bytes
;
LOG_D
(
MAC
,
"ul bytes to schedule: %d-%d
\n
"
,
UE_template
->
estimated_ul_buffer
,
UE_template
->
scheduled_ul_bytes
);
if
(
bytes_to_schedule
<
0
)
bytes_to_schedule
=
0
;
if
(
bytes_to_schedule
<
0
)
bytes_to_schedule
=
0
;
int
bits_to_schedule
=
bytes_to_schedule
*
8
;
int
bits_to_schedule
=
bytes_to_schedule
*
8
;
...
...
openair2/UTIL/OPT/probe.c
View file @
3321a351
...
@@ -396,11 +396,15 @@ void trace_pdu(int direction, uint8_t *pdu_buffer, unsigned int pdu_buffer_size,
...
@@ -396,11 +396,15 @@ void trace_pdu(int direction, uint8_t *pdu_buffer, unsigned int pdu_buffer_size,
MAC_Context_Info_t
pdu_context
;
MAC_Context_Info_t
pdu_context
;
int
radioType
=
FDD_RADIO
;
int
radioType
=
FDD_RADIO
;
if
(
RC
.
eNB
[
0
][
0
]
!=
NULL
)
if
(
RC
.
eNB
&&
RC
.
eNB
[
0
][
0
]
!=
NULL
)
radioType
=
RC
.
eNB
[
0
][
0
]
->
frame_parms
.
frame_type
==
FDD
?
FDD_RADIO
:
TDD_RADIO
;
radioType
=
RC
.
eNB
[
0
][
0
]
->
frame_parms
.
frame_type
==
FDD
?
FDD_RADIO
:
TDD_RADIO
;
if
(
PHY_vars_UE_g
[
0
][
0
]
!=
NULL
)
else
if
(
PHY_vars_UE_g
&&
PHY_vars_UE_g
[
0
][
0
]
!=
NULL
)
radioType
=
PHY_vars_UE_g
[
0
][
0
]
->
frame_parms
.
frame_type
==
FDD
?
FDD_RADIO
:
TDD_RADIO
;
radioType
=
PHY_vars_UE_g
[
0
][
0
]
->
frame_parms
.
frame_type
==
FDD
?
FDD_RADIO
:
TDD_RADIO
;
else
{
LOG_E
(
OPT
,
"not a eNB neither a UE!!!
\n
"
);
return
;
}
switch
(
opt_type
)
{
switch
(
opt_type
)
{
case
OPT_WIRESHARK
:
case
OPT_WIRESHARK
:
...
...
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