Commit 5217b764 authored by zhenghuangkun's avatar zhenghuangkun Committed by Noboru Kobayashi

Add DownlinkNASTransport Message

Add UEContextReleaseCommand Message
Modify NGSetup Message Encode failed
parent 3c84c4d8
...@@ -49,6 +49,7 @@ typedef enum { ...@@ -49,6 +49,7 @@ typedef enum {
MSC_RLC_ENB, MSC_RLC_ENB,
MSC_PDCP_ENB, MSC_PDCP_ENB,
MSC_RRC_ENB, MSC_RRC_ENB,
MSC_RRC_GNB,
MSC_IP_ENB, MSC_IP_ENB,
MSC_S1AP_ENB, MSC_S1AP_ENB,
MSC_NGAP_GNB, MSC_NGAP_GNB,
......
...@@ -410,6 +410,9 @@ typedef struct ngap_register_gnb_req_s { ...@@ -410,6 +410,9 @@ typedef struct ngap_register_gnb_req_s {
uint8_t mnc_digit_length[PLMN_LIST_MAX_SIZE]; uint8_t mnc_digit_length[PLMN_LIST_MAX_SIZE];
uint8_t num_plmn; uint8_t num_plmn;
uint16_t num_nssai[PLMN_LIST_MAX_SIZE];
ngap_allowed_NSSAI_t s_nssai[PLMN_LIST_MAX_SIZE][8];
/* Default Paging DRX of the gNB as defined in TS 36.304 */ /* Default Paging DRX of the gNB as defined in TS 36.304 */
ngap_paging_drx_t default_drx; ngap_paging_drx_t default_drx;
......
...@@ -145,7 +145,7 @@ typedef struct ccparams_nr_x2 { ...@@ -145,7 +145,7 @@ typedef struct ccparams_nr_x2 {
{GNB_CONFIG_STRING_GNB_ID, NULL, 0, uptr:NULL, defintval:0, TYPE_UINT, 0}, \ {GNB_CONFIG_STRING_GNB_ID, NULL, 0, uptr:NULL, defintval:0, TYPE_UINT, 0}, \
{GNB_CONFIG_STRING_CELL_TYPE, NULL, 0, strptr:NULL, defstrval:"CELL_MACRO_GNB", TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_CELL_TYPE, NULL, 0, strptr:NULL, defstrval:"CELL_MACRO_GNB", TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_GNB_NAME, NULL, 0, strptr:NULL, defstrval:"OAIgNodeB", TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_GNB_NAME, NULL, 0, strptr:NULL, defstrval:"OAIgNodeB", TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_TRACKING_AREA_CODE, NULL, 0, strptr:NULL, defstrval:"0", TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_TRACKING_AREA_CODE, NULL, 0, uptr:NULL, defuintval:0, TYPE_UINT, 0}, \
{GNB_CONFIG_STRING_MOBILE_COUNTRY_CODE_OLD, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_MOBILE_COUNTRY_CODE_OLD, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_MOBILE_NETWORK_CODE_OLD, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_MOBILE_NETWORK_CODE_OLD, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \
{GNB_CONFIG_STRING_TRANSPORT_S_PREFERENCE, NULL, 0, strptr:NULL, defstrval:"local_mac", TYPE_STRING, 0}, \ {GNB_CONFIG_STRING_TRANSPORT_S_PREFERENCE, NULL, 0, strptr:NULL, defstrval:"local_mac", TYPE_STRING, 0}, \
......
...@@ -202,10 +202,25 @@ void ngap_gNB_handle_register_gNB(instance_t instance, ngap_register_gnb_req_t * ...@@ -202,10 +202,25 @@ void ngap_gNB_handle_register_gNB(instance_t instance, ngap_register_gnb_req_t *
new_instance->mcc[i] = ngap_register_gNB->mcc[i]; new_instance->mcc[i] = ngap_register_gNB->mcc[i];
new_instance->mnc[i] = ngap_register_gNB->mnc[i]; new_instance->mnc[i] = ngap_register_gNB->mnc[i];
new_instance->mnc_digit_length[i] = ngap_register_gNB->mnc_digit_length[i]; new_instance->mnc_digit_length[i] = ngap_register_gNB->mnc_digit_length[i];
new_instance->num_nssai[i] = ngap_register_gNB->num_nssai[i];
} }
new_instance->num_plmn = ngap_register_gNB->num_plmn; new_instance->num_plmn = ngap_register_gNB->num_plmn;
new_instance->default_drx = ngap_register_gNB->default_drx; new_instance->default_drx = ngap_register_gNB->default_drx;
memcpy(new_instance->s_nssai, ngap_register_gNB->s_nssai, sizeof(ngap_register_gNB->s_nssai));
// config add? TBD
if(1) {
new_instance->num_nssai[0] = 1;
new_instance->s_nssai[0][0].sST = 1;
new_instance->s_nssai[0][0].sD_flag = 1;
new_instance->s_nssai[0][0].sD[0] = 1;
new_instance->s_nssai[0][0].sD[1] = 2;
new_instance->s_nssai[0][0].sD[2] = 3;
}
/* Add the new instance to the list of gNB (meaningfull in virtual mode) */ /* Add the new instance to the list of gNB (meaningfull in virtual mode) */
ngap_gNB_insert_new_instance(new_instance); ngap_gNB_insert_new_instance(new_instance);
NGAP_INFO("Registered new gNB[%d] and %s gNB id %u\n", NGAP_INFO("Registered new gNB[%d] and %s gNB id %u\n",
...@@ -489,7 +504,6 @@ static int ngap_gNB_generate_ng_setup_request( ...@@ -489,7 +504,6 @@ static int ngap_gNB_generate_ng_setup_request(
ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
} }
#if 0
/* mandatory */ /* mandatory */
ie = (NGAP_NGSetupRequestIEs_t *)calloc(1, sizeof(NGAP_NGSetupRequestIEs_t)); ie = (NGAP_NGSetupRequestIEs_t *)calloc(1, sizeof(NGAP_NGSetupRequestIEs_t));
ie->id = NGAP_ProtocolIE_ID_id_SupportedTAList; ie->id = NGAP_ProtocolIE_ID_id_SupportedTAList;
...@@ -497,7 +511,7 @@ static int ngap_gNB_generate_ng_setup_request( ...@@ -497,7 +511,7 @@ static int ngap_gNB_generate_ng_setup_request(
ie->value.present = NGAP_NGSetupRequestIEs__value_PR_SupportedTAList; ie->value.present = NGAP_NGSetupRequestIEs__value_PR_SupportedTAList;
{ {
ta = (NGAP_SupportedTAItem_t *)calloc(1, sizeof(NGAP_SupportedTAItem_t)); ta = (NGAP_SupportedTAItem_t *)calloc(1, sizeof(NGAP_SupportedTAItem_t));
INT16_TO_OCTET_STRING(instance_p->tac, &ta->tAC); INT24_TO_OCTET_STRING(instance_p->tac, &ta->tAC);
{ {
for (int i = 0; i < ngap_amf_data_p->broadcast_plmn_num; ++i) { for (int i = 0; i < ngap_amf_data_p->broadcast_plmn_num; ++i) {
plmn = (NGAP_BroadcastPLMNItem_t *)calloc(1, sizeof(NGAP_BroadcastPLMNItem_t)); plmn = (NGAP_BroadcastPLMNItem_t *)calloc(1, sizeof(NGAP_BroadcastPLMNItem_t));
...@@ -530,7 +544,6 @@ static int ngap_gNB_generate_ng_setup_request( ...@@ -530,7 +544,6 @@ static int ngap_gNB_generate_ng_setup_request(
} }
ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie); ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
#endif
/* mandatory */ /* mandatory */
ie = (NGAP_NGSetupRequestIEs_t *)calloc(1, sizeof(NGAP_NGSetupRequestIEs_t)); ie = (NGAP_NGSetupRequestIEs_t *)calloc(1, sizeof(NGAP_NGSetupRequestIEs_t));
ie->id = NGAP_ProtocolIE_ID_id_DefaultPagingDRX; ie->id = NGAP_ProtocolIE_ID_id_DefaultPagingDRX;
......
...@@ -262,9 +262,9 @@ typedef struct ngap_gNB_instance_s { ...@@ -262,9 +262,9 @@ typedef struct ngap_gNB_instance_s {
uint8_t num_plmn; uint8_t num_plmn;
uint16_t num_nssai[PLMN_LIST_MAX_SIZE]; uint16_t num_nssai[PLMN_LIST_MAX_SIZE];
ngap_gNB_NSSAI_t s_nssai[PLMN_LIST_MAX_SIZE][1024]; ngap_gNB_NSSAI_t s_nssai[PLMN_LIST_MAX_SIZE][8];
/* Default Paging DRX of the gNB as defined in TS 36.304 */ /* Default Paging DRX of the gNB as defined in TS 38.304 */
ngap_paging_drx_t default_drx; ngap_paging_drx_t default_drx;
} ngap_gNB_instance_t; } ngap_gNB_instance_t;
......
...@@ -1113,12 +1113,12 @@ static ...@@ -1113,12 +1113,12 @@ static
int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id, int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id,
uint32_t stream, uint32_t stream,
NGAP_NGAP_PDU_t *pdu) { NGAP_NGAP_PDU_t *pdu) {
#if 0
ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_amf_data_t *amf_desc_p = NULL;
ngap_gNB_ue_context_t *ue_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL;
MessageDef *message_p = NULL; MessageDef *message_p = NULL;
NGAP_AMF_UE_NGAP_ID_t amf_ue_ngap_id; uint64_t amf_ue_ngap_id;
NGAP_GNB_UE_NGAP_ID_t enb_ue_ngap_id; NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id;
NGAP_UEContextReleaseCommand_t *container; NGAP_UEContextReleaseCommand_t *container;
NGAP_UEContextReleaseCommand_IEs_t *ie; NGAP_UEContextReleaseCommand_IEs_t *ie;
DevAssert(pdu != NULL); DevAssert(pdu != NULL);
...@@ -1136,23 +1136,23 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id, ...@@ -1136,23 +1136,23 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id,
if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */
switch (ie->value.choice.UE_NGAP_IDs.present) { switch (ie->value.choice.UE_NGAP_IDs.present) {
case NGAP_UE_NGAP_IDs_PR_uE_NGAP_ID_pair: case NGAP_UE_NGAP_IDs_PR_uE_NGAP_ID_pair:
enb_ue_ngap_id = ie->value.choice.UE_NGAP_IDs.choice.uE_NGAP_ID_pair.gNB_UE_NGAP_ID; gnb_ue_ngap_id = ie->value.choice.UE_NGAP_IDs.choice.uE_NGAP_ID_pair.rAN_UE_NGAP_ID;
amf_ue_ngap_id = ie->value.choice.UE_NGAP_IDs.choice.uE_NGAP_ID_pair.mME_UE_NGAP_ID; asn_INTEGER2ulong(&(ie->value.choice.UE_NGAP_IDs.choice.uE_NGAP_ID_pair.aMF_UE_NGAP_ID), &amf_ue_ngap_id);
MSC_LOG_RX_MESSAGE( MSC_LOG_RX_MESSAGE(
MSC_NGAP_GNB, MSC_NGAP_GNB,
MSC_NGAP_AMF, MSC_NGAP_AMF,
NULL,0, NULL,0,
"0 UEContextRelease/%s gNB_ue_ngap_id "NGAP_UE_ID_FMT" amf_ue_ngap_id "NGAP_UE_ID_FMT" len %u", "0 UEContextRelease/%s gNB_ue_ngap_id "NGAP_UE_ID_FMT" amf_ue_ngap_id "NGAP_UE_ID_FMT" len %u",
ngap_direction2String(pdu->present - 1), ngap_direction2String(pdu->present - 1),
enb_ue_ngap_id, gnb_ue_ngap_id,
amf_ue_ngap_id); amf_ue_ngap_id);
if ((ue_desc_p = ngap_gNB_get_ue_context(amf_desc_p->ngap_gNB_instance, if ((ue_desc_p = ngap_gNB_get_ue_context(amf_desc_p->ngap_gNB_instance,
enb_ue_ngap_id)) == NULL) { gnb_ue_ngap_id)) == NULL) {
NGAP_ERROR("[SCTP %d] Received UE context release command for non " NGAP_ERROR("[SCTP %d] Received UE context release command for non "
"existing UE context 0x%06lx\n", "existing UE context 0x%06lx\n",
assoc_id, assoc_id,
enb_ue_ngap_id); gnb_ue_ngap_id);
return -1; return -1;
} else { } else {
MSC_LOG_TX_MESSAGE( MSC_LOG_TX_MESSAGE(
...@@ -1160,25 +1160,25 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id, ...@@ -1160,25 +1160,25 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id,
MSC_RRC_GNB, MSC_RRC_GNB,
NULL,0, NULL,0,
"0 NGAP_UE_CONTEXT_RELEASE_COMMAND/%d gNB_ue_ngap_id "NGAP_UE_ID_FMT" ", "0 NGAP_UE_CONTEXT_RELEASE_COMMAND/%d gNB_ue_ngap_id "NGAP_UE_ID_FMT" ",
enb_ue_ngap_id); gnb_ue_ngap_id);
message_p = itti_alloc_new_message(TASK_NGAP, NGAP_UE_CONTEXT_RELEASE_COMMAND); message_p = itti_alloc_new_message(TASK_NGAP, NGAP_UE_CONTEXT_RELEASE_COMMAND);
if (ue_desc_p->amf_ue_ngap_id == 0) { // case of Detach Request and switch off from RRC_IDLE mode if (ue_desc_p->amf_ue_ngap_id == 0) { // case of Detach Request and switch off from RRC_IDLE mode
ue_desc_p->amf_ue_ngap_id = amf_ue_ngap_id; ue_desc_p->amf_ue_ngap_id = amf_ue_ngap_id;
} }
NGAP_UE_CONTEXT_RELEASE_COMMAND(message_p).gNB_ue_ngap_id = enb_ue_ngap_id; NGAP_UE_CONTEXT_RELEASE_COMMAND(message_p).gNB_ue_ngap_id = gnb_ue_ngap_id;
itti_send_msg_to_task(TASK_RRC_GNB, ue_desc_p->gNB_instance->instance, message_p); itti_send_msg_to_task(TASK_RRC_GNB, ue_desc_p->gNB_instance->instance, message_p);
return 0; return 0;
} }
break; break;
//#warning "TODO mapping amf_ue_ngap_id enb_ue_ngap_id?" //#warning "TODO mapping amf_ue_ngap_id gnb_ue_ngap_id?"
case NGAP_UE_NGAP_IDs_PR_mME_UE_NGAP_ID: case NGAP_UE_NGAP_IDs_PR_aMF_UE_NGAP_ID:
amf_ue_ngap_id = ie->value.choice.UE_NGAP_IDs.choice.uE_NGAP_ID_pair.mME_UE_NGAP_ID; asn_INTEGER2ulong(&(ie->value.choice.UE_NGAP_IDs.choice.aMF_UE_NGAP_ID), &amf_ue_ngap_id);
NGAP_ERROR("TO DO mapping amf_ue_ngap_id enb_ue_ngap_id"); NGAP_ERROR("TO DO mapping amf_ue_ngap_id gnb_ue_ngap_id");
(void)amf_ue_ngap_id; /* TODO: remove - it's to remove gcc warning about unused var */ (void)amf_ue_ngap_id; /* TODO: remove - it's to remove gcc warning about unused var */
case NGAP_UE_NGAP_IDs_PR_NOTHING: case NGAP_UE_NGAP_IDs_PR_NOTHING:
...@@ -1193,7 +1193,7 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id, ...@@ -1193,7 +1193,7 @@ int ngap_gNB_handle_ue_context_release_command(uint32_t assoc_id,
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_UEContextReleaseCommand_IEs_t, ie, container, NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_UEContextReleaseCommand_IEs_t, ie, container,
NGAP_ProtocolIE_ID_id_Cause, true); NGAP_ProtocolIE_ID_id_Cause, true);
/* TBD */ /* TBD */
#endif
return 0; return 0;
} }
......
...@@ -324,14 +324,14 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id, ...@@ -324,14 +324,14 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id,
NGAP_NGAP_PDU_t *pdu) NGAP_NGAP_PDU_t *pdu)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
{ {
#if 0
ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_amf_data_t *amf_desc_p = NULL;
ngap_gNB_ue_context_t *ue_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL;
ngap_gNB_instance_t *ngap_gNB_instance = NULL; ngap_gNB_instance_t *ngap_gNB_instance = NULL;
NGAP_DownlinkNASTransport_t *container; NGAP_DownlinkNASTransport_t *container;
NGAP_DownlinkNASTransport_IEs_t *ie; NGAP_DownlinkNASTransport_IEs_t *ie;
NGAP_GNB_UE_NGAP_ID_t enb_ue_ngap_id; NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id;
NGAP_AMF_UE_NGAP_ID_t amf_ue_ngap_id; uint64_t amf_ue_ngap_id;
DevAssert(pdu != NULL); DevAssert(pdu != NULL);
/* UE-related procedure -> stream != 0 */ /* UE-related procedure -> stream != 0 */
...@@ -353,25 +353,26 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id, ...@@ -353,25 +353,26 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id,
container = &pdu->choice.initiatingMessage.value.choice.DownlinkNASTransport; container = &pdu->choice.initiatingMessage.value.choice.DownlinkNASTransport;
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container, NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true); NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
amf_ue_ngap_id = ie->value.choice.AMF_UE_NGAP_ID; asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &amf_ue_ngap_id);
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container, NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID, true); NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
enb_ue_ngap_id = ie->value.choice.GNB_UE_NGAP_ID; gnb_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
if ((ue_desc_p = ngap_gNB_get_ue_context(ngap_gNB_instance, if ((ue_desc_p = ngap_gNB_get_ue_context(ngap_gNB_instance,
enb_ue_ngap_id)) == NULL) { gnb_ue_ngap_id)) == NULL) {
MSC_LOG_RX_DISCARDED_MESSAGE( MSC_LOG_RX_DISCARDED_MESSAGE(
MSC_NGAP_GNB, MSC_NGAP_GNB,
MSC_NGAP_AMF, MSC_NGAP_AMF,
NULL, NULL,
0, 0,
MSC_AS_TIME_FMT" downlinkNASTransport gNB_ue_ngap_id %u amf_ue_ngap_id %u", MSC_AS_TIME_FMT" downlinkNASTransport gNB_ue_ngap_id %u amf_ue_ngap_id %u",
enb_ue_ngap_id, gnb_ue_ngap_id,
amf_ue_ngap_id); amf_ue_ngap_id);
NGAP_ERROR("[SCTP %d] Received NAS downlink message for non existing UE context gNB_UE_NGAP_ID: 0x%lx\n", NGAP_ERROR("[SCTP %d] Received NAS downlink message for non existing UE context gNB_UE_NGAP_ID: 0x%lx\n",
assoc_id, assoc_id,
enb_ue_ngap_id); gnb_ue_ngap_id);
return -1; return -1;
} }
...@@ -391,10 +392,10 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id, ...@@ -391,10 +392,10 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id,
} else { } else {
/* We already have a amf ue ngap id check the received is the same */ /* We already have a amf ue ngap id check the received is the same */
if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id) { if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id) {
NGAP_ERROR("[SCTP %d] Mismatch in AMF UE NGAP ID (0x%lx != 0x%"PRIx32"\n", NGAP_ERROR("[SCTP %d] Mismatch in AMF UE NGAP ID (0x%lx != 0x%"PRIx64"\n",
assoc_id, assoc_id,
amf_ue_ngap_id, amf_ue_ngap_id,
ue_desc_p->amf_ue_ngap_id (uint64_t)ue_desc_p->amf_ue_ngap_id
); );
return -1; return -1;
} }
...@@ -411,13 +412,13 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id, ...@@ -411,13 +412,13 @@ int ngap_gNB_handle_nas_downlink(uint32_t assoc_id,
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container, NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
NGAP_ProtocolIE_ID_id_NAS_PDU, true); NGAP_ProtocolIE_ID_id_NAS_PDU, true);
/* Forward the NAS PDU to RRC */ /* Forward the NAS PDU to NR-RRC */
ngap_gNB_itti_send_nas_downlink_ind(ngap_gNB_instance->instance, ngap_gNB_itti_send_nas_downlink_ind(ngap_gNB_instance->instance,
ue_desc_p->ue_initial_id, ue_desc_p->ue_initial_id,
ue_desc_p->gNB_ue_ngap_id, ue_desc_p->gNB_ue_ngap_id,
ie->value.choice.NAS_PDU.buf, ie->value.choice.NAS_PDU.buf,
ie->value.choice.NAS_PDU.size); ie->value.choice.NAS_PDU.size);
#endif
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment