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
lizhongxiao
OpenXG-RAN
Commits
7b6290db
Commit
7b6290db
authored
May 11, 2023
by
Vaibhav Shrivastava
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Compilatior issue fix for gNB and nrUE
parent
e1505102
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
236 additions
and
4 deletions
+236
-4
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
+1
-4
openair2/RRC/NR/MESSAGES/asn1_msg.c
openair2/RRC/NR/MESSAGES/asn1_msg.c
+105
-0
openair2/RRC/NR/rrc_gNB.c
openair2/RRC/NR/rrc_gNB.c
+128
-0
openair2/RRC/NR_UE/rrc_UE.c
openair2/RRC/NR_UE/rrc_UE.c
+2
-0
No files found.
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c
View file @
7b6290db
...
...
@@ -1685,10 +1685,7 @@ void schedule_nr_PCH(module_id_t module_idP,
get_info_from_tda_tables
(
type0_PDCCH_CSS_config
->
type0_pdcch_ss_mux_pattern
,
time_domain_allocation
,
gNB_mac
->
common_channels
->
ServingCellConfigCommon
->
dmrs_TypeA_Position
,
1
,
&
is_typeA
,
&
startSymbolIndex
,
&
nrOfSymbols
);
1
);
NR_tda_info_t
tda_info
=
{
.
mapping_type
=
is_typeA
?
typeA
:
typeB
,
...
...
openair2/RRC/NR/MESSAGES/asn1_msg.c
View file @
7b6290db
...
...
@@ -687,6 +687,85 @@ int do_RRCReject(uint8_t Mod_id,
return
(
enc_rval
.
encoded
+
7
)
/
8
;
}
NR_RLC_Config_t
*
fill_rb_rlc_config
(
NR_RLC_Config_t
*
ss_rlc_config
)
{
NR_RLC_Config_t
*
rlc_Config
=
NULL
;
rlc_Config
=
calloc
(
1
,
sizeof
(
NR_RLC_Config_t
));
if
(
ss_rlc_config
){
rlc_Config
->
present
=
ss_rlc_config
->
present
;
if
(
rlc_Config
->
present
==
NR_RLC_Config_PR_am
){
rlc_Config
->
choice
.
am
=
calloc
(
1
,
sizeof
(
*
rlc_Config
->
choice
.
am
));
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthAM_t
));
if
(
ss_rlc_config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
)
=*
(
ss_rlc_config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthAM_size12
;
}
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
t_Reassembly
=
ss_rlc_config
->
choice
.
am
->
dl_AM_RLC
.
t_Reassembly
;
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
t_StatusProhibit
=
ss_rlc_config
->
choice
.
am
->
dl_AM_RLC
.
t_StatusProhibit
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthAM_t
));
if
(
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
)
=*
(
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthAM_size12
;
}
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
t_PollRetransmit
=
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
t_PollRetransmit
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
pollPDU
=
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
pollPDU
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
pollByte
=
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
pollByte
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
maxRetxThreshold
=
ss_rlc_config
->
choice
.
am
->
ul_AM_RLC
.
maxRetxThreshold
;
}
else
if
(
rlc_Config
->
present
==
NR_RLC_Config_PR_um_Bi_Directional
){
rlc_Config
->
choice
.
um_Bi_Directional
=
calloc
(
1
,
sizeof
(
*
rlc_Config
->
choice
.
um_Bi_Directional
));
rlc_Config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthUM_t
));
if
(
ss_rlc_config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
sn_FieldLength
)
=
*
(
ss_rlc_config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthUM_size12
;
}
rlc_Config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
t_Reassembly
=
ss_rlc_config
->
choice
.
um_Bi_Directional
->
dl_UM_RLC
.
t_Reassembly
;
rlc_Config
->
choice
.
um_Bi_Directional
->
ul_UM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthUM_t
));
if
(
ss_rlc_config
->
choice
.
um_Bi_Directional
->
ul_UM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
um_Bi_Directional
->
ul_UM_RLC
.
sn_FieldLength
)
=
*
(
ss_rlc_config
->
choice
.
um_Bi_Directional
->
ul_UM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
um_Bi_Directional
->
ul_UM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthUM_size12
;
}
}
else
if
(
rlc_Config
->
present
==
NR_RLC_Config_PR_um_Uni_Directional_DL
){
rlc_Config
->
choice
.
um_Uni_Directional_DL
=
calloc
(
1
,
sizeof
(
*
rlc_Config
->
choice
.
um_Uni_Directional_DL
));
if
(
ss_rlc_config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
sn_FieldLength
)
=
*
(
ss_rlc_config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthUM_size12
;
}
rlc_Config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
t_Reassembly
=
ss_rlc_config
->
choice
.
um_Uni_Directional_DL
->
dl_UM_RLC
.
t_Reassembly
;
}
else
if
(
rlc_Config
->
present
==
NR_RLC_Config_PR_um_Uni_Directional_UL
){
rlc_Config
->
choice
.
um_Uni_Directional_UL
=
calloc
(
1
,
sizeof
(
*
rlc_Config
->
choice
.
um_Uni_Directional_UL
));
rlc_Config
->
choice
.
um_Uni_Directional_UL
->
ul_UM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthUM_t
));
if
(
ss_rlc_config
->
choice
.
um_Uni_Directional_UL
->
ul_UM_RLC
.
sn_FieldLength
){
*
(
rlc_Config
->
choice
.
um_Uni_Directional_UL
->
ul_UM_RLC
.
sn_FieldLength
)
=
*
(
ss_rlc_config
->
choice
.
um_Uni_Directional_UL
->
ul_UM_RLC
.
sn_FieldLength
);
}
else
{
*
(
rlc_Config
->
choice
.
um_Uni_Directional_UL
->
ul_UM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthUM_size12
;
}
}
}
else
{
rlc_Config
->
present
=
NR_RLC_Config_PR_am
;
rlc_Config
->
choice
.
am
=
calloc
(
1
,
sizeof
(
*
rlc_Config
->
choice
.
am
));
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthAM_t
));
*
(
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthAM_size12
;
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
t_Reassembly
=
NR_T_Reassembly_ms35
;
rlc_Config
->
choice
.
am
->
dl_AM_RLC
.
t_StatusProhibit
=
NR_T_StatusProhibit_ms0
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
=
calloc
(
1
,
sizeof
(
NR_SN_FieldLengthAM_t
));
*
(
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
sn_FieldLength
)
=
NR_SN_FieldLengthAM_size12
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
t_PollRetransmit
=
NR_T_PollRetransmit_ms45
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
pollPDU
=
NR_PollPDU_infinity
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
pollByte
=
NR_PollByte_infinity
;
rlc_Config
->
choice
.
am
->
ul_AM_RLC
.
maxRetxThreshold
=
NR_UL_AM_RLC__maxRetxThreshold_t8
;
}
return
rlc_Config
;
}
void
fill_initial_SpCellConfig
(
int
uid
,
NR_SpCellConfig_t
*
SpCellConfig
,
const
NR_ServingCellConfigCommon_t
*
scc
,
...
...
@@ -1203,6 +1282,32 @@ void fill_initial_cellGroupConfig(int uid,
cellGroupConfig
->
sCellToReleaseList
=
NULL
;
}
bool
update_rrcReconfig_cellGroupConfig
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
rrc_gNB_ue_context_t
*
ue_context_pP
,
NR_CellGroupConfig_t
*
cellGroupConfig
)
{
int
CC_id
=
ue_context_pP
->
ue_context
.
primaryCC_id
;
bool
update_flag
=
false
;
if
(
RC
.
ss
.
mode
<
SS_SOFTMODEM
){
return
update_flag
;
}
if
(
cellGroupConfig
&&
cellGroupConfig
->
rlc_BearerToAddModList
){
for
(
int
i
=
0
;
i
<
cellGroupConfig
->
rlc_BearerToAddModList
->
list
.
count
;
i
++
){
NR_RLC_BearerConfig_t
*
rlc_BearerConfig
=
cellGroupConfig
->
rlc_BearerToAddModList
->
list
.
array
[
i
];
/* only check rlc_config as it is missing from TTCN */
if
(
!
rlc_BearerConfig
->
rlc_Config
&&
rlc_BearerConfig
->
servedRadioBearer
){
int
rbIndex
=
(
rlc_BearerConfig
->
servedRadioBearer
->
present
==
NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity
)
?
rlc_BearerConfig
->
servedRadioBearer
->
choice
.
srb_Identity
:
(
rlc_BearerConfig
->
servedRadioBearer
->
choice
.
drb_Identity
+
2
);
if
(
RC
.
NR_RB_Config
[
CC_id
][
rbIndex
].
isRBConfigValid
&&
RC
.
NR_RB_Config
[
CC_id
][
rbIndex
].
RlcBearer
){
rlc_BearerConfig
->
rlc_Config
=
fill_rb_rlc_config
(
RC
.
NR_RB_Config
[
CC_id
][
rbIndex
].
RlcBearer
->
rlc_Config
);
update_flag
=
true
;
}
}
}
}
return
update_flag
;
}
//------------------------------------------------------------------------------
int
do_RRCSetup
(
rrc_gNB_ue_context_t
*
const
ue_context_pP
,
uint8_t
*
const
buffer
,
...
...
openair2/RRC/NR/rrc_gNB.c
View file @
7b6290db
...
...
@@ -97,6 +97,7 @@
#include "openair2/F1AP/f1ap_common.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
#include "cucp_cuup_if.h"
#include "PHY/defs_gNB.h"
#include "BIT_STRING.h"
#include "assertions.h"
...
...
@@ -844,6 +845,133 @@ static void rrc_gNB_generate_defaultRRCReconfiguration(const protocol_ctxt_t *co
}
}
//-----------------------------------------------------------------------------
/*
* store rrcReconfiguration config in UE context
*/
void
rrc_gNB_store_RRCReconfiguration
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
rrc_gNB_ue_context_t
*
ue_context_pP
,
NR_RRCReconfiguration_t
*
rrcReconfiguration
)
//-----------------------------------------------------------------------------
{
NR_SRB_ToAddModList_t
**
srb2addList
=
NULL
;
NR_DRB_ToAddModList_t
**
drb2addList
=
NULL
;
NR_DRB_ToAddModList_t
**
drb2releaseList
=
NULL
;
NR_CellGroupConfig_t
*
cellGroupConfig
=
NULL
;
long
xid
;
AssertFatal
(
rrcReconfiguration
!=
NULL
,
"%s rrcReconfiguration is NULL
\n
"
,
__FUNCTION__
);
xid
=
rrcReconfiguration
->
rrc_TransactionIdentifier
;
LOG_D
(
NR_RRC
,
"rrc_gNB_store_RRCReconfiguration for transaction %ld
\n
"
,
xid
);
srb2addList
=
&
ue_context_pP
->
ue_context
.
SRB_configList2
[
xid
];
drb2addList
=
&
ue_context_pP
->
ue_context
.
DRB_configList2
[
xid
];
drb2releaseList
=
&
ue_context_pP
->
ue_context
.
DRB_Release_configList2
[
xid
];
if
(
*
srb2addList
){
LOG_D
(
NR_RRC
,
"%s free old SRB_configList2
\n
"
,
__FUNCTION__
);
ASN_STRUCT_FREE
(
asn_DEF_NR_SRB_ToAddModList
,
*
srb2addList
);
*
srb2addList
=
NULL
;
}
if
(
*
drb2addList
){
LOG_D
(
NR_RRC
,
"%s free old DRB_configList2
\n
"
,
__FUNCTION__
);
ASN_STRUCT_FREE
(
asn_DEF_NR_DRB_ToAddModList
,
*
drb2addList
);
*
drb2addList
=
NULL
;
}
if
(
*
drb2releaseList
){
LOG_D
(
NR_RRC
,
"%s free old DRB_Release_configList2
\n
"
,
__FUNCTION__
);
ASN_STRUCT_FREE
(
asn_DEF_NR_DRB_ToAddModList
,
*
drb2releaseList
);
*
drb2releaseList
=
NULL
;
}
if
(
rrcReconfiguration
->
criticalExtensions
.
present
==
NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration
&&
rrcReconfiguration
->
criticalExtensions
.
choice
.
rrcReconfiguration
){
NR_RRCReconfiguration_IEs_t
*
ie
=
rrcReconfiguration
->
criticalExtensions
.
choice
.
rrcReconfiguration
;
if
(
ie
->
radioBearerConfig
){
if
(
ie
->
radioBearerConfig
->
srb_ToAddModList
){
LOG_D
(
NR_RRC
,
"%s store srb_ToAddModList
\n
"
,
__FUNCTION__
);
*
srb2addList
=
ie
->
radioBearerConfig
->
srb_ToAddModList
;
ie
->
radioBearerConfig
->
srb_ToAddModList
=
NULL
;
/* to avoid the content be released externally */
}
if
(
ie
->
radioBearerConfig
->
drb_ToAddModList
){
LOG_D
(
NR_RRC
,
"%s store drb_ToAddModList
\n
"
,
__FUNCTION__
);
*
drb2addList
=
ie
->
radioBearerConfig
->
drb_ToAddModList
;
ie
->
radioBearerConfig
->
drb_ToAddModList
=
NULL
;
/* to avoid the content be released externally*/
}
if
(
ie
->
radioBearerConfig
->
drb_ToReleaseList
){
LOG_D
(
NR_RRC
,
"%s store drb_ToReleaseList
\n
"
,
__FUNCTION__
);
*
drb2releaseList
=
ie
->
radioBearerConfig
->
drb_ToReleaseList
;
ie
->
radioBearerConfig
->
drb_ToReleaseList
=
NULL
;
/* to avoid the content be released externally*/
}
}
if
(
ie
->
nonCriticalExtension
){
if
(
ie
->
nonCriticalExtension
->
masterCellGroup
){
uper_decode
(
NULL
,
&
asn_DEF_NR_CellGroupConfig
,
(
void
**
)
&
cellGroupConfig
,
(
uint8_t
*
)
ie
->
nonCriticalExtension
->
masterCellGroup
->
buf
,
ie
->
nonCriticalExtension
->
masterCellGroup
->
size
,
0
,
0
);
if
(
LOG_DEBUGFLAG
(
DEBUG_ASN1
)
)
{
xer_fprint
(
stdout
,
&
asn_DEF_NR_CellGroupConfig
,
(
const
void
*
)
cellGroupConfig
);
}
if
(
cellGroupConfig
){
#if 1
NR_CellGroupConfig_t
*
masterCellGroup
=
ue_context_pP
->
ue_context
.
masterCellGroup
;
if
(
masterCellGroup
&&
cellGroupConfig
->
rlc_BearerToAddModList
){
/* we only care the added Bearer configuration here */
int
count
=
cellGroupConfig
->
rlc_BearerToAddModList
->
list
.
count
;
if
(
count
&&
masterCellGroup
->
rlc_BearerToAddModList
==
NULL
){
/* code would not get here */
masterCellGroup
->
rlc_BearerToAddModList
=
CALLOC
(
1
,
sizeof
(
struct
NR_CellGroupConfig__rlc_BearerToAddModList
));
}
LOG_D
(
NR_RRC
,
"%s add rlc_BearerConfig into UE masterCellGroup
\n
"
,
__FUNCTION__
);
for
(
int
i
=
0
;
i
<
count
;
i
++
){
ASN_SEQUENCE_ADD
(
&
masterCellGroup
->
rlc_BearerToAddModList
->
list
,
cellGroupConfig
->
rlc_BearerToAddModList
->
list
.
array
[
i
]);
}
if
(
LOG_DEBUGFLAG
(
DEBUG_ASN1
)
)
{
xer_fprint
(
stdout
,
&
asn_DEF_NR_CellGroupConfig
,
(
const
void
*
)
masterCellGroup
);
}
cellGroupConfig
->
rlc_BearerToAddModList
->
list
.
free
=
NULL
;
/*not free item pointer as it already added to another list */
asn_sequence_empty
(
&
cellGroupConfig
->
rlc_BearerToAddModList
->
list
);
}
ASN_STRUCT_FREE
(
asn_DEF_NR_CellGroupConfig
,
cellGroupConfig
);
#else
if
(
ue_context_pP
->
ue_context
.
masterCellGroup
){
/* There is issue to free masterCellGroup content here. */
ASN_STRUCT_FREE
(
asn_DEF_NR_CellGroupConfig
,
ue_context_pP
->
ue_context
.
masterCellGroup
);
}
LOG_D
(
NR_RRC
,
"%s store cellGroupConfig
\n
"
,
__FUNCTION__
);
ue_context_pP
->
ue_context
.
masterCellGroup
=
cellGroupConfig
;
#endif
}
}
}
}
gNB_RRC_INST
*
rrc
=
RC
.
nrrrc
[
ctxt_pP
->
module_id
];
if
(
NODE_IS_DU
(
rrc
->
node_type
)
||
NODE_IS_MONOLITHIC
(
rrc
->
node_type
))
{
gNB_RRC_UE_t
*
ue_p
=
&
ue_context_pP
->
ue_context
;
nr_mac_update_cellgroup
(
RC
.
nrmac
[
rrc
->
module_id
],
ue_p
->
rnti
,
ue_p
->
masterCellGroup
);
uint32_t
delay_ms
=
ue_context_pP
->
ue_context
.
masterCellGroup
&&
ue_context_pP
->
ue_context
.
masterCellGroup
->
spCellConfig
&&
ue_context_pP
->
ue_context
.
masterCellGroup
->
spCellConfig
->
spCellConfigDedicated
&&
ue_context_pP
->
ue_context
.
masterCellGroup
->
spCellConfig
->
spCellConfigDedicated
->
downlinkBWP_ToAddModList
?
NR_RRC_RECONFIGURATION_DELAY_MS
+
NR_RRC_BWP_SWITCHING_DELAY_MS
:
NR_RRC_RECONFIGURATION_DELAY_MS
;
nr_mac_enable_ue_rrc_processing_timer
(
ctxt_pP
->
module_id
,
ue_context_pP
->
ue_context
.
rnti
,
*
rrc
->
carrier
.
servingcellconfigcommon
->
ssbSubcarrierSpacing
,
delay_ms
);
}
}
void
fill_DRB_configList
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
rrc_gNB_ue_context_t
*
ue_context_pP
)
{
...
...
openair2/RRC/NR_UE/rrc_UE.c
View file @
7b6290db
...
...
@@ -91,6 +91,8 @@ static const char nr_nas_attach_req_imsi[] = {
0x01
,
0x27
,
0x11
,
};
static
size_t
nr_rrc_ue_RRCSetupRequest_count
=
0
;
extern
void
pdcp_config_set_security
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
pdcp_t
*
const
pdcp_pP
,
...
...
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