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
ebd622ac
Commit
ebd622ac
authored
Oct 27, 2022
by
Robert Schmidt
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/NR_MAC_PUCCH_rework' into integration_2022_wk43
parents
cab10e85
3247cdd0
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
268 additions
and
409 deletions
+268
-409
openair1/PHY/NR_TRANSPORT/pucch_rx.c
openair1/PHY/NR_TRANSPORT/pucch_rx.c
+0
-6
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
+1
-1
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
+1
-1
openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
+1
-1
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
+4
-6
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
+1
-1
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
+2
-2
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
+1
-1
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+45
-9
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
+199
-376
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
+5
-2
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+8
-3
No files found.
openair1/PHY/NR_TRANSPORT/pucch_rx.c
View file @
ebd622ac
...
...
@@ -1536,12 +1536,6 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB,
corr32_im
[
symb
][
group
][
aa
]
+
((
int16_t
*
)(
&
prod_im
[
aa
]))[
0
]);
#endif
LOG_D
(
PHY
,
"pucch2 cw %d group %d aa %d: (%d,%d)+(%d,%d) = (%d,%d)
\n
"
,
cw
,
group
,
aa
,
corr32_re
[
symb
][
group
][
aa
],
corr32_im
[
symb
][
group
][
aa
],
((
int16_t
*
)(
&
prod_re
[
aa
]))[
0
],
((
int16_t
*
)(
&
prod_im
[
aa
]))[
0
],
corr32_re
[
symb
][
group
][
aa
]
+
((
int16_t
*
)(
&
prod_re
[
aa
]))[
0
],
corr32_im
[
symb
][
group
][
aa
]
+
((
int16_t
*
)(
&
prod_im
[
aa
]))[
0
]);
corr_re
=
(
corr32_re
[
symb
][
group
][
aa
]
+
((
int16_t
*
)(
&
prod_re
[
aa
]))[
0
]);
corr_im
=
(
corr32_im
[
symb
][
group
][
aa
]
+
((
int16_t
*
)(
&
prod_im
[
aa
]))[
0
]);
...
...
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
View file @
ebd622ac
...
...
@@ -4427,7 +4427,7 @@ void fill_searchSpaceZero(NR_SearchSpace_t *ss0, NR_Type0_PDCCH_CSS_config_t *ty
}
void
find_period_off
est_SR
(
NR_SchedulingRequestResourceConfig_t
*
SchedulingReqRec
,
int
*
period
,
int
*
offset
)
{
void
find_period_off
set_SR
(
const
NR_SchedulingRequestResourceConfig_t
*
SchedulingReqRec
,
int
*
period
,
int
*
offset
)
{
NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR
P_O
=
SchedulingReqRec
->
periodicityAndOffset
->
present
;
switch
(
P_O
){
case
NR_SchedulingRequestResourceConfig__periodicityAndOffset_PR_sl1
:
...
...
openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
View file @
ebd622ac
...
...
@@ -221,7 +221,7 @@ uint16_t compute_pucch_prb_size(uint8_t format,
int16_t
get_N_RA_RB
(
int
delta_f_RA_PRACH
,
int
delta_f_PUSCH
);
void
find_period_off
est_SR
(
NR_SchedulingRequestResourceConfig_t
*
SchedulingReqRec
,
int
*
period
,
int
*
offset
);
void
find_period_off
set_SR
(
const
NR_SchedulingRequestResourceConfig_t
*
SchedulingReqRec
,
int
*
period
,
int
*
offset
);
void
csi_period_offset
(
NR_CSI_ReportConfig_t
*
csirep
,
struct
NR_CSI_ResourcePeriodicityAndOffset
*
periodicityAndOffset
,
...
...
openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c
View file @
ebd622ac
...
...
@@ -2389,7 +2389,7 @@ bool trigger_periodic_scheduling_request(NR_UE_MAC_INST_t *mac,
NR_SchedulingRequestResourceConfig_t
*
SchedulingRequestResourceConfig
=
pucch_Config
->
schedulingRequestResourceToAddModList
->
list
.
array
[
SR_resource_id
];
int
SR_period
;
int
SR_offset
;
find_period_off
es
t_SR
(
SchedulingRequestResourceConfig
,
&
SR_period
,
&
SR_offset
);
find_period_off
se
t_SR
(
SchedulingRequestResourceConfig
,
&
SR_period
,
&
SR_offset
);
int
sfn_sf
=
frame
*
n_slots_frame
+
slot
;
if
((
sfn_sf
-
SR_offset
)
%
SR_period
==
0
)
{
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
View file @
ebd622ac
...
...
@@ -215,9 +215,8 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
// Schedule CSI-RS transmission
nr_csirs_scheduling
(
module_idP
,
frame
,
slot
,
nr_slots_per_frame
[
*
scc
->
ssbSubcarrierSpacing
]);
// Schedule CSI measurement reporting: check in slot 0 for the whole frame
if
(
slot
==
0
)
nr_csi_meas_reporting
(
module_idP
,
frame
,
slot
);
// Schedule CSI measurement reporting
nr_csi_meas_reporting
(
module_idP
,
frame
,
slot
);
// Schedule SRS: check in slot 0 for the whole frame
if
(
slot
==
0
)
...
...
@@ -237,11 +236,10 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
nr_schedule_ue_spec
(
module_idP
,
frame
,
slot
);
stop_meas
(
&
gNB
->
schedule_dlsch
);
nr_schedule_pucch
(
RC
.
nrmac
[
module_idP
],
frame
,
slot
);
// This schedule SR after PUCCH for multiplexing
nr_sr_reporting
(
RC
.
nrmac
[
module_idP
],
frame
,
slot
);
nr_schedule_pucch
(
RC
.
nrmac
[
module_idP
],
frame
,
slot
);
stop_meas
(
&
RC
.
nrmac
[
module_idP
]
->
eNB_scheduler
);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
(
VCD_SIGNAL_DUMPER_FUNCTIONS_gNB_DLSCH_ULSCH_SCHEDULER
,
VCD_FUNCTION_OUT
);
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
View file @
ebd622ac
...
...
@@ -1469,7 +1469,7 @@ void nr_generate_Msg4(module_id_t module_idP, int CC_id, frame_t frameP, sub_fra
LOG_D
(
NR_MAC
,
"[RAPROC] Msg4 r_pucch %d (CCEIndex %d, nb_of_candidates %d, delta_PRI %d)
\n
"
,
r_pucch
,
CCEIndex
,
nr_of_candidates
,
delta_PRI
);
int
alloc
=
nr_acknack_scheduling
(
module_idP
,
UE
,
frameP
,
slotP
,
r_pucch
,
1
);
int
alloc
=
nr_acknack_scheduling
(
nr_mac
,
UE
,
frameP
,
slotP
,
r_pucch
,
1
);
if
(
alloc
<
0
)
{
LOG_D
(
NR_MAC
,
"Couldn't find a pucch allocation for ack nack (msg4) in frame %d slot %d
\n
"
,
frameP
,
slotP
);
return
;
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
View file @
ebd622ac
...
...
@@ -508,7 +508,7 @@ bool allocate_dl_retransmission(module_id_t module_id,
* allocation after CCE alloc fail would be more complex) */
int
r_pucch
=
nr_get_pucch_resource
(
sched_ctrl
->
coreset
,
ul_bwp
->
pucch_Config
,
CCEIndex
);
const
int
alloc
=
nr_acknack_scheduling
(
module_id
,
UE
,
frame
,
slot
,
r_pucch
,
0
);
const
int
alloc
=
nr_acknack_scheduling
(
nr_mac
,
UE
,
frame
,
slot
,
r_pucch
,
0
);
if
(
alloc
<
0
)
{
LOG_D
(
MAC
,
"could not find PUCCH for UE %04x@%d.%d
\n
"
,
...
...
@@ -701,7 +701,7 @@ void pf_dl(module_id_t module_id,
* allocation after CCE alloc fail would be more complex) */
int
r_pucch
=
nr_get_pucch_resource
(
sched_ctrl
->
coreset
,
ul_bwp
->
pucch_Config
,
CCEIndex
);
const
int
alloc
=
nr_acknack_scheduling
(
m
odule_id
,
iterator
->
UE
,
frame
,
slot
,
r_pucch
,
0
);
const
int
alloc
=
nr_acknack_scheduling
(
m
ac
,
iterator
->
UE
,
frame
,
slot
,
r_pucch
,
0
);
if
(
alloc
<
0
)
{
LOG_D
(
NR_MAC
,
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
View file @
ebd622ac
...
...
@@ -277,7 +277,7 @@ void nr_preprocessor_phytest(module_id_t module_id,
UE
->
rnti
);
int
r_pucch
=
nr_get_pucch_resource
(
sched_ctrl
->
coreset
,
UE
->
current_UL_BWP
.
pucch_Config
,
CCEIndex
);
const
int
alloc
=
nr_acknack_scheduling
(
module_id
,
UE
,
frame
,
slot
,
r_pucch
,
0
);
const
int
alloc
=
nr_acknack_scheduling
(
RC
.
nrmac
[
module_id
]
,
UE
,
frame
,
slot
,
r_pucch
,
0
);
if
(
alloc
<
0
)
{
LOG_D
(
MAC
,
"%s(): could not find PUCCH for UE %04x@%d.%d
\n
"
,
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
View file @
ebd622ac
...
...
@@ -1087,7 +1087,7 @@ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu,
pucch_pdu
->
cyclic_prefix
=
(
current_BWP
->
cyclicprefix
==
NULL
)
?
0
:
*
current_BWP
->
cyclicprefix
;
NR_PUCCH_Config_t
*
pucch_Config
=
current_BWP
->
pucch_Config
;
if
(
r_pucch
<
0
||
pucch_Config
){
if
(
r_pucch
<
0
||
pucch_Config
)
{
LOG_D
(
NR_MAC
,
"pucch_acknak: Filling dedicated configuration for PUCCH
\n
"
);
AssertFatal
(
pucch_Config
->
resourceSetToAddModList
!=
NULL
,
...
...
@@ -1166,6 +1166,7 @@ void nr_configure_pucch(nfapi_nr_pucch_pdu_t* pucch_pdu,
break
;
case
NR_PUCCH_Resource__format_PR_format2
:
pucch_pdu
->
format_type
=
2
;
pucch_pdu
->
sr_flag
=
O_sr
;
pucch_pdu
->
nr_of_symbols
=
pucchres
->
format
.
choice
.
format2
->
nrofSymbols
;
pucch_pdu
->
start_symbol_index
=
pucchres
->
format
.
choice
.
format2
->
startingSymbolIndex
;
pucch_pdu
->
data_scrambling_id
=
pusch_id
!=
NULL
?
*
pusch_id
:
*
scc
->
physCellId
;
...
...
@@ -2209,6 +2210,20 @@ void delete_nr_ue_data(NR_UE_info_t *UE, NR_COMMON_channels_t *ccPtr, uid_alloca
}
}
void
set_max_fb_time
(
NR_UE_UL_BWP_t
*
UL_BWP
,
const
NR_UE_DL_BWP_t
*
DL_BWP
)
{
UL_BWP
->
max_fb_time
=
8
;
// default value
// take the maximum in dl_DataToUL_ACK list
if
(
DL_BWP
->
dci_format
!=
NR_DL_DCI_FORMAT_1_0
&&
UL_BWP
->
pucch_Config
)
{
const
struct
NR_PUCCH_Config__dl_DataToUL_ACK
*
fb_times
=
UL_BWP
->
pucch_Config
->
dl_DataToUL_ACK
;
for
(
int
i
=
0
;
i
<
fb_times
->
list
.
count
;
i
++
)
{
if
(
*
fb_times
->
list
.
array
[
i
]
>
UL_BWP
->
max_fb_time
)
UL_BWP
->
max_fb_time
=
*
fb_times
->
list
.
array
[
i
];
}
}
}
// main function to configure parameters of current BWP
void
configure_UE_BWP
(
gNB_MAC_INST
*
nr_mac
,
NR_ServingCellConfigCommon_t
*
scc
,
...
...
@@ -2375,7 +2390,6 @@ void configure_UE_BWP(gNB_MAC_INST *nr_mac,
else
UL_BWP
->
pucch_ConfigCommon
=
scc
->
uplinkConfigCommon
->
initialUplinkBWP
->
pucch_ConfigCommon
->
choice
.
setup
;
if
(
UE
)
{
// setting PDCCH related structures for sched_ctrl
sched_ctrl
->
search_space
=
get_searchspace
(
scc
,
...
...
@@ -2410,6 +2424,8 @@ void configure_UE_BWP(gNB_MAC_INST *nr_mac,
if
(
UL_BWP
->
csi_MeasConfig
)
compute_csi_bitlen
(
UL_BWP
->
csi_MeasConfig
,
UE
->
csi_report_template
);
set_max_fb_time
(
UL_BWP
,
DL_BWP
);
set_sched_pucch_list
(
sched_ctrl
,
UL_BWP
,
scc
);
}
if
(
ra
)
{
...
...
@@ -2456,6 +2472,7 @@ void configure_UE_BWP(gNB_MAC_INST *nr_mac,
NR_RNTI_C
,
target_ss
,
false
);
}
void
reset_srs_stats
(
NR_UE_info_t
*
UE
)
{
...
...
@@ -2553,6 +2570,31 @@ NR_UE_info_t *add_new_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rntiP, NR_CellGroupConf
return
(
UE
);
}
void
set_sched_pucch_list
(
NR_UE_sched_ctrl_t
*
sched_ctrl
,
const
NR_UE_UL_BWP_t
*
ul_bwp
,
const
NR_ServingCellConfigCommon_t
*
scc
)
{
const
NR_TDD_UL_DL_Pattern_t
*
tdd
=
scc
->
tdd_UL_DL_ConfigurationCommon
?
&
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
:
NULL
;
const
int
n_slots_frame
=
nr_slots_per_frame
[
ul_bwp
->
scs
];
const
int
nr_slots_period
=
tdd
?
n_slots_frame
/
get_nb_periods_per_frame
(
tdd
->
dl_UL_TransmissionPeriodicity
)
:
n_slots_frame
;
const
int
n_ul_slots_period
=
tdd
?
tdd
->
nrofUplinkSlots
+
(
tdd
->
nrofUplinkSymbols
>
0
?
1
:
0
)
:
n_slots_frame
;
// PUCCH list size is given by the number of UL slots in the PUCCH period
// the length PUCCH period is determined by max_fb_time since we may need to prepare PUCCH for ACK/NACK max_fb_time slots ahead
const
int
list_size
=
n_ul_slots_period
<<
(
ul_bwp
->
max_fb_time
/
nr_slots_period
);
if
(
!
sched_ctrl
->
sched_pucch
)
{
sched_ctrl
->
sched_pucch
=
calloc
(
list_size
,
sizeof
(
*
sched_ctrl
->
sched_pucch
));
sched_ctrl
->
sched_pucch_size
=
list_size
;
}
else
if
(
list_size
>
sched_ctrl
->
sched_pucch_size
)
{
sched_ctrl
->
sched_pucch
=
realloc
(
sched_ctrl
->
sched_pucch
,
list_size
*
sizeof
(
*
sched_ctrl
->
sched_pucch
));
for
(
int
i
=
sched_ctrl
->
sched_pucch_size
;
i
<
list_size
;
i
++
){
NR_sched_pucch_t
*
curr_pucch
=
&
sched_ctrl
->
sched_pucch
[
i
];
memset
(
curr_pucch
,
0
,
sizeof
(
*
curr_pucch
));
}
sched_ctrl
->
sched_pucch_size
=
list_size
;
}
}
void
create_dl_harq_list
(
NR_UE_sched_ctrl_t
*
sched_ctrl
,
const
NR_PDSCH_ServingCellConfig_t
*
pdsch
)
{
const
int
nrofHARQ
=
pdsch
&&
pdsch
->
nrofHARQ_ProcessesForPDSCH
?
...
...
@@ -2672,23 +2714,17 @@ uint8_t nr_get_tpc(int target, uint8_t cqi, int incr) {
void
get_pdsch_to_harq_feedback
(
NR_PUCCH_Config_t
*
pucch_Config
,
nr_dci_format_t
dci_format
,
int
*
max_fb_time
,
uint8_t
*
pdsch_to_harq_feedback
)
{
if
(
dci_format
==
NR_DL_DCI_FORMAT_1_0
)
{
for
(
int
i
=
0
;
i
<
8
;
i
++
)
{
for
(
int
i
=
0
;
i
<
8
;
i
++
)
pdsch_to_harq_feedback
[
i
]
=
i
+
1
;
if
(
pdsch_to_harq_feedback
[
i
]
>*
max_fb_time
)
*
max_fb_time
=
pdsch_to_harq_feedback
[
i
];
}
}
else
{
AssertFatal
(
pucch_Config
!=
NULL
,
"pucch_Config shouldn't be null here
\n
"
);
if
(
pucch_Config
->
dl_DataToUL_ACK
!=
NULL
)
{
for
(
int
i
=
0
;
i
<
8
;
i
++
)
{
pdsch_to_harq_feedback
[
i
]
=
*
pucch_Config
->
dl_DataToUL_ACK
->
list
.
array
[
i
];
if
(
pdsch_to_harq_feedback
[
i
]
>*
max_fb_time
)
*
max_fb_time
=
pdsch_to_harq_feedback
[
i
];
}
}
else
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c
View file @
ebd622ac
This diff is collapsed.
Click to expand it.
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
View file @
ebd622ac
...
...
@@ -209,7 +209,7 @@ void nr_csi_meas_reporting(int Mod_idP,
frame_t
frameP
,
sub_frame_t
slotP
);
int
nr_acknack_scheduling
(
int
Mod_idP
,
int
nr_acknack_scheduling
(
gNB_MAC_INST
*
mac
,
NR_UE_info_t
*
UE
,
frame_t
frameP
,
sub_frame_t
slotP
,
...
...
@@ -218,7 +218,6 @@ int nr_acknack_scheduling(int Mod_idP,
void
get_pdsch_to_harq_feedback
(
NR_PUCCH_Config_t
*
pucch_Config
,
nr_dci_format_t
dci_format
,
int
*
max_fb_time
,
uint8_t
*
pdsch_to_harq_feedback
);
void
nr_configure_css_dci_initial
(
nfapi_nr_dl_tti_pdcch_pdu_rel15_t
*
pdcch_pdu
,
...
...
@@ -466,6 +465,10 @@ uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx);
uint8_t
get_dl_nrOfLayers
(
const
NR_UE_sched_ctrl_t
*
sched_ctrl
,
const
nr_dci_format_t
dci_format
);
void
set_sched_pucch_list
(
NR_UE_sched_ctrl_t
*
sched_ctrl
,
const
NR_UE_UL_BWP_t
*
ul_bwp
,
const
NR_ServingCellConfigCommon_t
*
scc
);
const
int
get_dl_tda
(
const
gNB_MAC_INST
*
nrmac
,
const
NR_ServingCellConfigCommon_t
*
scc
,
int
slot
);
const
int
get_ul_tda
(
const
gNB_MAC_INST
*
nrmac
,
const
NR_ServingCellConfigCommon_t
*
scc
,
int
slot
);
...
...
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
View file @
ebd622ac
...
...
@@ -123,6 +123,7 @@ typedef struct NR_UE_UL_BWP {
uint8_t
transform_precoding
;
uint8_t
mcs_table
;
nr_dci_format_t
dci_format
;
int
max_fb_time
;
}
NR_UE_UL_BWP_t
;
typedef
enum
{
...
...
@@ -354,6 +355,7 @@ typedef struct UE_info {
}
NR_UE_mac_ce_ctrl_t
;
typedef
struct
NR_sched_pucch
{
bool
active
;
int
frame
;
int
ul_slot
;
bool
sr_flag
;
...
...
@@ -560,9 +562,12 @@ typedef struct {
/// corresponding to the sched_pusch/sched_pdsch structures below
int
cce_index
;
uint8_t
aggregation_level
;
/// PUCCH scheduling information. Array of two: HARQ+SR in the first field,
/// CSI in second. This order is important for nr_acknack_scheduling()!
NR_sched_pucch_t
sched_pucch
[
2
];
/// Array of PUCCH scheduling information
/// Its size depends on TDD configuration and max feedback time
/// There will be a structure for each UL slot in the active period determined by the size
NR_sched_pucch_t
*
sched_pucch
;
int
sched_pucch_size
;
/// Sched PUSCH: scheduling decisions, copied into HARQ and cleared every TTI
NR_sched_pusch_t
sched_pusch
;
...
...
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