Introduce separate gNB-CU-UP ID in configuration
3GPP has the concept of a gNB-CU-UP ID and a (separate) gNB ID (for gNB, CU-CP). This commit introduces the gNB-CU-UP as a separate ID that has to be set in the configuration file when running as a CU-UP. For the CU/monolithic gNB, it is optional (but needs to be the same as the gNB-ID if specified). The CU-UP ID is necessary, as some entities, e.g., the e2 agent, need to signal both IDs, e.g., to reconcile a CU-CP and (multiple) CU-UP(s) belonging together.
This commit is contained in:
@@ -8,6 +8,7 @@ gNBs =
|
|||||||
{
|
{
|
||||||
////////// Identification parameters:
|
////////// Identification parameters:
|
||||||
gNB_ID = 0xe00;
|
gNB_ID = 0xe00;
|
||||||
|
gNB_CU_UP_ID = 0xe00;
|
||||||
|
|
||||||
# cell_type = "CELL_MACRO_GNB";
|
# cell_type = "CELL_MACRO_GNB";
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ gNBs =
|
|||||||
{
|
{
|
||||||
////////// Identification parameters:
|
////////// Identification parameters:
|
||||||
gNB_ID = 0xe00;
|
gNB_ID = 0xe00;
|
||||||
|
gNB_CU_UP_ID = 0xe00;
|
||||||
|
|
||||||
# cell_type = "CELL_MACRO_GNB";
|
# cell_type = "CELL_MACRO_GNB";
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ int main(int argc, char **argv)
|
|||||||
AssertFatal(rc >= 0, "Create task for CUUP E1 failed\n");
|
AssertFatal(rc >= 0, "Create task for CUUP E1 failed\n");
|
||||||
nr_pdcp_layer_init(true);
|
nr_pdcp_layer_init(true);
|
||||||
cu_init_f1_ue_data(); // for CU-UP/CP mapping: we use the same
|
cu_init_f1_ue_data(); // for CU-UP/CP mapping: we use the same
|
||||||
MessageDef *msg = RCconfig_NR_CU_E1(true);
|
E1_t e1type = UPtype;
|
||||||
|
MessageDef *msg = RCconfig_NR_CU_E1(&e1type);
|
||||||
AssertFatal(msg != NULL, "Send init to task for E1AP UP failed\n");
|
AssertFatal(msg != NULL, "Send init to task for E1AP UP failed\n");
|
||||||
itti_send_msg_to_task(TASK_CUUP_E1, 0, msg);
|
itti_send_msg_to_task(TASK_CUUP_E1, 0, msg);
|
||||||
|
|
||||||
|
|||||||
@@ -402,9 +402,9 @@ static int create_gNB_tasks(ngran_node_t node_type, configmodule_interface_t *cf
|
|||||||
|
|
||||||
// If CU
|
// If CU
|
||||||
if (node_type == ngran_gNB_CU || node_type == ngran_gNB) {
|
if (node_type == ngran_gNB_CU || node_type == ngran_gNB) {
|
||||||
MessageDef *msg = RCconfig_NR_CU_E1(false);
|
MessageDef *msg = RCconfig_NR_CU_E1(NULL);
|
||||||
instance_t inst = 0;
|
instance_t inst = 0;
|
||||||
createE1inst(UPtype, inst, &E1AP_REGISTER_REQ(msg).net_config, NULL);
|
createE1inst(UPtype, inst, E1AP_REGISTER_REQ(msg).gnb_id, &E1AP_REGISTER_REQ(msg).net_config, NULL);
|
||||||
cuup_init_n3(inst);
|
cuup_init_n3(inst);
|
||||||
RC.nrrrc[gnb_id_start]->e1_inst = inst; // stupid instance !!!*/
|
RC.nrrrc[gnb_id_start]->e1_inst = inst; // stupid instance !!!*/
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ typedef struct e1ap_setup_req_s {
|
|||||||
typedef struct e1ap_register_req_t {
|
typedef struct e1ap_register_req_t {
|
||||||
e1ap_setup_req_t setup_req;
|
e1ap_setup_req_t setup_req;
|
||||||
e1ap_net_config_t net_config;
|
e1ap_net_config_t net_config;
|
||||||
|
uint32_t gnb_id; // unused in CU-UP, but might be necessary for some functionality, e.g., E2 agent
|
||||||
} e1ap_register_req_t;
|
} e1ap_register_req_t;
|
||||||
|
|
||||||
typedef struct e1ap_setup_resp_s {
|
typedef struct e1ap_setup_resp_s {
|
||||||
|
|||||||
@@ -1801,7 +1801,7 @@ void cucp_task_send_sctp_init_req(instance_t instance, char *my_addr) {
|
|||||||
void e1_task_handle_sctp_association_ind(E1_t type, instance_t instance, sctp_new_association_ind_t *sctp_new_ind)
|
void e1_task_handle_sctp_association_ind(E1_t type, instance_t instance, sctp_new_association_ind_t *sctp_new_ind)
|
||||||
{
|
{
|
||||||
if (!getCxtE1(instance))
|
if (!getCxtE1(instance))
|
||||||
createE1inst(type, instance, NULL, NULL);
|
createE1inst(type, instance, 0, NULL, NULL);
|
||||||
e1ap_upcp_inst_t *inst = getCxtE1(instance);
|
e1ap_upcp_inst_t *inst = getCxtE1(instance);
|
||||||
inst->sockState = SCTP_STATE_ESTABLISHED;
|
inst->sockState = SCTP_STATE_ESTABLISHED;
|
||||||
if (type == UPtype)
|
if (type == UPtype)
|
||||||
@@ -1897,7 +1897,7 @@ void *E1AP_CUUP_task(void *arg) {
|
|||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case E1AP_REGISTER_REQ: {
|
case E1AP_REGISTER_REQ: {
|
||||||
e1ap_register_req_t *reg_req = &E1AP_REGISTER_REQ(msg);
|
e1ap_register_req_t *reg_req = &E1AP_REGISTER_REQ(msg);
|
||||||
createE1inst(UPtype, myInstance, ®_req->net_config, ®_req->setup_req);
|
createE1inst(UPtype, myInstance, reg_req->gnb_id, ®_req->net_config, ®_req->setup_req);
|
||||||
e1_task_send_sctp_association_req(TASK_CUUP_E1, myInstance, ®_req->net_config);
|
e1_task_send_sctp_association_req(TASK_CUUP_E1, myInstance, ®_req->net_config);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ e1ap_upcp_inst_t *getCxtE1(instance_t instance)
|
|||||||
return e1ap_inst[instance];
|
return e1ap_inst[instance];
|
||||||
}
|
}
|
||||||
|
|
||||||
void createE1inst(E1_t type, instance_t instance, e1ap_net_config_t *nc, e1ap_setup_req_t *req)
|
void createE1inst(E1_t type, instance_t instance, uint64_t gnb_id, e1ap_net_config_t *nc, e1ap_setup_req_t *req)
|
||||||
{
|
{
|
||||||
AssertFatal(e1ap_inst[instance] == NULL, "Double call to E1 instance %d\n", (int)instance);
|
AssertFatal(e1ap_inst[instance] == NULL, "Double call to E1 instance %d\n", (int)instance);
|
||||||
e1ap_inst[instance] = calloc(1, sizeof(e1ap_upcp_inst_t));
|
e1ap_inst[instance] = calloc(1, sizeof(e1ap_upcp_inst_t));
|
||||||
e1ap_inst[instance]->type = type;
|
e1ap_inst[instance]->type = type;
|
||||||
e1ap_inst[instance]->instance = instance;
|
e1ap_inst[instance]->instance = instance;
|
||||||
|
e1ap_inst[instance]->gnb_id = gnb_id;
|
||||||
e1ap_inst[instance]->cuup.assoc_id = -1;
|
e1ap_inst[instance]->cuup.assoc_id = -1;
|
||||||
if (nc)
|
if (nc)
|
||||||
memcpy(&e1ap_inst[instance]->net_config, nc, sizeof(*nc));
|
memcpy(&e1ap_inst[instance]->net_config, nc, sizeof(*nc));
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
typedef struct e1ap_upcp_inst_s {
|
typedef struct e1ap_upcp_inst_s {
|
||||||
instance_t instance;
|
instance_t instance;
|
||||||
|
uint32_t gnb_id; // associated gNB's ID, unused in E1 but necessary for e.g. E2 agent
|
||||||
E1_t type;
|
E1_t type;
|
||||||
enum sctp_state_e sockState;
|
enum sctp_state_e sockState;
|
||||||
struct {
|
struct {
|
||||||
@@ -52,7 +53,7 @@ e1ap_upcp_inst_t *getCxtE1(instance_t instance);
|
|||||||
long E1AP_get_next_transaction_identifier();
|
long E1AP_get_next_transaction_identifier();
|
||||||
void E1AP_free_transaction_identifier(long id);
|
void E1AP_free_transaction_identifier(long id);
|
||||||
|
|
||||||
void createE1inst(E1_t type, instance_t instance, e1ap_net_config_t *nc, e1ap_setup_req_t *req);
|
void createE1inst(E1_t type, instance_t instance, uint64_t gnb_id, e1ap_net_config_t *nc, e1ap_setup_req_t *req);
|
||||||
|
|
||||||
int e1ap_encode_send(E1_t type, sctp_assoc_t assoc_id, struct E1AP_E1AP_PDU *pdu, uint16_t stream, const char *func);
|
int e1ap_encode_send(E1_t type, sctp_assoc_t assoc_id, struct E1AP_E1AP_PDU *pdu, uint16_t stream, const char *func);
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ static void get_NGU_S1U_addr(char **addr, uint16_t *port)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process)
|
MessageDef *RCconfig_NR_CU_E1(const E1_t *entity)
|
||||||
{
|
{
|
||||||
MessageDef *msgConfig = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_REGISTER_REQ);
|
MessageDef *msgConfig = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_REGISTER_REQ);
|
||||||
if (!msgConfig)
|
if (!msgConfig)
|
||||||
@@ -82,14 +82,12 @@ MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process)
|
|||||||
sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0);
|
sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0);
|
||||||
int num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt;
|
int num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt;
|
||||||
AssertFatal(num_gnbs == 1, "Support only one gNB per process\n");
|
AssertFatal(num_gnbs == 1, "Support only one gNB per process\n");
|
||||||
|
config_getlist(config_get_if(), &GNBParamList, GNBParams, sizeofArray(GNBParams), NULL);
|
||||||
|
paramdef_t *gnbParms = GNBParamList.paramarray[0];
|
||||||
|
e1ap_setup_req_t *e1Setup = &E1AP_REGISTER_REQ(msgConfig).setup_req;
|
||||||
|
|
||||||
if (num_gnbs > 0) {
|
if (num_gnbs > 0) {
|
||||||
config_getlist(config_get_if(), &GNBParamList, GNBParams, sizeofArray(GNBParams), NULL);
|
|
||||||
paramdef_t *gnbParms = GNBParamList.paramarray[0];
|
|
||||||
AssertFatal(gnbParms[GNB_GNB_ID_IDX].uptr != NULL, "gNB id %d is not defined in configuration file\n", 0);
|
|
||||||
e1ap_setup_req_t *e1Setup = &E1AP_REGISTER_REQ(msgConfig).setup_req;
|
|
||||||
msgConfig->ittiMsgHeader.destinationInstance = 0;
|
msgConfig->ittiMsgHeader.destinationInstance = 0;
|
||||||
e1Setup->gNB_cu_up_id = *(gnbParms[GNB_GNB_ID_IDX].uptr);
|
|
||||||
if (*gnbParms[GNB_GNB_NAME_IDX].strptr)
|
if (*gnbParms[GNB_GNB_NAME_IDX].strptr)
|
||||||
e1Setup->gNB_cu_up_name = *(gnbParms[GNB_GNB_NAME_IDX].strptr);
|
e1Setup->gNB_cu_up_name = *(gnbParms[GNB_GNB_NAME_IDX].strptr);
|
||||||
|
|
||||||
@@ -132,7 +130,11 @@ MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process)
|
|||||||
get_NGU_S1U_addr(&e1ap_nc->localAddressN3, &e1ap_nc->localPortN3);
|
get_NGU_S1U_addr(&e1ap_nc->localAddressN3, &e1ap_nc->localPortN3);
|
||||||
e1ap_nc->remotePortN3 = e1ap_nc->localPortN3 ;
|
e1ap_nc->remotePortN3 = e1ap_nc->localPortN3 ;
|
||||||
|
|
||||||
if (separate_CUUP_process) {
|
AssertFatal(config_isparamset(gnbParms, GNB_GNB_ID_IDX), "%s is not defined in configuration file\n", GNB_CONFIG_STRING_GNB_ID);
|
||||||
|
uint32_t gnb_id = *gnbParms[GNB_GNB_ID_IDX].uptr;
|
||||||
|
E1AP_REGISTER_REQ(msgConfig).gnb_id = gnb_id;
|
||||||
|
|
||||||
|
if (entity != NULL) {
|
||||||
paramlist_def_t GNBE1ParamList = {GNB_CONFIG_STRING_E1_PARAMETERS, NULL, 0};
|
paramlist_def_t GNBE1ParamList = {GNB_CONFIG_STRING_E1_PARAMETERS, NULL, 0};
|
||||||
paramdef_t GNBE1Params[] = GNBE1PARAMS_DESC;
|
paramdef_t GNBE1Params[] = GNBE1PARAMS_DESC;
|
||||||
config_getlist(config_get_if(), &GNBE1ParamList, GNBE1Params, sizeofArray(GNBE1Params), aprefix);
|
config_getlist(config_get_if(), &GNBE1ParamList, GNBE1Params, sizeofArray(GNBE1Params), aprefix);
|
||||||
@@ -141,6 +143,24 @@ MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process)
|
|||||||
e1ap_nc->CUCP_e1_ip_address.ipv4 = 1;
|
e1ap_nc->CUCP_e1_ip_address.ipv4 = 1;
|
||||||
strcpy(e1ap_nc->CUUP_e1_ip_address.ipv4_address, *(e1Parms[GNB_CONFIG_E1_IPV4_ADDRESS_CUUP].strptr));
|
strcpy(e1ap_nc->CUUP_e1_ip_address.ipv4_address, *(e1Parms[GNB_CONFIG_E1_IPV4_ADDRESS_CUUP].strptr));
|
||||||
e1ap_nc->CUUP_e1_ip_address.ipv4 = 1;
|
e1ap_nc->CUUP_e1_ip_address.ipv4 = 1;
|
||||||
|
if (*entity == CPtype) {
|
||||||
|
// CP needs gNB_ID (although not used, but other parts check it as
|
||||||
|
// well), and gNB-CU-UP ID should NOT be present as it comes through E1!
|
||||||
|
AssertFatal(!config_isparamset(gnbParms, GNB_GNB_CU_UP_ID_IDX), "%s must not be defined in configuration file\n", GNB_CONFIG_STRING_GNB_CU_UP_ID);
|
||||||
|
} else { // UPtype
|
||||||
|
AssertFatal(config_isparamset(gnbParms, GNB_GNB_CU_UP_ID_IDX), "%s is not be defined in configuration file\n", GNB_CONFIG_STRING_GNB_CU_UP_ID);
|
||||||
|
e1Setup->gNB_cu_up_id = *gnbParms[GNB_GNB_CU_UP_ID_IDX].u64ptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// integrated CU-CP/UP. We don't care about the gNB-CU-UP ID that much,
|
||||||
|
// but if it's there, check it's the same as gNB ID
|
||||||
|
uint64_t *gnb_cu_up_id = gnbParms[GNB_GNB_CU_UP_ID_IDX].u64ptr;
|
||||||
|
AssertFatal(!config_isparamset(gnbParms, GNB_GNB_CU_UP_ID_IDX) || *gnb_cu_up_id == gnb_id,
|
||||||
|
"%s is different of %s: they need to match or remove %s from config\n",
|
||||||
|
GNB_CONFIG_STRING_GNB_CU_UP_ID,
|
||||||
|
GNB_CONFIG_STRING_GNB_ID,
|
||||||
|
GNB_CONFIG_STRING_GNB_CU_UP_ID);
|
||||||
|
e1Setup->gNB_cu_up_id = gnb_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msgConfig;
|
return msgConfig;
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ void *gNB_app_task(void *args_p)
|
|||||||
if (node_type == ngran_gNB_CUCP) {
|
if (node_type == ngran_gNB_CUCP) {
|
||||||
if (itti_create_task(TASK_CUCP_E1, E1AP_CUCP_task, NULL) < 0)
|
if (itti_create_task(TASK_CUCP_E1, E1AP_CUCP_task, NULL) < 0)
|
||||||
AssertFatal(false, "Create task for E1AP CP failed\n");
|
AssertFatal(false, "Create task for E1AP CP failed\n");
|
||||||
MessageDef *msg = RCconfig_NR_CU_E1(true);
|
E1_t e1type = CPtype;
|
||||||
|
MessageDef *msg = RCconfig_NR_CU_E1(&e1type);
|
||||||
AssertFatal(msg != NULL, "Send inti to task for E1AP CP failed\n");
|
AssertFatal(msg != NULL, "Send inti to task for E1AP CP failed\n");
|
||||||
// this sends the E1AP_REGISTER_REQ to CU-CP so it sets up the socket
|
// this sends the E1AP_REGISTER_REQ to CU-CP so it sets up the socket
|
||||||
// it does NOT use the E1AP part
|
// it does NOT use the E1AP part
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i);
|
|||||||
int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i);
|
int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i);
|
||||||
void wait_f1_setup_response(void);
|
void wait_f1_setup_response(void);
|
||||||
int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update);
|
int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update);
|
||||||
MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process);
|
MessageDef *RCconfig_NR_CU_E1(const E1_t *entity);
|
||||||
ngran_node_t get_node_type(void);
|
ngran_node_t get_node_type(void);
|
||||||
|
|
||||||
#ifdef E2_AGENT
|
#ifdef E2_AGENT
|
||||||
|
|||||||
@@ -129,11 +129,13 @@ typedef enum {
|
|||||||
#define GNB_CONFIG_STRING_ENABLE_SDAP "enable_sdap"
|
#define GNB_CONFIG_STRING_ENABLE_SDAP "enable_sdap"
|
||||||
#define GNB_CONFIG_STRING_DRBS "drbs"
|
#define GNB_CONFIG_STRING_DRBS "drbs"
|
||||||
#define GNB_CONFIG_STRING_GNB_DU_ID "gNB_DU_ID"
|
#define GNB_CONFIG_STRING_GNB_DU_ID "gNB_DU_ID"
|
||||||
|
#define GNB_CONFIG_STRING_GNB_CU_UP_ID "gNB_CU_UP_ID"
|
||||||
|
|
||||||
#define GNB_CONFIG_HLP_STRING_ENABLE_SDAP "enable the SDAP layer\n"
|
#define GNB_CONFIG_HLP_STRING_ENABLE_SDAP "enable the SDAP layer\n"
|
||||||
#define GNB_CONFIG_HLP_FORCE256QAMOFF "suppress activation of 256 QAM despite UE support"
|
#define GNB_CONFIG_HLP_FORCE256QAMOFF "suppress activation of 256 QAM despite UE support"
|
||||||
#define GNB_CONFIG_HLP_STRING_DRBS "Number of total DRBs to establish, including the mandatory for PDU SEssion (default=1)\n"
|
#define GNB_CONFIG_HLP_STRING_DRBS "Number of total DRBs to establish, including the mandatory for PDU SEssion (default=1)\n"
|
||||||
#define GNB_CONFIG_HLP_GNB_DU_ID "defines the gNB-DU ID (only applicable for DU)"
|
#define GNB_CONFIG_HLP_GNB_DU_ID "defines the gNB-DU ID (only applicable for DU)"
|
||||||
|
#define GNB_CONFIG_HLP_GNB_CU_UP_ID "defines the gNB-CU-UP ID (only applicable for CU-UP)"
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------------------------------------------------------------------------*/
|
||||||
/* cell configuration parameters */
|
/* cell configuration parameters */
|
||||||
@@ -170,6 +172,7 @@ typedef enum {
|
|||||||
{GNB_CONFIG_STRING_ENABLE_SDAP, GNB_CONFIG_HLP_STRING_ENABLE_SDAP, PARAMFLAG_BOOL,.iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
{GNB_CONFIG_STRING_ENABLE_SDAP, GNB_CONFIG_HLP_STRING_ENABLE_SDAP, PARAMFLAG_BOOL,.iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||||
{GNB_CONFIG_STRING_DRBS, GNB_CONFIG_HLP_STRING_DRBS, 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \
|
{GNB_CONFIG_STRING_DRBS, GNB_CONFIG_HLP_STRING_DRBS, 0, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \
|
||||||
{GNB_CONFIG_STRING_GNB_DU_ID, GNB_CONFIG_HLP_GNB_DU_ID, 0, .u64ptr=NULL, .defint64val=1, TYPE_UINT64, 0}, \
|
{GNB_CONFIG_STRING_GNB_DU_ID, GNB_CONFIG_HLP_GNB_DU_ID, 0, .u64ptr=NULL, .defint64val=1, TYPE_UINT64, 0}, \
|
||||||
|
{GNB_CONFIG_STRING_GNB_CU_UP_ID, GNB_CONFIG_HLP_GNB_CU_UP_ID, 0, .u64ptr=NULL, .defint64val=1, TYPE_UINT64, 0}, \
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@@ -203,6 +206,7 @@ typedef enum {
|
|||||||
#define GNB_ENABLE_SDAP_IDX 26
|
#define GNB_ENABLE_SDAP_IDX 26
|
||||||
#define GNB_DRBS 27
|
#define GNB_DRBS 27
|
||||||
#define GNB_GNB_DU_ID_IDX 28
|
#define GNB_GNB_DU_ID_IDX 28
|
||||||
|
#define GNB_GNB_CU_UP_ID_IDX 29
|
||||||
|
|
||||||
#define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD}
|
#define TRACKING_AREA_CODE_OKRANGE {0x0001,0xFFFD}
|
||||||
#define GNBPARAMS_CHECK { \
|
#define GNBPARAMS_CHECK { \
|
||||||
|
|||||||
Reference in New Issue
Block a user