diff --git a/lib/include/srslte/common/epoll_helper.h b/lib/include/srslte/common/epoll_helper.h index 1e3bad445..0194b37b7 100644 --- a/lib/include/srslte/common/epoll_helper.h +++ b/lib/include/srslte/common/epoll_helper.h @@ -95,7 +95,7 @@ private: }; ///< Create periodic epoll timer every 1ms -int create_tti_timer() +inline int create_tti_timer() { int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); if (timer_fd == -1) { @@ -119,7 +119,7 @@ int create_tti_timer() } ///< Create signalfd for handling signals -int add_signalfd() +inline int add_signalfd() { // block all signals. we take signals synchronously via signalfd sigset_t all; @@ -145,7 +145,7 @@ int add_signalfd() } ///< Add fd to epoll fd -int add_epoll(int fd, int epoll_fd) +inline int add_epoll(int fd, int epoll_fd) { struct epoll_event ev = {}; ev.data.fd = fd; @@ -158,7 +158,7 @@ int add_epoll(int fd, int epoll_fd) } ///< Remove fd from epoll -int del_epoll(int fd, int epoll_fd) +inline int del_epoll(int fd, int epoll_fd) { struct epoll_event ev = {}; ev.data.fd = fd; diff --git a/srsue/test/ttcn3/hdr/dut_utils.h b/srsue/test/ttcn3/hdr/dut_utils.h index 2d9fd2198..b834cb9cc 100644 --- a/srsue/test/ttcn3/hdr/dut_utils.h +++ b/srsue/test/ttcn3/hdr/dut_utils.h @@ -28,6 +28,7 @@ #include #include +#include std::string get_filename_with_tc_name(const std::string& str, const uint32_t run_id, const std::string tc_name) { diff --git a/srsue/test/ttcn3/hdr/ttcn3_syssim.h b/srsue/test/ttcn3/hdr/ttcn3_syssim.h index 6c1795231..c9dc22cee 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_syssim.h +++ b/srsue/test/ttcn3/hdr/ttcn3_syssim.h @@ -22,7 +22,6 @@ #ifndef SRSUE_TTCN3_SYSSIM_H #define SRSUE_TTCN3_SYSSIM_H -#include "dut_utils.h" #include "srslte/mac/pdu_queue.h" #include "srslte/test/ue_test_interfaces.h" #include "srslte/upper/pdcp.h" @@ -49,979 +48,136 @@ class ttcn3_syssim : public syssim_interface_phy, public srslte::pdu_queue::process_callback { public: - ttcn3_syssim(srslte::logger_file* logger_file_, ttcn3_ue* ue_) : - log{"SS "}, - mac_msg_ul(20, ss_mac_log), - mac_msg_dl(20, ss_mac_log), - pdus(128), - logger(logger_file_), - logger_file(logger_file_), - pool(byte_buffer_pool::get_instance()), - ue(ue_), - rlc(ss_rlc_log->get_service_name().c_str()), - signal_handler(&running), - timer_handler(create_tti_timer(), [&](uint64_t res) { new_tti_indication(res); }), - pdcp(&stack, ss_pdcp_log->get_service_name().c_str()) - { - if (ue->init(all_args_t{}, logger, this, "INIT_TEST") != SRSLTE_SUCCESS) { - ue->stop(); - fprintf(stderr, "Couldn't initialize UE.\n"); - } - } - - ~ttcn3_syssim(){}; - - int init(const all_args_t& args_) - { - args = args_; - - // Make sure to get SS logging as well - if (args.log.filename == "stdout") { - logger = &logger_stdout; - } - srslte::logmap::set_default_logger(logger); - - // init and configure logging - srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS ", logger, true}}); - ut_log.init("UT ", logger); - sys_log.init("SYS ", logger); - ip_sock_log.init("IP_S", logger); - ip_ctrl_log.init("IP_C", logger); - srb_log.init("SRB ", logger); - drb_log.init("DRB ", logger); - srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS-RLC", logger}}); - srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS-PDCP", logger}}); - - log->set_level(args.log.all_level); - ut_log.set_level(args.log.all_level); - sys_log.set_level(args.log.all_level); - ip_sock_log.set_level(args.log.all_level); - ip_ctrl_log.set_level(args.log.all_level); - srb_log.set_level(args.log.all_level); - drb_log.set_level(args.log.all_level); - ss_mac_log->set_level(args.log.all_level); - ss_rlc_log->set_level(args.log.all_level); - ss_pdcp_log->set_level(args.log.all_level); - - log->set_hex_limit(args.log.all_hex_limit); - ut_log.set_hex_limit(args.log.all_hex_limit); - sys_log.set_hex_limit(args.log.all_hex_limit); - ip_sock_log.set_hex_limit(args.log.all_hex_limit); - ip_ctrl_log.set_hex_limit(args.log.all_hex_limit); - srb_log.set_hex_limit(args.log.all_hex_limit); - drb_log.set_hex_limit(args.log.all_hex_limit); - ss_mac_log->set_hex_limit(args.log.all_hex_limit); - ss_rlc_log->set_hex_limit(args.log.all_hex_limit); - ss_pdcp_log->set_hex_limit(args.log.all_hex_limit); - - // Init epoll socket and add FDs - epoll_fd = epoll_create1(0); - if (epoll_fd == -1) { - log->error("Error creating epoll\n"); - return SRSLTE_ERROR; - } - - // add signalfd - signal_fd = add_signalfd(); - if (add_epoll(signal_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding signalfd to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({signal_fd, &signal_handler}); - - // init system interfaces to tester - if (add_port_handler() != SRSLTE_SUCCESS) { - log->error("Error creating port handlers\n"); - return SRSLTE_ERROR; - } - - // Init SS layers - pdus.init(this, log); - rlc.init(&pdcp, this, &stack.timers, 0 /* RB_ID_SRB0 */); - pdcp.init(&rlc, this, nullptr); - - return SRSLTE_SUCCESS; - } - - void set_forced_lcid(int lcid) { force_lcid = lcid; } - - int add_port_handler() - { - // UT port - int ut_fd = ut.init(this, &ut_log, listen_address, UT_PORT); - if (add_epoll(ut_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding UT port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({ut_fd, &ut}); - log->console("UT handler listening on SCTP port %d\n", UT_PORT); - - // SYS port - int sys_fd = sys.init(this, &sys_log, listen_address, SYS_PORT); - if (add_epoll(sys_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding SYS port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({sys_fd, &sys}); - log->console("SYS handler listening on SCTP port %d\n", SYS_PORT); - - // IPsock port - int ip_sock_fd = ip_sock.init(&ip_sock_log, listen_address, IPSOCK_PORT); - if (add_epoll(ip_sock_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding IP sock port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({ip_sock_fd, &ip_sock}); - log->console("IPSOCK handler listening on SCTP port %d\n", IPSOCK_PORT); - - // IPctrl port - int ip_ctrl_fd = ip_ctrl.init(&ip_ctrl_log, listen_address, IPCTRL_PORT); - if (add_epoll(ip_ctrl_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding IP ctrl port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({ip_ctrl_fd, &ip_ctrl}); - log->console("IPCTRL handler listening on SCTP port %d\n", IPCTRL_PORT); - - // add SRB fd - int srb_fd = srb.init(this, &srb_log, listen_address, SRB_PORT); - if (add_epoll(srb_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding SRB port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({srb_fd, &srb}); - log->console("SRB handler listening on SCTP port %d\n", SRB_PORT); - - // add DRB fd - int drb_fd = drb.init(this, &drb_log, listen_address, DRB_PORT); - if (add_epoll(drb_fd, epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding DRB port to epoll\n"); - return SRSLTE_ERROR; - } - event_handler.insert({drb_fd, &drb}); - log->console("DRB handler listening on SCTP port %d\n", DRB_PORT); - - return SRSLTE_SUCCESS; - } + ttcn3_syssim(srslte::logger_file* logger_file_, ttcn3_ue* ue_); - ///< Function called by epoll timer handler when TTI timer expires - void new_tti_indication(uint64_t res) - { - tti = (tti + 1) % 10240; - - log->step(tti); - log->debug("Start TTI\n"); - - // Make sure to step SS - step_stack(); - - // inform UE about new TTI - ue->set_current_tti(tti); - - // check scheduled actions for this TTI - if (tti_actions.find(tti) != tti_actions.end()) { - while (!tti_actions[tti].empty()) { - log->debug("Running scheduled action\n"); - move_task_t task = std::move(tti_actions[tti].back()); - task(); - tti_actions[tti].pop_back(); - } - } - - // process events, if any - while (not event_queue.empty()) { - ss_events_t ev = event_queue.wait_pop(); - switch (ev) { - case UE_SWITCH_ON: - log->console("Switching on UE ID=%d\n", run_id); - ue->switch_on(); - break; - case UE_SWITCH_OFF: - log->console("Switching off UE ID=%d\n", run_id); - ue->switch_off(); - break; - case ENABLE_DATA: - log->console("Enabling data for UE ID=%d\n", run_id); - ue->enable_data(); - break; - case DISABLE_DATA: - log->console("Disabling data for UE ID=%d\n", run_id); - ue->disable_data(); - break; - } - } - - if (pcell_idx == -1) { - log->debug("Skipping TTI. Pcell not yet selected.\n"); - return; - } - - // DL/UL processing if UE has selected cell - dl_rnti = ue->get_dl_sched_rnti(tti); - if (SRSLTE_RNTI_ISSI(dl_rnti)) { - // deliver SIBs one after another - mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; - dl_grant.tti = tti; - dl_grant.pid = get_pid(tti); - dl_grant.rnti = dl_rnti; - dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->N_bytes; - dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); - ue->new_tb(dl_grant, cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->msg); - log->info("Delivered SIB%d for pcell_idx=%d\n", cells[pcell_idx]->sib_idx, pcell_idx); - cells[pcell_idx]->sib_idx = (cells[pcell_idx]->sib_idx + 1) % cells[pcell_idx]->sibs.size(); - } else if (SRSLTE_RNTI_ISRAR(dl_rnti)) { - if (prach_tti != -1) { - rar_tti = (prach_tti + 3) % 10240; - if (tti == rar_tti) { - send_rar(prach_preamble_index); - } - } - } else if (SRSLTE_RNTI_ISPA(dl_rnti)) { - log->debug("Searching for paging RNTI\n"); - // PCH will be triggered from SYSSIM after receiving Paging - } else if (SRSLTE_RNTI_ISUSER(dl_rnti)) { - // check if this is for contention resolution after PRACH/RAR - if (dl_rnti == crnti) { - log->debug("Searching for C-RNTI=%d\n", crnti); - - if (rar_tti != -1) { - msg3_tti = (rar_tti + 3) % 10240; - if (tti == msg3_tti) { - send_msg3_grant(); - rar_tti = -1; - } - } - } - - // check for SR - if (sr_tti != -1) { - send_sr_ul_grant(); - } - - if (dl_rnti != SRSLTE_INVALID_RNTI) { - log->debug("Searching for RNTI=%d\n", dl_rnti); - - // look for DL data to be send in each bearer and provide grant accordingly - for (int lcid = 0; lcid < SRSLTE_N_RADIO_BEARERS; lcid++) { - uint32_t buf_state = rlc.get_buffer_state(lcid); - // Schedule DL transmission if there is data in RLC buffer or we need to send Msg4 - if ((buf_state > 0 && bearer_follow_on_map[lcid] == false) || (msg3_tti != -1 && conres_id != 0)) { - log->debug("LCID=%d, buffer_state=%d\n", lcid, buf_state); - tx_payload_buffer.clear(); - - const uint32_t mac_header_size = 10; // Add MAC header (10 B for all subheaders, etc) - mac_msg_dl.init_tx(&tx_payload_buffer, buf_state + mac_header_size, false); - - // check if this is Msg4 that needs to contain the contention resolution ID CE - if (msg3_tti != -1 && lcid == 0 && conres_id != 0) { - if (mac_msg_dl.new_subh()) { - if (mac_msg_dl.get()->set_con_res_id(conres_id)) { - log->info("CE: Added Contention Resolution ID=0x%" PRIx64 "\n", conres_id); - } else { - log->error("CE: Setting Contention Resolution ID CE\n"); - } - conres_id = 0; // reset CR so it's not sent twice - } else { - log->error("CE: Setting Contention Resolution ID CE. No space for a subheader\n"); - } - msg3_tti = -1; - } - - bool has_single_sdu = false; - - // allocate SDUs - while (buf_state > 0) { // there is pending SDU to allocate - if (mac_msg_dl.new_subh()) { - int n = mac_msg_dl.get()->set_sdu(lcid, buf_state, &rlc); - if (n == -1) { - log->error("Error while adding SDU (%d B) to MAC PDU\n", buf_state); - mac_msg_dl.del_subh(); - } - - // update buffer state - buf_state = rlc.get_buffer_state(lcid); - - if (mac_msg_dl.nof_subh() == 1) { - has_single_sdu = true; - } else { - has_single_sdu = false; - } - } - } - - // Assemble entire MAC PDU - uint8_t* mac_pdu_ptr = mac_msg_dl.write_packet(log); - if (mac_pdu_ptr != nullptr) { - if (force_lcid != -1 && lcid == 0) { - if (has_single_sdu) { - log->info("Patched lcid in mac header to: %d\n", force_lcid); - mac_pdu_ptr[0] = (mac_pdu_ptr[0] & 0xe0) | (force_lcid & 0x1f); - } else if (mac_msg_dl.nof_subh() > 1) { - log->warning( - "Not patching lcid to %d in mac header (nof_subh == %d)\n", force_lcid, mac_msg_dl.nof_subh()); - } - } - log->info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU (%d B):\n", mac_msg_dl.get_pdu_len()); - - // Prepare MAC grant for CCCH - mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; - dl_grant.tti = tti; - dl_grant.pid = get_pid(tti); - dl_grant.rnti = dl_rnti; - dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len(); - dl_grant.tb[0].ndi_present = true; - dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); - - ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr); - } else { - log->error("Error writing DL MAC PDU\n"); - } - mac_msg_dl.reset(); - - } else if (bearer_follow_on_map[lcid]) { - log->info("Waiting for more PDUs for transmission on LCID=%d\n", lcid); - } - } - // Check if we need to provide a UL grant as well - } - } else { - log->debug("Not handling RNTI=%d\n", dl_rnti); - } - } - - void stop() - { - running = false; - ue->stop(); - } - - void reset() - { - log->info("Resetting SS\n"); - rlc.reset(); - pdcp.reset(); - cells.clear(); - pcell_idx = -1; - pending_bearer_config.clear(); - } + ~ttcn3_syssim(); - // Called from UT before starting testcase - void tc_start(const char* name) - { - // strip testsuite name - std::string tc_name = get_tc_name(name); - - // Make a copy of the UE args for this run - all_args_t local_args = args; - - // set up logging - if (args.log.filename == "stdout") { - logger = &logger_stdout; - } else { - logger_file->init(get_filename_with_tc_name(local_args.log.filename, run_id, tc_name).c_str(), -1); - logger = logger_file; - } - - log->info("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str()); - log->console("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str()); - - // Patch UE config - local_args.stack.pcap.filename = get_filename_with_tc_name(args.stack.pcap.filename, run_id, tc_name); - local_args.stack.pcap.nas_filename = get_filename_with_tc_name(args.stack.pcap.nas_filename, run_id, tc_name); - - // bring up UE - if (ue->init(local_args, logger, this, tc_name)) { - ue->stop(); - std::string err("Couldn't initialize UE.\n"); - log->error("%s\n", err.c_str()); - log->console("%s\n", err.c_str()); - return; - } - - // create and add TTI timer to epoll - if (add_epoll(timer_handler.get_timer_fd(), epoll_fd) != SRSLTE_SUCCESS) { - log->error("Error while adding TTI timer to epoll\n"); - } - event_handler.insert({timer_handler.get_timer_fd(), &timer_handler}); - } + int init(const all_args_t& args_); - // Called from UT to terminate the testcase - void tc_end() - { - log->info("Deinitializing UE ID=%d\n", run_id); - log->console("Deinitializing UE ID=%d\n", run_id); - ue->stop(); + void set_forced_lcid(int lcid); - // stop TTI timer - del_epoll(timer_handler.get_timer_fd(), epoll_fd); + int add_port_handler(); - logger_file->stop(); + ///< Function called by epoll timer handler when TTI timer expires + void new_tti_indication(uint64_t res); - run_id++; + void stop(); - // Reset SS' RLC and PDCP - reset(); - } + void reset(); - void power_off_ue() - { - // only return after new UE instance is up and running - } + // Called from UT before starting testcase + void tc_start(const char* name); + + // Called from UT to terminate the testcase + void tc_end(); + + void power_off_ue(); // Called from outside - void switch_on_ue() { event_queue.push(UE_SWITCH_ON); } + void switch_on_ue(); - void switch_off_ue() { event_queue.push(UE_SWITCH_OFF); } + void switch_off_ue(); - void enable_data() { event_queue.push(ENABLE_DATA); } + void enable_data(); - void disable_data() { event_queue.push(DISABLE_DATA); } + void disable_data(); // Called from PHY but always from the SS main thread with lock being hold - void prach_indication(uint32_t preamble_index_, const uint32_t& cell_id) - { - // verify that UE intends to send PRACH on current Pcell - if (cells[pcell_idx]->cell.id != cell_id) { - log->error("UE is attempting to PRACH on pci=%d, current Pcell=%d\n", cell_id, cells[pcell_idx]->cell.id); - return; - } - - // store TTI for providing UL grant for Msg3 transmission - prach_tti = tti; - prach_preamble_index = preamble_index_; - } + void prach_indication(uint32_t preamble_index_, const uint32_t& cell_id); // Called from PHY - void sr_req(uint32_t tti_tx) - { - log->info("Received SR from PHY\n"); - sr_tti = tti_tx; - } + void sr_req(uint32_t tti_tx); // Called from PHY - void tx_pdu(const uint8_t* payload, const int len, const uint32_t tx_tti) - { - if (payload == NULL) { - ss_mac_log->error("Received NULL as PDU payload. Dropping.\n"); - return; - } - - log->info_hex(payload, len, "UL MAC PDU (%d B):\n", len); - - // Parse MAC - mac_msg_ul.init_rx(len, true); - mac_msg_ul.parse_packet((uint8_t*)payload); - - while (mac_msg_ul.next()) { - assert(mac_msg_ul.get()); - if (mac_msg_ul.get()->is_sdu()) { - // Push PDU to our own RLC (needed to handle status reporting, etc. correctly - ss_mac_log->info_hex(mac_msg_ul.get()->get_sdu_ptr(), - mac_msg_ul.get()->get_payload_size(), - "Route UL PDU to LCID=%d (%d B)\n", - mac_msg_ul.get()->get_sdu_lcid(), - mac_msg_ul.get()->get_payload_size()); - rlc.write_pdu( - mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size()); - - // Save contention resolution if lcid == 0 - if (mac_msg_ul.get()->get_sdu_lcid() == 0) { - int nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN; - if (mac_msg_ul.get()->get_payload_size() >= (uint32_t)nbytes) { - uint8_t* ue_cri_ptr = (uint8_t*)&conres_id; - uint8_t* pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the - for (int i = 0; i < nbytes; i++) { - ue_cri_ptr[nbytes - i - 1] = pkt_ptr[i]; - } - ss_mac_log->info_hex(ue_cri_ptr, nbytes, "Contention resolution ID:\n"); - } else { - ss_mac_log->error("Received CCCH UL message of invalid size=%d bytes\n", - mac_msg_ul.get()->get_payload_size()); - } - } - } - } - mac_msg_ul.reset(); - - // Process CE after all SDUs because we need to update BSR after - bool bsr_received = false; - while (mac_msg_ul.next()) { - assert(mac_msg_ul.get()); - if (!mac_msg_ul.get()->is_sdu()) { - // Process MAC Control Element - bsr_received |= process_ce(mac_msg_ul.get()); - } - } - } + void tx_pdu(const uint8_t* payload, const int len, const uint32_t tx_tti); // Internal function called from main thread - void send_rar(uint32_t preamble_index) - { - log->info("Sending RAR for RAPID=%d\n", preamble_index); - - // Prepare RAR grant - uint8_t grant_buffer[64] = {}; - srslte_dci_rar_grant_t rar_grant = {}; - rar_grant.tpc_pusch = 3; - srslte_dci_rar_pack(&rar_grant, grant_buffer); - - // Create MAC PDU and add RAR subheader - srslte::rar_pdu rar_pdu; - rar_buffer.clear(); - - const int rar_pdu_len = 64; - rar_pdu.init_tx(&rar_buffer, rar_pdu_len); - rar_pdu.set_backoff(11); // Backoff of 480ms to prevent UE from PRACHing too fast - if (rar_pdu.new_subh()) { - rar_pdu.get()->set_rapid(preamble_index); - rar_pdu.get()->set_ta_cmd(0); - rar_pdu.get()->set_temp_crnti(crnti); - rar_pdu.get()->set_sched_grant(grant_buffer); - } - rar_pdu.write_packet(rar_buffer.msg); - rar_buffer.N_bytes = rar_pdu_len; - - // Prepare grant and pass all to MAC - mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; - dl_grant.tti = tti; - dl_grant.pid = get_pid(tti); - dl_grant.rnti = 0x1; // must be a valid RAR-RNTI - dl_grant.tb[0].tbs = rar_buffer.N_bytes; - dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); - - // send grant and pass payload to TB data (grant contains length) - ue->new_tb(dl_grant, rar_buffer.msg); - - // reset last PRACH transmission tti - prach_tti = -1; - } + void send_rar(uint32_t preamble_index); // Internal function called from main thread - void send_msg3_grant() - { - log->info("Sending Msg3 grant for C-RNTI=%d\n", crnti); - mac_interface_phy_lte::mac_grant_ul_t ul_grant = {}; - - ul_grant.tti_tx = (tti + 3) % 10240; - ul_grant.tb.tbs = 32; - ul_grant.tb.ndi_present = true; - ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti); - ul_grant.rnti = crnti; - ul_grant.pid = get_pid(tti); - ul_grant.is_rar = true; - - ue->new_grant_ul(ul_grant); - } + void send_msg3_grant(); // Internal function called from main thread - void send_sr_ul_grant() - { - // Provide new UL grant to UE - mac_interface_phy_lte::mac_grant_ul_t ul_grant = {}; - ul_grant.tti_tx = (tti + 3) % 10240; - ul_grant.tb.tbs = 100; // TODO: reasonable size? - ul_grant.tb.ndi_present = true; - ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti); - ul_grant.rnti = crnti; - ul_grant.pid = get_pid(tti); - - ue->new_grant_ul(ul_grant); - - sr_tti = -1; - } + void send_sr_ul_grant(); // internal function called from tx_pdu (called from main thread) - bool process_ce(srslte::sch_subh* subh) - { - uint16_t rnti = dl_rnti; - - uint32_t buff_size[4] = {0, 0, 0, 0}; - float phr = 0; - int32_t idx = 0; - uint16_t old_rnti = 0; - bool is_bsr = false; - switch (subh->ul_sch_ce_type()) { - case srslte::ul_sch_lcid::PHR_REPORT: - phr = subh->get_phr(); - ss_mac_log->info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr); - break; - case srslte::ul_sch_lcid::CRNTI: - old_rnti = subh->get_c_rnti(); - ss_mac_log->info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti); - break; - case srslte::ul_sch_lcid::TRUNC_BSR: - case srslte::ul_sch_lcid::SHORT_BSR: - idx = subh->get_bsr(buff_size); - if (idx == -1) { - ss_mac_log->error("Invalid Index Passed to lc groups\n"); - break; - } - ss_mac_log->info("CE: Received %s BSR rnti=0x%x, lcg=%d, value=%d\n", - subh->ul_sch_ce_type() == srslte::ul_sch_lcid::SHORT_BSR ? "Short" : "Trunc", - rnti, - idx, - buff_size[idx]); - is_bsr = true; - break; - case srslte::ul_sch_lcid::LONG_BSR: - subh->get_bsr(buff_size); - is_bsr = true; - ss_mac_log->info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n", - rnti, - buff_size[0], - buff_size[1], - buff_size[2], - buff_size[3]); - break; - case srslte::ul_sch_lcid::PADDING: - ss_mac_log->debug("CE: Received padding for rnti=0x%x\n", rnti); - break; - default: - ss_mac_log->error("CE: Invalid lcid=0x%x\n", subh->lcid_value()); - break; - } - return is_bsr; - } - - uint32_t get_pid(const uint32_t tti_) { return tti_ % SRSLTE_FDD_NOF_HARQ; } - - bool get_ndi_for_new_ul_tx(const uint32_t tti_) - { - // toggle NDI to always create new Tx - const uint32_t pid = get_pid(tti_); - last_ul_ndi[pid] = !last_ul_ndi[pid]; - log->info("UL-PID=%d NDI=%s\n", pid, last_ul_ndi[pid] ? "1" : "0"); - return last_ul_ndi[pid]; - } - - bool get_ndi_for_new_dl_tx(const uint32_t tti_) - { - // toggle NDI to always create new Tx - const uint32_t pid = get_pid(tti_); - last_dl_ndi[pid] = !last_dl_ndi[pid]; - log->info("DL-PID=%d NDI=%s\n", pid, last_dl_ndi[pid] ? "1" : "0"); - return last_dl_ndi[pid]; - } - - int run() - { - running = true; - - while (running) { - // wait for event - const int32_t epoll_timeout_ms = -1; - const uint32_t MAX_EVENTS = 1; - struct epoll_event events[MAX_EVENTS] = {}; - int nof_events = epoll_wait(epoll_fd, events, MAX_EVENTS, epoll_timeout_ms); - - // handle event - if (nof_events == -1) { - perror("epoll_wait() error"); - break; - } - if (nof_events == 0) { - printf("time out %f sec expired\n", epoll_timeout_ms / 1000.0); - continue; - } - - for (int i = 0; i < nof_events; ++i) { - if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { - ///< An error has occured on this fd, or the socket is not ready for reading - fprintf(stderr, "epoll error\n"); - close(events[i].data.fd); - continue; - } - - int fd = events[i].data.fd; - if (event_handler.find(fd) != event_handler.end()) { - event_handler[fd]->handle_event(fd, events[i], epoll_fd); - } - } - } - return SRSLTE_SUCCESS; - } - - uint32_t get_tti() { return tti; } - - void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel) {} + bool process_ce(srslte::sch_subh* subh); + + uint32_t get_pid(const uint32_t tti_); + + bool get_ndi_for_new_ul_tx(const uint32_t tti_); + + bool get_ndi_for_new_dl_tx(const uint32_t tti_); + + int run(); + + uint32_t get_tti(); + + void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel); void set_cell_config(const ttcn3_helpers::timing_info_t timing, const std::string cell_name, const uint32_t earfcn, const srslte_cell_t cell, - const float power) - { - if (timing.now) { - set_cell_config_impl(cell_name, earfcn, cell, power); - } else { - log->debug("Scheduling Cell configuration of %s for TTI=%d\n", cell_name.c_str(), timing.tti); - tti_actions[timing.tti].push_back( - [this, cell_name, earfcn, cell, power]() { set_cell_config_impl(cell_name, earfcn, cell, power); }); - } - } + const float power); void set_cell_config_impl(const std::string cell_name_, const uint32_t earfcn_, const srslte_cell_t cell_, - const float power_) - { - // check if cell already exists - if (not syssim_has_cell(cell_name_)) { - // insert new cell - log->info("Adding cell %s with cellId=%d and power=%.2f dBm\n", cell_name_.c_str(), cell_.id, power_); - unique_syssim_cell_t cell = unique_syssim_cell_t(new syssim_cell_t); - cell->name = cell_name_; - cell->cell = cell_; - cell->initial_power = power_; - cell->earfcn = earfcn_; - cells.push_back(std::move(cell)); - } else { - // cell is already there - log->info("Cell already there, reconfigure\n"); - } - - update_cell_map(); - } + const float power_); // internal function - bool syssim_has_cell(std::string cell_name) - { - for (auto& cell : cells) { - if (cell->name == cell_name) { - return true; - } - } - return false; - } - - void set_cell_attenuation(const ttcn3_helpers::timing_info_t timing, const std::string cell_name, const float value) - { - if (timing.now) { - set_cell_attenuation_impl(cell_name, value); - } else { - log->debug("Scheduling Cell attenuation reconfiguration of %s for TTI=%d\n", cell_name.c_str(), timing.tti); - tti_actions[timing.tti].push_back([this, cell_name, value]() { set_cell_attenuation_impl(cell_name, value); }); - } - } - - void set_cell_attenuation_impl(const std::string cell_name, const float value) - { - if (not syssim_has_cell(cell_name)) { - log->error("Can't set cell power. Cell not found.\n"); - } - - // update cell's power - for (auto& cell : cells) { - if (cell->name == cell_name) { - cell->attenuation = value; - break; - } - } - - update_cell_map(); - } + bool syssim_has_cell(std::string cell_name); + + void set_cell_attenuation(const ttcn3_helpers::timing_info_t timing, const std::string cell_name, const float value); + + void set_cell_attenuation_impl(const std::string cell_name, const float value); // Internal function - void update_cell_map() - { - // convert syssim cell list to phy cell list - { - lte_ttcn3_phy::cell_list_t phy_cells; - for (auto& ss_cell : cells) { - lte_ttcn3_phy::cell_t phy_cell = {}; - phy_cell.info = ss_cell->cell; - phy_cell.power = ss_cell->initial_power - ss_cell->attenuation; - phy_cell.earfcn = ss_cell->earfcn; - log->info("Configuring cell with PCI=%d with TxPower=%.2f\n", phy_cell.info.id, phy_cell.power); - phy_cells.push_back(phy_cell); - } - - // SYSSIM defines what cells the UE can connect to - ue->set_cell_map(phy_cells); - } - } - - bool have_valid_pcell() { return (pcell_idx >= 0 && pcell_idx < static_cast(cells.size())); } - - void add_bcch_dlsch_pdu(const string cell_name, unique_byte_buffer_t pdu) - { - if (not syssim_has_cell(cell_name)) { - log->error("Can't add BCCH to cell. Cell not found.\n"); - } - - // add SIB - for (auto& cell : cells) { - if (cell->name == cell_name) { - cell->sibs.push_back(std::move(pdu)); - break; - } - } - } - - void add_ccch_pdu(const ttcn3_helpers::timing_info_t timing, unique_byte_buffer_t pdu) - { - if (timing.now) { - // Add to SRB0 Tx queue - rlc.write_sdu(0, std::move(pdu)); - } else { - log->debug("Scheduling CCCH PDU for TTI=%d\n", timing.tti); - auto task = [this](srslte::unique_byte_buffer_t& pdu) { rlc.write_sdu(0, std::move(pdu)); }; - tti_actions[timing.tti].push_back(std::bind(task, std::move(pdu))); - } - } + void update_cell_map(); + + bool have_valid_pcell(); + + void add_bcch_dlsch_pdu(const string cell_name, unique_byte_buffer_t pdu); + + void add_ccch_pdu(const ttcn3_helpers::timing_info_t timing, unique_byte_buffer_t pdu); void - add_dcch_pdu(const ttcn3_helpers::timing_info_t timing, uint32_t lcid, unique_byte_buffer_t pdu, bool follow_on_flag) - { - if (timing.now) { - add_dcch_pdu_impl(lcid, std::move(pdu), follow_on_flag); - } else { - log->debug("Scheduling DCCH PDU for TTI=%d\n", timing.tti); - auto task = [this](uint32_t lcid, srslte::unique_byte_buffer_t& pdu, bool follow_on_flag) { - add_dcch_pdu_impl(lcid, std::move(pdu), follow_on_flag); - }; - tti_actions[timing.tti].push_back(std::bind(task, lcid, std::move(pdu), follow_on_flag)); - } - } - - void add_dcch_pdu_impl(uint32_t lcid, unique_byte_buffer_t pdu, bool follow_on_flag) - { - // push to PDCP and create DL grant for it - log->info("Writing PDU (%d B) to LCID=%d\n", pdu->N_bytes, lcid); - pdcp.write_sdu(lcid, std::move(pdu), true); - bearer_follow_on_map[lcid] = follow_on_flag; - } - - void add_pch_pdu(unique_byte_buffer_t pdu) - { - log->info("Received PCH PDU (%d B)\n", pdu->N_bytes); - - // Prepare MAC grant for PCH - mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; - dl_grant.tti = tti; - dl_grant.pid = get_pid(tti); - dl_grant.rnti = SRSLTE_PRNTI; - dl_grant.tb[0].tbs = pdu->N_bytes; - dl_grant.tb[0].ndi_present = true; - dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); - ue->new_tb(dl_grant, (const uint8_t*)pdu->msg); - } - - void step_stack() { stack.run_tti(); } - - void add_srb(const ttcn3_helpers::timing_info_t timing, const uint32_t lcid, const pdcp_config_t pdcp_config) - { - if (timing.now) { - add_srb_impl(lcid, pdcp_config); - } else { - log->debug("Scheduling SRB%d addition for TTI=%d\n", lcid, timing.tti); - tti_actions[timing.tti].push_back([this, lcid, pdcp_config]() { add_srb_impl(lcid, pdcp_config); }); - } - } - - void add_srb_impl(const uint32_t lcid, const pdcp_config_t pdcp_config) - { - log->info("Adding SRB%d\n", lcid); - pdcp.add_bearer(lcid, pdcp_config); - rlc.add_bearer(lcid, srslte::rlc_config_t::srb_config(lcid)); - } - - void reestablish_bearer(uint32_t lcid) - { - log->info("Reestablishing LCID=%d\n", lcid); - pdcp.reestablish(lcid); - rlc.reestablish(lcid); - } - - void del_srb(const ttcn3_helpers::timing_info_t timing, const uint32_t lcid) - { - if (timing.now) { - del_srb_impl(lcid); - } else { - log->debug("Scheduling SRB%d deletion for TTI=%d\n", lcid, timing.tti); - tti_actions[timing.tti].push_back([this, lcid]() { del_srb_impl(lcid); }); - } - } - - void del_srb_impl(uint32_t lcid) - { - log->info("Deleting SRB%d\n", lcid); - // Only delete SRB1/2 - if (lcid > 0) { - pdcp.del_bearer(lcid); - rlc.del_bearer(lcid); - } - } + add_dcch_pdu(const ttcn3_helpers::timing_info_t timing, uint32_t lcid, unique_byte_buffer_t pdu, bool follow_on_flag); + + void add_dcch_pdu_impl(uint32_t lcid, unique_byte_buffer_t pdu, bool follow_on_flag); + + void add_pch_pdu(unique_byte_buffer_t pdu); + + void step_stack(); + + void add_srb(const ttcn3_helpers::timing_info_t timing, const uint32_t lcid, const pdcp_config_t pdcp_config); + + void add_srb_impl(const uint32_t lcid, const pdcp_config_t pdcp_config); + + void reestablish_bearer(uint32_t lcid); + + void del_srb(const ttcn3_helpers::timing_info_t timing, const uint32_t lcid); + + void del_srb_impl(uint32_t lcid); // RRC interface for PDCP, PDCP calls RRC to push RRC SDU - void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) - { - log->info_hex(pdu->msg, - pdu->N_bytes, - "RRC SDU received for LCID=%d cell_id=%d (%d B)\n", - lcid, - cells[pcell_idx]->cell.id, - pdu->N_bytes); - - // check cell ID - if (cells[pcell_idx]->cell.id > 256) { - log->error("Cell ID too large to fit in single byte.\n"); - return; - } - - // We don't handle RRC, prepend LCID - pdu->msg--; - *pdu->msg = lcid; - pdu->N_bytes++; - - // prepend pcell PCID - pdu->msg--; - *pdu->msg = static_cast(cells[pcell_idx]->cell.id); - pdu->N_bytes++; - - // push content to Titan - srb.tx(std::move(pdu)); - } + void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu); // Not supported right now - void write_pdu_bcch_bch(unique_byte_buffer_t pdu) { log->error("%s not implemented.\n", __FUNCTION__); } - void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) { log->error("%s not implemented.\n", __FUNCTION__); } - void write_pdu_pcch(unique_byte_buffer_t pdu) { log->error("%s not implemented.\n", __FUNCTION__); } - void write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu) { log->error("%s not implemented.\n", __FUNCTION__); } - void max_retx_attempted() { log->error("%s not implemented.\n", __FUNCTION__); } - - std::string get_rb_name(uint32_t lcid) - { - if (lcid < rb_id_vec.size()) { - return rb_id_vec.at(lcid); - } - return std::string("RB"); - }; - - void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, bool blocking = true) - { - log->info_hex(sdu->msg, sdu->N_bytes, "Received SDU on LCID=%d\n", lcid); - - uint8_t* mac_pdu_ptr; - mac_pdu_ptr = mac_msg_dl.write_packet(log); - log->info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU:\n"); - - // Prepare MAC grant for CCCH - mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; - dl_grant.pid = get_pid(tti); - dl_grant.tti = tti; - dl_grant.rnti = dl_rnti; - dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len(); - dl_grant.tb[0].ndi_present = true; - dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); - - // Pass to UE - ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr); - } - - void discard_sdu(uint32_t lcid, uint32_t sn) {} - - bool rb_is_um(uint32_t lcid) { return false; } + void write_pdu_bcch_bch(unique_byte_buffer_t pdu); + void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu); + void write_pdu_pcch(unique_byte_buffer_t pdu); + void write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu); + void max_retx_attempted(); + + std::string get_rb_name(uint32_t lcid); + + void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, bool blocking = true); + + void discard_sdu(uint32_t lcid, uint32_t sn); + + bool rb_is_um(uint32_t lcid); void set_as_security(const ttcn3_helpers::timing_info_t timing, std::array k_rrc_enc_, @@ -1029,90 +185,22 @@ public: std::array k_up_enc_, const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_, - const ttcn3_helpers::pdcp_count_map_t bearers_) - { - if (timing.now) { - set_as_security_impl(k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_); - } else { - log->debug("Scheduling AS security configuration for TTI=%d\n", timing.tti); - tti_actions[timing.tti].push_back( - [this, k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_]() { - set_as_security_impl(k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_); - }); - } - } + const ttcn3_helpers::pdcp_count_map_t bearers_); void set_as_security_impl(std::array k_rrc_enc_, std::array k_rrc_int_, std::array k_up_enc_, const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_, - const ttcn3_helpers::pdcp_count_map_t bearers) - { - // store security config for later use (i.e. new bearer added) - sec_cfg = {.k_rrc_int = k_rrc_int_, - .k_rrc_enc = k_rrc_enc_, - .k_up_int = {}, - .k_up_enc = k_up_enc_, - .integ_algo = integ_algo_, - .cipher_algo = cipher_algo_}; - - for (auto& lcid : bearers) { - pdcp.config_security(lcid.rb_id, sec_cfg); - - log->info("Setting AS security for LCID=%d in DL direction from SN=%d onwards\n", lcid.rb_id, lcid.dl_value); - pdcp.enable_security_timed(lcid.rb_id, DIRECTION_TX, lcid.dl_value); - - log->info("Setting AS security for LCID=%d in UL direction from SN=%d onwards\n", lcid.rb_id, lcid.ul_value); - pdcp.enable_security_timed(lcid.rb_id, DIRECTION_RX, lcid.ul_value); - } - } - - void release_as_security(const ttcn3_helpers::timing_info_t timing) - { - if (timing.now) { - release_as_security_impl(); - } else { - log->debug("Scheduling Release of AS security for TTI=%d\n", timing.tti); - tti_actions[timing.tti].push_back([this]() { release_as_security_impl(); }); - } - } - - void release_as_security_impl() - { - log->info("Releasing AS security\n"); - pending_bearer_config.clear(); - } - - void select_cell(srslte_cell_t phy_cell) - { - // find matching cell in SS cell list - for (uint32_t i = 0; i < cells.size(); ++i) { - if (cells[i]->cell.id == phy_cell.id) { - pcell_idx = i; - log->info("New PCell: PCI=%d\n", cells[pcell_idx]->cell.id); - return; - } - } - } - - ttcn3_helpers::pdcp_count_map_t get_pdcp_count() - { - // prepare response to SS - std::vector bearers; - for (uint32_t i = 0; i < rb_id_vec.size(); i++) { - if (pdcp.is_lcid_enabled(i)) { - ttcn3_helpers::pdcp_count_t bearer; - uint16_t tmp; // not handling HFN - pdcp.get_bearer_status(i, &bearer.dl_value, &tmp, &bearer.ul_value, &tmp); - bearer.rb_is_srb = i <= 2; - bearer.rb_id = i; - log->debug("PDCP count lcid=%d, dl=%d, ul=%d\n", bearer.rb_id, bearer.dl_value, bearer.ul_value); - bearers.push_back(bearer); - } - } - return bearers; - } + const ttcn3_helpers::pdcp_count_map_t bearers); + + void release_as_security(const ttcn3_helpers::timing_info_t timing); + + void release_as_security_impl(); + + void select_cell(srslte_cell_t phy_cell); + + ttcn3_helpers::pdcp_count_map_t get_pdcp_count(); private: // SYS interface diff --git a/srsue/test/ttcn3/hdr/ttcn3_ue.h b/srsue/test/ttcn3/hdr/ttcn3_ue.h index 24f75dae9..621e01641 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ue.h +++ b/srsue/test/ttcn3/hdr/ttcn3_ue.h @@ -29,200 +29,48 @@ class ttcn3_ue : public phy_interface_syssim, public gw_interface_stack { public: - ttcn3_ue() : tft_matcher(&log) {} + ttcn3_ue(); virtual ~ttcn3_ue() {} - int init(all_args_t args, srslte::logger* logger_, syssim_interface_phy* syssim_, const std::string tc_name_) - { - logger = logger_; - - // Init UE log - log.init("UE ", logger); - log.set_level(srslte::LOG_LEVEL_INFO); - log.info("Built in %s mode using %s.\n", srslte_get_build_mode(), srslte_get_build_info()); - - // Patch args - args.stack.nas.force_imsi_attach = true; - args.stack.nas.eia = "1,2,3"; - args.stack.nas.eea = "0,1,2,3"; - - // Configure default parameters - args.stack.usim.algo = "xor"; - args.stack.usim.imei = "356092040793011"; - args.stack.usim.imsi = "001010123456789"; // Anritsu test USIM - args.stack.usim.k = "000102030405060708090A0B0C0D0E0F"; // fixed as per TS 34.108 Sec. 8.2 - - args.stack.rrc.feature_group = 0xe6041000; - args.stack.rrc.ue_category_str = SRSLTE_UE_CATEGORY_DEFAULT; - args.stack.rrc.ue_category = strtol(args.stack.rrc.ue_category_str.c_str(), nullptr, 10); - args.stack.rrc.nof_supported_bands = 1; - args.stack.rrc.supported_bands[0] = 7; - args.stack.rrc.release = 8; - args.stack.rrc.mbms_service_id = -1; - - args.phy.dl_earfcn = "3400"; - args.rf.type = "none"; - args.stack.type = "lte"; - args.phy.type = "lte_ttcn3"; - - // Instantiate layers and stack together our UE - if (args.stack.type == "lte") { - stack = std::unique_ptr(new ue_stack_lte()); - if (!stack) { - log.console("Error creating LTE stack instance.\n"); - return SRSLTE_ERROR; - } - - phy = std::unique_ptr(new srsue::lte_ttcn3_phy(logger)); - if (!phy) { - log.console("Error creating LTE PHY instance.\n"); - return SRSLTE_ERROR; - } - } else { - log.console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str()); - return SRSLTE_ERROR; - } - - // init layers - if (phy->init(args.phy, stack.get(), syssim_)) { - log.console("Error initializing PHY.\n"); - return SRSLTE_ERROR; - } - - if (stack->init(args.stack, logger, phy.get(), this)) { - log.console("Error initializing stack.\n"); - return SRSLTE_ERROR; - } - - return SRSLTE_SUCCESS; - } - - void stop() - { - if (stack) { - stack->stop(); - } - - if (phy) { - phy->stop(); - } - } - - bool switch_on() { return stack->switch_on(); } - - bool switch_off() { return stack->switch_off(); } - - bool enable_data() { return stack->enable_data(); } - - bool disable_data() { return stack->disable_data(); } + int init(all_args_t args, srslte::logger* logger_, syssim_interface_phy* syssim_, const std::string tc_name_); + + void stop(); + + bool switch_on(); + + bool switch_off(); + + bool enable_data(); + + bool disable_data(); // The interface for SYSSIM - void set_cell_map(lte_ttcn3_phy::cell_list_t phy_cell_map) { phy->set_cell_map(phy_cell_map); } + void set_cell_map(lte_ttcn3_phy::cell_list_t phy_cell_map); - void new_grant_ul(const srsue::mac_interface_phy_lte::mac_grant_ul_t grant) { phy->new_grant_ul(grant); } + void new_grant_ul(const srsue::mac_interface_phy_lte::mac_grant_ul_t grant); - void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t mac_grant, const uint8_t* data) - { - phy->new_tb(mac_grant, data); - } + void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t mac_grant, const uint8_t* data); - void set_current_tti(uint32_t tti) { phy->set_current_tti(tti); } + void set_current_tti(uint32_t tti); - uint16_t get_dl_sched_rnti(uint32_t tti) { return stack->get_dl_sched_rnti(tti); } + uint16_t get_dl_sched_rnti(uint32_t tti); // GW interface - void add_mch_port(uint32_t lcid, uint32_t port) {} - void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) - { - log.debug_hex(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d\n", pdu->N_bytes, lcid); - switch (test_loop_mode) { - case TEST_LOOP_INACTIVE: - log.warning("Test loop inactive. Dropping PDU.\n"); - break; - case TEST_LOOP_MODE_A_ACTIVE: - log.error("Test loop mode A not implemented. Dropping PDU.\n"); - break; - case TEST_LOOP_MODE_B_ACTIVE: - // Section 5.4.4 in TS 36.509 - if (pdu_delay_timer.is_running()) { - pdu_queue[lcid].push(std::move(pdu)); - } else { - if (pdu_delay_timer.is_valid()) { - pdu_queue[lcid].push(std::move(pdu)); - pdu_delay_timer.run(); // timer is already set - } else { - loop_back_pdu_with_tft(lcid, std::move(pdu)); - } - } - break; - case TEST_LOOP_MODE_C_ACTIVE: - log.error("Test loop mode C not implemented. Dropping PDU.\n"); - break; - } - } - void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {} - int setup_if_addr(uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) { return 0; } + void add_mch_port(uint32_t lcid, uint32_t port); + void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu); + void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu); + int setup_if_addr(uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str); int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const uint8_t& lcid, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) - { - return tft_matcher.apply_traffic_flow_template(eps_bearer_id, lcid, tft); - } - - void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_ = 0) - { - test_loop_mode = mode; - switch (test_loop_mode) { - case TEST_LOOP_INACTIVE: - // deactivate timer - log.info("Deactivating Test Loop Mode\n"); - pdu_delay_timer.release(); - break; - case TEST_LOOP_MODE_A_ACTIVE: - log.error("Test loop mode A not implemented\n"); - break; - case TEST_LOOP_MODE_B_ACTIVE: - log.info("Activating Test loop mode B with %d ms PDU delay\n", ip_pdu_delay_ms_); - // only create timer if needed - if (ip_pdu_delay_ms_ > 0) { - pdu_delay_timer = stack->get_unique_timer(); - pdu_delay_timer.set(ip_pdu_delay_ms_, [this](uint32_t tid) { timer_expired(tid); }); - } - break; - case TEST_LOOP_MODE_C_ACTIVE: - log.error("Test loop mode A not implemented\n"); - break; - } - } - - void timer_expired(uint32_t timeout_id) - { - if (timeout_id == pdu_delay_timer.id()) { - log.info("Testmode B PDU delay timer expired\n"); - for (auto& bearer_pdu_queue : pdu_queue) { - log.info("Delivering %zd buffered PDUs for LCID=%d\n", bearer_pdu_queue.second.size(), bearer_pdu_queue.first); - while (not pdu_queue.empty()) { - srslte::unique_byte_buffer_t pdu; - bearer_pdu_queue.second.try_pop(&pdu); - loop_back_pdu_with_tft(bearer_pdu_queue.first, std::move(pdu)); - } - } - } - } - - void loop_back_pdu_with_tft(uint32_t input_lcid, srslte::unique_byte_buffer_t pdu) - { - uint8_t output_lcid = tft_matcher.check_tft_filter_match(pdu); - log.info_hex(pdu->msg, - pdu->N_bytes, - "Rx PDU (%d B) on lcid=%d, looping back to lcid=%d\n", - pdu->N_bytes, - input_lcid, - output_lcid); - stack->write_sdu(input_lcid, std::move(pdu), false); - } + const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); + + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_ = 0); + + void timer_expired(uint32_t timeout_id); + + void loop_back_pdu_with_tft(uint32_t input_lcid, srslte::unique_byte_buffer_t pdu); private: std::unique_ptr phy; @@ -240,4 +88,4 @@ private: all_args_t args = {}; }; -#endif // SRSUE_TTCN3_UE_H \ No newline at end of file +#endif // SRSUE_TTCN3_UE_H diff --git a/srsue/test/ttcn3/src/CMakeLists.txt b/srsue/test/ttcn3/src/CMakeLists.txt index eab13456e..c0ef99fdf 100644 --- a/srsue/test/ttcn3/src/CMakeLists.txt +++ b/srsue/test/ttcn3/src/CMakeLists.txt @@ -18,7 +18,8 @@ # and at http://www.gnu.org/licenses/. # -add_executable(ttcn3_dut ttcn3_dut.cc lte_ttcn3_phy.cc) +set(SOURCES ttcn3_ue.cc ttcn3_syssim.cc) +add_executable(ttcn3_dut ttcn3_dut.cc lte_ttcn3_phy.cc ${SOURCES}) target_link_libraries(ttcn3_dut srsue_stack srsue_upper srsue_rrc @@ -33,4 +34,4 @@ target_link_libraries(ttcn3_dut srsue_stack rrc_asn1 sctp ${Boost_LIBRARIES}) -include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr) \ No newline at end of file +include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr) diff --git a/srsue/test/ttcn3/src/ttcn3_syssim.cc b/srsue/test/ttcn3/src/ttcn3_syssim.cc new file mode 100644 index 000000000..804a013ac --- /dev/null +++ b/srsue/test/ttcn3/src/ttcn3_syssim.cc @@ -0,0 +1,1151 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/test/ttcn3/hdr/ttcn3_syssim.h" +#include "dut_utils.h" +#include "srslte/mac/pdu_queue.h" +#include "srslte/test/ue_test_interfaces.h" +#include "srslte/upper/pdcp.h" +#include "srslte/upper/rlc.h" +#include "ttcn3_common.h" +#include "ttcn3_drb_interface.h" +#include "ttcn3_ip_ctrl_interface.h" +#include "ttcn3_ip_sock_interface.h" +#include "ttcn3_srb_interface.h" +#include "ttcn3_sys_interface.h" +#include "ttcn3_ue.h" +#include "ttcn3_ut_interface.h" +#include + +ttcn3_syssim::ttcn3_syssim(srslte::logger_file* logger_file_, ttcn3_ue* ue_) : + log{"SS "}, + mac_msg_ul(20, ss_mac_log), + mac_msg_dl(20, ss_mac_log), + pdus(128), + logger(logger_file_), + logger_file(logger_file_), + pool(byte_buffer_pool::get_instance()), + ue(ue_), + rlc(ss_rlc_log->get_service_name().c_str()), + signal_handler(&running), + timer_handler(create_tti_timer(), [&](uint64_t res) { new_tti_indication(res); }), + pdcp(&stack, ss_pdcp_log->get_service_name().c_str()) +{ + if (ue->init(all_args_t{}, logger, this, "INIT_TEST") != SRSLTE_SUCCESS) { + ue->stop(); + fprintf(stderr, "Couldn't initialize UE.\n"); + } +} + +ttcn3_syssim::~ttcn3_syssim(){}; + +int ttcn3_syssim::init(const all_args_t& args_) +{ + args = args_; + + // Make sure to get SS logging as well + if (args.log.filename == "stdout") { + logger = &logger_stdout; + } + srslte::logmap::set_default_logger(logger); + + // init and configure logging + srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS ", logger, true}}); + ut_log.init("UT ", logger); + sys_log.init("SYS ", logger); + ip_sock_log.init("IP_S", logger); + ip_ctrl_log.init("IP_C", logger); + srb_log.init("SRB ", logger); + drb_log.init("DRB ", logger); + srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS-RLC", logger}}); + srslte::logmap::register_log(std::unique_ptr{new log_filter{"SS-PDCP", logger}}); + + log->set_level(args.log.all_level); + ut_log.set_level(args.log.all_level); + sys_log.set_level(args.log.all_level); + ip_sock_log.set_level(args.log.all_level); + ip_ctrl_log.set_level(args.log.all_level); + srb_log.set_level(args.log.all_level); + drb_log.set_level(args.log.all_level); + ss_mac_log->set_level(args.log.all_level); + ss_rlc_log->set_level(args.log.all_level); + ss_pdcp_log->set_level(args.log.all_level); + + log->set_hex_limit(args.log.all_hex_limit); + ut_log.set_hex_limit(args.log.all_hex_limit); + sys_log.set_hex_limit(args.log.all_hex_limit); + ip_sock_log.set_hex_limit(args.log.all_hex_limit); + ip_ctrl_log.set_hex_limit(args.log.all_hex_limit); + srb_log.set_hex_limit(args.log.all_hex_limit); + drb_log.set_hex_limit(args.log.all_hex_limit); + ss_mac_log->set_hex_limit(args.log.all_hex_limit); + ss_rlc_log->set_hex_limit(args.log.all_hex_limit); + ss_pdcp_log->set_hex_limit(args.log.all_hex_limit); + + // Init epoll socket and add FDs + epoll_fd = epoll_create1(0); + if (epoll_fd == -1) { + log->error("Error creating epoll\n"); + return SRSLTE_ERROR; + } + + // add signalfd + signal_fd = add_signalfd(); + if (add_epoll(signal_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding signalfd to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({signal_fd, &signal_handler}); + + // init system interfaces to tester + if (add_port_handler() != SRSLTE_SUCCESS) { + log->error("Error creating port handlers\n"); + return SRSLTE_ERROR; + } + + // Init SS layers + pdus.init(this, log); + rlc.init(&pdcp, this, &stack.timers, 0 /* RB_ID_SRB0 */); + pdcp.init(&rlc, this, nullptr); + + return SRSLTE_SUCCESS; +} + +void ttcn3_syssim::set_forced_lcid(int lcid) +{ + force_lcid = lcid; +} + +int ttcn3_syssim::add_port_handler() +{ + // UT port + int ut_fd = ut.init(this, &ut_log, listen_address, UT_PORT); + if (add_epoll(ut_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding UT port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({ut_fd, &ut}); + log->console("UT handler listening on SCTP port %d\n", UT_PORT); + + // SYS port + int sys_fd = sys.init(this, &sys_log, listen_address, SYS_PORT); + if (add_epoll(sys_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding SYS port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({sys_fd, &sys}); + log->console("SYS handler listening on SCTP port %d\n", SYS_PORT); + + // IPsock port + int ip_sock_fd = ip_sock.init(&ip_sock_log, listen_address, IPSOCK_PORT); + if (add_epoll(ip_sock_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding IP sock port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({ip_sock_fd, &ip_sock}); + log->console("IPSOCK handler listening on SCTP port %d\n", IPSOCK_PORT); + + // IPctrl port + int ip_ctrl_fd = ip_ctrl.init(&ip_ctrl_log, listen_address, IPCTRL_PORT); + if (add_epoll(ip_ctrl_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding IP ctrl port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({ip_ctrl_fd, &ip_ctrl}); + log->console("IPCTRL handler listening on SCTP port %d\n", IPCTRL_PORT); + + // add SRB fd + int srb_fd = srb.init(this, &srb_log, listen_address, SRB_PORT); + if (add_epoll(srb_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding SRB port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({srb_fd, &srb}); + log->console("SRB handler listening on SCTP port %d\n", SRB_PORT); + + // add DRB fd + int drb_fd = drb.init(this, &drb_log, listen_address, DRB_PORT); + if (add_epoll(drb_fd, epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding DRB port to epoll\n"); + return SRSLTE_ERROR; + } + event_handler.insert({drb_fd, &drb}); + log->console("DRB handler listening on SCTP port %d\n", DRB_PORT); + + return SRSLTE_SUCCESS; +} + +///< Function called by epoll timer handler when TTI timer expires +void ttcn3_syssim::new_tti_indication(uint64_t res) +{ + tti = (tti + 1) % 10240; + + log->step(tti); + log->debug("Start TTI\n"); + + // Make sure to step SS + step_stack(); + + // inform UE about new TTI + ue->set_current_tti(tti); + + // check scheduled actions for this TTI + if (tti_actions.find(tti) != tti_actions.end()) { + while (!tti_actions[tti].empty()) { + log->debug("Running scheduled action\n"); + move_task_t task = std::move(tti_actions[tti].back()); + task(); + tti_actions[tti].pop_back(); + } + } + + // process events, if any + while (not event_queue.empty()) { + ss_events_t ev = event_queue.wait_pop(); + switch (ev) { + case UE_SWITCH_ON: + log->console("Switching on UE ID=%d\n", run_id); + ue->switch_on(); + break; + case UE_SWITCH_OFF: + log->console("Switching off UE ID=%d\n", run_id); + ue->switch_off(); + break; + case ENABLE_DATA: + log->console("Enabling data for UE ID=%d\n", run_id); + ue->enable_data(); + break; + case DISABLE_DATA: + log->console("Disabling data for UE ID=%d\n", run_id); + ue->disable_data(); + break; + } + } + + if (pcell_idx == -1) { + log->debug("Skipping TTI. Pcell not yet selected.\n"); + return; + } + + // DL/UL processing if UE has selected cell + dl_rnti = ue->get_dl_sched_rnti(tti); + if (SRSLTE_RNTI_ISSI(dl_rnti)) { + // deliver SIBs one after another + mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; + dl_grant.tti = tti; + dl_grant.pid = get_pid(tti); + dl_grant.rnti = dl_rnti; + dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->N_bytes; + dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); + ue->new_tb(dl_grant, cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->msg); + log->info("Delivered SIB%d for pcell_idx=%d\n", cells[pcell_idx]->sib_idx, pcell_idx); + cells[pcell_idx]->sib_idx = (cells[pcell_idx]->sib_idx + 1) % cells[pcell_idx]->sibs.size(); + } else if (SRSLTE_RNTI_ISRAR(dl_rnti)) { + if (prach_tti != -1) { + rar_tti = (prach_tti + 3) % 10240; + if (tti == rar_tti) { + send_rar(prach_preamble_index); + } + } + } else if (SRSLTE_RNTI_ISPA(dl_rnti)) { + log->debug("Searching for paging RNTI\n"); + // PCH will be triggered from SYSSIM after receiving Paging + } else if (SRSLTE_RNTI_ISUSER(dl_rnti)) { + // check if this is for contention resolution after PRACH/RAR + if (dl_rnti == crnti) { + log->debug("Searching for C-RNTI=%d\n", crnti); + + if (rar_tti != -1) { + msg3_tti = (rar_tti + 3) % 10240; + if (tti == msg3_tti) { + send_msg3_grant(); + rar_tti = -1; + } + } + } + + // check for SR + if (sr_tti != -1) { + send_sr_ul_grant(); + } + + if (dl_rnti != SRSLTE_INVALID_RNTI) { + log->debug("Searching for RNTI=%d\n", dl_rnti); + + // look for DL data to be send in each bearer and provide grant accordingly + for (int lcid = 0; lcid < SRSLTE_N_RADIO_BEARERS; lcid++) { + uint32_t buf_state = rlc.get_buffer_state(lcid); + // Schedule DL transmission if there is data in RLC buffer or we need to send Msg4 + if ((buf_state > 0 && bearer_follow_on_map[lcid] == false) || (msg3_tti != -1 && conres_id != 0)) { + log->debug("LCID=%d, buffer_state=%d\n", lcid, buf_state); + tx_payload_buffer.clear(); + + const uint32_t mac_header_size = 10; // Add MAC header (10 B for all subheaders, etc) + mac_msg_dl.init_tx(&tx_payload_buffer, buf_state + mac_header_size, false); + + // check if this is Msg4 that needs to contain the contention resolution ID CE + if (msg3_tti != -1 && lcid == 0 && conres_id != 0) { + if (mac_msg_dl.new_subh()) { + if (mac_msg_dl.get()->set_con_res_id(conres_id)) { + log->info("CE: Added Contention Resolution ID=0x%" PRIx64 "\n", conres_id); + } else { + log->error("CE: Setting Contention Resolution ID CE\n"); + } + conres_id = 0; // reset CR so it's not sent twice + } else { + log->error("CE: Setting Contention Resolution ID CE. No space for a subheader\n"); + } + msg3_tti = -1; + } + + bool has_single_sdu = false; + + // allocate SDUs + while (buf_state > 0) { // there is pending SDU to allocate + if (mac_msg_dl.new_subh()) { + int n = mac_msg_dl.get()->set_sdu(lcid, buf_state, &rlc); + if (n == -1) { + log->error("Error while adding SDU (%d B) to MAC PDU\n", buf_state); + mac_msg_dl.del_subh(); + } + + // update buffer state + buf_state = rlc.get_buffer_state(lcid); + + if (mac_msg_dl.nof_subh() == 1) { + has_single_sdu = true; + } else { + has_single_sdu = false; + } + } + } + + // Assemble entire MAC PDU + uint8_t* mac_pdu_ptr = mac_msg_dl.write_packet(log); + if (mac_pdu_ptr != nullptr) { + if (force_lcid != -1 && lcid == 0) { + if (has_single_sdu) { + log->info("Patched lcid in mac header to: %d\n", force_lcid); + mac_pdu_ptr[0] = (mac_pdu_ptr[0] & 0xe0) | (force_lcid & 0x1f); + } else if (mac_msg_dl.nof_subh() > 1) { + log->warning( + "Not patching lcid to %d in mac header (nof_subh == %d)\n", force_lcid, mac_msg_dl.nof_subh()); + } + } + log->info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU (%d B):\n", mac_msg_dl.get_pdu_len()); + + // Prepare MAC grant for CCCH + mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; + dl_grant.tti = tti; + dl_grant.pid = get_pid(tti); + dl_grant.rnti = dl_rnti; + dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len(); + dl_grant.tb[0].ndi_present = true; + dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); + + ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr); + } else { + log->error("Error writing DL MAC PDU\n"); + } + mac_msg_dl.reset(); + + } else if (bearer_follow_on_map[lcid]) { + log->info("Waiting for more PDUs for transmission on LCID=%d\n", lcid); + } + } + // Check if we need to provide a UL grant as well + } + } else { + log->debug("Not handling RNTI=%d\n", dl_rnti); + } +} + +void ttcn3_syssim::stop() +{ + running = false; + ue->stop(); +} + +void ttcn3_syssim::reset() +{ + log->info("Resetting SS\n"); + rlc.reset(); + pdcp.reset(); + cells.clear(); + pcell_idx = -1; + pending_bearer_config.clear(); +} + +// Called from UT before starting testcase +void ttcn3_syssim::tc_start(const char* name) +{ + // strip testsuite name + std::string tc_name = get_tc_name(name); + + // Make a copy of the UE args for this run + all_args_t local_args = args; + + // set up logging + if (args.log.filename == "stdout") { + logger = &logger_stdout; + } else { + logger_file->init(get_filename_with_tc_name(local_args.log.filename, run_id, tc_name).c_str(), -1); + logger = logger_file; + } + + log->info("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str()); + log->console("Initializing UE ID=%d for TC=%s\n", run_id, tc_name.c_str()); + + // Patch UE config + local_args.stack.pcap.filename = get_filename_with_tc_name(args.stack.pcap.filename, run_id, tc_name); + local_args.stack.pcap.nas_filename = get_filename_with_tc_name(args.stack.pcap.nas_filename, run_id, tc_name); + + // bring up UE + if (ue->init(local_args, logger, this, tc_name)) { + ue->stop(); + std::string err("Couldn't initialize UE.\n"); + log->error("%s\n", err.c_str()); + log->console("%s\n", err.c_str()); + return; + } + + // create and add TTI timer to epoll + if (add_epoll(timer_handler.get_timer_fd(), epoll_fd) != SRSLTE_SUCCESS) { + log->error("Error while adding TTI timer to epoll\n"); + } + event_handler.insert({timer_handler.get_timer_fd(), &timer_handler}); +} + +// Called from UT to terminate the testcase +void ttcn3_syssim::tc_end() +{ + log->info("Deinitializing UE ID=%d\n", run_id); + log->console("Deinitializing UE ID=%d\n", run_id); + ue->stop(); + + // stop TTI timer + del_epoll(timer_handler.get_timer_fd(), epoll_fd); + + logger_file->stop(); + + run_id++; + + // Reset SS' RLC and PDCP + reset(); +} + +void ttcn3_syssim::power_off_ue() +{ + // only return after new UE instance is up and running +} + +// Called from outside +void ttcn3_syssim::switch_on_ue() +{ + event_queue.push(UE_SWITCH_ON); +} + +void ttcn3_syssim::switch_off_ue() +{ + event_queue.push(UE_SWITCH_OFF); +} + +void ttcn3_syssim::enable_data() +{ + event_queue.push(ENABLE_DATA); +} + +void ttcn3_syssim::disable_data() +{ + event_queue.push(DISABLE_DATA); +} + +// Called from PHY but always from the SS main thread with lock being hold +void ttcn3_syssim::prach_indication(uint32_t preamble_index_, const uint32_t& cell_id) +{ + // verify that UE intends to send PRACH on current Pcell + if (cells[pcell_idx]->cell.id != cell_id) { + log->error("UE is attempting to PRACH on pci=%d, current Pcell=%d\n", cell_id, cells[pcell_idx]->cell.id); + return; + } + + // store TTI for providing UL grant for Msg3 transmission + prach_tti = tti; + prach_preamble_index = preamble_index_; +} + +// Called from PHY +void ttcn3_syssim::sr_req(uint32_t tti_tx) +{ + log->info("Received SR from PHY\n"); + sr_tti = tti_tx; +} + +// Called from PHY +void ttcn3_syssim::tx_pdu(const uint8_t* payload, const int len, const uint32_t tx_tti) +{ + if (payload == NULL) { + ss_mac_log->error("Received NULL as PDU payload. Dropping.\n"); + return; + } + + log->info_hex(payload, len, "UL MAC PDU (%d B):\n", len); + + // Parse MAC + mac_msg_ul.init_rx(len, true); + mac_msg_ul.parse_packet((uint8_t*)payload); + + while (mac_msg_ul.next()) { + assert(mac_msg_ul.get()); + if (mac_msg_ul.get()->is_sdu()) { + // Push PDU to our own RLC (needed to handle status reporting, etc. correctly + ss_mac_log->info_hex(mac_msg_ul.get()->get_sdu_ptr(), + mac_msg_ul.get()->get_payload_size(), + "Route UL PDU to LCID=%d (%d B)\n", + mac_msg_ul.get()->get_sdu_lcid(), + mac_msg_ul.get()->get_payload_size()); + rlc.write_pdu( + mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size()); + + // Save contention resolution if lcid == 0 + if (mac_msg_ul.get()->get_sdu_lcid() == 0) { + int nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN; + if (mac_msg_ul.get()->get_payload_size() >= (uint32_t)nbytes) { + uint8_t* ue_cri_ptr = (uint8_t*)&conres_id; + uint8_t* pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the + for (int i = 0; i < nbytes; i++) { + ue_cri_ptr[nbytes - i - 1] = pkt_ptr[i]; + } + ss_mac_log->info_hex(ue_cri_ptr, nbytes, "Contention resolution ID:\n"); + } else { + ss_mac_log->error("Received CCCH UL message of invalid size=%d bytes\n", + mac_msg_ul.get()->get_payload_size()); + } + } + } + } + mac_msg_ul.reset(); + + // Process CE after all SDUs because we need to update BSR after + bool bsr_received = false; + while (mac_msg_ul.next()) { + assert(mac_msg_ul.get()); + if (!mac_msg_ul.get()->is_sdu()) { + // Process MAC Control Element + bsr_received |= process_ce(mac_msg_ul.get()); + } + } +} + +// Internal function called from main thread +void ttcn3_syssim::send_rar(uint32_t preamble_index) +{ + log->info("Sending RAR for RAPID=%d\n", preamble_index); + + // Prepare RAR grant + uint8_t grant_buffer[64] = {}; + srslte_dci_rar_grant_t rar_grant = {}; + rar_grant.tpc_pusch = 3; + srslte_dci_rar_pack(&rar_grant, grant_buffer); + + // Create MAC PDU and add RAR subheader + srslte::rar_pdu rar_pdu; + rar_buffer.clear(); + + const int rar_pdu_len = 64; + rar_pdu.init_tx(&rar_buffer, rar_pdu_len); + rar_pdu.set_backoff(11); // Backoff of 480ms to prevent UE from PRACHing too fast + if (rar_pdu.new_subh()) { + rar_pdu.get()->set_rapid(preamble_index); + rar_pdu.get()->set_ta_cmd(0); + rar_pdu.get()->set_temp_crnti(crnti); + rar_pdu.get()->set_sched_grant(grant_buffer); + } + rar_pdu.write_packet(rar_buffer.msg); + rar_buffer.N_bytes = rar_pdu_len; + + // Prepare grant and pass all to MAC + mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; + dl_grant.tti = tti; + dl_grant.pid = get_pid(tti); + dl_grant.rnti = 0x1; // must be a valid RAR-RNTI + dl_grant.tb[0].tbs = rar_buffer.N_bytes; + dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); + + // send grant and pass payload to TB data (grant contains length) + ue->new_tb(dl_grant, rar_buffer.msg); + + // reset last PRACH transmission tti + prach_tti = -1; +} + +// Internal function called from main thread +void ttcn3_syssim::send_msg3_grant() +{ + log->info("Sending Msg3 grant for C-RNTI=%d\n", crnti); + mac_interface_phy_lte::mac_grant_ul_t ul_grant = {}; + + ul_grant.tti_tx = (tti + 3) % 10240; + ul_grant.tb.tbs = 32; + ul_grant.tb.ndi_present = true; + ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti); + ul_grant.rnti = crnti; + ul_grant.pid = get_pid(tti); + ul_grant.is_rar = true; + + ue->new_grant_ul(ul_grant); +} + +// Internal function called from main thread +void ttcn3_syssim::send_sr_ul_grant() +{ + // Provide new UL grant to UE + mac_interface_phy_lte::mac_grant_ul_t ul_grant = {}; + ul_grant.tti_tx = (tti + 3) % 10240; + ul_grant.tb.tbs = 100; // TODO: reasonable size? + ul_grant.tb.ndi_present = true; + ul_grant.tb.ndi = get_ndi_for_new_ul_tx(tti); + ul_grant.rnti = crnti; + ul_grant.pid = get_pid(tti); + + ue->new_grant_ul(ul_grant); + + sr_tti = -1; +} + +// internal function called from tx_pdu (called from main thread) +bool ttcn3_syssim::process_ce(srslte::sch_subh* subh) +{ + uint16_t rnti = dl_rnti; + + uint32_t buff_size[4] = {0, 0, 0, 0}; + float phr = 0; + int32_t idx = 0; + uint16_t old_rnti = 0; + bool is_bsr = false; + switch (subh->ul_sch_ce_type()) { + case srslte::ul_sch_lcid::PHR_REPORT: + phr = subh->get_phr(); + ss_mac_log->info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr); + break; + case srslte::ul_sch_lcid::CRNTI: + old_rnti = subh->get_c_rnti(); + ss_mac_log->info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti); + break; + case srslte::ul_sch_lcid::TRUNC_BSR: + case srslte::ul_sch_lcid::SHORT_BSR: + idx = subh->get_bsr(buff_size); + if (idx == -1) { + ss_mac_log->error("Invalid Index Passed to lc groups\n"); + break; + } + ss_mac_log->info("CE: Received %s BSR rnti=0x%x, lcg=%d, value=%d\n", + subh->ul_sch_ce_type() == srslte::ul_sch_lcid::SHORT_BSR ? "Short" : "Trunc", + rnti, + idx, + buff_size[idx]); + is_bsr = true; + break; + case srslte::ul_sch_lcid::LONG_BSR: + subh->get_bsr(buff_size); + is_bsr = true; + ss_mac_log->info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n", + rnti, + buff_size[0], + buff_size[1], + buff_size[2], + buff_size[3]); + break; + case srslte::ul_sch_lcid::PADDING: + ss_mac_log->debug("CE: Received padding for rnti=0x%x\n", rnti); + break; + default: + ss_mac_log->error("CE: Invalid lcid=0x%x\n", subh->lcid_value()); + break; + } + return is_bsr; +} + +uint32_t ttcn3_syssim::get_pid(const uint32_t tti_) +{ + return tti_ % SRSLTE_FDD_NOF_HARQ; +} + +bool ttcn3_syssim::get_ndi_for_new_ul_tx(const uint32_t tti_) +{ + // toggle NDI to always create new Tx + const uint32_t pid = get_pid(tti_); + last_ul_ndi[pid] = !last_ul_ndi[pid]; + log->info("UL-PID=%d NDI=%s\n", pid, last_ul_ndi[pid] ? "1" : "0"); + return last_ul_ndi[pid]; +} + +bool ttcn3_syssim::get_ndi_for_new_dl_tx(const uint32_t tti_) +{ + // toggle NDI to always create new Tx + const uint32_t pid = get_pid(tti_); + last_dl_ndi[pid] = !last_dl_ndi[pid]; + log->info("DL-PID=%d NDI=%s\n", pid, last_dl_ndi[pid] ? "1" : "0"); + return last_dl_ndi[pid]; +} + +int ttcn3_syssim::run() +{ + running = true; + + while (running) { + // wait for event + const int32_t epoll_timeout_ms = -1; + const uint32_t MAX_EVENTS = 1; + struct epoll_event events[MAX_EVENTS] = {}; + int nof_events = epoll_wait(epoll_fd, events, MAX_EVENTS, epoll_timeout_ms); + + // handle event + if (nof_events == -1) { + perror("epoll_wait() error"); + break; + } + if (nof_events == 0) { + printf("time out %f sec expired\n", epoll_timeout_ms / 1000.0); + continue; + } + + for (int i = 0; i < nof_events; ++i) { + if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { + ///< An error has occured on this fd, or the socket is not ready for reading + fprintf(stderr, "epoll error\n"); + close(events[i].data.fd); + continue; + } + + int fd = events[i].data.fd; + if (event_handler.find(fd) != event_handler.end()) { + event_handler[fd]->handle_event(fd, events[i], epoll_fd); + } + } + } + return SRSLTE_SUCCESS; +} + +uint32_t ttcn3_syssim::get_tti() +{ + return tti; +} + +void ttcn3_syssim::process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel) {} + +void ttcn3_syssim::set_cell_config(const ttcn3_helpers::timing_info_t timing, + const std::string cell_name, + const uint32_t earfcn, + const srslte_cell_t cell, + const float power) +{ + if (timing.now) { + set_cell_config_impl(cell_name, earfcn, cell, power); + } else { + log->debug("Scheduling Cell configuration of %s for TTI=%d\n", cell_name.c_str(), timing.tti); + tti_actions[timing.tti].push_back( + [this, cell_name, earfcn, cell, power]() { set_cell_config_impl(cell_name, earfcn, cell, power); }); + } +} + +void ttcn3_syssim::set_cell_config_impl(const std::string cell_name_, + const uint32_t earfcn_, + const srslte_cell_t cell_, + const float power_) +{ + // check if cell already exists + if (not syssim_has_cell(cell_name_)) { + // insert new cell + log->info("Adding cell %s with cellId=%d and power=%.2f dBm\n", cell_name_.c_str(), cell_.id, power_); + unique_syssim_cell_t cell = unique_syssim_cell_t(new syssim_cell_t); + cell->name = cell_name_; + cell->cell = cell_; + cell->initial_power = power_; + cell->earfcn = earfcn_; + cells.push_back(std::move(cell)); + } else { + // cell is already there + log->info("Cell already there, reconfigure\n"); + } + + update_cell_map(); +} + +// internal function +bool ttcn3_syssim::syssim_has_cell(std::string cell_name) +{ + for (auto& cell : cells) { + if (cell->name == cell_name) { + return true; + } + } + return false; +} + +void ttcn3_syssim::set_cell_attenuation(const ttcn3_helpers::timing_info_t timing, + const std::string cell_name, + const float value) +{ + if (timing.now) { + set_cell_attenuation_impl(cell_name, value); + } else { + log->debug("Scheduling Cell attenuation reconfiguration of %s for TTI=%d\n", cell_name.c_str(), timing.tti); + tti_actions[timing.tti].push_back([this, cell_name, value]() { set_cell_attenuation_impl(cell_name, value); }); + } +} + +void ttcn3_syssim::set_cell_attenuation_impl(const std::string cell_name, const float value) +{ + if (not syssim_has_cell(cell_name)) { + log->error("Can't set cell power. Cell not found.\n"); + } + + // update cell's power + for (auto& cell : cells) { + if (cell->name == cell_name) { + cell->attenuation = value; + break; + } + } + + update_cell_map(); +} + +// Internal function +void ttcn3_syssim::update_cell_map() +{ + // convert syssim cell list to phy cell list + { + lte_ttcn3_phy::cell_list_t phy_cells; + for (auto& ss_cell : cells) { + lte_ttcn3_phy::cell_t phy_cell = {}; + phy_cell.info = ss_cell->cell; + phy_cell.power = ss_cell->initial_power - ss_cell->attenuation; + phy_cell.earfcn = ss_cell->earfcn; + log->info("Configuring cell with PCI=%d with TxPower=%.2f\n", phy_cell.info.id, phy_cell.power); + phy_cells.push_back(phy_cell); + } + + // SYSSIM defines what cells the UE can connect to + ue->set_cell_map(phy_cells); + } +} + +bool ttcn3_syssim::have_valid_pcell() +{ + return (pcell_idx >= 0 && pcell_idx < static_cast(cells.size())); +} + +void ttcn3_syssim::add_bcch_dlsch_pdu(const string cell_name, unique_byte_buffer_t pdu) +{ + if (not syssim_has_cell(cell_name)) { + log->error("Can't add BCCH to cell. Cell not found.\n"); + } + + // add SIB + for (auto& cell : cells) { + if (cell->name == cell_name) { + cell->sibs.push_back(std::move(pdu)); + break; + } + } +} + +void ttcn3_syssim::add_ccch_pdu(const ttcn3_helpers::timing_info_t timing, unique_byte_buffer_t pdu) +{ + if (timing.now) { + // Add to SRB0 Tx queue + rlc.write_sdu(0, std::move(pdu)); + } else { + log->debug("Scheduling CCCH PDU for TTI=%d\n", timing.tti); + auto task = [this](srslte::unique_byte_buffer_t& pdu) { rlc.write_sdu(0, std::move(pdu)); }; + tti_actions[timing.tti].push_back(std::bind(task, std::move(pdu))); + } +} + +void ttcn3_syssim::add_dcch_pdu(const ttcn3_helpers::timing_info_t timing, + uint32_t lcid, + unique_byte_buffer_t pdu, + bool follow_on_flag) +{ + if (timing.now) { + add_dcch_pdu_impl(lcid, std::move(pdu), follow_on_flag); + } else { + log->debug("Scheduling DCCH PDU for TTI=%d\n", timing.tti); + auto task = [this](uint32_t lcid, srslte::unique_byte_buffer_t& pdu, bool follow_on_flag) { + add_dcch_pdu_impl(lcid, std::move(pdu), follow_on_flag); + }; + tti_actions[timing.tti].push_back(std::bind(task, lcid, std::move(pdu), follow_on_flag)); + } +} + +void ttcn3_syssim::add_dcch_pdu_impl(uint32_t lcid, unique_byte_buffer_t pdu, bool follow_on_flag) +{ + // push to PDCP and create DL grant for it + log->info("Writing PDU (%d B) to LCID=%d\n", pdu->N_bytes, lcid); + pdcp.write_sdu(lcid, std::move(pdu), true); + bearer_follow_on_map[lcid] = follow_on_flag; +} + +void ttcn3_syssim::add_pch_pdu(unique_byte_buffer_t pdu) +{ + log->info("Received PCH PDU (%d B)\n", pdu->N_bytes); + + // Prepare MAC grant for PCH + mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; + dl_grant.tti = tti; + dl_grant.pid = get_pid(tti); + dl_grant.rnti = SRSLTE_PRNTI; + dl_grant.tb[0].tbs = pdu->N_bytes; + dl_grant.tb[0].ndi_present = true; + dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); + ue->new_tb(dl_grant, (const uint8_t*)pdu->msg); +} + +void ttcn3_syssim::step_stack() +{ + stack.run_tti(); +} + +void ttcn3_syssim::add_srb(const ttcn3_helpers::timing_info_t timing, + const uint32_t lcid, + const pdcp_config_t pdcp_config) +{ + if (timing.now) { + add_srb_impl(lcid, pdcp_config); + } else { + log->debug("Scheduling SRB%d addition for TTI=%d\n", lcid, timing.tti); + tti_actions[timing.tti].push_back([this, lcid, pdcp_config]() { add_srb_impl(lcid, pdcp_config); }); + } +} + +void ttcn3_syssim::add_srb_impl(const uint32_t lcid, const pdcp_config_t pdcp_config) +{ + log->info("Adding SRB%d\n", lcid); + pdcp.add_bearer(lcid, pdcp_config); + rlc.add_bearer(lcid, srslte::rlc_config_t::srb_config(lcid)); +} + +void ttcn3_syssim::reestablish_bearer(uint32_t lcid) +{ + log->info("Reestablishing LCID=%d\n", lcid); + pdcp.reestablish(lcid); + rlc.reestablish(lcid); +} + +void ttcn3_syssim::del_srb(const ttcn3_helpers::timing_info_t timing, const uint32_t lcid) +{ + if (timing.now) { + del_srb_impl(lcid); + } else { + log->debug("Scheduling SRB%d deletion for TTI=%d\n", lcid, timing.tti); + tti_actions[timing.tti].push_back([this, lcid]() { del_srb_impl(lcid); }); + } +} + +void ttcn3_syssim::del_srb_impl(uint32_t lcid) +{ + log->info("Deleting SRB%d\n", lcid); + // Only delete SRB1/2 + if (lcid > 0) { + pdcp.del_bearer(lcid); + rlc.del_bearer(lcid); + } +} + +// RRC interface for PDCP, PDCP calls RRC to push RRC SDU +void ttcn3_syssim::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) +{ + log->info_hex(pdu->msg, + pdu->N_bytes, + "RRC SDU received for LCID=%d cell_id=%d (%d B)\n", + lcid, + cells[pcell_idx]->cell.id, + pdu->N_bytes); + + // check cell ID + if (cells[pcell_idx]->cell.id > 256) { + log->error("Cell ID too large to fit in single byte.\n"); + return; + } + + // We don't handle RRC, prepend LCID + pdu->msg--; + *pdu->msg = lcid; + pdu->N_bytes++; + + // prepend pcell PCID + pdu->msg--; + *pdu->msg = static_cast(cells[pcell_idx]->cell.id); + pdu->N_bytes++; + + // push content to Titan + srb.tx(std::move(pdu)); +} + +// Not supported right now +void ttcn3_syssim::write_pdu_bcch_bch(unique_byte_buffer_t pdu) +{ + log->error("%s not implemented.\n", __FUNCTION__); +} +void ttcn3_syssim::write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) +{ + log->error("%s not implemented.\n", __FUNCTION__); +} +void ttcn3_syssim::write_pdu_pcch(unique_byte_buffer_t pdu) +{ + log->error("%s not implemented.\n", __FUNCTION__); +} +void ttcn3_syssim::write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu) +{ + log->error("%s not implemented.\n", __FUNCTION__); +} +void ttcn3_syssim::max_retx_attempted() +{ + log->error("%s not implemented.\n", __FUNCTION__); +} + +std::string ttcn3_syssim::get_rb_name(uint32_t lcid) +{ + if (lcid < rb_id_vec.size()) { + return rb_id_vec.at(lcid); + } + return std::string("RB"); +}; + +void ttcn3_syssim::write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, bool blocking) +{ + log->info_hex(sdu->msg, sdu->N_bytes, "Received SDU on LCID=%d\n", lcid); + + uint8_t* mac_pdu_ptr; + mac_pdu_ptr = mac_msg_dl.write_packet(log); + log->info_hex(mac_pdu_ptr, mac_msg_dl.get_pdu_len(), "DL MAC PDU:\n"); + + // Prepare MAC grant for CCCH + mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; + dl_grant.pid = get_pid(tti); + dl_grant.tti = tti; + dl_grant.rnti = dl_rnti; + dl_grant.tb[0].tbs = mac_msg_dl.get_pdu_len(); + dl_grant.tb[0].ndi_present = true; + dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); + + // Pass to UE + ue->new_tb(dl_grant, (const uint8_t*)mac_pdu_ptr); +} + +void ttcn3_syssim::discard_sdu(uint32_t lcid, uint32_t sn) {} + +bool ttcn3_syssim::rb_is_um(uint32_t lcid) +{ + return false; +} + +void ttcn3_syssim::set_as_security(const ttcn3_helpers::timing_info_t timing, + std::array k_rrc_enc_, + std::array k_rrc_int_, + std::array k_up_enc_, + const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_, + const ttcn3_helpers::pdcp_count_map_t bearers_) +{ + if (timing.now) { + set_as_security_impl(k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_); + } else { + log->debug("Scheduling AS security configuration for TTI=%d\n", timing.tti); + tti_actions[timing.tti].push_back([this, k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_]() { + set_as_security_impl(k_rrc_enc_, k_rrc_int_, k_up_enc_, cipher_algo_, integ_algo_, bearers_); + }); + } +} + +void ttcn3_syssim::set_as_security_impl(std::array k_rrc_enc_, + std::array k_rrc_int_, + std::array k_up_enc_, + const srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + const srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_, + const ttcn3_helpers::pdcp_count_map_t bearers) +{ + // store security config for later use (i.e. new bearer added) + sec_cfg = {.k_rrc_int = k_rrc_int_, + .k_rrc_enc = k_rrc_enc_, + .k_up_int = {}, + .k_up_enc = k_up_enc_, + .integ_algo = integ_algo_, + .cipher_algo = cipher_algo_}; + + for (auto& lcid : bearers) { + pdcp.config_security(lcid.rb_id, sec_cfg); + + log->info("Setting AS security for LCID=%d in DL direction from SN=%d onwards\n", lcid.rb_id, lcid.dl_value); + pdcp.enable_security_timed(lcid.rb_id, DIRECTION_TX, lcid.dl_value); + + log->info("Setting AS security for LCID=%d in UL direction from SN=%d onwards\n", lcid.rb_id, lcid.ul_value); + pdcp.enable_security_timed(lcid.rb_id, DIRECTION_RX, lcid.ul_value); + } +} + +void ttcn3_syssim::release_as_security(const ttcn3_helpers::timing_info_t timing) +{ + if (timing.now) { + release_as_security_impl(); + } else { + log->debug("Scheduling Release of AS security for TTI=%d\n", timing.tti); + tti_actions[timing.tti].push_back([this]() { release_as_security_impl(); }); + } +} + +void ttcn3_syssim::release_as_security_impl() +{ + log->info("Releasing AS security\n"); + pending_bearer_config.clear(); +} + +void ttcn3_syssim::select_cell(srslte_cell_t phy_cell) +{ + // find matching cell in SS cell list + for (uint32_t i = 0; i < cells.size(); ++i) { + if (cells[i]->cell.id == phy_cell.id) { + pcell_idx = i; + log->info("New PCell: PCI=%d\n", cells[pcell_idx]->cell.id); + return; + } + } +} + +ttcn3_helpers::pdcp_count_map_t ttcn3_syssim::get_pdcp_count() +{ + // prepare response to SS + std::vector bearers; + for (uint32_t i = 0; i < rb_id_vec.size(); i++) { + if (pdcp.is_lcid_enabled(i)) { + ttcn3_helpers::pdcp_count_t bearer; + uint16_t tmp; // not handling HFN + pdcp.get_bearer_status(i, &bearer.dl_value, &tmp, &bearer.ul_value, &tmp); + bearer.rb_is_srb = i <= 2; + bearer.rb_id = i; + log->debug("PDCP count lcid=%d, dl=%d, ul=%d\n", bearer.rb_id, bearer.dl_value, bearer.ul_value); + bearers.push_back(bearer); + } + } + return bearers; +} diff --git a/srsue/test/ttcn3/src/ttcn3_ue.cc b/srsue/test/ttcn3/src/ttcn3_ue.cc new file mode 100644 index 000000000..24690153b --- /dev/null +++ b/srsue/test/ttcn3/src/ttcn3_ue.cc @@ -0,0 +1,250 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/test/ttcn3/hdr/ttcn3_ue.h" +#include "lte_ttcn3_phy.h" +#include "srsue/hdr/stack/ue_stack_lte.h" +#include + +extern "C" { +SRSLTE_API char* srslte_get_build_info(); +SRSLTE_API char* srslte_get_build_mode(); +} + +ttcn3_ue::ttcn3_ue() : tft_matcher(&log) {} + +int ttcn3_ue::init(all_args_t args, srslte::logger* logger_, syssim_interface_phy* syssim_, const std::string tc_name_) +{ + logger = logger_; + + // Init UE log + log.init("UE ", logger); + log.set_level(srslte::LOG_LEVEL_INFO); + log.info("Built in %s mode using %s.\n", srslte_get_build_mode(), srslte_get_build_info()); + + // Patch args + args.stack.nas.force_imsi_attach = true; + args.stack.nas.eia = "1,2,3"; + args.stack.nas.eea = "0,1,2,3"; + + // Configure default parameters + args.stack.usim.algo = "xor"; + args.stack.usim.imei = "356092040793011"; + args.stack.usim.imsi = "001010123456789"; // Anritsu test USIM + args.stack.usim.k = "000102030405060708090A0B0C0D0E0F"; // fixed as per TS 34.108 Sec. 8.2 + + args.stack.rrc.feature_group = 0xe6041000; + args.stack.rrc.ue_category_str = SRSLTE_UE_CATEGORY_DEFAULT; + args.stack.rrc.ue_category = strtol(args.stack.rrc.ue_category_str.c_str(), nullptr, 10); + args.stack.rrc.nof_supported_bands = 1; + args.stack.rrc.supported_bands[0] = 7; + args.stack.rrc.release = 8; + args.stack.rrc.mbms_service_id = -1; + + args.phy.dl_earfcn = "3400"; + args.rf.type = "none"; + args.stack.type = "lte"; + args.phy.type = "lte_ttcn3"; + + // Instantiate layers and stack together our UE + if (args.stack.type == "lte") { + stack = std::unique_ptr(new ue_stack_lte()); + if (!stack) { + log.console("Error creating LTE stack instance.\n"); + return SRSLTE_ERROR; + } + + phy = std::unique_ptr(new srsue::lte_ttcn3_phy(logger)); + if (!phy) { + log.console("Error creating LTE PHY instance.\n"); + return SRSLTE_ERROR; + } + } else { + log.console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str()); + return SRSLTE_ERROR; + } + + // init layers + if (phy->init(args.phy, stack.get(), syssim_)) { + log.console("Error initializing PHY.\n"); + return SRSLTE_ERROR; + } + + if (stack->init(args.stack, logger, phy.get(), this)) { + log.console("Error initializing stack.\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +void ttcn3_ue::stop() +{ + if (stack) { + stack->stop(); + } + + if (phy) { + phy->stop(); + } +} + +bool ttcn3_ue::switch_on() +{ + return stack->switch_on(); +} + +bool ttcn3_ue::switch_off() +{ + return stack->switch_off(); +} + +bool ttcn3_ue::enable_data() +{ + return stack->enable_data(); +} + +bool ttcn3_ue::disable_data() +{ + return stack->disable_data(); +} + +// The interface for SYSSIM +void ttcn3_ue::set_cell_map(lte_ttcn3_phy::cell_list_t phy_cell_map) +{ + phy->set_cell_map(phy_cell_map); +} + +void ttcn3_ue::new_grant_ul(const srsue::mac_interface_phy_lte::mac_grant_ul_t grant) +{ + phy->new_grant_ul(grant); +} + +void ttcn3_ue::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t mac_grant, const uint8_t* data) +{ + phy->new_tb(mac_grant, data); +} + +void ttcn3_ue::set_current_tti(uint32_t tti) +{ + phy->set_current_tti(tti); +} + +uint16_t ttcn3_ue::get_dl_sched_rnti(uint32_t tti) +{ + return stack->get_dl_sched_rnti(tti); +} + +// GW interface +void ttcn3_ue::add_mch_port(uint32_t lcid, uint32_t port) {} +void ttcn3_ue::write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) +{ + log.debug_hex(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d\n", pdu->N_bytes, lcid); + switch (test_loop_mode) { + case TEST_LOOP_INACTIVE: + log.warning("Test loop inactive. Dropping PDU.\n"); + break; + case TEST_LOOP_MODE_A_ACTIVE: + log.error("Test loop mode A not implemented. Dropping PDU.\n"); + break; + case TEST_LOOP_MODE_B_ACTIVE: + // Section 5.4.4 in TS 36.509 + if (pdu_delay_timer.is_running()) { + pdu_queue[lcid].push(std::move(pdu)); + } else { + if (pdu_delay_timer.is_valid()) { + pdu_queue[lcid].push(std::move(pdu)); + pdu_delay_timer.run(); // timer is already set + } else { + loop_back_pdu_with_tft(lcid, std::move(pdu)); + } + } + break; + case TEST_LOOP_MODE_C_ACTIVE: + log.error("Test loop mode C not implemented. Dropping PDU.\n"); + break; + } +} +void ttcn3_ue::write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {} +int ttcn3_ue::setup_if_addr(uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) +{ + return 0; +} + +int ttcn3_ue::apply_traffic_flow_template(const uint8_t& eps_bearer_id, + const uint8_t& lcid, + const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) +{ + return tft_matcher.apply_traffic_flow_template(eps_bearer_id, lcid, tft); +} + +void ttcn3_ue::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_) +{ + test_loop_mode = mode; + switch (test_loop_mode) { + case TEST_LOOP_INACTIVE: + // deactivate timer + log.info("Deactivating Test Loop Mode\n"); + pdu_delay_timer.release(); + break; + case TEST_LOOP_MODE_A_ACTIVE: + log.error("Test loop mode A not implemented\n"); + break; + case TEST_LOOP_MODE_B_ACTIVE: + log.info("Activating Test loop mode B with %d ms PDU delay\n", ip_pdu_delay_ms_); + // only create timer if needed + if (ip_pdu_delay_ms_ > 0) { + pdu_delay_timer = stack->get_unique_timer(); + pdu_delay_timer.set(ip_pdu_delay_ms_, [this](uint32_t tid) { timer_expired(tid); }); + } + break; + case TEST_LOOP_MODE_C_ACTIVE: + log.error("Test loop mode A not implemented\n"); + break; + } +} + +void ttcn3_ue::timer_expired(uint32_t timeout_id) +{ + if (timeout_id == pdu_delay_timer.id()) { + log.info("Testmode B PDU delay timer expired\n"); + for (auto& bearer_pdu_queue : pdu_queue) { + log.info("Delivering %zd buffered PDUs for LCID=%d\n", bearer_pdu_queue.second.size(), bearer_pdu_queue.first); + while (not pdu_queue.empty()) { + srslte::unique_byte_buffer_t pdu; + bearer_pdu_queue.second.try_pop(&pdu); + loop_back_pdu_with_tft(bearer_pdu_queue.first, std::move(pdu)); + } + } + } +} + +void ttcn3_ue::loop_back_pdu_with_tft(uint32_t input_lcid, srslte::unique_byte_buffer_t pdu) +{ + uint8_t output_lcid = tft_matcher.check_tft_filter_match(pdu); + log.info_hex(pdu->msg, + pdu->N_bytes, + "Rx PDU (%d B) on lcid=%d, looping back to lcid=%d\n", + pdu->N_bytes, + input_lcid, + output_lcid); + stack->write_sdu(input_lcid, std::move(pdu), false); +}