Remove FlexRAN
- Remove any FlexRAN code - Cleanup config files - Remove LFDS7, libyaml dependencies
This commit is contained in:
@@ -1,163 +0,0 @@
|
||||
package protocol;
|
||||
|
||||
|
||||
message fsp_header {
|
||||
optional uint32 version = 1;
|
||||
optional uint32 type = 2;
|
||||
optional uint32 xid = 4;
|
||||
}
|
||||
|
||||
enum fsp_type {
|
||||
// Discovery and maintenance messages
|
||||
FSPT_HELLO = 0;
|
||||
FSPT_ECHO_REQUEST = 1;
|
||||
FSPT_ECHO_REPLY = 2;
|
||||
FSPT_RLC_DATA_REQ = 3;
|
||||
FSPT_RLC_DATA_REQ_ACK = 4;
|
||||
FSPT_PDCP_DATA_IND = 5;
|
||||
FSPT_PDCP_DATA_IND_ACK = 6;
|
||||
}
|
||||
|
||||
enum f1u_type {
|
||||
// Discovery and maintenance messages
|
||||
F1U_DL_DATA = 0;
|
||||
F1U_DL_STATUS = 1;
|
||||
}
|
||||
|
||||
|
||||
message f1u_message{
|
||||
optional flexsplit_direction msg_dir = 100;
|
||||
oneof msg{
|
||||
dl_user_data dl_data = 1;
|
||||
dl_data_delivery_status dl_status = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
message flexsplit_message {
|
||||
optional flexsplit_direction msg_dir = 100;
|
||||
oneof msg {
|
||||
fsp_hello hello_msg = 1;
|
||||
fsp_echo_request echo_request_msg = 2;
|
||||
fsp_echo_reply echo_reply_msg = 3;
|
||||
fspRlcDataReq data_req_msg = 4;
|
||||
fspRlcDataReqAck data_req_ack = 5;
|
||||
fspPdcpDataInd data_ind_msg = 6;
|
||||
fspPdcpDataIndAck data_ind_ack = 7;
|
||||
}
|
||||
}
|
||||
|
||||
enum flexsplit_direction {
|
||||
//option allow_alias = true;
|
||||
NOT_SET = 0;
|
||||
INITIATING_MESSAGE = 1;
|
||||
SUCCESSFUL_OUTCOME=2;
|
||||
UNSUCCESSFUL_OUTCOME=3;
|
||||
}
|
||||
|
||||
enum flexsplit_err {
|
||||
// message errors
|
||||
NO_ERR = 0;
|
||||
MSG_DEQUEUING = -1;
|
||||
MSG_ENQUEUING = -2;
|
||||
MSG_DECODING = -3;
|
||||
MSG_ENCODING = -4;
|
||||
MSG_BUILD = -5;
|
||||
MSG_NOT_SUPPORTED = -6;
|
||||
MSG_NOT_HANDLED = -7;
|
||||
MSG_NOT_VALIDATED = -8;
|
||||
MSG_OUT_DATED = -9;
|
||||
|
||||
// other erros
|
||||
UNEXPECTED = -100;
|
||||
}
|
||||
|
||||
message fsp_ctxt {
|
||||
optional uint32 fsp_mod_id = 1;
|
||||
optional bool fsp_enb_flag = 2;
|
||||
optional uint32 fsp_instance = 3;
|
||||
optional uint32 fsp_rnti = 4;
|
||||
optional uint32 fsp_frame = 5;
|
||||
optional uint32 fsp_subframe = 6;
|
||||
optional uint32 fsp_eNB_index = 7;
|
||||
|
||||
}
|
||||
|
||||
message fspRlcPdu {
|
||||
optional bytes fsp_pdu_data = 1; // Maximum PDU to be transfered
|
||||
}
|
||||
|
||||
message fspRlcData {
|
||||
optional fsp_ctxt fsp_ctxt = 1;
|
||||
optional bool fsp_srb_flag = 2;
|
||||
optional bool fsp_mbms_flag = 3;
|
||||
optional uint32 fsp_rb_id = 4;
|
||||
optional uint32 fsp_muip = 5;
|
||||
optional uint32 fsp_confirm = 6;
|
||||
optional int32 fsp_sdu_buffer_size = 7;
|
||||
optional fspRlcPdu fsp_pdu = 8;
|
||||
}
|
||||
|
||||
//
|
||||
// Maintenance and discovery messages
|
||||
//
|
||||
|
||||
message fsp_hello {
|
||||
optional fsp_header header = 1;
|
||||
}
|
||||
|
||||
message fsp_echo_request {
|
||||
optional fsp_header header = 1;
|
||||
// extensions 100 to 199;
|
||||
}
|
||||
|
||||
|
||||
message fsp_echo_reply {
|
||||
optional fsp_header header = 1;
|
||||
// extensions 100 to 199;
|
||||
}
|
||||
|
||||
|
||||
message fspRlcDataReq {
|
||||
optional fsp_header header = 1;
|
||||
optional uint32 eNB_id = 2;
|
||||
optional fspRlcData pdcp_data = 3;
|
||||
}
|
||||
|
||||
message fspRlcDataReqAck {
|
||||
optional fsp_header header = 1;
|
||||
optional uint32 result = 2;
|
||||
}
|
||||
|
||||
message fspPdcpDataInd {
|
||||
optional fsp_header header = 1;
|
||||
optional uint32 eNB_id = 2;
|
||||
optional fspRlcData rlc_data = 3;
|
||||
}
|
||||
|
||||
message fspPdcpDataIndAck {
|
||||
optional fsp_header header = 1;
|
||||
optional uint32 result = 2;
|
||||
}
|
||||
|
||||
message dl_data_header {
|
||||
required uint32 fields = 1;
|
||||
}
|
||||
|
||||
message dl_user_data {
|
||||
required dl_data_header header = 1;
|
||||
required bytes pdu = 2;
|
||||
required uint32 frame = 3;
|
||||
required uint32 subframe = 4;
|
||||
required uint32 rnti = 5;
|
||||
}
|
||||
|
||||
|
||||
message dl_data_delivery_status {
|
||||
required dl_data_header header = 1;
|
||||
required bytes pdu = 2;
|
||||
required uint32 frame = 3;
|
||||
required uint32 subframe = 4;
|
||||
required uint32 rnti = 5;
|
||||
}
|
||||
@@ -256,16 +256,6 @@ THREAD_STRUCT = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
|
||||
@@ -202,15 +202,5 @@ log_config = {
|
||||
global_log_level = "info";
|
||||
pdcp_log_level = "info";
|
||||
rrc_log_level = "info";
|
||||
flexran_agent_log_level = "info";
|
||||
gtp_log_level = "info";
|
||||
};
|
||||
|
||||
NETWORK_CONTROLLER : {
|
||||
FLEXRAN_ENABLED = "yes";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
@@ -100,14 +100,4 @@ log_config = {
|
||||
phy_log_level = "info";
|
||||
mac_log_level = "info";
|
||||
rlc_log_level = "info";
|
||||
flexran_agent_log_level = "info";
|
||||
};
|
||||
|
||||
NETWORK_CONTROLLER : {
|
||||
FLEXRAN_ENABLED = "yes";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
@@ -234,16 +234,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -234,16 +234,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -262,16 +262,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -234,16 +234,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -233,16 +233,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -264,16 +264,6 @@ RUs = (
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
|
||||
@@ -224,13 +224,3 @@ THREAD_STRUCT = (
|
||||
worker_config = "WORKER_ENABLE";
|
||||
}
|
||||
);
|
||||
|
||||
NETWORK_CONTROLLER :
|
||||
{
|
||||
FLEXRAN_ENABLED = "no";
|
||||
FLEXRAN_INTERFACE_NAME = "lo";
|
||||
FLEXRAN_IPV4_ADDRESS = "127.0.0.1";
|
||||
FLEXRAN_PORT = 2210;
|
||||
FLEXRAN_CACHE = "/mnt/oai_agent_cache";
|
||||
FLEXRAN_AWAIT_RECONF = "no";
|
||||
};
|
||||
|
||||
@@ -388,92 +388,6 @@ void terminate_task(module_id_t mod_id, task_id_t from, task_id_t to) {
|
||||
extern void free_transport(PHY_VARS_eNB *);
|
||||
extern void phy_free_RU(RU_t *);
|
||||
|
||||
int stop_L1L2(module_id_t enb_id) {
|
||||
LOG_W(ENB_APP, "stopping lte-softmodem\n");
|
||||
|
||||
if (!RC.ru) {
|
||||
LOG_UI(ENB_APP, "no RU configured\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* these tasks need to pick up new configuration */
|
||||
terminate_task(enb_id, TASK_ENB_APP, TASK_RRC_ENB);
|
||||
oai_exit = 1;
|
||||
LOG_I(ENB_APP, "calling kill_RU_proc() for instance %d\n", enb_id);
|
||||
kill_RU_proc(RC.ru[enb_id]);
|
||||
LOG_I(ENB_APP, "calling kill_eNB_proc() for instance %d\n", enb_id);
|
||||
kill_eNB_proc(enb_id);
|
||||
oai_exit = 0;
|
||||
|
||||
for (int cc_id = 0; cc_id < RC.nb_CC[enb_id]; cc_id++) {
|
||||
free_transport(RC.eNB[enb_id][cc_id]);
|
||||
phy_free_lte_eNB(RC.eNB[enb_id][cc_id]);
|
||||
}
|
||||
|
||||
phy_free_RU(RC.ru[enb_id]);
|
||||
free_lte_top();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restart the lte-softmodem after it has been soft-stopped with stop_L1L2()
|
||||
*/
|
||||
int restart_L1L2(module_id_t enb_id) {
|
||||
int cc_id;
|
||||
MessageDef *msg_p = NULL;
|
||||
LOG_W(ENB_APP, "restarting lte-softmodem\n");
|
||||
/* block threads */
|
||||
pthread_mutex_lock(&sync_mutex);
|
||||
sync_var = -1;
|
||||
pthread_mutex_unlock(&sync_mutex);
|
||||
|
||||
for (cc_id = 0; cc_id < RC.nb_L1_CC[enb_id]; cc_id++) {
|
||||
RC.eNB[enb_id][cc_id]->configured = 0;
|
||||
for (int ru_id=0;ru_id<RC.eNB[enb_id][cc_id]->num_RU;ru_id++) {
|
||||
int ru_idx = RC.eNB[enb_id][cc_id]->RU_list[ru_id]->idx;
|
||||
RC.ru_mask |= (1 << ru_idx);
|
||||
set_function_spec_param(RC.ru[ru_idx]);
|
||||
}
|
||||
}
|
||||
/* reset the list of connected UEs in the MAC, since in this process with
|
||||
* loose all UEs (have to reconnect) */
|
||||
init_UE_info(&RC.mac[enb_id]->UE_info);
|
||||
LOG_I(ENB_APP, "attempting to create ITTI tasks\n");
|
||||
|
||||
if (itti_create_task (TASK_RRC_ENB, rrc_enb_task, NULL) < 0) {
|
||||
LOG_E(RRC, "Create task for RRC eNB failed\n");
|
||||
return -1;
|
||||
} else {
|
||||
LOG_I(RRC, "Re-created task for RRC eNB successfully\n");
|
||||
}
|
||||
|
||||
/* pass a reconfiguration request which will configure everything down to
|
||||
* RC.eNB[i][j]->frame_parms, too */
|
||||
msg_p = itti_alloc_new_message(TASK_ENB_APP, 0, RRC_CONFIGURATION_REQ);
|
||||
RRC_CONFIGURATION_REQ(msg_p) = RC.rrc[enb_id]->configuration;
|
||||
itti_send_msg_to_task(TASK_RRC_ENB, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);
|
||||
/* TODO XForms might need to be restarted, but it is currently (09/02/18)
|
||||
* broken, so we cannot test it */
|
||||
wait_eNBs();
|
||||
for (int cc_id=0;cc_id<RC.nb_L1_CC[enb_id]; cc_id++) {
|
||||
for (int ru_id=0;ru_id<RC.eNB[enb_id][cc_id]->num_RU;ru_id++) {
|
||||
int ru_idx = RC.eNB[enb_id][cc_id]->RU_list[ru_id]->idx;
|
||||
|
||||
init_RU_proc(RC.ru[ru_idx]);
|
||||
RC.ru[ru_idx]->rf_map.card = 0;
|
||||
RC.ru[ru_idx]->rf_map.chain = 0; /* CC_id + chain_offset;*/
|
||||
}
|
||||
}
|
||||
wait_RUs();
|
||||
init_eNB_afterRU();
|
||||
printf("Sending sync to all threads\n");
|
||||
pthread_mutex_lock(&sync_mutex);
|
||||
sync_var=0;
|
||||
pthread_cond_broadcast(&sync_cond);
|
||||
pthread_mutex_unlock(&sync_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_pdcp(void) {
|
||||
if (!NODE_IS_DU(RC.rrc[0]->node_type)) {
|
||||
pdcp_layer_init();
|
||||
@@ -509,7 +423,6 @@ static void wait_nfapi_init(char *thread_name) {
|
||||
|
||||
int main ( int argc, char **argv )
|
||||
{
|
||||
int i;
|
||||
int CC_id = 0;
|
||||
int ru_id;
|
||||
int node_type = ngran_eNB;
|
||||
@@ -564,22 +477,7 @@ int main ( int argc, char **argv )
|
||||
RCconfig_L1();
|
||||
}
|
||||
|
||||
/* We need to read RU configuration before FlexRAN starts so it knows what
|
||||
* splits to report. Actual RU start comes later. */
|
||||
if (RC.nb_RU > 0 && NFAPI_MODE != NFAPI_MODE_VNF) {
|
||||
RC.ru = RCconfig_RU(RC.nb_RU,RC.nb_L1_inst,RC.eNB,&RC.ru_mask,&RC.ru_mutex,&RC.ru_cond);
|
||||
LOG_I(PHY,
|
||||
"number of L1 instances %d, number of RU %d, number of CPU cores %d\n",
|
||||
RC.nb_L1_inst, RC.nb_RU, get_nprocs());
|
||||
}
|
||||
|
||||
if (RC.nb_inst > 0) {
|
||||
/* Start the agent. If it is turned off in the configuration, it won't start */
|
||||
for (i = 0; i < RC.nb_inst; i++) {
|
||||
if(NFAPI_MODE != NFAPI_MODE_PNF)
|
||||
flexran_agent_start(i);
|
||||
}
|
||||
|
||||
/* initializes PDCP and sets correct RLC Request/PDCP Indication callbacks
|
||||
* for monolithic/F1 modes */
|
||||
init_pdcp();
|
||||
@@ -665,8 +563,11 @@ int main ( int argc, char **argv )
|
||||
// RU thread and some L1 procedure aren't necessary in VNF or L2 FAPI simulator.
|
||||
// but RU thread deals with pre_scd and this is necessary in VNF and simulator.
|
||||
// some initialization is necessary and init_ru_vnf do this.
|
||||
if (RC.nb_RU >0 && NFAPI_MODE!=NFAPI_MODE_VNF) {
|
||||
printf("Initializing RU threads\n");
|
||||
if (RC.nb_RU > 0 && NFAPI_MODE != NFAPI_MODE_VNF) {
|
||||
RC.ru = RCconfig_RU(RC.nb_RU, RC.nb_L1_inst, RC.eNB, &RC.ru_mask, &RC.ru_mutex, &RC.ru_cond);
|
||||
LOG_I(PHY,
|
||||
"number of L1 instances %d, number of RU %d, number of CPU cores %d: Initializing RU threads\n",
|
||||
RC.nb_L1_inst, RC.nb_RU, get_nprocs());
|
||||
init_RU(RC.ru,RC.nb_RU,RC.eNB,RC.nb_L1_inst,RC.nb_L1_CC,get_softmodem_params()->rf_config_file,get_softmodem_params()->send_dmrs_sync);
|
||||
|
||||
for (ru_id=0; ru_id<RC.nb_RU; ru_id++) {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "PHY/types.h"
|
||||
#include "PHY/defs_eNB.h"
|
||||
#include "PHY/defs_UE.h"
|
||||
#include "flexran_agent.h"
|
||||
#include "s1ap_eNB.h"
|
||||
#include "SIMULATION/ETH_TRANSPORT/proto.h"
|
||||
#include "executables/softmodem-common.h"
|
||||
@@ -194,8 +193,6 @@ extern void init_ue_devices(PHY_VARS_UE *);
|
||||
PHY_VARS_UE *init_ue_vars(LTE_DL_FRAME_PARMS *frame_parms, uint8_t UE_id, uint8_t abstraction_flag);
|
||||
|
||||
void init_eNB_afterRU(void);
|
||||
extern int stop_L1L2(module_id_t enb_id);
|
||||
extern int restart_L1L2(module_id_t enb_id);
|
||||
|
||||
extern void init_UE_stub_single_thread(int nb_inst, int eMBMS_active, int uecap_xer_in, char *emul_iface);
|
||||
extern void init_UE_standalone_thread(int ue_idx);
|
||||
|
||||
@@ -497,15 +497,6 @@ static inline void wait_nfapi_init(char *thread_name) {
|
||||
printf( "NFAPI: got sync (%s)\n", thread_name);
|
||||
}
|
||||
|
||||
int stop_L1L2(module_id_t enb_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int restart_L1L2(module_id_t enb_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_pdcp(int ue_id) {
|
||||
uint32_t pdcp_initmask = (!IS_SOFTMODEM_NOS1) ? LINK_ENB_PDCP_TO_GTPV1U_BIT : (LINK_ENB_PDCP_TO_GTPV1U_BIT | PDCP_USE_NETLINK_BIT | LINK_ENB_PDCP_TO_IP_DRIVER_BIT);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user