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
canghaiwuhen
OpenXG-RAN
Commits
b0ff2cef
Commit
b0ff2cef
authored
May 20, 2020
by
Francesco Mani
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
rearranged function to prepare pucch scheduling
parent
49991e10
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
48 additions
and
106 deletions
+48
-106
openair2/LAYER2/NR_MAC_gNB/config.c
openair2/LAYER2/NR_MAC_gNB/config.c
+1
-2
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
+21
-14
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+25
-88
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
+1
-1
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+0
-1
No files found.
openair2/LAYER2/NR_MAC_gNB/config.c
View file @
b0ff2cef
...
@@ -290,8 +290,7 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm
...
@@ -290,8 +290,7 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSlots
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSlots
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSymbols
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSymbols
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
,
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
);
);
if
(
return_tdd
!=
0
){
if
(
return_tdd
!=
0
){
LOG_E
(
PHY
,
"TDD configuration can not be done
\n
"
);
LOG_E
(
PHY
,
"TDD configuration can not be done
\n
"
);
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
View file @
b0ff2cef
...
@@ -305,12 +305,14 @@ void nr_schedule_pucch(int Mod_idP,
...
@@ -305,12 +305,14 @@ void nr_schedule_pucch(int Mod_idP,
NR_BWP_Uplink_t
*
ubwp
=
secondaryCellGroup
->
spCellConfig
->
spCellConfigDedicated
->
uplinkConfig
->
uplinkBWP_ToAddModList
->
list
.
array
[
bwp_id
-
1
];
NR_BWP_Uplink_t
*
ubwp
=
secondaryCellGroup
->
spCellConfig
->
spCellConfigDedicated
->
uplinkConfig
->
uplinkBWP_ToAddModList
->
list
.
array
[
bwp_id
-
1
];
nfapi_nr_ul_tti_request_t
*
UL_tti_req
=
&
RC
.
nrmac
[
Mod_idP
]
->
UL_tti_req
[
0
];
nfapi_nr_ul_tti_request_t
*
UL_tti_req
=
&
RC
.
nrmac
[
Mod_idP
]
->
UL_tti_req
[
0
];
NR_sched_pucch
*
curr_pucch
=
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
;
NR_sched_pucch
*
curr_pucch
;
NR_sched_pucch
*
temp_pucch
;
int
nr_ulmix_slots
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
;
int
release_pucch
=
0
;
if
(
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
!=
0
)
nr_ulmix_slots
++
;
if
(
curr_pucch
!=
NULL
)
{
if
((
frameP
==
curr_pucch
->
frame
)
&&
(
slotP
==
curr_pucch
->
ul_slot
))
{
for
(
int
k
=
0
;
k
<
nr_ulmix_slots
;
k
++
)
{
curr_pucch
=
&
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
[
k
];
if
((
curr_pucch
->
dai_c
>
0
)
&&
(
frameP
==
curr_pucch
->
frame
)
&&
(
slotP
==
curr_pucch
->
ul_slot
))
{
UL_tti_req
->
SFN
=
frameP
;
UL_tti_req
->
SFN
=
frameP
;
UL_tti_req
->
Slot
=
slotP
;
UL_tti_req
->
Slot
=
slotP
;
UL_tti_req
->
pdus_list
[
UL_tti_req
->
n_pdus
].
pdu_type
=
NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE
;
UL_tti_req
->
pdus_list
[
UL_tti_req
->
n_pdus
].
pdu_type
=
NFAPI_NR_UL_CONFIG_PUCCH_PDU_TYPE
;
...
@@ -331,16 +333,9 @@ void nr_schedule_pucch(int Mod_idP,
...
@@ -331,16 +333,9 @@ void nr_schedule_pucch(int Mod_idP,
O_ack
,
O_ack
,
SR_flag
);
SR_flag
);
release_pucch
=
1
;
curr_pucch
->
dai_c
=
0
;
}
}
}
}
if
(
release_pucch
)
{
temp_pucch
=
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
;
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
=
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
->
next_sched_pucch
;
free
(
temp_pucch
);
}
}
}
bool
is_xlsch_in_slot
(
uint64_t
bitmap
,
sub_frame_t
slot
){
bool
is_xlsch_in_slot
(
uint64_t
bitmap
,
sub_frame_t
slot
){
...
@@ -378,6 +373,18 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
...
@@ -378,6 +373,18 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP,
NR_ServingCellConfigCommon_t
*
scc
=
cc
->
ServingCellConfigCommon
;
NR_ServingCellConfigCommon_t
*
scc
=
cc
->
ServingCellConfigCommon
;
int
num_slots_per_tdd
=
(
nr_slots_per_frame
[
*
scc
->
ssbSubcarrierSpacing
])
>>
(
7
-
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
dl_UL_TransmissionPeriodicity
);
int
num_slots_per_tdd
=
(
nr_slots_per_frame
[
*
scc
->
ssbSubcarrierSpacing
])
>>
(
7
-
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
dl_UL_TransmissionPeriodicity
);
int
nr_ulmix_slots
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
;
if
(
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
!=
0
)
nr_ulmix_slots
++
;
if
(
slot_txP
==
0
)
{
for
(
int
k
=
0
;
k
<
nr_ulmix_slots
;
k
++
)
{
memset
((
void
*
)
&
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
[
k
],
0
,
sizeof
(
NR_sched_pucch
));
}
}
start_meas
(
&
RC
.
nrmac
[
module_idP
]
->
eNB_scheduler
);
start_meas
(
&
RC
.
nrmac
[
module_idP
]
->
eNB_scheduler
);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
(
VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER
,
VCD_FUNCTION_IN
);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
(
VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER
,
VCD_FUNCTION_IN
);
...
...
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
View file @
b0ff2cef
...
@@ -1330,6 +1330,11 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
...
@@ -1330,6 +1330,11 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
int
UE_id
;
int
UE_id
;
int
i
;
int
i
;
NR_UE_list_t
*
UE_list
=
&
RC
.
nrmac
[
mod_idP
]
->
UE_list
;
NR_UE_list_t
*
UE_list
=
&
RC
.
nrmac
[
mod_idP
]
->
UE_list
;
NR_COMMON_channels_t
*
cc
=
RC
.
nrmac
[
mod_idP
]
->
common_channels
;
NR_ServingCellConfigCommon_t
*
scc
=
cc
->
ServingCellConfigCommon
;
int
num_slots_ul
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
;
if
(
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
>
0
)
num_slots_ul
++
;
LOG_I
(
MAC
,
"[gNB %d] Adding UE with rnti %x (next avail %d, num_UEs %d)
\n
"
,
LOG_I
(
MAC
,
"[gNB %d] Adding UE with rnti %x (next avail %d, num_UEs %d)
\n
"
,
mod_idP
,
mod_idP
,
rntiP
,
rntiP
,
...
@@ -1348,6 +1353,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
...
@@ -1348,6 +1353,7 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
memset
((
void
*
)
&
UE_list
->
UE_sched_ctrl
[
UE_id
],
memset
((
void
*
)
&
UE_list
->
UE_sched_ctrl
[
UE_id
],
0
,
0
,
sizeof
(
NR_UE_sched_ctrl_t
));
sizeof
(
NR_UE_sched_ctrl_t
));
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
=
(
NR_sched_pucch
*
)
malloc
(
num_slots_ul
*
sizeof
(
NR_sched_pucch
));
LOG_I
(
MAC
,
"gNB %d] Add NR UE_id %d : rnti %x
\n
"
,
LOG_I
(
MAC
,
"gNB %d] Add NR UE_id %d : rnti %x
\n
"
,
mod_idP
,
mod_idP
,
UE_id
,
UE_id
,
...
@@ -1420,15 +1426,13 @@ void nr_update_pucch_scheduling(int Mod_idP,
...
@@ -1420,15 +1426,13 @@ void nr_update_pucch_scheduling(int Mod_idP,
frame_t
frameP
,
frame_t
frameP
,
sub_frame_t
slotP
,
sub_frame_t
slotP
,
int
slots_per_tdd
,
int
slots_per_tdd
,
NR_sched_pucch
*
sched
_pucch
)
{
NR_sched_pucch
*
curr
_pucch
)
{
NR_ServingCellConfigCommon_t
*
scc
=
RC
.
nrmac
[
Mod_idP
]
->
common_channels
->
ServingCellConfigCommon
;
NR_ServingCellConfigCommon_t
*
scc
=
RC
.
nrmac
[
Mod_idP
]
->
common_channels
->
ServingCellConfigCommon
;
NR_UE_list_t
*
UE_list
=
&
RC
.
nrmac
[
Mod_idP
]
->
UE_list
;
NR_UE_list_t
*
UE_list
=
&
RC
.
nrmac
[
Mod_idP
]
->
UE_list
;
int
first_ul_slot_tdd
,
k
;
int
first_ul_slot_tdd
,
k
,
i
;
NR_sched_pucch
*
curr_pucch
;
uint8_t
pdsch_to_harq_feedback
[
8
];
uint8_t
pdsch_to_harq_feedback
[
8
];
int
found
=
0
;
int
found
=
0
;
int
i
=
0
;
int
nr_ulmix_slots
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
;
int
nr_ulmix_slots
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSlots
;
if
(
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
!=
0
)
if
(
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofUplinkSymbols
!=
0
)
nr_ulmix_slots
++
;
nr_ulmix_slots
++
;
...
@@ -1437,101 +1441,34 @@ void nr_update_pucch_scheduling(int Mod_idP,
...
@@ -1437,101 +1441,34 @@ void nr_update_pucch_scheduling(int Mod_idP,
NR_SearchSpace__searchSpaceType_PR
ss_type
=
NR_SearchSpace__searchSpaceType_PR_ue_Specific
;
NR_SearchSpace__searchSpaceType_PR
ss_type
=
NR_SearchSpace__searchSpaceType_PR_ue_Specific
;
get_pdsch_to_harq_feedback
(
Mod_idP
,
UE_id
,
ss_type
,
pdsch_to_harq_feedback
);
get_pdsch_to_harq_feedback
(
Mod_idP
,
UE_id
,
ss_type
,
pdsch_to_harq_feedback
);
// if the list of pucch to be scheduled is empty
// for each possible ul or mixed slot
if
(
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
==
NULL
)
{
for
(
k
=
0
;
k
<
nr_ulmix_slots
;
k
++
)
{
sched_pucch
->
frame
=
frameP
;
curr_pucch
=
&
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
[
k
];
sched_pucch
->
next_sched_pucch
=
NULL
;
// if there is free room in current pucch structure
sched_pucch
->
dai_c
=
1
;
if
(
curr_pucch
->
dai_c
<
MAX_ACK_BITS
)
{
sched_pucch
->
resource_indicator
=
0
;
// in phytest with only 1 UE we are using just the 1st resource
curr_pucch
->
frame
=
frameP
;
if
(
nr_ulmix_slots
>
0
)
{
curr_pucch
->
dai_c
++
;
curr_pucch
->
resource_indicator
=
0
;
// in phytest with only 1 UE we are using just the 1st resource
// first pucch occasion in first UL or MIXED slot
// first pucch occasion in first UL or MIXED slot
first_ul_slot_tdd
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSlots
;
first_ul_slot_tdd
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSlots
;
for
(
k
=
0
;
k
<
nr_ulmix_slots
;
k
++
)
{
// for each possible UL or mixed slot
i
=
0
;
while
(
i
<
8
&&
found
==
0
)
{
// look if timing indicator is among allowed values
while
(
i
<
8
&&
found
==
0
)
{
// look if timing indicator is among allowed values
if
(
pdsch_to_harq_feedback
[
i
]
==
(
first_ul_slot_tdd
+
k
)
-
(
slotP
%
slots_per_tdd
))
if
(
pdsch_to_harq_feedback
[
i
]
==
(
first_ul_slot_tdd
+
k
)
-
(
slotP
%
slots_per_tdd
))
found
=
1
;
if
(
found
==
0
)
i
++
;
}
if
(
found
==
1
)
break
;
}
if
(
found
==
1
)
{
// computing slot in which pucch is scheduled
sched_pucch
->
ul_slot
=
first_ul_slot_tdd
+
k
+
(
slotP
-
(
slotP
%
slots_per_tdd
));
sched_pucch
->
timing_indicator
=
i
;
// index in the list of timing indicators
}
else
AssertFatal
(
1
==
0
,
"No Uplink slot available in accordance to allowed timing indicator
\n
"
);
}
else
AssertFatal
(
1
==
0
,
"No Uplink Slots in this Frame
\n
"
);
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
=
sched_pucch
;
}
else
{
// to be tested
curr_pucch
=
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
;
if
(
curr_pucch
->
dai_c
<
MAX_ACK_BITS
)
{
// we are scheduling at most MAX_UCI_BITS harq-ack in the same pucch
while
(
i
<
8
&&
found
==
0
)
{
// look if timing indicator is among allowed values for current pucch
if
(
pdsch_to_harq_feedback
[
i
]
==
(
curr_pucch
->
ul_slot
%
slots_per_tdd
)
-
(
slotP
%
slots_per_tdd
))
found
=
1
;
found
=
1
;
if
(
found
==
0
)
i
++
;
if
(
found
==
0
)
i
++
;
}
}
if
(
found
==
1
)
{
// scheduling this harq-ack in current pucch
if
(
found
==
1
)
{
curr_pucch
->
dai_c
=
1
+
curr_pucch
->
dai_c
;
// computing slot in which pucch is scheduled
memcpy
(
sched_pucch
,
curr_pucch
,
sizeof
(
NR_sched_pucch
));
curr_pucch
->
ul_slot
=
first_ul_slot_tdd
+
k
+
(
slotP
-
(
slotP
%
slots_per_tdd
));
sched_pucch
->
timing_indicator
=
i
;
curr_pucch
->
timing_indicator
=
i
;
// index in the list of timing indicators
return
;
return
;
}
}
}
}
if
(
curr_pucch
->
dai_c
==
MAX_ACK_BITS
||
found
==
0
)
{
// if current pucch is full or no timing indicator allowed
// look for pucch occasions in other UL of mixed slots
for
(
k
=
scc
->
tdd_UL_DL_ConfigurationCommon
->
pattern1
.
nrofDownlinkSlots
;
k
<
slots_per_tdd
;
k
++
)
{
// for each possible UL or mixed slot
if
(
k
!=
(
curr_pucch
->
ul_slot
%
slots_per_tdd
))
{
// skip current scheduled slot (already checked)
i
=
0
;
while
(
i
<
8
&&
found
==
0
)
{
// look if timing indicator is among allowed values
if
(
pdsch_to_harq_feedback
[
i
]
==
k
-
(
slotP
%
slots_per_tdd
))
found
=
1
;
if
(
found
==
0
)
i
++
;
}
if
(
found
==
1
)
{
if
(
k
<
(
curr_pucch
->
ul_slot
%
slots_per_tdd
))
{
// we need to add a pucch occasion before current pucch
sched_pucch
->
frame
=
frameP
;
sched_pucch
->
ul_slot
=
k
+
(
slotP
-
(
slotP
%
slots_per_tdd
));
sched_pucch
->
next_sched_pucch
=
curr_pucch
;
sched_pucch
->
dai_c
=
1
;
sched_pucch
->
resource_indicator
=
0
;
// in phytest with only 1 UE we are using just the 1st resource
sched_pucch
->
timing_indicator
=
i
;
UE_list
->
UE_sched_ctrl
[
UE_id
].
sched_pucch
=
sched_pucch
;
}
else
{
while
(
curr_pucch
->
next_sched_pucch
!=
NULL
&&
k
!=
(
curr_pucch
->
ul_slot
%
slots_per_tdd
))
curr_pucch
=
curr_pucch
->
next_sched_pucch
;
if
(
curr_pucch
==
NULL
)
{
// creating a new item in the list
sched_pucch
->
frame
=
frameP
;
sched_pucch
->
next_sched_pucch
=
NULL
;
sched_pucch
->
dai_c
=
1
;
sched_pucch
->
timing_indicator
=
i
;
sched_pucch
->
resource_indicator
=
0
;
// in phytest with only 1 UE we are using just the 1st resource
sched_pucch
->
ul_slot
=
k
+
(
slotP
-
(
slotP
%
slots_per_tdd
));
curr_pucch
->
next_sched_pucch
=
(
NR_sched_pucch
*
)
malloc
(
sizeof
(
NR_sched_pucch
));
curr_pucch
->
next_sched_pucch
=
sched_pucch
;
}
else
{
if
(
curr_pucch
->
dai_c
==
MAX_ACK_BITS
)
found
=
0
;
// if pucch at index k is already full we have to find a new one in a following occasion
else
{
// scheduling this harq-ack in current pucch
sched_pucch
=
curr_pucch
;
sched_pucch
->
dai_c
=
1
+
sched_pucch
->
dai_c
;
sched_pucch
->
timing_indicator
=
i
;
}
}
}
}
}
}
}
}
}
AssertFatal
(
1
==
0
,
"No Uplink slot available in accordance to allowed timing indicator
\n
"
);
}
}
void
find_monitoring_periodicity_offset_common
(
NR_SearchSpace_t
*
ss
,
void
find_monitoring_periodicity_offset_common
(
NR_SearchSpace_t
*
ss
,
uint16_t
*
slot_period
,
uint16_t
*
slot_period
,
uint16_t
*
offset
)
{
uint16_t
*
offset
)
{
...
...
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
View file @
b0ff2cef
...
@@ -157,7 +157,7 @@ void nr_update_pucch_scheduling(int Mod_idP,
...
@@ -157,7 +157,7 @@ void nr_update_pucch_scheduling(int Mod_idP,
frame_t
frameP
,
frame_t
frameP
,
sub_frame_t
slotP
,
sub_frame_t
slotP
,
int
slots_per_tdd
,
int
slots_per_tdd
,
NR_sched_pucch
*
sched
_pucch
);
NR_sched_pucch
*
curr
_pucch
);
void
get_pdsch_to_harq_feedback
(
int
Mod_idP
,
void
get_pdsch_to_harq_feedback
(
int
Mod_idP
,
int
UE_id
,
int
UE_id
,
...
...
openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
View file @
b0ff2cef
...
@@ -185,7 +185,6 @@ typedef struct NR_sched_pucch {
...
@@ -185,7 +185,6 @@ typedef struct NR_sched_pucch {
uint8_t
dai_c
;
uint8_t
dai_c
;
uint8_t
timing_indicator
;
uint8_t
timing_indicator
;
uint8_t
resource_indicator
;
uint8_t
resource_indicator
;
struct
NR_sched_pucch
*
next_sched_pucch
;
}
NR_sched_pucch
;
}
NR_sched_pucch
;
typedef
struct
NR_UE_harq
{
typedef
struct
NR_UE_harq
{
...
...
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