From c73bc2ea086348840f267983604b1cc8fa3a6999 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Sun, 2 Sep 2018 22:56:55 +0200 Subject: Import srslte_18.06.1-1.debian.tar.xz [dgit import tarball srslte 18.06.1-1 srslte_18.06.1-1.debian.tar.xz] --- changelog | 5 + compat | 1 + control | 43 +++++ copyright | 118 ++++++++++++ gbp.conf | 2 + man/genmanpages.sh | 5 + man/srsenb.txt | 35 ++++ man/srsepc.txt | 26 +++ man/srsue.txt | 32 ++++ patches/0001-Install-dir-for-private-library.patch | 19 ++ patches/0002-Set-RPATH-to-usr-lib-srslte.patch | 51 ++++++ patches/0003-Spelling-error-fixes.patch | 200 +++++++++++++++++++++ patches/series | 3 + rules | 31 ++++ source/format | 1 + srslte-dev.install | 2 + srslte.install | 5 + srslte.manpages | 1 + upstream/metadata | 6 + watch | 4 + 20 files changed, 590 insertions(+) create mode 100644 changelog create mode 100644 compat create mode 100644 control create mode 100644 copyright create mode 100644 gbp.conf create mode 100755 man/genmanpages.sh create mode 100644 man/srsenb.txt create mode 100644 man/srsepc.txt create mode 100644 man/srsue.txt create mode 100644 patches/0001-Install-dir-for-private-library.patch create mode 100644 patches/0002-Set-RPATH-to-usr-lib-srslte.patch create mode 100644 patches/0003-Spelling-error-fixes.patch create mode 100644 patches/series create mode 100755 rules create mode 100644 source/format create mode 100644 srslte-dev.install create mode 100644 srslte.install create mode 100644 srslte.manpages create mode 100644 upstream/metadata create mode 100644 watch diff --git a/changelog b/changelog new file mode 100644 index 0000000..64b9448 --- /dev/null +++ b/changelog @@ -0,0 +1,5 @@ +srslte (18.06.1-1) unstable; urgency=low + + * Initial release (Closes: #904221) + + -- Ruben Undheim Sun, 02 Sep 2018 22:56:55 +0200 diff --git a/compat b/compat new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/compat @@ -0,0 +1 @@ +11 diff --git a/control b/control new file mode 100644 index 0000000..df49d25 --- /dev/null +++ b/control @@ -0,0 +1,43 @@ +Source: srslte +Section: net +Priority: optional +Maintainer: Debian Mobcom Maintainers +Uploaders: Ruben Undheim +Build-Depends: debhelper (>= 11), + cmake, + libfftw3-dev, + libmbedtls-dev, + libboost-program-options-dev, + libconfig++-dev, + libsctp-dev, + libuhd-dev, + uhd-host, + txt2man +Standards-Version: 4.2.1 +Homepage: http://www.softwareradiosystems.com +Vcs-Git: https://salsa.debian.org/debian-mobcom-team/srslte.git +Vcs-Browser: https://salsa.debian.org/debian-mobcom-team/srslte + +Package: srslte-dev +Architecture: any +Section: libdevel +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srslte (= ${binary:Version}) +Description: Static libraries and headers for srslte + This software allows you to run a full end-to-end, open-source LTE system. + It contains a UE, eNB and EPC implementation. + . + This package contains the development files - static libraries and headers + +Package: srslte +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends} +Suggests: nextepc +Description: LTE software suite for software defined radios + This software allows you to run a full end-to-end, open-source LTE system. + It contains a UE, eNB and EPC implementation. + . + For running a full network, also an implementation of the Evolved Packet Core + (EPC) is needed. diff --git a/copyright b/copyright new file mode 100644 index 0000000..95b5e8f --- /dev/null +++ b/copyright @@ -0,0 +1,118 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: srsLTE +Upstream-Contact: srsLTE packaging team +License: AGPL-3+ +Copyright: 2013-2018, Software Radio Systems Limited. +Source: https://www.github.com/srsLTE + + +Files: * +Copyright: 2013-2018 Software Radio Systems Limited. + 2015 The srsUE Developers +License: AGPL-3+ + +Files: lib/src/phy/fec/viterbi37_port.c + lib/src/phy/fec/viterbi37_sse.c + lib/src/phy/fec/viterbi37_neon.c + lib/src/phy/fec/viterbi37_avx2_16bit.c + lib/src/phy/fec/viterbi37_avx2.c + lib/src/phy/fec/parity.* +Copyright: 2004,2009, Phil Karn, KA9Q +License: LGPL-2.1 + +Files: srsue/src/upper/pcsc_usim.cc + srsue/hdr/upper/pcsc_usim.h +Copyright: 2002-2014, Jouni Malinen +License: BSD-3-clause + +Files: cmake/modules/CheckCSourceRuns.cmake + cmake/modules/CheckFunctionExistsMath.cmake +Copyright: 2002-2011 Kitware, Inc. +License: BSD-3-clause +Comment: The header says "OSI approved BSD", but assuming + BSD-3-clause is meant since it is the most restrictive of the + "OSI approved BSD licenses" + +Files: lib/include/srslte/asn1/liblte_common.h + lib/include/srslte/asn1/liblte_rrc.h + lib/include/srslte/asn1/liblte_mme.h + lib/include/srslte/common/liblte_security.h + lib/src/asn1/liblte_common.cc + lib/src/asn1/liblte_mme.cc + lib/src/asn1/liblte_rrc.cc + lib/src/common/liblte_security.cc +Copyright: 2012-2015 Ben Wojtowicz + 2014 Andrew Murphy +License: AGPL-3+ + +Files: srsue/hdr/upper/pcsc_usim.h +Copyright: Jouni Malinen + 2013-2015 Software Radio Systems Limited +License: BSD-3-clause and AGPL-3+ +Comment: The class "scard" comes from the PC/SC smartcard implementation + WPA Supplicant and is written by Jouni Malinen and licensed with a BSD + license. + +Files: debian/* +Copyright: 2018 Ruben Undheim +License: AGPL-3+ + + +License: AGPL-3+ + This program 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. + . + This program 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. + . + On Debian systems, the complete text of the AGPL 3 can be found in + /usr/share/doc/srslte/LICENSE + + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + 3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gbp.conf b/gbp.conf new file mode 100644 index 0000000..cec628c --- /dev/null +++ b/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +pristine-tar = True diff --git a/man/genmanpages.sh b/man/genmanpages.sh new file mode 100755 index 0000000..b51ffbe --- /dev/null +++ b/man/genmanpages.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +txt2man -d "${CHANGELOG_DATE}" -t SRSENB -s 1 srsenb.txt > srsenb.1 +txt2man -d "${CHANGELOG_DATE}" -t SRSEPC -s 1 srsepc.txt > srsepc.1 +txt2man -d "${CHANGELOG_DATE}" -t SRSUE -s 1 srsue.txt > srsue.1 diff --git a/man/srsenb.txt b/man/srsenb.txt new file mode 100644 index 0000000..e4fbc9b --- /dev/null +++ b/man/srsenb.txt @@ -0,0 +1,35 @@ +NAME + srsenb - Evolved Node B in LTE network for Software Defined Radios + +SYNOPSIS + srsenb [options] + +DESCRIPTION + + srsenb provides the Evolved Node B (eNodeB or eNB) part in the LTE network. + It is the hardware that is connected to the mobile phone network that + communicates directly wirelessly with mobile handsets (UEs), like a base + transceiver station (BTS) in GSM networks. + + Features: + + - Round Robin MAC scheduler with FAPI-like C++ API + - SR support + - Periodic and Aperiodic CQI feedback support + - Standard S1AP and GTP-U interfaces to the Core Network + - 150 Mbps DL in 20 MHz MIMO TM3/TM4 with commercial UEs + - 75 Mbps DL in SISO configuration with commercial UEs + - 50 Mbps UL in 20 MHz with commercial UEs + + An example configuration file is available in /usr/share/srslte/enb.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/man/srsepc.txt b/man/srsepc.txt new file mode 100644 index 0000000..9da3893 --- /dev/null +++ b/man/srsepc.txt @@ -0,0 +1,26 @@ +NAME + srsepc - light-weight LTE core network implementation with MME, HSS and S/P-GW + +SYNOPSIS + srsepc [options] + +DESCRIPTION + + srsepc provides a light-weight LTE core network implementation. It constist of a single binary, and provides the following features: + + - MME (Mobility Management Entity) with standard S1AP and GTP-U interface to eNB + - S/P-GW with standard SGi exposed as virtual network interface (TUN device) + - HSS (Home Subscriber Server) with configurable user database in CSV format + + An example configuration file is available in /usr/share/srslte/epc.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/man/srsue.txt b/man/srsue.txt new file mode 100644 index 0000000..46caa8f --- /dev/null +++ b/man/srsue.txt @@ -0,0 +1,32 @@ +NAME + srsue - User Equipment implementation for LTE + +SYNOPSIS + srsue [options] + +DESCRIPTION + + srsue provides a complete SDR LTE UE application featuring all layers from PHY to IP. + + Features: + + - Cell search and synchronization procedure for the UE + - Soft USIM supporting Milenage and XOR authentication + - Hard USIM support using PCSC framework + - Virtual network interface tun_srsue created upon network attach + - 150 Mbps DL in 20 MHz MIMO TM3/TM4 configuration in i7 Quad-Core CPU. + - 75 Mbps DL in 20 MHz SISO configuration in i7 Quad-Core CPU. + - 36 Mbps DL in 10 MHz SISO configuration in i5 Dual-Core CPU. + + An example configuration file is available in /usr/share/srslte/ue.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/patches/0001-Install-dir-for-private-library.patch b/patches/0001-Install-dir-for-private-library.patch new file mode 100644 index 0000000..303797a --- /dev/null +++ b/patches/0001-Install-dir-for-private-library.patch @@ -0,0 +1,19 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 13:12:07 +0000 +Subject: Install dir for private library + +--- + lib/src/phy/rf/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt +index 6cec5a6..d961106 100644 +--- a/lib/src/phy/rf/CMakeLists.txt ++++ b/lib/src/phy/rf/CMakeLists.txt +@@ -60,5 +60,5 @@ if(RF_FOUND) + endif (SOAPYSDR_FOUND) + + +- INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) ++ INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) + endif(RF_FOUND) diff --git a/patches/0002-Set-RPATH-to-usr-lib-srslte.patch b/patches/0002-Set-RPATH-to-usr-lib-srslte.patch new file mode 100644 index 0000000..d843c01 --- /dev/null +++ b/patches/0002-Set-RPATH-to-usr-lib-srslte.patch @@ -0,0 +1,51 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 13:25:31 +0000 +Subject: Set RPATH to /usr/lib/srslte + +--- + srsenb/src/CMakeLists.txt | 2 +- + srsepc/src/CMakeLists.txt | 4 ++-- + srsue/src/CMakeLists.txt | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt +index ef66c03..0d2cc8d 100644 +--- a/srsenb/src/CMakeLists.txt ++++ b/srsenb/src/CMakeLists.txt +@@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper + ${SCTP_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) +diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt +index 9fb56ce..f16c2bf 100644 +--- a/srsepc/src/CMakeLists.txt ++++ b/srsepc/src/CMakeLists.txt +@@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + if (RPATH) +- set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") +- set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") ++ set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) +diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt +index 0755a64..f4eac8b 100644 +--- a/srsue/src/CMakeLists.txt ++++ b/srsue/src/CMakeLists.txt +@@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac + ${Boost_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) diff --git a/patches/0003-Spelling-error-fixes.patch b/patches/0003-Spelling-error-fixes.patch new file mode 100644 index 0000000..7c8019b --- /dev/null +++ b/patches/0003-Spelling-error-fixes.patch @@ -0,0 +1,200 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 18:16:22 +0000 +Subject: Spelling error fixes + +--- + srsenb/src/main.cc | 2 +- + srsenb/src/phy/txrx.cc | 2 +- + srsenb/src/upper/rrc.cc | 4 ++-- + srsenb/src/upper/s1ap.cc | 2 +- + srsepc/src/main.cc | 2 +- + srsepc/src/mbms-gw/mbms-gw.cc | 2 +- + srsepc/src/mme/mme_gtpc.cc | 2 +- + srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- + srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- + srsepc/src/mme/s1ap_nas_transport.cc | 2 +- + srsue/src/mac/proc_bsr.cc | 2 +- + srsue/src/main.cc | 2 +- + srsue/src/phy/phch_recv.cc | 2 +- + 13 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc +index 3622f8d..c0815ec 100644 +--- a/srsenb/src/main.cc ++++ b/srsenb/src/main.cc +@@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { + ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") +- ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") ++ ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") + ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") + ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") + ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") +diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc +index 7f6503b..d088550 100644 +--- a/srsenb/src/phy/txrx.cc ++++ b/srsenb/src/phy/txrx.cc +@@ -125,7 +125,7 @@ void txrx::run_thread() + srslte_timestamp_copy(&tx_time, &rx_time); + srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); + +- Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", ++ Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + tti, tx_mutex_cnt, + tx_time.full_secs, tx_time.frac_secs, + worker->get_id()); +diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc +index 2e481ce..1dc3359 100644 +--- a/srsenb/src/upper/rrc.cc ++++ b/srsenb/src/upper/rrc.cc +@@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) + if (users.count(rnti)) { + users[rnti].parse_ul_dcch(lcid, pdu); + } else { +- rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); ++ rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); + } + } + } +@@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) + rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); + } + } else { +- rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); ++ rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); + } + } + +diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc +index 1e2b259..1babe89 100644 +--- a/srsenb/src/upper/s1ap.cc ++++ b/srsenb/src/upper/s1ap.cc +@@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) + cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; + break; + default: +- cause += "unkown"; ++ cause += "unknown"; + break; + } + return cause; +diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc +index beed677..29f4659 100644 +--- a/srsepc/src/main.cc ++++ b/srsepc/src/main.cc +@@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { + ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") +- ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") ++ ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") + ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") + ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") + ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") +diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc +index b373df4..e87eed4 100644 +--- a/srsepc/src/mbms-gw/mbms-gw.cc ++++ b/srsepc/src/mbms-gw/mbms-gw.cc +@@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) + int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, + (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); + if(n<0){ +- m_mbms_gw_log->console("Error writting to M1-U socket.\n"); ++ m_mbms_gw_log->console("Error writing to M1-U socket.\n"); + } + else{ + m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); +diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc +index 6ef0e2c..94ab6f2 100644 +--- a/srsepc/src/mme/mme_gtpc.cc ++++ b/srsepc/src/mme/mme_gtpc.cc +@@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) + //Check UE Ipv4 address was allocated + if(cs_resp->paa_present != true) + { +- m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); ++ m_mme_gtpc_log->error("PDN Address Allocation not present\n"); + return; + } + if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) +diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +index b9d599d..97453d0 100644 +--- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc ++++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +@@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, + + struct in_addr addr; + addr.s_addr = htonl(sgw_s1u_ip); +- m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); ++ m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); + m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); +diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc +index 079eca1..dbd45cd 100644 +--- a/srsepc/src/mme/s1ap_mngmt_proc.cc ++++ b/srsepc/src/mme/s1ap_mngmt_proc.cc +@@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU + //Check matching PLMNs + if(enb_ctx.plmn!=m_s1ap->get_plmn()){ + +- m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); +- m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); ++ m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); ++ m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); + pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); + } + else{ +diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc +index 1bb871f..661c117 100644 +--- a/srsepc/src/mme/s1ap_nas_transport.cc ++++ b/srsepc/src/mme/s1ap_nas_transport.cc +@@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ + } + if(attach_req->additional_guti_present) + { +- m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); ++ m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); + } + if(attach_req->last_visited_registered_tai_present) + { +diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc +index bd49671..309c1f9 100644 +--- a/srsue/src/mac/proc_bsr.cc ++++ b/srsue/src/mac/proc_bsr.cc +@@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) + generate_bsr(bsr, 0); + bsr_sz = bsr->format==LONG_BSR?3:1; + if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { +- Debug("Grant is not enough to accomodate the BSR MAC CE\n"); ++ Debug("Grant is not enough to accommodate the BSR MAC CE\n"); + } else { + Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", + grant_size, total_data, bsr_sz); +diff --git a/srsue/src/main.cc b/srsue/src/main.cc +index f71f9ac..ea5b528 100644 +--- a/srsue/src/main.cc ++++ b/srsue/src/main.cc +@@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { + ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") + ("usim.k", bpo::value(&args->usim.k), "USIM K") + ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") +- ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") ++ ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") + + /* Expert section */ + ("expert.ip_netmask", +diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc +index 9f9ef94..e16ccfd 100644 +--- a/srsue/src/phy/phch_recv.cc ++++ b/srsue/src/phy/phch_recv.cc +@@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples + } + if (receiving == true) { + if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { +- Warning("Error writting to ringbuffer\n"); ++ Warning("Error writing to ringbuffer\n"); + receiving = false; + } else { + receive_cnt++; diff --git a/patches/series b/patches/series new file mode 100644 index 0000000..3907eac --- /dev/null +++ b/patches/series @@ -0,0 +1,3 @@ +0001-Install-dir-for-private-library.patch +0002-Set-RPATH-to-usr-lib-srslte.patch +0003-Spelling-error-fixes.patch diff --git a/rules b/rules new file mode 100755 index 0000000..ad8c44e --- /dev/null +++ b/rules @@ -0,0 +1,31 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 + +include /usr/share/dpkg/pkg-info.mk + + +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +CHANGELOG_DATE ?= $(shell LC_ALL=C date -u -d @$(SOURCE_DATE_EPOCH) +"%d %B %Y") + + +%: + dh $@ + +override_dh_auto_configure:: + dh_auto_configure -- -DRPATH=1 + +override_dh_installman: + cd debian/man ; CHANGELOG_DATE="$(CHANGELOG_DATE)" ./genmanpages.sh + dh_installman + +override_dh_auto_clean: + dh_auto_clean + $(RM) debian/man/*.1 + +# Skipping tests for now since quite a few of the 456 tests are unreliable: +override_dh_auto_test: + # skip executing tests diff --git a/source/format b/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/srslte-dev.install b/srslte-dev.install new file mode 100644 index 0000000..d6ba448 --- /dev/null +++ b/srslte-dev.install @@ -0,0 +1,2 @@ +usr/lib/*/*.a +usr/include/srslte diff --git a/srslte.install b/srslte.install new file mode 100644 index 0000000..ac57e04 --- /dev/null +++ b/srslte.install @@ -0,0 +1,5 @@ +usr/bin/srsenb +usr/bin/srsepc +usr/bin/srsue +usr/lib/*/libsrslte_rf.so +usr/share/srslte diff --git a/srslte.manpages b/srslte.manpages new file mode 100644 index 0000000..13cdaf4 --- /dev/null +++ b/srslte.manpages @@ -0,0 +1 @@ +debian/man/*.1 diff --git a/upstream/metadata b/upstream/metadata new file mode 100644 index 0000000..d606377 --- /dev/null +++ b/upstream/metadata @@ -0,0 +1,6 @@ +--- +Bug-Database: https://github.com/srsLTE/srsLTE/issues +Bug-Submit: https://github.com/srsLTE/srsLTE/issues/new +Name: srsLTE +Repository: https://github.com/srsLTE/srsLTE.git +Repository-Browse: https://github.com/srsLTE/srsLTE diff --git a/watch b/watch new file mode 100644 index 0000000..af69794 --- /dev/null +++ b/watch @@ -0,0 +1,4 @@ +version=3 +opts=dversionmangle=s/\./_/g,\ +oversionmangle=s/_/\./g \ +https://github.com/srsLTE/srsLTE/releases /srsLTE/srsLTE/archive/release_(\d\S+)\.tar\.(?:bz2|gz|xz) -- cgit v1.2.3 From 5eb4d0355ad00d2203d7e36985cbdcb5315f0294 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From fe103e905d71d86cdb20edd10f2493274b97dc06 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 0b0bda13b4fd40edc9e457a014b2b5b095b42a6d Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From d4d45feae9f800572bd0787d304c781dd55a253e Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Sun, 2 Sep 2018 22:56:55 +0200 Subject: Import srslte_18.06.1.orig.tar.gz [dgit import orig srslte_18.06.1.orig.tar.gz] --- CHANGELOG | 79 + CMakeLists.txt | 412 + COPYRIGHT | 516 + CTestConfig.cmake | 14 + CTestCustom.cmake.in | 15 + LICENSE | 697 + README.md | 204 + cmake/modules/CheckCSourceRuns.cmake | 107 + cmake/modules/CheckFunctionExists.c | 25 + cmake/modules/CheckFunctionExistsMath.cmake | 67 + cmake/modules/FindFFTW3F.cmake | 44 + cmake/modules/FindLibConfig.cmake | 75 + cmake/modules/FindLimeSDR.cmake | 28 + cmake/modules/FindMKL.cmake | 53 + cmake/modules/FindMbedTLS.cmake | 54 + cmake/modules/FindPCSCLite.cmake | 48 + cmake/modules/FindPolarssl.cmake | 53 + cmake/modules/FindSCTP.cmake | 38 + cmake/modules/FindSRSGUI.cmake | 42 + cmake/modules/FindSSE.cmake | 176 + cmake/modules/FindSoapySDR.cmake | 32 + cmake/modules/FindUHD.cmake | 31 + cmake/modules/FindbladeRF.cmake | 27 + cmake/modules/SRSLTEPackage.cmake | 63 + cmake/modules/SRSLTEVersion.cmake | 24 + cmake/modules/SRSLTE_install_configs.sh.in | 55 + cmake/modules/SRSLTEbuildinfo.cmake.in | 21 + cmake_uninstall.cmake.in | 26 + debian/changelog | 17 + debian/compat | 1 + debian/config | 13 + debian/control | 35 + debian/copyright | 81 + debian/postinst | 14 + debian/rules | 18 + debian/source/format | 1 + debian/templates | 6 + lib/CMakeLists.txt | 34 + lib/examples/CMakeLists.txt | 86 + lib/examples/cell_measurement.c | 420 + lib/examples/cell_search.c | 283 + lib/examples/pdsch_enodeb.c | 1055 + lib/examples/pdsch_ue.c | 1102 + lib/examples/synch_file.c | 269 + lib/examples/usrp_capture.c | 182 + lib/examples/usrp_capture_sync.c | 218 + lib/examples/usrp_txrx.c | 201 + lib/include/CMakeLists.txt | 25 + lib/include/srslte/CMakeLists.txt | 28 + lib/include/srslte/asn1/gtpc.h | 97 + lib/include/srslte/asn1/gtpc_ies.h | 451 + lib/include/srslte/asn1/gtpc_msg.h | 446 + lib/include/srslte/asn1/liblte_common.h | 180 + lib/include/srslte/asn1/liblte_mme.h | 4044 ++ lib/include/srslte/asn1/liblte_rrc.h | 7143 +++ lib/include/srslte/asn1/liblte_s1ap.h | 10355 +++++ lib/include/srslte/build_info.h.in | 64 + lib/include/srslte/common/bcd_helpers.h | 197 + lib/include/srslte/common/block_queue.h | 213 + lib/include/srslte/common/buffer_pool.h | 210 + lib/include/srslte/common/common.h | 251 + lib/include/srslte/common/config_file.h | 59 + lib/include/srslte/common/gen_mch_tables.h | 52 + lib/include/srslte/common/interfaces_common.h | 105 + lib/include/srslte/common/liblte_security.h | 359 + lib/include/srslte/common/liblte_ssl.h | 65 + lib/include/srslte/common/log.h | 157 + lib/include/srslte/common/log_filter.h | 101 + lib/include/srslte/common/logger.h | 48 + lib/include/srslte/common/logger_file.h | 77 + lib/include/srslte/common/logger_stdout.h | 54 + lib/include/srslte/common/mac_pcap.h | 65 + lib/include/srslte/common/metrics_hub.h | 82 + lib/include/srslte/common/nas_pcap.h | 25 + lib/include/srslte/common/pcap.h | 401 + lib/include/srslte/common/pdu.h | 426 + lib/include/srslte/common/pdu_queue.h | 91 + lib/include/srslte/common/rlc_pcap.h | 65 + lib/include/srslte/common/security.h | 188 + lib/include/srslte/common/snow_3g.h | 74 + lib/include/srslte/common/task_dispatcher.h | 61 + lib/include/srslte/common/thread_pool.h | 105 + lib/include/srslte/common/threads.h | 166 + lib/include/srslte/common/timeout.h | 131 + lib/include/srslte/common/timers.h | 176 + lib/include/srslte/common/trace.h | 101 + lib/include/srslte/common/tti_sync.h | 80 + lib/include/srslte/common/tti_sync_cv.h | 59 + lib/include/srslte/config.h | 67 + lib/include/srslte/interfaces/enb_interfaces.h | 288 + .../srslte/interfaces/enb_metrics_interface.h | 67 + lib/include/srslte/interfaces/epc_interfaces.h | 19 + lib/include/srslte/interfaces/sched_interface.h | 270 + lib/include/srslte/interfaces/ue_interfaces.h | 670 + lib/include/srslte/phy/agc/agc.h | 108 + .../srslte/phy/ch_estimation/chest_common.h | 55 + lib/include/srslte/phy/ch_estimation/chest_dl.h | 204 + lib/include/srslte/phy/ch_estimation/chest_ul.h | 121 + .../srslte/phy/ch_estimation/refsignal_dl.h | 123 + .../srslte/phy/ch_estimation/refsignal_ul.h | 212 + lib/include/srslte/phy/channel/ch_awgn.h | 57 + lib/include/srslte/phy/common/phy_common.h | 308 + lib/include/srslte/phy/common/phy_logger.h | 59 + lib/include/srslte/phy/common/sequence.h | 103 + lib/include/srslte/phy/common/timestamp.h | 68 + lib/include/srslte/phy/dft/dft.h | 161 + lib/include/srslte/phy/dft/dft_precoding.h | 71 + lib/include/srslte/phy/dft/ofdm.h | 161 + lib/include/srslte/phy/enb/enb_dl.h | 216 + lib/include/srslte/phy/enb/enb_ul.h | 156 + lib/include/srslte/phy/fec/cbsegm.h | 59 + lib/include/srslte/phy/fec/convcoder.h | 56 + lib/include/srslte/phy/fec/crc.h | 76 + lib/include/srslte/phy/fec/rm_conv.h | 66 + lib/include/srslte/phy/fec/rm_turbo.h | 88 + lib/include/srslte/phy/fec/softbuffer.h | 85 + lib/include/srslte/phy/fec/tc_interl.h | 58 + lib/include/srslte/phy/fec/turbocoder.h | 79 + lib/include/srslte/phy/fec/turbodecoder.h | 120 + lib/include/srslte/phy/fec/turbodecoder_gen.h | 99 + lib/include/srslte/phy/fec/turbodecoder_simd.h | 122 + .../srslte/phy/fec/turbodecoder_simd_inter.h | 119 + lib/include/srslte/phy/fec/turbodecoder_sse.h | 101 + lib/include/srslte/phy/fec/viterbi.h | 122 + lib/include/srslte/phy/io/binsource.h | 71 + lib/include/srslte/phy/io/filesink.h | 67 + lib/include/srslte/phy/io/filesource.h | 70 + lib/include/srslte/phy/io/format.h | 40 + lib/include/srslte/phy/io/netsink.h | 74 + lib/include/srslte/phy/io/netsource.h | 78 + lib/include/srslte/phy/mimo/layermap.h | 94 + lib/include/srslte/phy/mimo/precoding.h | 141 + lib/include/srslte/phy/modem/demod_hard.h | 60 + lib/include/srslte/phy/modem/demod_soft.h | 56 + lib/include/srslte/phy/modem/mod.h | 55 + lib/include/srslte/phy/modem/modem_table.h | 86 + lib/include/srslte/phy/phch/cqi.h | 185 + lib/include/srslte/phy/phch/dci.h | 196 + lib/include/srslte/phy/phch/pbch.h | 118 + lib/include/srslte/phy/phch/pcfich.h | 111 + lib/include/srslte/phy/phch/pdcch.h | 189 + lib/include/srslte/phy/phch/pdsch.h | 177 + lib/include/srslte/phy/phch/pdsch_cfg.h | 62 + lib/include/srslte/phy/phch/phich.h | 127 + lib/include/srslte/phy/phch/pmch.h | 152 + lib/include/srslte/phy/phch/prach.h | 181 + lib/include/srslte/phy/phch/pucch.h | 214 + lib/include/srslte/phy/phch/pusch.h | 152 + lib/include/srslte/phy/phch/pusch_cfg.h | 61 + lib/include/srslte/phy/phch/ra.h | 303 + lib/include/srslte/phy/phch/regs.h | 141 + lib/include/srslte/phy/phch/sch.h | 162 + lib/include/srslte/phy/phch/uci.h | 156 + lib/include/srslte/phy/resampling/decim.h | 51 + lib/include/srslte/phy/resampling/interp.h | 136 + lib/include/srslte/phy/resampling/resample_arb.h | 68 + lib/include/srslte/phy/rf/rf.h | 227 + lib/include/srslte/phy/rf/rf_utils.h | 68 + lib/include/srslte/phy/scrambling/scrambling.h | 79 + lib/include/srslte/phy/sync/cfo.h | 70 + lib/include/srslte/phy/sync/cp.h | 58 + lib/include/srslte/phy/sync/pss.h | 179 + lib/include/srslte/phy/sync/sfo.h | 49 + lib/include/srslte/phy/sync/sss.h | 157 + lib/include/srslte/phy/sync/sync.h | 244 + lib/include/srslte/phy/ue/ue_cell_search.h | 114 + lib/include/srslte/phy/ue/ue_dl.h | 257 + lib/include/srslte/phy/ue/ue_mib.h | 133 + lib/include/srslte/phy/ue/ue_phy.h | 168 + lib/include/srslte/phy/ue/ue_sync.h | 265 + lib/include/srslte/phy/ue/ue_ul.h | 217 + lib/include/srslte/phy/utils/bit.h | 123 + lib/include/srslte/phy/utils/cexptab.h | 61 + lib/include/srslte/phy/utils/convolution.h | 97 + lib/include/srslte/phy/utils/debug.h | 83 + lib/include/srslte/phy/utils/filter.h | 61 + lib/include/srslte/phy/utils/mat.h | 221 + lib/include/srslte/phy/utils/ringbuffer.h | 45 + lib/include/srslte/phy/utils/simd.h | 1779 + lib/include/srslte/phy/utils/vector.h | 167 + lib/include/srslte/phy/utils/vector_simd.h | 147 + lib/include/srslte/radio/radio.h | 187 + lib/include/srslte/radio/radio_multi.h | 55 + lib/include/srslte/srslte.h | 131 + lib/include/srslte/upper/gtpu.h | 96 + lib/include/srslte/upper/pdcp.h | 101 + lib/include/srslte/upper/pdcp_entity.h | 149 + lib/include/srslte/upper/rlc.h | 118 + lib/include/srslte/upper/rlc_am.h | 230 + lib/include/srslte/upper/rlc_common.h | 185 + lib/include/srslte/upper/rlc_entity.h | 87 + lib/include/srslte/upper/rlc_interface.h | 145 + lib/include/srslte/upper/rlc_metrics.h | 41 + lib/include/srslte/upper/rlc_tm.h | 85 + lib/include/srslte/upper/rlc_tx_queue.h | 123 + lib/include/srslte/upper/rlc_um.h | 161 + lib/include/srslte/version.h.in | 58 + lib/src/CMakeLists.txt | 25 + lib/src/asn1/CMakeLists.txt | 29 + lib/src/asn1/gtpc.cc | 40 + lib/src/asn1/liblte_common.cc | 198 + lib/src/asn1/liblte_mme.cc | 11191 +++++ lib/src/asn1/liblte_rrc.cc | 14399 ++++++ lib/src/asn1/liblte_s1ap.cc | 44058 +++++++++++++++++++ lib/src/common/CMakeLists.txt | 30 + lib/src/common/buffer_pool.cc | 59 + lib/src/common/gen_mch_tables.c | 62 + lib/src/common/liblte_security.cc | 1940 + lib/src/common/log_filter.cc | 300 + lib/src/common/logger_file.cc | 128 + lib/src/common/mac_pcap.cc | 107 + lib/src/common/nas_pcap.cc | 35 + lib/src/common/pdu.cc | 947 + lib/src/common/pdu_queue.cc | 111 + lib/src/common/rlc_pcap.cc | 90 + lib/src/common/security.cc | 300 + lib/src/common/snow_3g.cc | 577 + lib/src/common/task_dispatcher.cc | 75 + lib/src/common/thread_pool.cc | 288 + lib/src/common/threads.c | 182 + lib/src/common/tti_sync_cv.cc | 85 + lib/src/common/version.c | 45 + lib/src/phy/CMakeLists.txt | 61 + lib/src/phy/agc/CMakeLists.txt | 22 + lib/src/phy/agc/agc.c | 197 + lib/src/phy/ch_estimation/CMakeLists.txt | 23 + lib/src/phy/ch_estimation/chest_common.c | 82 + lib/src/phy/ch_estimation/chest_dl.c | 846 + lib/src/phy/ch_estimation/chest_ul.c | 400 + lib/src/phy/ch_estimation/refsignal_dl.c | 464 + lib/src/phy/ch_estimation/refsignal_ul.c | 946 + lib/src/phy/ch_estimation/test/CMakeLists.txt | 54 + lib/src/phy/ch_estimation/test/chest_test_dl.c | 253 + lib/src/phy/ch_estimation/test/chest_test_dl_mex.c | 159 + .../ch_estimation/test/chest_test_dl_mex.mexa64 | Bin 0 -> 12713 bytes lib/src/phy/ch_estimation/test/chest_test_ul.c | 259 + lib/src/phy/ch_estimation/test/chest_test_ul_mex.c | 181 + .../phy/ch_estimation/test/refsignal_pusch_mex.c | 151 + lib/src/phy/ch_estimation/test/refsignal_srs_mex.c | 165 + lib/src/phy/ch_estimation/test/refsignal_ul_test.c | 169 + lib/src/phy/ch_estimation/ul_rs_tables.h | 115 + lib/src/phy/channel/CMakeLists.txt | 22 + lib/src/phy/channel/ch_awgn.c | 58 + lib/src/phy/channel/gauss.c | 43 + lib/src/phy/channel/gauss.h | 28 + lib/src/phy/common/CMakeLists.txt | 22 + lib/src/phy/common/phy_common.c | 615 + lib/src/phy/common/phy_logger.c | 58 + lib/src/phy/common/sequence.c | 188 + lib/src/phy/common/timestamp.c | 86 + lib/src/phy/dft/CMakeLists.txt | 23 + lib/src/phy/dft/dft_fftw.c | 355 + lib/src/phy/dft/dft_precoding.c | 118 + lib/src/phy/dft/ofdm.c | 599 + lib/src/phy/dft/test/CMakeLists.txt | 33 + lib/src/phy/dft/test/ofdm_test.c | 178 + lib/src/phy/enb/CMakeLists.txt | 22 + lib/src/phy/enb/enb_dl.c | 545 + lib/src/phy/enb/enb_ul.c | 415 + lib/src/phy/fec/CMakeLists.txt | 23 + lib/src/phy/fec/cbsegm.c | 155 + lib/src/phy/fec/convcoder.c | 79 + lib/src/phy/fec/crc.c | 190 + lib/src/phy/fec/parity.c | 30 + lib/src/phy/fec/parity.h | 36 + lib/src/phy/fec/rm_conv.c | 227 + lib/src/phy/fec/rm_turbo.c | 761 + lib/src/phy/fec/softbuffer.c | 224 + lib/src/phy/fec/tc_interl_lte.c | 98 + lib/src/phy/fec/tc_interl_umts.c | 272 + lib/src/phy/fec/test/CMakeLists.txt | 82 + lib/src/phy/fec/test/crc_test.c | 114 + lib/src/phy/fec/test/crc_test.h | 70 + lib/src/phy/fec/test/rm_conv_test.c | 134 + lib/src/phy/fec/test/rm_turbo_rx_mex.c | 99 + lib/src/phy/fec/test/rm_turbo_test.c | 208 + lib/src/phy/fec/test/turbocoder_test.c | 126 + lib/src/phy/fec/test/turbodecoder_test.c | 322 + lib/src/phy/fec/test/turbodecoder_test.h | 167 + lib/src/phy/fec/test/turbodecoder_test_mex.c | 106 + lib/src/phy/fec/test/viterbi_test.c | 299 + lib/src/phy/fec/test/viterbi_test.h | 104 + lib/src/phy/fec/test/viterbi_test_mex.c | 89 + lib/src/phy/fec/turbocoder.c | 342 + lib/src/phy/fec/turbodecoder.c | 174 + lib/src/phy/fec/turbodecoder_avx.c | 475 + lib/src/phy/fec/turbodecoder_gen.c | 400 + lib/src/phy/fec/turbodecoder_simd.c | 542 + lib/src/phy/fec/turbodecoder_simd_inter.c | 299 + lib/src/phy/fec/turbodecoder_sse.c | 498 + lib/src/phy/fec/turbodecoder_sse_inter.c | 202 + lib/src/phy/fec/viterbi.c | 598 + lib/src/phy/fec/viterbi37.h | 132 + lib/src/phy/fec/viterbi37_avx2.c | 341 + lib/src/phy/fec/viterbi37_avx2_16bit.c | 363 + lib/src/phy/fec/viterbi37_neon.c | 354 + lib/src/phy/fec/viterbi37_port.c | 235 + lib/src/phy/fec/viterbi37_sse.c | 295 + lib/src/phy/io/CMakeLists.txt | 22 + lib/src/phy/io/binsource.c | 135 + lib/src/phy/io/filesink.c | 158 + lib/src/phy/io/filesource.c | 146 + lib/src/phy/io/netsink.c | 116 + lib/src/phy/io/netsource.c | 136 + lib/src/phy/mimo/CMakeLists.txt | 23 + lib/src/phy/mimo/layermap.c | 221 + lib/src/phy/mimo/precoding.c | 2924 ++ lib/src/phy/mimo/test/CMakeLists.txt | 81 + lib/src/phy/mimo/test/layermap_test.c | 162 + lib/src/phy/mimo/test/pmi_select_test.c | 158 + lib/src/phy/mimo/test/pmi_select_test.h | 237 + lib/src/phy/mimo/test/precoder_mex.c | 145 + lib/src/phy/mimo/test/precoder_test.c | 343 + lib/src/phy/mimo/test/predecoder_mex.c | 213 + lib/src/phy/modem/CMakeLists.txt | 23 + lib/src/phy/modem/demod_hard.c | 68 + lib/src/phy/modem/demod_soft.c | 283 + lib/src/phy/modem/hard_demod_lte.c | 205 + lib/src/phy/modem/hard_demod_lte.h | 50 + lib/src/phy/modem/lte_tables.c | 166 + lib/src/phy/modem/lte_tables.h | 60 + lib/src/phy/modem/mod.c | 157 + lib/src/phy/modem/modem_table.c | 161 + lib/src/phy/modem/test/CMakeLists.txt | 43 + lib/src/phy/modem/test/modem_test.c | 212 + lib/src/phy/modem/test/soft_demod_test.c | 226 + lib/src/phy/phch/CMakeLists.txt | 23 + lib/src/phy/phch/cqi.c | 552 + lib/src/phy/phch/dci.c | 1499 + lib/src/phy/phch/dci_sz_table.h | 132 + lib/src/phy/phch/pbch.c | 615 + lib/src/phy/phch/pcfich.c | 299 + lib/src/phy/phch/pdcch.c | 645 + lib/src/phy/phch/pdsch.c | 1117 + lib/src/phy/phch/phich.c | 425 + lib/src/phy/phch/pmch.c | 495 + lib/src/phy/phch/prach.c | 716 + lib/src/phy/phch/prb_dl.c | 108 + lib/src/phy/phch/prb_dl.h | 34 + lib/src/phy/phch/pucch.c | 874 + lib/src/phy/phch/pusch.c | 653 + lib/src/phy/phch/ra.c | 879 + lib/src/phy/phch/regs.c | 815 + lib/src/phy/phch/sch.c | 860 + lib/src/phy/phch/sequences.c | 86 + lib/src/phy/phch/tbs_tables.h | 308 + lib/src/phy/phch/test/CMakeLists.txt | 350 + lib/src/phy/phch/test/dlsch_encode_test_mex.c | 157 + lib/src/phy/phch/test/pbch_file_test.c | 255 + lib/src/phy/phch/test/pbch_test.c | 156 + lib/src/phy/phch/test/pbch_test_mex.c | 202 + lib/src/phy/phch/test/pcfich_file_test.c | 257 + lib/src/phy/phch/test/pcfich_test.c | 165 + lib/src/phy/phch/test/pcfich_test_mex.c | 171 + lib/src/phy/phch/test/pdcch_file_test.c | 293 + lib/src/phy/phch/test/pdcch_test.c | 364 + lib/src/phy/phch/test/pdcch_test_mex.c | 225 + lib/src/phy/phch/test/pdsch_pdcch_file_test.c | 211 + lib/src/phy/phch/test/pdsch_test.c | 591 + lib/src/phy/phch/test/pdsch_test_mex.c | 342 + lib/src/phy/phch/test/phich_file_test.c | 292 + lib/src/phy/phch/test/phich_test.c | 221 + lib/src/phy/phch/test/phich_test_mex.c | 173 + lib/src/phy/phch/test/pmch_100prbs_MCS2_SR0.bin | Bin 0 -> 184320 bytes lib/src/phy/phch/test/pmch_file_test.c | 211 + lib/src/phy/phch/test/pmch_test.c | 489 + lib/src/phy/phch/test/prach_detect_test_mex.c | 129 + lib/src/phy/phch/test/prach_test.c | 130 + lib/src/phy/phch/test/prach_test_mex.c | 115 + lib/src/phy/phch/test/prach_test_multi.c | 144 + lib/src/phy/phch/test/prach_test_usrp.c | 242 + lib/src/phy/phch/test/pucch_encode_test_mex.c | 229 + lib/src/phy/phch/test/pucch_test.c | 249 + lib/src/phy/phch/test/pucch_test_mex.c | 227 + lib/src/phy/phch/test/pusch_encode_test_mex.c | 251 + lib/src/phy/phch/test/pusch_test.c | 472 + lib/src/phy/phch/test/pusch_test_mex.c | 309 + lib/src/phy/phch/test/signal.1.92M.amar.dat | Bin 0 -> 153600 bytes lib/src/phy/phch/test/signal.1.92M.dat | Bin 0 -> 76808 bytes lib/src/phy/phch/test/signal.10M.dat | Bin 0 -> 61448 bytes lib/src/phy/phch/test/ulsch_encode_test_mex.c | 220 + lib/src/phy/phch/uci.c | 790 + lib/src/phy/resampling/CMakeLists.txt | 23 + lib/src/phy/resampling/decim.c | 48 + lib/src/phy/resampling/interp.c | 273 + lib/src/phy/resampling/resample_arb.c | 182 + lib/src/phy/resampling/test/CMakeLists.txt | 34 + lib/src/phy/resampling/test/resample_arb_bench.c | 67 + lib/src/phy/resampling/test/resample_arb_test.c | 90 + lib/src/phy/rf/CMakeLists.txt | 64 + lib/src/phy/rf/rf_blade_imp.c | 569 + lib/src/phy/rf/rf_blade_imp.h | 136 + lib/src/phy/rf/rf_dev.h | 248 + lib/src/phy/rf/rf_helper.h | 52 + lib/src/phy/rf/rf_imp.c | 366 + lib/src/phy/rf/rf_soapy_imp.c | 925 + lib/src/phy/rf/rf_soapy_imp.h | 131 + lib/src/phy/rf/rf_uhd_imp.c | 945 + lib/src/phy/rf/rf_uhd_imp.h | 140 + lib/src/phy/rf/rf_utils.c | 249 + lib/src/phy/rf/uhd_c_api.cpp | 56 + lib/src/phy/rf/uhd_c_api.h | 37 + lib/src/phy/scrambling/CMakeLists.txt | 23 + lib/src/phy/scrambling/scrambling.c | 103 + lib/src/phy/scrambling/test/CMakeLists.txt | 34 + lib/src/phy/scrambling/test/scrambling_test.c | 184 + lib/src/phy/sync/CMakeLists.txt | 23 + lib/src/phy/sync/cfo.c | 108 + lib/src/phy/sync/cp.c | 92 + lib/src/phy/sync/find_sss.c | 203 + lib/src/phy/sync/gen_sss.c | 162 + lib/src/phy/sync/pss.c | 628 + lib/src/phy/sync/sfo.c | 56 + lib/src/phy/sync/sss.c | 153 + lib/src/phy/sync/sync.c | 688 + lib/src/phy/sync/test/CMakeLists.txt | 75 + lib/src/phy/sync/test/cfo_test.c | 119 + lib/src/phy/sync/test/cp_mex.c | 93 + lib/src/phy/sync/test/pss_file.c | 407 + lib/src/phy/sync/test/pss_mex.c | 101 + lib/src/phy/sync/test/pss_usrp.c | 424 + lib/src/phy/sync/test/sss_mex.c | 127 + lib/src/phy/sync/test/sync_test.c | 193 + lib/src/phy/ue/CMakeLists.txt | 22 + lib/src/phy/ue/ue_cell_search.c | 343 + lib/src/phy/ue/ue_dl.c | 991 + lib/src/phy/ue/ue_mib.c | 293 + lib/src/phy/ue/ue_sync.c | 823 + lib/src/phy/ue/ue_ul.c | 675 + lib/src/phy/utils/CMakeLists.txt | 28 + lib/src/phy/utils/bit.c | 793 + lib/src/phy/utils/cexptab.c | 83 + lib/src/phy/utils/convolution.c | 231 + lib/src/phy/utils/debug.c | 101 + lib/src/phy/utils/filter.c | 126 + lib/src/phy/utils/mat.c | 296 + lib/src/phy/utils/ringbuffer.c | 117 + lib/src/phy/utils/test/CMakeLists.txt | 48 + lib/src/phy/utils/test/dft_test.c | 165 + lib/src/phy/utils/test/mat_test.c | 540 + lib/src/phy/utils/test/vector_test.c | 993 + lib/src/phy/utils/vector.c | 448 + lib/src/phy/utils/vector_simd.c | 1361 + lib/src/radio/CMakeLists.txt | 27 + lib/src/radio/radio.cc | 483 + lib/src/radio/radio_multi.cc | 64 + lib/src/radio/test/CMakeLists.txt | 26 + lib/src/radio/test/benchmark_radio.cc | 230 + lib/src/upper/CMakeLists.txt | 24 + lib/src/upper/gtpu.cc | 96 + lib/src/upper/pdcp.cc | 237 + lib/src/upper/pdcp_entity.cc | 471 + lib/src/upper/rlc.cc | 387 + lib/src/upper/rlc_am.cc | 1816 + lib/src/upper/rlc_entity.cc | 170 + lib/src/upper/rlc_tm.cc | 183 + lib/src/upper/rlc_um.cc | 863 + lib/test/CMakeLists.txt | 24 + lib/test/asn1/CMakeLists.txt | 27 + lib/test/asn1/rrc_meas_test.cc | 91 + lib/test/asn1/srslte_asn1_rrc_mcch_test.cc | 160 + lib/test/asn1/srslte_asn1_rrc_meas_test.cc | 91 + lib/test/common/CMakeLists.txt | 50 + lib/test/common/bcd_helpers_test.cc | 67 + lib/test/common/log_filter_test.cc | 135 + lib/test/common/logger_test.cc | 116 + lib/test/common/msg_queue_test.cc | 85 + lib/test/common/test_eea1.cc | 581 + lib/test/common/test_eea2.cc | 583 + lib/test/common/test_f12345.cc | 181 + lib/test/common/timeout_test.cc | 144 + lib/test/phy/CMakeLists.txt | 27 + lib/test/phy/phy_dl_test.c | 377 + lib/test/upper/CMakeLists.txt | 56 + lib/test/upper/rlc_am_control_test.cc | 70 + lib/test/upper/rlc_am_data_test.cc | 145 + lib/test/upper/rlc_am_test.cc | 1463 + lib/test/upper/rlc_stress_test.cc | 411 + lib/test/upper/rlc_um_data_test.cc | 71 + lib/test/upper/rlc_um_test.cc | 549 + srsenb/CMakeLists.txt | 58 + srsenb/drb.conf.example | 54 + srsenb/enb.conf.example | 180 + srsenb/hdr/CMakeLists.txt | 5 + srsenb/hdr/cfg_parser.h | 41 + srsenb/hdr/enb.h | 231 + srsenb/hdr/mac/mac.h | 233 + srsenb/hdr/mac/mac_metrics.h | 54 + srsenb/hdr/mac/scheduler.h | 237 + srsenb/hdr/mac/scheduler_harq.h | 129 + srsenb/hdr/mac/scheduler_metric.h | 89 + srsenb/hdr/mac/scheduler_ue.h | 219 + srsenb/hdr/mac/ue.h | 173 + srsenb/hdr/metrics_stdout.h | 74 + srsenb/hdr/parser.h | 310 + srsenb/hdr/phy/phch_common.h | 176 + srsenb/hdr/phy/phch_worker.h | 143 + srsenb/hdr/phy/phy.h | 106 + srsenb/hdr/phy/phy_metrics.h | 59 + srsenb/hdr/phy/prach_worker.h | 88 + srsenb/hdr/phy/txrx.h | 76 + srsenb/hdr/upper/common_enb.h | 184 + srsenb/hdr/upper/gtpu.h | 146 + srsenb/hdr/upper/pdcp.h | 123 + srsenb/hdr/upper/rlc.h | 114 + srsenb/hdr/upper/rrc.h | 383 + srsenb/hdr/upper/rrc_metrics.h | 58 + srsenb/hdr/upper/s1ap.h | 149 + srsenb/hdr/upper/s1ap_metrics.h | 46 + srsenb/rr.conf.example | 50 + srsenb/sib.conf.example | 127 + srsenb/sib.conf.mbsfn.example | 153 + srsenb/src/CMakeLists.txt | 48 + srsenb/src/enb.cc | 345 + srsenb/src/enb_cfg_parser.cc | 1316 + srsenb/src/enb_cfg_parser.h | 120 + srsenb/src/mac/CMakeLists.txt | 24 + srsenb/src/mac/mac.cc | 996 + srsenb/src/mac/scheduler.cc | 1130 + srsenb/src/mac/scheduler_harq.cc | 274 + srsenb/src/mac/scheduler_metric.cc | 408 + srsenb/src/mac/scheduler_ue.cc | 1234 + srsenb/src/mac/ue.cc | 482 + srsenb/src/main.cc | 437 + srsenb/src/metrics_stdout.cc | 227 + srsenb/src/parser.cc | 146 + srsenb/src/phy/CMakeLists.txt | 29 + srsenb/src/phy/phch_common.cc | 358 + srsenb/src/phy/phch_worker.cc | 1231 + srsenb/src/phy/phy.cc | 259 + srsenb/src/phy/prach_worker.cc | 166 + srsenb/src/phy/txrx.cc | 151 + srsenb/src/upper/CMakeLists.txt | 24 + srsenb/src/upper/gtpu.cc | 410 + srsenb/src/upper/pdcp.cc | 192 + srsenb/src/upper/rlc.cc | 251 + srsenb/src/upper/rrc.cc | 1973 + srsenb/src/upper/s1ap.cc | 1042 + srsenb/test/CMakeLists.txt | 3 + srsenb/test/mac/CMakeLists.txt | 9 + srsenb/test/mac/scheduler_test.cc | 161 + srsenb/test/upper/CMakeLists.txt | 17 + srsenb/test/upper/ip_test.cc | 684 + srsenb/test/upper/plmn_test.cc | 77 + srsepc/CMakeLists.txt | 57 + srsepc/epc.conf.example | 77 + srsepc/hdr/CMakeLists.txt | 5 + srsepc/hdr/hss/hss.h | 133 + srsepc/hdr/mbms-gw/mbms-gw.h | 102 + srsepc/hdr/mme/mme.h | 92 + srsepc/hdr/mme/mme_gtpc.h | 81 + srsepc/hdr/mme/s1ap.h | 139 + srsepc/hdr/mme/s1ap_common.h | 167 + srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h | 73 + srsepc/hdr/mme/s1ap_mngmt_proc.h | 68 + srsepc/hdr/mme/s1ap_nas_transport.h | 133 + srsepc/hdr/spgw/spgw.h | 138 + srsepc/mbms.conf.example | 40 + srsepc/src/CMakeLists.txt | 76 + srsepc/src/hss/CMakeLists.txt | 24 + srsepc/src/hss/hss.cc | 739 + srsepc/src/main.cc | 358 + srsepc/src/mbms-gw/CMakeLists.txt | 23 + srsepc/src/mbms-gw/main.cc | 223 + srsepc/src/mbms-gw/mbms-gw.cc | 326 + srsepc/src/mme/CMakeLists.txt | 24 + srsepc/src/mme/mme.cc | 169 + srsepc/src/mme/mme_gtpc.cc | 388 + srsepc/src/mme/s1ap.cc | 620 + srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 417 + srsepc/src/mme/s1ap_mngmt_proc.cc | 271 + srsepc/src/mme/s1ap_nas_transport.cc | 2202 + srsepc/src/spgw/CMakeLists.txt | 23 + srsepc/src/spgw/spgw.cc | 666 + srsepc/srsepc_if_masq.sh | 37 + srsepc/user_db.csv.example | 16 + srsue/CMakeLists.txt | 51 + srsue/hdr/mac/demux.h | 97 + srsue/hdr/mac/dl_harq.h | 458 + srsue/hdr/mac/dl_sps.h | 53 + srsue/hdr/mac/mac.h | 197 + srsue/hdr/mac/mac_metrics.h | 46 + srsue/hdr/mac/mux.h | 118 + srsue/hdr/mac/proc.h | 59 + srsue/hdr/mac/proc_bsr.h | 91 + srsue/hdr/mac/proc_phr.h | 77 + srsue/hdr/mac/proc_ra.h | 219 + srsue/hdr/mac/proc_sr.h | 67 + srsue/hdr/mac/ul_harq.h | 432 + srsue/hdr/mac/ul_sps.h | 53 + srsue/hdr/metrics_csv.h | 67 + srsue/hdr/metrics_stdout.h | 67 + srsue/hdr/phy/phch_common.h | 239 + srsue/hdr/phy/phch_recv.h | 446 + srsue/hdr/phy/phch_worker.h | 200 + srsue/hdr/phy/phy.h | 194 + srsue/hdr/phy/phy_metrics.h | 69 + srsue/hdr/phy/prach.h | 88 + srsue/hdr/ue.h | 129 + srsue/hdr/ue_base.h | 196 + srsue/hdr/ue_metrics_interface.h | 65 + srsue/hdr/upper/gw.h | 107 + srsue/hdr/upper/gw_metrics.h | 41 + srsue/hdr/upper/nas.h | 231 + srsue/hdr/upper/pcsc_usim.h | 270 + srsue/hdr/upper/rrc.h | 653 + srsue/hdr/upper/rrc_common.h | 45 + srsue/hdr/upper/usim.h | 143 + srsue/hdr/upper/usim_base.h | 113 + srsue/src/CMakeLists.txt | 61 + srsue/src/mac/CMakeLists.txt | 23 + srsue/src/mac/demux.cc | 263 + srsue/src/mac/mac.cc | 564 + srsue/src/mac/mux.cc | 388 + srsue/src/mac/proc_bsr.cc | 409 + srsue/src/mac/proc_phr.cc | 163 + srsue/src/mac/proc_ra.cc | 604 + srsue/src/mac/proc_sr.cc | 129 + srsue/src/main.cc | 593 + srsue/src/metrics_csv.cc | 121 + srsue/src/metrics_stdout.cc | 145 + srsue/src/phy/CMakeLists.txt | 28 + srsue/src/phy/phch_common.cc | 542 + srsue/src/phy/phch_recv.cc | 1654 + srsue/src/phy/phch_worker.cc | 1799 + srsue/src/phy/phy.cc | 489 + srsue/src/phy/prach.cc | 207 + srsue/src/set_net_admin_caps.cc | 53 + srsue/src/ue.cc | 370 + srsue/src/ue_base.cc | 149 + srsue/src/upper/CMakeLists.txt | 33 + srsue/src/upper/gw.cc | 392 + srsue/src/upper/nas.cc | 1517 + srsue/src/upper/pcsc_usim.cc | 1370 + srsue/src/upper/rrc.cc | 3400 ++ srsue/src/upper/usim.cc | 533 + srsue/src/upper/usim_base.cc | 59 + srsue/test/CMakeLists.txt | 27 + srsue/test/mac/CMakeLists.txt | 23 + srsue/test/mac/mac_test.cc | 508 + srsue/test/metrics_test.cc | 122 + srsue/test/phy/CMakeLists.txt | 25 + srsue/test/phy/ue_itf_test_prach.cc | 403 + srsue/test/phy/ue_itf_test_sib1.cc | 224 + srsue/test/upper/CMakeLists.txt | 47 + srsue/test/upper/ip_test.cc | 646 + srsue/test/upper/nas_test.cc | 321 + srsue/test/upper/pcsc_usim_test.cc | 66 + srsue/test/upper/rrc_reconfig_test.cc | 136 + srsue/test/upper/usim_test.cc | 88 + srsue/ue.conf.example | 264 + 651 files changed, 247397 insertions(+) create mode 100644 CHANGELOG create mode 100644 CMakeLists.txt create mode 100644 COPYRIGHT create mode 100644 CTestConfig.cmake create mode 100644 CTestCustom.cmake.in create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/modules/CheckCSourceRuns.cmake create mode 100644 cmake/modules/CheckFunctionExists.c create mode 100644 cmake/modules/CheckFunctionExistsMath.cmake create mode 100644 cmake/modules/FindFFTW3F.cmake create mode 100644 cmake/modules/FindLibConfig.cmake create mode 100644 cmake/modules/FindLimeSDR.cmake create mode 100644 cmake/modules/FindMKL.cmake create mode 100644 cmake/modules/FindMbedTLS.cmake create mode 100644 cmake/modules/FindPCSCLite.cmake create mode 100644 cmake/modules/FindPolarssl.cmake create mode 100644 cmake/modules/FindSCTP.cmake create mode 100644 cmake/modules/FindSRSGUI.cmake create mode 100644 cmake/modules/FindSSE.cmake create mode 100644 cmake/modules/FindSoapySDR.cmake create mode 100644 cmake/modules/FindUHD.cmake create mode 100644 cmake/modules/FindbladeRF.cmake create mode 100644 cmake/modules/SRSLTEPackage.cmake create mode 100644 cmake/modules/SRSLTEVersion.cmake create mode 100755 cmake/modules/SRSLTE_install_configs.sh.in create mode 100644 cmake/modules/SRSLTEbuildinfo.cmake.in create mode 100644 cmake_uninstall.cmake.in create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100755 debian/config create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/postinst create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/templates create mode 100644 lib/CMakeLists.txt create mode 100644 lib/examples/CMakeLists.txt create mode 100644 lib/examples/cell_measurement.c create mode 100644 lib/examples/cell_search.c create mode 100644 lib/examples/pdsch_enodeb.c create mode 100644 lib/examples/pdsch_ue.c create mode 100644 lib/examples/synch_file.c create mode 100644 lib/examples/usrp_capture.c create mode 100644 lib/examples/usrp_capture_sync.c create mode 100644 lib/examples/usrp_txrx.c create mode 100644 lib/include/CMakeLists.txt create mode 100644 lib/include/srslte/CMakeLists.txt create mode 100644 lib/include/srslte/asn1/gtpc.h create mode 100644 lib/include/srslte/asn1/gtpc_ies.h create mode 100644 lib/include/srslte/asn1/gtpc_msg.h create mode 100644 lib/include/srslte/asn1/liblte_common.h create mode 100644 lib/include/srslte/asn1/liblte_mme.h create mode 100644 lib/include/srslte/asn1/liblte_rrc.h create mode 100644 lib/include/srslte/asn1/liblte_s1ap.h create mode 100644 lib/include/srslte/build_info.h.in create mode 100644 lib/include/srslte/common/bcd_helpers.h create mode 100644 lib/include/srslte/common/block_queue.h create mode 100644 lib/include/srslte/common/buffer_pool.h create mode 100644 lib/include/srslte/common/common.h create mode 100644 lib/include/srslte/common/config_file.h create mode 100644 lib/include/srslte/common/gen_mch_tables.h create mode 100644 lib/include/srslte/common/interfaces_common.h create mode 100644 lib/include/srslte/common/liblte_security.h create mode 100644 lib/include/srslte/common/liblte_ssl.h create mode 100644 lib/include/srslte/common/log.h create mode 100644 lib/include/srslte/common/log_filter.h create mode 100644 lib/include/srslte/common/logger.h create mode 100644 lib/include/srslte/common/logger_file.h create mode 100644 lib/include/srslte/common/logger_stdout.h create mode 100644 lib/include/srslte/common/mac_pcap.h create mode 100644 lib/include/srslte/common/metrics_hub.h create mode 100644 lib/include/srslte/common/nas_pcap.h create mode 100644 lib/include/srslte/common/pcap.h create mode 100644 lib/include/srslte/common/pdu.h create mode 100644 lib/include/srslte/common/pdu_queue.h create mode 100644 lib/include/srslte/common/rlc_pcap.h create mode 100644 lib/include/srslte/common/security.h create mode 100644 lib/include/srslte/common/snow_3g.h create mode 100644 lib/include/srslte/common/task_dispatcher.h create mode 100644 lib/include/srslte/common/thread_pool.h create mode 100644 lib/include/srslte/common/threads.h create mode 100644 lib/include/srslte/common/timeout.h create mode 100644 lib/include/srslte/common/timers.h create mode 100644 lib/include/srslte/common/trace.h create mode 100644 lib/include/srslte/common/tti_sync.h create mode 100644 lib/include/srslte/common/tti_sync_cv.h create mode 100644 lib/include/srslte/config.h create mode 100644 lib/include/srslte/interfaces/enb_interfaces.h create mode 100644 lib/include/srslte/interfaces/enb_metrics_interface.h create mode 100644 lib/include/srslte/interfaces/epc_interfaces.h create mode 100644 lib/include/srslte/interfaces/sched_interface.h create mode 100644 lib/include/srslte/interfaces/ue_interfaces.h create mode 100644 lib/include/srslte/phy/agc/agc.h create mode 100644 lib/include/srslte/phy/ch_estimation/chest_common.h create mode 100644 lib/include/srslte/phy/ch_estimation/chest_dl.h create mode 100644 lib/include/srslte/phy/ch_estimation/chest_ul.h create mode 100644 lib/include/srslte/phy/ch_estimation/refsignal_dl.h create mode 100644 lib/include/srslte/phy/ch_estimation/refsignal_ul.h create mode 100644 lib/include/srslte/phy/channel/ch_awgn.h create mode 100644 lib/include/srslte/phy/common/phy_common.h create mode 100644 lib/include/srslte/phy/common/phy_logger.h create mode 100644 lib/include/srslte/phy/common/sequence.h create mode 100644 lib/include/srslte/phy/common/timestamp.h create mode 100644 lib/include/srslte/phy/dft/dft.h create mode 100644 lib/include/srslte/phy/dft/dft_precoding.h create mode 100644 lib/include/srslte/phy/dft/ofdm.h create mode 100644 lib/include/srslte/phy/enb/enb_dl.h create mode 100644 lib/include/srslte/phy/enb/enb_ul.h create mode 100644 lib/include/srslte/phy/fec/cbsegm.h create mode 100644 lib/include/srslte/phy/fec/convcoder.h create mode 100644 lib/include/srslte/phy/fec/crc.h create mode 100644 lib/include/srslte/phy/fec/rm_conv.h create mode 100644 lib/include/srslte/phy/fec/rm_turbo.h create mode 100644 lib/include/srslte/phy/fec/softbuffer.h create mode 100644 lib/include/srslte/phy/fec/tc_interl.h create mode 100644 lib/include/srslte/phy/fec/turbocoder.h create mode 100644 lib/include/srslte/phy/fec/turbodecoder.h create mode 100644 lib/include/srslte/phy/fec/turbodecoder_gen.h create mode 100644 lib/include/srslte/phy/fec/turbodecoder_simd.h create mode 100644 lib/include/srslte/phy/fec/turbodecoder_simd_inter.h create mode 100644 lib/include/srslte/phy/fec/turbodecoder_sse.h create mode 100644 lib/include/srslte/phy/fec/viterbi.h create mode 100644 lib/include/srslte/phy/io/binsource.h create mode 100644 lib/include/srslte/phy/io/filesink.h create mode 100644 lib/include/srslte/phy/io/filesource.h create mode 100644 lib/include/srslte/phy/io/format.h create mode 100644 lib/include/srslte/phy/io/netsink.h create mode 100644 lib/include/srslte/phy/io/netsource.h create mode 100644 lib/include/srslte/phy/mimo/layermap.h create mode 100644 lib/include/srslte/phy/mimo/precoding.h create mode 100644 lib/include/srslte/phy/modem/demod_hard.h create mode 100644 lib/include/srslte/phy/modem/demod_soft.h create mode 100644 lib/include/srslte/phy/modem/mod.h create mode 100644 lib/include/srslte/phy/modem/modem_table.h create mode 100644 lib/include/srslte/phy/phch/cqi.h create mode 100644 lib/include/srslte/phy/phch/dci.h create mode 100644 lib/include/srslte/phy/phch/pbch.h create mode 100644 lib/include/srslte/phy/phch/pcfich.h create mode 100644 lib/include/srslte/phy/phch/pdcch.h create mode 100644 lib/include/srslte/phy/phch/pdsch.h create mode 100644 lib/include/srslte/phy/phch/pdsch_cfg.h create mode 100644 lib/include/srslte/phy/phch/phich.h create mode 100644 lib/include/srslte/phy/phch/pmch.h create mode 100644 lib/include/srslte/phy/phch/prach.h create mode 100644 lib/include/srslte/phy/phch/pucch.h create mode 100644 lib/include/srslte/phy/phch/pusch.h create mode 100644 lib/include/srslte/phy/phch/pusch_cfg.h create mode 100644 lib/include/srslte/phy/phch/ra.h create mode 100644 lib/include/srslte/phy/phch/regs.h create mode 100644 lib/include/srslte/phy/phch/sch.h create mode 100644 lib/include/srslte/phy/phch/uci.h create mode 100644 lib/include/srslte/phy/resampling/decim.h create mode 100644 lib/include/srslte/phy/resampling/interp.h create mode 100644 lib/include/srslte/phy/resampling/resample_arb.h create mode 100644 lib/include/srslte/phy/rf/rf.h create mode 100644 lib/include/srslte/phy/rf/rf_utils.h create mode 100644 lib/include/srslte/phy/scrambling/scrambling.h create mode 100644 lib/include/srslte/phy/sync/cfo.h create mode 100644 lib/include/srslte/phy/sync/cp.h create mode 100644 lib/include/srslte/phy/sync/pss.h create mode 100644 lib/include/srslte/phy/sync/sfo.h create mode 100644 lib/include/srslte/phy/sync/sss.h create mode 100644 lib/include/srslte/phy/sync/sync.h create mode 100644 lib/include/srslte/phy/ue/ue_cell_search.h create mode 100644 lib/include/srslte/phy/ue/ue_dl.h create mode 100644 lib/include/srslte/phy/ue/ue_mib.h create mode 100644 lib/include/srslte/phy/ue/ue_phy.h create mode 100644 lib/include/srslte/phy/ue/ue_sync.h create mode 100644 lib/include/srslte/phy/ue/ue_ul.h create mode 100644 lib/include/srslte/phy/utils/bit.h create mode 100644 lib/include/srslte/phy/utils/cexptab.h create mode 100644 lib/include/srslte/phy/utils/convolution.h create mode 100644 lib/include/srslte/phy/utils/debug.h create mode 100644 lib/include/srslte/phy/utils/filter.h create mode 100644 lib/include/srslte/phy/utils/mat.h create mode 100644 lib/include/srslte/phy/utils/ringbuffer.h create mode 100644 lib/include/srslte/phy/utils/simd.h create mode 100644 lib/include/srslte/phy/utils/vector.h create mode 100644 lib/include/srslte/phy/utils/vector_simd.h create mode 100644 lib/include/srslte/radio/radio.h create mode 100644 lib/include/srslte/radio/radio_multi.h create mode 100644 lib/include/srslte/srslte.h create mode 100644 lib/include/srslte/upper/gtpu.h create mode 100644 lib/include/srslte/upper/pdcp.h create mode 100644 lib/include/srslte/upper/pdcp_entity.h create mode 100644 lib/include/srslte/upper/rlc.h create mode 100644 lib/include/srslte/upper/rlc_am.h create mode 100644 lib/include/srslte/upper/rlc_common.h create mode 100644 lib/include/srslte/upper/rlc_entity.h create mode 100644 lib/include/srslte/upper/rlc_interface.h create mode 100644 lib/include/srslte/upper/rlc_metrics.h create mode 100644 lib/include/srslte/upper/rlc_tm.h create mode 100644 lib/include/srslte/upper/rlc_tx_queue.h create mode 100644 lib/include/srslte/upper/rlc_um.h create mode 100644 lib/include/srslte/version.h.in create mode 100644 lib/src/CMakeLists.txt create mode 100644 lib/src/asn1/CMakeLists.txt create mode 100644 lib/src/asn1/gtpc.cc create mode 100644 lib/src/asn1/liblte_common.cc create mode 100644 lib/src/asn1/liblte_mme.cc create mode 100644 lib/src/asn1/liblte_rrc.cc create mode 100644 lib/src/asn1/liblte_s1ap.cc create mode 100644 lib/src/common/CMakeLists.txt create mode 100644 lib/src/common/buffer_pool.cc create mode 100644 lib/src/common/gen_mch_tables.c create mode 100644 lib/src/common/liblte_security.cc create mode 100644 lib/src/common/log_filter.cc create mode 100644 lib/src/common/logger_file.cc create mode 100644 lib/src/common/mac_pcap.cc create mode 100644 lib/src/common/nas_pcap.cc create mode 100644 lib/src/common/pdu.cc create mode 100644 lib/src/common/pdu_queue.cc create mode 100644 lib/src/common/rlc_pcap.cc create mode 100644 lib/src/common/security.cc create mode 100644 lib/src/common/snow_3g.cc create mode 100644 lib/src/common/task_dispatcher.cc create mode 100644 lib/src/common/thread_pool.cc create mode 100644 lib/src/common/threads.c create mode 100644 lib/src/common/tti_sync_cv.cc create mode 100644 lib/src/common/version.c create mode 100644 lib/src/phy/CMakeLists.txt create mode 100644 lib/src/phy/agc/CMakeLists.txt create mode 100644 lib/src/phy/agc/agc.c create mode 100644 lib/src/phy/ch_estimation/CMakeLists.txt create mode 100644 lib/src/phy/ch_estimation/chest_common.c create mode 100644 lib/src/phy/ch_estimation/chest_dl.c create mode 100644 lib/src/phy/ch_estimation/chest_ul.c create mode 100644 lib/src/phy/ch_estimation/refsignal_dl.c create mode 100644 lib/src/phy/ch_estimation/refsignal_ul.c create mode 100644 lib/src/phy/ch_estimation/test/CMakeLists.txt create mode 100644 lib/src/phy/ch_estimation/test/chest_test_dl.c create mode 100644 lib/src/phy/ch_estimation/test/chest_test_dl_mex.c create mode 100755 lib/src/phy/ch_estimation/test/chest_test_dl_mex.mexa64 create mode 100644 lib/src/phy/ch_estimation/test/chest_test_ul.c create mode 100644 lib/src/phy/ch_estimation/test/chest_test_ul_mex.c create mode 100644 lib/src/phy/ch_estimation/test/refsignal_pusch_mex.c create mode 100644 lib/src/phy/ch_estimation/test/refsignal_srs_mex.c create mode 100644 lib/src/phy/ch_estimation/test/refsignal_ul_test.c create mode 100644 lib/src/phy/ch_estimation/ul_rs_tables.h create mode 100644 lib/src/phy/channel/CMakeLists.txt create mode 100644 lib/src/phy/channel/ch_awgn.c create mode 100644 lib/src/phy/channel/gauss.c create mode 100644 lib/src/phy/channel/gauss.h create mode 100644 lib/src/phy/common/CMakeLists.txt create mode 100644 lib/src/phy/common/phy_common.c create mode 100644 lib/src/phy/common/phy_logger.c create mode 100644 lib/src/phy/common/sequence.c create mode 100644 lib/src/phy/common/timestamp.c create mode 100644 lib/src/phy/dft/CMakeLists.txt create mode 100644 lib/src/phy/dft/dft_fftw.c create mode 100644 lib/src/phy/dft/dft_precoding.c create mode 100644 lib/src/phy/dft/ofdm.c create mode 100644 lib/src/phy/dft/test/CMakeLists.txt create mode 100644 lib/src/phy/dft/test/ofdm_test.c create mode 100644 lib/src/phy/enb/CMakeLists.txt create mode 100644 lib/src/phy/enb/enb_dl.c create mode 100644 lib/src/phy/enb/enb_ul.c create mode 100644 lib/src/phy/fec/CMakeLists.txt create mode 100644 lib/src/phy/fec/cbsegm.c create mode 100644 lib/src/phy/fec/convcoder.c create mode 100644 lib/src/phy/fec/crc.c create mode 100644 lib/src/phy/fec/parity.c create mode 100644 lib/src/phy/fec/parity.h create mode 100644 lib/src/phy/fec/rm_conv.c create mode 100644 lib/src/phy/fec/rm_turbo.c create mode 100644 lib/src/phy/fec/softbuffer.c create mode 100644 lib/src/phy/fec/tc_interl_lte.c create mode 100644 lib/src/phy/fec/tc_interl_umts.c create mode 100644 lib/src/phy/fec/test/CMakeLists.txt create mode 100644 lib/src/phy/fec/test/crc_test.c create mode 100644 lib/src/phy/fec/test/crc_test.h create mode 100644 lib/src/phy/fec/test/rm_conv_test.c create mode 100644 lib/src/phy/fec/test/rm_turbo_rx_mex.c create mode 100644 lib/src/phy/fec/test/rm_turbo_test.c create mode 100644 lib/src/phy/fec/test/turbocoder_test.c create mode 100644 lib/src/phy/fec/test/turbodecoder_test.c create mode 100644 lib/src/phy/fec/test/turbodecoder_test.h create mode 100644 lib/src/phy/fec/test/turbodecoder_test_mex.c create mode 100644 lib/src/phy/fec/test/viterbi_test.c create mode 100644 lib/src/phy/fec/test/viterbi_test.h create mode 100644 lib/src/phy/fec/test/viterbi_test_mex.c create mode 100644 lib/src/phy/fec/turbocoder.c create mode 100644 lib/src/phy/fec/turbodecoder.c create mode 100644 lib/src/phy/fec/turbodecoder_avx.c create mode 100644 lib/src/phy/fec/turbodecoder_gen.c create mode 100644 lib/src/phy/fec/turbodecoder_simd.c create mode 100644 lib/src/phy/fec/turbodecoder_simd_inter.c create mode 100644 lib/src/phy/fec/turbodecoder_sse.c create mode 100644 lib/src/phy/fec/turbodecoder_sse_inter.c create mode 100644 lib/src/phy/fec/viterbi.c create mode 100644 lib/src/phy/fec/viterbi37.h create mode 100644 lib/src/phy/fec/viterbi37_avx2.c create mode 100644 lib/src/phy/fec/viterbi37_avx2_16bit.c create mode 100644 lib/src/phy/fec/viterbi37_neon.c create mode 100644 lib/src/phy/fec/viterbi37_port.c create mode 100644 lib/src/phy/fec/viterbi37_sse.c create mode 100644 lib/src/phy/io/CMakeLists.txt create mode 100644 lib/src/phy/io/binsource.c create mode 100644 lib/src/phy/io/filesink.c create mode 100644 lib/src/phy/io/filesource.c create mode 100644 lib/src/phy/io/netsink.c create mode 100644 lib/src/phy/io/netsource.c create mode 100644 lib/src/phy/mimo/CMakeLists.txt create mode 100644 lib/src/phy/mimo/layermap.c create mode 100644 lib/src/phy/mimo/precoding.c create mode 100644 lib/src/phy/mimo/test/CMakeLists.txt create mode 100644 lib/src/phy/mimo/test/layermap_test.c create mode 100644 lib/src/phy/mimo/test/pmi_select_test.c create mode 100644 lib/src/phy/mimo/test/pmi_select_test.h create mode 100644 lib/src/phy/mimo/test/precoder_mex.c create mode 100644 lib/src/phy/mimo/test/precoder_test.c create mode 100644 lib/src/phy/mimo/test/predecoder_mex.c create mode 100644 lib/src/phy/modem/CMakeLists.txt create mode 100644 lib/src/phy/modem/demod_hard.c create mode 100644 lib/src/phy/modem/demod_soft.c create mode 100644 lib/src/phy/modem/hard_demod_lte.c create mode 100644 lib/src/phy/modem/hard_demod_lte.h create mode 100644 lib/src/phy/modem/lte_tables.c create mode 100644 lib/src/phy/modem/lte_tables.h create mode 100644 lib/src/phy/modem/mod.c create mode 100644 lib/src/phy/modem/modem_table.c create mode 100644 lib/src/phy/modem/test/CMakeLists.txt create mode 100644 lib/src/phy/modem/test/modem_test.c create mode 100644 lib/src/phy/modem/test/soft_demod_test.c create mode 100644 lib/src/phy/phch/CMakeLists.txt create mode 100644 lib/src/phy/phch/cqi.c create mode 100644 lib/src/phy/phch/dci.c create mode 100644 lib/src/phy/phch/dci_sz_table.h create mode 100644 lib/src/phy/phch/pbch.c create mode 100644 lib/src/phy/phch/pcfich.c create mode 100644 lib/src/phy/phch/pdcch.c create mode 100644 lib/src/phy/phch/pdsch.c create mode 100644 lib/src/phy/phch/phich.c create mode 100644 lib/src/phy/phch/pmch.c create mode 100644 lib/src/phy/phch/prach.c create mode 100644 lib/src/phy/phch/prb_dl.c create mode 100644 lib/src/phy/phch/prb_dl.h create mode 100644 lib/src/phy/phch/pucch.c create mode 100644 lib/src/phy/phch/pusch.c create mode 100644 lib/src/phy/phch/ra.c create mode 100644 lib/src/phy/phch/regs.c create mode 100644 lib/src/phy/phch/sch.c create mode 100644 lib/src/phy/phch/sequences.c create mode 100644 lib/src/phy/phch/tbs_tables.h create mode 100644 lib/src/phy/phch/test/CMakeLists.txt create mode 100644 lib/src/phy/phch/test/dlsch_encode_test_mex.c create mode 100644 lib/src/phy/phch/test/pbch_file_test.c create mode 100644 lib/src/phy/phch/test/pbch_test.c create mode 100644 lib/src/phy/phch/test/pbch_test_mex.c create mode 100644 lib/src/phy/phch/test/pcfich_file_test.c create mode 100644 lib/src/phy/phch/test/pcfich_test.c create mode 100644 lib/src/phy/phch/test/pcfich_test_mex.c create mode 100644 lib/src/phy/phch/test/pdcch_file_test.c create mode 100644 lib/src/phy/phch/test/pdcch_test.c create mode 100644 lib/src/phy/phch/test/pdcch_test_mex.c create mode 100644 lib/src/phy/phch/test/pdsch_pdcch_file_test.c create mode 100644 lib/src/phy/phch/test/pdsch_test.c create mode 100644 lib/src/phy/phch/test/pdsch_test_mex.c create mode 100644 lib/src/phy/phch/test/phich_file_test.c create mode 100644 lib/src/phy/phch/test/phich_test.c create mode 100644 lib/src/phy/phch/test/phich_test_mex.c create mode 100644 lib/src/phy/phch/test/pmch_100prbs_MCS2_SR0.bin create mode 100644 lib/src/phy/phch/test/pmch_file_test.c create mode 100644 lib/src/phy/phch/test/pmch_test.c create mode 100644 lib/src/phy/phch/test/prach_detect_test_mex.c create mode 100644 lib/src/phy/phch/test/prach_test.c create mode 100644 lib/src/phy/phch/test/prach_test_mex.c create mode 100644 lib/src/phy/phch/test/prach_test_multi.c create mode 100644 lib/src/phy/phch/test/prach_test_usrp.c create mode 100644 lib/src/phy/phch/test/pucch_encode_test_mex.c create mode 100644 lib/src/phy/phch/test/pucch_test.c create mode 100644 lib/src/phy/phch/test/pucch_test_mex.c create mode 100644 lib/src/phy/phch/test/pusch_encode_test_mex.c create mode 100644 lib/src/phy/phch/test/pusch_test.c create mode 100644 lib/src/phy/phch/test/pusch_test_mex.c create mode 100644 lib/src/phy/phch/test/signal.1.92M.amar.dat create mode 100644 lib/src/phy/phch/test/signal.1.92M.dat create mode 100644 lib/src/phy/phch/test/signal.10M.dat create mode 100644 lib/src/phy/phch/test/ulsch_encode_test_mex.c create mode 100644 lib/src/phy/phch/uci.c create mode 100644 lib/src/phy/resampling/CMakeLists.txt create mode 100644 lib/src/phy/resampling/decim.c create mode 100644 lib/src/phy/resampling/interp.c create mode 100644 lib/src/phy/resampling/resample_arb.c create mode 100644 lib/src/phy/resampling/test/CMakeLists.txt create mode 100644 lib/src/phy/resampling/test/resample_arb_bench.c create mode 100644 lib/src/phy/resampling/test/resample_arb_test.c create mode 100644 lib/src/phy/rf/CMakeLists.txt create mode 100644 lib/src/phy/rf/rf_blade_imp.c create mode 100644 lib/src/phy/rf/rf_blade_imp.h create mode 100644 lib/src/phy/rf/rf_dev.h create mode 100644 lib/src/phy/rf/rf_helper.h create mode 100644 lib/src/phy/rf/rf_imp.c create mode 100644 lib/src/phy/rf/rf_soapy_imp.c create mode 100644 lib/src/phy/rf/rf_soapy_imp.h create mode 100644 lib/src/phy/rf/rf_uhd_imp.c create mode 100644 lib/src/phy/rf/rf_uhd_imp.h create mode 100644 lib/src/phy/rf/rf_utils.c create mode 100644 lib/src/phy/rf/uhd_c_api.cpp create mode 100644 lib/src/phy/rf/uhd_c_api.h create mode 100644 lib/src/phy/scrambling/CMakeLists.txt create mode 100644 lib/src/phy/scrambling/scrambling.c create mode 100644 lib/src/phy/scrambling/test/CMakeLists.txt create mode 100644 lib/src/phy/scrambling/test/scrambling_test.c create mode 100644 lib/src/phy/sync/CMakeLists.txt create mode 100644 lib/src/phy/sync/cfo.c create mode 100644 lib/src/phy/sync/cp.c create mode 100644 lib/src/phy/sync/find_sss.c create mode 100644 lib/src/phy/sync/gen_sss.c create mode 100644 lib/src/phy/sync/pss.c create mode 100644 lib/src/phy/sync/sfo.c create mode 100644 lib/src/phy/sync/sss.c create mode 100644 lib/src/phy/sync/sync.c create mode 100644 lib/src/phy/sync/test/CMakeLists.txt create mode 100644 lib/src/phy/sync/test/cfo_test.c create mode 100644 lib/src/phy/sync/test/cp_mex.c create mode 100644 lib/src/phy/sync/test/pss_file.c create mode 100644 lib/src/phy/sync/test/pss_mex.c create mode 100644 lib/src/phy/sync/test/pss_usrp.c create mode 100644 lib/src/phy/sync/test/sss_mex.c create mode 100644 lib/src/phy/sync/test/sync_test.c create mode 100644 lib/src/phy/ue/CMakeLists.txt create mode 100644 lib/src/phy/ue/ue_cell_search.c create mode 100644 lib/src/phy/ue/ue_dl.c create mode 100644 lib/src/phy/ue/ue_mib.c create mode 100644 lib/src/phy/ue/ue_sync.c create mode 100644 lib/src/phy/ue/ue_ul.c create mode 100644 lib/src/phy/utils/CMakeLists.txt create mode 100644 lib/src/phy/utils/bit.c create mode 100644 lib/src/phy/utils/cexptab.c create mode 100644 lib/src/phy/utils/convolution.c create mode 100644 lib/src/phy/utils/debug.c create mode 100644 lib/src/phy/utils/filter.c create mode 100644 lib/src/phy/utils/mat.c create mode 100644 lib/src/phy/utils/ringbuffer.c create mode 100644 lib/src/phy/utils/test/CMakeLists.txt create mode 100644 lib/src/phy/utils/test/dft_test.c create mode 100644 lib/src/phy/utils/test/mat_test.c create mode 100644 lib/src/phy/utils/test/vector_test.c create mode 100644 lib/src/phy/utils/vector.c create mode 100644 lib/src/phy/utils/vector_simd.c create mode 100644 lib/src/radio/CMakeLists.txt create mode 100644 lib/src/radio/radio.cc create mode 100644 lib/src/radio/radio_multi.cc create mode 100644 lib/src/radio/test/CMakeLists.txt create mode 100644 lib/src/radio/test/benchmark_radio.cc create mode 100644 lib/src/upper/CMakeLists.txt create mode 100644 lib/src/upper/gtpu.cc create mode 100644 lib/src/upper/pdcp.cc create mode 100644 lib/src/upper/pdcp_entity.cc create mode 100644 lib/src/upper/rlc.cc create mode 100644 lib/src/upper/rlc_am.cc create mode 100644 lib/src/upper/rlc_entity.cc create mode 100644 lib/src/upper/rlc_tm.cc create mode 100644 lib/src/upper/rlc_um.cc create mode 100644 lib/test/CMakeLists.txt create mode 100644 lib/test/asn1/CMakeLists.txt create mode 100644 lib/test/asn1/rrc_meas_test.cc create mode 100644 lib/test/asn1/srslte_asn1_rrc_mcch_test.cc create mode 100644 lib/test/asn1/srslte_asn1_rrc_meas_test.cc create mode 100644 lib/test/common/CMakeLists.txt create mode 100644 lib/test/common/bcd_helpers_test.cc create mode 100644 lib/test/common/log_filter_test.cc create mode 100644 lib/test/common/logger_test.cc create mode 100644 lib/test/common/msg_queue_test.cc create mode 100644 lib/test/common/test_eea1.cc create mode 100644 lib/test/common/test_eea2.cc create mode 100644 lib/test/common/test_f12345.cc create mode 100644 lib/test/common/timeout_test.cc create mode 100644 lib/test/phy/CMakeLists.txt create mode 100644 lib/test/phy/phy_dl_test.c create mode 100644 lib/test/upper/CMakeLists.txt create mode 100644 lib/test/upper/rlc_am_control_test.cc create mode 100644 lib/test/upper/rlc_am_data_test.cc create mode 100644 lib/test/upper/rlc_am_test.cc create mode 100644 lib/test/upper/rlc_stress_test.cc create mode 100644 lib/test/upper/rlc_um_data_test.cc create mode 100644 lib/test/upper/rlc_um_test.cc create mode 100644 srsenb/CMakeLists.txt create mode 100644 srsenb/drb.conf.example create mode 100644 srsenb/enb.conf.example create mode 100644 srsenb/hdr/CMakeLists.txt create mode 100644 srsenb/hdr/cfg_parser.h create mode 100644 srsenb/hdr/enb.h create mode 100644 srsenb/hdr/mac/mac.h create mode 100644 srsenb/hdr/mac/mac_metrics.h create mode 100644 srsenb/hdr/mac/scheduler.h create mode 100644 srsenb/hdr/mac/scheduler_harq.h create mode 100644 srsenb/hdr/mac/scheduler_metric.h create mode 100644 srsenb/hdr/mac/scheduler_ue.h create mode 100644 srsenb/hdr/mac/ue.h create mode 100644 srsenb/hdr/metrics_stdout.h create mode 100644 srsenb/hdr/parser.h create mode 100644 srsenb/hdr/phy/phch_common.h create mode 100644 srsenb/hdr/phy/phch_worker.h create mode 100644 srsenb/hdr/phy/phy.h create mode 100644 srsenb/hdr/phy/phy_metrics.h create mode 100644 srsenb/hdr/phy/prach_worker.h create mode 100644 srsenb/hdr/phy/txrx.h create mode 100644 srsenb/hdr/upper/common_enb.h create mode 100644 srsenb/hdr/upper/gtpu.h create mode 100644 srsenb/hdr/upper/pdcp.h create mode 100644 srsenb/hdr/upper/rlc.h create mode 100644 srsenb/hdr/upper/rrc.h create mode 100644 srsenb/hdr/upper/rrc_metrics.h create mode 100644 srsenb/hdr/upper/s1ap.h create mode 100644 srsenb/hdr/upper/s1ap_metrics.h create mode 100644 srsenb/rr.conf.example create mode 100644 srsenb/sib.conf.example create mode 100644 srsenb/sib.conf.mbsfn.example create mode 100644 srsenb/src/CMakeLists.txt create mode 100644 srsenb/src/enb.cc create mode 100644 srsenb/src/enb_cfg_parser.cc create mode 100644 srsenb/src/enb_cfg_parser.h create mode 100644 srsenb/src/mac/CMakeLists.txt create mode 100644 srsenb/src/mac/mac.cc create mode 100644 srsenb/src/mac/scheduler.cc create mode 100644 srsenb/src/mac/scheduler_harq.cc create mode 100644 srsenb/src/mac/scheduler_metric.cc create mode 100644 srsenb/src/mac/scheduler_ue.cc create mode 100644 srsenb/src/mac/ue.cc create mode 100644 srsenb/src/main.cc create mode 100644 srsenb/src/metrics_stdout.cc create mode 100644 srsenb/src/parser.cc create mode 100644 srsenb/src/phy/CMakeLists.txt create mode 100644 srsenb/src/phy/phch_common.cc create mode 100644 srsenb/src/phy/phch_worker.cc create mode 100644 srsenb/src/phy/phy.cc create mode 100644 srsenb/src/phy/prach_worker.cc create mode 100644 srsenb/src/phy/txrx.cc create mode 100644 srsenb/src/upper/CMakeLists.txt create mode 100644 srsenb/src/upper/gtpu.cc create mode 100644 srsenb/src/upper/pdcp.cc create mode 100644 srsenb/src/upper/rlc.cc create mode 100644 srsenb/src/upper/rrc.cc create mode 100644 srsenb/src/upper/s1ap.cc create mode 100644 srsenb/test/CMakeLists.txt create mode 100644 srsenb/test/mac/CMakeLists.txt create mode 100644 srsenb/test/mac/scheduler_test.cc create mode 100644 srsenb/test/upper/CMakeLists.txt create mode 100644 srsenb/test/upper/ip_test.cc create mode 100644 srsenb/test/upper/plmn_test.cc create mode 100644 srsepc/CMakeLists.txt create mode 100644 srsepc/epc.conf.example create mode 100644 srsepc/hdr/CMakeLists.txt create mode 100644 srsepc/hdr/hss/hss.h create mode 100644 srsepc/hdr/mbms-gw/mbms-gw.h create mode 100644 srsepc/hdr/mme/mme.h create mode 100644 srsepc/hdr/mme/mme_gtpc.h create mode 100644 srsepc/hdr/mme/s1ap.h create mode 100644 srsepc/hdr/mme/s1ap_common.h create mode 100644 srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h create mode 100644 srsepc/hdr/mme/s1ap_mngmt_proc.h create mode 100644 srsepc/hdr/mme/s1ap_nas_transport.h create mode 100644 srsepc/hdr/spgw/spgw.h create mode 100644 srsepc/mbms.conf.example create mode 100644 srsepc/src/CMakeLists.txt create mode 100644 srsepc/src/hss/CMakeLists.txt create mode 100644 srsepc/src/hss/hss.cc create mode 100644 srsepc/src/main.cc create mode 100644 srsepc/src/mbms-gw/CMakeLists.txt create mode 100644 srsepc/src/mbms-gw/main.cc create mode 100644 srsepc/src/mbms-gw/mbms-gw.cc create mode 100644 srsepc/src/mme/CMakeLists.txt create mode 100644 srsepc/src/mme/mme.cc create mode 100644 srsepc/src/mme/mme_gtpc.cc create mode 100644 srsepc/src/mme/s1ap.cc create mode 100644 srsepc/src/mme/s1ap_ctx_mngmt_proc.cc create mode 100644 srsepc/src/mme/s1ap_mngmt_proc.cc create mode 100644 srsepc/src/mme/s1ap_nas_transport.cc create mode 100644 srsepc/src/spgw/CMakeLists.txt create mode 100644 srsepc/src/spgw/spgw.cc create mode 100755 srsepc/srsepc_if_masq.sh create mode 100644 srsepc/user_db.csv.example create mode 100644 srsue/CMakeLists.txt create mode 100644 srsue/hdr/mac/demux.h create mode 100644 srsue/hdr/mac/dl_harq.h create mode 100644 srsue/hdr/mac/dl_sps.h create mode 100644 srsue/hdr/mac/mac.h create mode 100644 srsue/hdr/mac/mac_metrics.h create mode 100644 srsue/hdr/mac/mux.h create mode 100644 srsue/hdr/mac/proc.h create mode 100644 srsue/hdr/mac/proc_bsr.h create mode 100644 srsue/hdr/mac/proc_phr.h create mode 100644 srsue/hdr/mac/proc_ra.h create mode 100644 srsue/hdr/mac/proc_sr.h create mode 100644 srsue/hdr/mac/ul_harq.h create mode 100644 srsue/hdr/mac/ul_sps.h create mode 100644 srsue/hdr/metrics_csv.h create mode 100644 srsue/hdr/metrics_stdout.h create mode 100644 srsue/hdr/phy/phch_common.h create mode 100644 srsue/hdr/phy/phch_recv.h create mode 100644 srsue/hdr/phy/phch_worker.h create mode 100644 srsue/hdr/phy/phy.h create mode 100644 srsue/hdr/phy/phy_metrics.h create mode 100644 srsue/hdr/phy/prach.h create mode 100644 srsue/hdr/ue.h create mode 100644 srsue/hdr/ue_base.h create mode 100644 srsue/hdr/ue_metrics_interface.h create mode 100644 srsue/hdr/upper/gw.h create mode 100644 srsue/hdr/upper/gw_metrics.h create mode 100644 srsue/hdr/upper/nas.h create mode 100644 srsue/hdr/upper/pcsc_usim.h create mode 100644 srsue/hdr/upper/rrc.h create mode 100644 srsue/hdr/upper/rrc_common.h create mode 100644 srsue/hdr/upper/usim.h create mode 100644 srsue/hdr/upper/usim_base.h create mode 100644 srsue/src/CMakeLists.txt create mode 100644 srsue/src/mac/CMakeLists.txt create mode 100644 srsue/src/mac/demux.cc create mode 100644 srsue/src/mac/mac.cc create mode 100644 srsue/src/mac/mux.cc create mode 100644 srsue/src/mac/proc_bsr.cc create mode 100644 srsue/src/mac/proc_phr.cc create mode 100644 srsue/src/mac/proc_ra.cc create mode 100644 srsue/src/mac/proc_sr.cc create mode 100644 srsue/src/main.cc create mode 100644 srsue/src/metrics_csv.cc create mode 100644 srsue/src/metrics_stdout.cc create mode 100644 srsue/src/phy/CMakeLists.txt create mode 100644 srsue/src/phy/phch_common.cc create mode 100644 srsue/src/phy/phch_recv.cc create mode 100644 srsue/src/phy/phch_worker.cc create mode 100644 srsue/src/phy/phy.cc create mode 100644 srsue/src/phy/prach.cc create mode 100644 srsue/src/set_net_admin_caps.cc create mode 100644 srsue/src/ue.cc create mode 100644 srsue/src/ue_base.cc create mode 100644 srsue/src/upper/CMakeLists.txt create mode 100644 srsue/src/upper/gw.cc create mode 100644 srsue/src/upper/nas.cc create mode 100644 srsue/src/upper/pcsc_usim.cc create mode 100644 srsue/src/upper/rrc.cc create mode 100644 srsue/src/upper/usim.cc create mode 100644 srsue/src/upper/usim_base.cc create mode 100644 srsue/test/CMakeLists.txt create mode 100644 srsue/test/mac/CMakeLists.txt create mode 100644 srsue/test/mac/mac_test.cc create mode 100644 srsue/test/metrics_test.cc create mode 100644 srsue/test/phy/CMakeLists.txt create mode 100644 srsue/test/phy/ue_itf_test_prach.cc create mode 100644 srsue/test/phy/ue_itf_test_sib1.cc create mode 100644 srsue/test/upper/CMakeLists.txt create mode 100644 srsue/test/upper/ip_test.cc create mode 100644 srsue/test/upper/nas_test.cc create mode 100644 srsue/test/upper/pcsc_usim_test.cc create mode 100644 srsue/test/upper/rrc_reconfig_test.cc create mode 100644 srsue/test/upper/usim_test.cc create mode 100644 srsue/ue.conf.example diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..7283171 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,79 @@ +Change Log for Releases +============================== + +## 18.06.1 + * Fixed RLC reestablish + * Fixed aperiodic QCI retx + * Fixed eNB instability + * Fixed Debian packaging + +## 18.06 + * Added eMBMS support in srsUE/srsENB/srsEPC + * Added support for hard SIM cards + * Many bug-fixes and improved stability and performance in all parts + +## 18.03.1 + * Fixed compilation for NEON + * Fixed logging and RLC AM issue + +## 18.03 + * Many bug-fixes and improved stability and performance in all parts + +## 17.12 + * Added support for MIMO 2x2 in srsENB (i.e. TM3/TM4) + * Added srsEPC, a light-weight core network implementation + * Added support for X2/S1 handover in srsUE + * Added support for user-plane encryption in srsUE + * Many bug-fixes and improved stability and performance in srsUE/srsENB + +## 17.09 + * Added MIMO 2x2 in the PHY layer and srsUE (i.e. TM3/TM4) + * eMBMS support in the PHY layer + * Many bug-fixes and improved stability and performance in srsUE/srsENB + +## 002.000.000 + * Added fully functional srsENB to srsLTE code + * Merged srsUE code into srsLTE and reestructured PHY code + * Added support for SoapySDR devices (eg LimeSDR) + * Fixed issues in RLC AM + * Added support for NEON and AVX in many kernels and Viterbi decoder + * Added support for CPU affinity + * Other minor bug-fixes and new features + +## 001.004.000 + * Fixed issue in rv for format1C causing incorrect SIB1 decoding in some networks + * Improved PDCCH decoding BER (fixed incorrect trellis initialization) + * Improved PUCCH RX performance + +## 001.003.000 + +* Bugfixes: + * x300 master clock rate + * PHICH: fixed bug causing more NACKs + * PBCH: fixed bug in encoding function + * channel estimation: fixed issue in time interpolation + * DCI: Fixed bug in Format1A packing + * DCI: Fixed bug in Format1C for RA-RNTI + * DCI: Fixed overflow in MIMO formats + +* Improvements: + * Changed and cleaned DCI blind search API + * Added eNodeB PHY processing functions + +## 001.002.000 + +* Bugfixes: + * Estimation of extrapolated of out-of-band carriers + * PDCCH REG interleaving for certain cell IDs + * MIB decoding + * Overflow in viterbi in PBCH + +* Improvements: + * Synchronization in long multipath channels + * Better calibration of synchronization and estimation + * Averaging in channel estimation + * Improved 2-port diversity decoding + + +## 001.001.000 +* Added support for BladeRF diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fad9252 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,412 @@ +# +# Copyright 2013-2017 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/. +# + + +######################################################################## +# Prevent in-tree builds +######################################################################## +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "Prevented in-tree build. This is bad practice.") +endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + + +######################################################################## +# Project setup +######################################################################## +cmake_minimum_required(VERSION 2.6) +project( SRSLTE ) +message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) +message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) +message( STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER} ) + +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") +include(SRSLTEVersion) #sets version information +include(SRSLTEPackage) #setup cpack + +include(CTest) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake" + IMMEDIATE @ONLY) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) + message(STATUS "Build type not specified: defaulting to Release.") +endif(NOT CMAKE_BUILD_TYPE) +set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") + +# Generate CMake to include build information +configure_file( + ${CMAKE_SOURCE_DIR}/cmake/modules/SRSLTEbuildinfo.cmake.in + ${CMAKE_BINARY_DIR}/SRSLTEbuildinfo.cmake +) + +######################################################################## +# Options +######################################################################## +option(ENABLE_SRSUE "Build srsUE application" ON) +option(ENABLE_SRSENB "Build srsENB application" ON) +option(ENABLE_SRSEPC "Build srsEPC application" ON) +option(DISABLE_SIMD "disable simd instructions" OFF) + +option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) +option(ENABLE_BLADERF "Enable BladeRF" ON) +option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) + +option(BUILD_STATIC "Attempt to statically link external deps" OFF) +option(RPATH "Enable RPATH" OFF) +option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) + +option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) + +set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") + + +######################################################################## +# Find dependencies +######################################################################## + +# Threads +find_package(Threads REQUIRED) + +# FFT +find_package(MKL) +if(MKL_FOUND) + include_directories(${MKL_INCLUDE_DIRS}) + link_directories(${MKL_LIBRARY_DIRS}) + set(FFT_LIBRARIES "${MKL_STATIC_LIBRARIES}") # Static by default +else(MKL_FOUND) + find_package(FFTW3F REQUIRED) + if(FFTW3F_FOUND) + include_directories(${FFTW3F_INCLUDE_DIRS}) + link_directories(${FFTW3F_LIBRARY_DIRS}) + if(BUILD_STATIC) + set(FFT_LIBRARIES "${FFTW3F_STATIC_LIBRARIES}") + else(BUILD_STATIC) + set(FFT_LIBRARIES "${FFTW3F_LIBRARIES}") + endif(BUILD_STATIC) + message(STATUS "FFT_LIBRARIES: " ${FFT_LIBRARIES}) + endif(FFTW3F_FOUND) +endif(MKL_FOUND) + +# Crypto +find_package(Polarssl) +if (POLARSSL_FOUND) + set(SEC_INCLUDE_DIRS "${POLARSSL_INCLUDE_DIRS}") + if(BUILD_STATIC) + set(SEC_LIBRARIES "${POLARSSL_STATIC_LIBRARIES}") + else(BUILD_STATIC) + set(SEC_LIBRARIES "${POLARSSL_LIBRARIES}") + endif(BUILD_STATIC) + add_definitions(-DHAVE_POLARSSL) +else(POLARSSL_FOUND) + find_package(MbedTLS REQUIRED) + if (MBEDTLS_FOUND) + set(SEC_INCLUDE_DIRS "${MBEDTLS_INCLUDE_DIRS}") + if(BUILD_STATIC) + set(SEC_LIBRARIES "${MBEDTLS_STATIC_LIBRARIES}") + else(BUILD_STATIC) + set(SEC_LIBRARIES "${MBEDTLS_LIBRARIES}") + endif(BUILD_STATIC) + add_definitions(-DHAVE_MBEDTLS) + else(MBEDTLS_FOUND) + message(FATAL_ERROR "Either PolarSSL or mbedTLS are required to build srsLTE") + endif (MBEDTLS_FOUND) +endif(POLARSSL_FOUND) + +# Hard-SIM support +find_package(PCSCLite) +if (PCSCLITE_FOUND) + message(STATUS "Building with PCSC support.") + add_definitions(-DHAVE_PCSC) + set(HAVE_PCSC TRUE) + include_directories(${PCSCLITE_INCLUDE_DIR}) + #link_directories(${PCSCLITE_LIBRARIES}) +endif (PCSCLITE_FOUND) + +# UHD +find_package(UHD) +if(UHD_FOUND) + include_directories(${UHD_INCLUDE_DIRS}) + link_directories(${UHD_LIBRARY_DIRS}) +endif(UHD_FOUND) + +# BladeRF +if(ENABLE_BLADERF) + find_package(bladeRF) + if(BLADERF_FOUND) + include_directories(${BLADERF_INCLUDE_DIRS}) + link_directories(${BLADERF_LIBRARY_DIRS}) + endif(BLADERF_FOUND) +endif(ENABLE_BLADERF) + +# Soapy +if(ENABLE_SOAPYSDR) + find_package(SoapySDR) + if(SOAPYSDR_FOUND) + include_directories(${SOAPYSDR_INCLUDE_DIRS}) + link_directories(${SOAPYSDR_LIBRARY_DIRS}) + endif(SOAPYSDR_FOUND) +endif(ENABLE_SOAPYSDR) + +if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND) + set(RF_FOUND TRUE CACHE INTERNAL "RF frontend found") +else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND) + set(RF_FOUND FALSE CACHE INTERNAL "RF frontend found") + add_definitions(-DDISABLE_RF) +endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND) + +# Boost +if(ENABLE_SRSUE OR ENABLE_SRSENB OR ENABLE_SRSEPC) + if(BUILD_STATIC) + set(Boost_USE_STATIC_LIBS ON) + endif(BUILD_STATIC) + + set(BOOST_REQUIRED_COMPONENTS + program_options + ) + if(UNIX AND EXISTS "/usr/lib64") + list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix + endif(UNIX AND EXISTS "/usr/lib64") + set(Boost_ADDITIONAL_VERSIONS + "1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" + "1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" + "1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" + "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54" + "1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59" + "1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64" + "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" + ) + find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) +endif(ENABLE_SRSUE OR ENABLE_SRSENB OR ENABLE_SRSEPC) + +# srsGUI +if(ENABLE_GUI) + find_package(SRSGUI) + if(SRSGUI_FOUND) + add_definitions(-DENABLE_GUI) + include_directories(${SRSGUI_INCLUDE_DIRS}) + link_directories(${SRSGUI_LIBRARY_DIRS}) + endif(SRSGUI_FOUND) +endif(ENABLE_GUI) + +######################################################################## +# Install Dirs +######################################################################## +if (NOT CMAKE_INSTALL_LIBDIR) + include(GNUInstallDirs) +endif (NOT CMAKE_INSTALL_LIBDIR) + +# Fall back to just "lib" if the item provided by GNUInstallDirs doesn't exist +if (NOT EXISTS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + message(STATUS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} does not exist. Defaulting install location to ${CMAKE_INSTALL_PREFIX}/lib.") + set(CMAKE_INSTALL_LIBDIR lib) +endif() + +set(RUNTIME_DIR bin) +set(LIBRARY_DIR ${CMAKE_INSTALL_LIBDIR}) +set(INCLUDE_DIR include) +set(DOC_DIR "share/doc/${CPACK_PACKAGE_NAME}") +set(DATA_DIR share/${CPACK_PACKAGE_NAME}) + +# Auto-generate config install helper and mark for installation +configure_file( + ${CMAKE_SOURCE_DIR}/cmake/modules/SRSLTE_install_configs.sh.in + ${CMAKE_BINARY_DIR}/srslte_install_configs.sh +) +install(PROGRAMS ${CMAKE_BINARY_DIR}/srslte_install_configs.sh DESTINATION ${RUNTIME_DIR}) + +######################################################################## +# Compiler specific setup +######################################################################## +macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(${flag} ${have}) + if(${have}) + add_definitions(${flag}) + endif(${have}) +endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + + find_package(SSE) + if (HAVE_AVX2) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") + else (HAVE_AVX2) + if(HAVE_AVX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") + elseif(HAVE_SSE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") + endif(HAVE_AVX) + endif (HAVE_AVX2) +endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + +ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) + +if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + + if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") + else(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + if(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -DBUILD_TYPE_RELWITHDEBINFO") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -DBUILD_TYPE_RELWITHDEBINFO") + else(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -DBUILD_TYPE_RELEASE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DBUILD_TYPE_RELEASE") + endif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo") + endif(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + + if (USE_LTE_RATES) + message(STATUS "Using standard LTE sampling rates") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFORCE_STANDARD_RATE") + endif (USE_LTE_RATES) + + find_package(SSE) + if (HAVE_AVX2) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") + else (HAVE_AVX2) + if(HAVE_AVX) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") + elseif(HAVE_SSE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") + endif(HAVE_AVX) + endif (HAVE_AVX2) + + if (HAVE_FMA) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -DLV_HAVE_FMA") + endif (HAVE_FMA) + + if (HAVE_AVX512) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") + endif(HAVE_AVX512) + + if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + if(HAVE_SSE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -funroll-loops") + endif(HAVE_SSE) + endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + + + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) + + if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) + message(FATAL_ERROR "no SIMD instructions found") + endif(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) + + if(NOT WIN32) + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) + endif(NOT WIN32) + if (ENABLE_ASAN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + endif (ENABLE_ASAN) +endif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # The following is needed for weak linking to work under OS X + set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") +endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + +message(STATUS "CMAKE_C_FLAGS is ${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_CXX_FLAGS is ${CMAKE_CXX_FLAGS}") + +######################################################################## +# Create uninstall targets +######################################################################## +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + +######################################################################## +# Add -fPIC property to all targets +######################################################################## +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +######################################################################## +# Print summary +######################################################################## +message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "Building for version: ${VERSION}") + +######################################################################## +# Add general includes and dependencies +######################################################################## +include_directories(${PROJECT_BINARY_DIR}/lib/include) +include_directories(${PROJECT_SOURCE_DIR}/lib/include) + +######################################################################## +# Add headers to cmake project (useful for IDEs) +######################################################################## +set(HEADERS_ALL "") +file(GLOB headers *) +foreach(_header ${headers}) + if(IS_DIRECTORY ${_header}) + file(GLOB_RECURSE tmp "${_header}/*.h") + list(APPEND HEADERS_ALL ${tmp}) + endif(IS_DIRECTORY ${_header}) +endforeach() +add_custom_target(add_srslte_headers SOURCES ${HEADERS_ALL}) + +######################################################################## +# Add the subdirectories +######################################################################## +add_subdirectory(lib) + +if(RF_FOUND) + if(ENABLE_SRSUE) + message(STATUS "Building with srsUE") + add_subdirectory(srsue) + else(ENABLE_SRSUE) + message(STATUS "srsUE build disabled") + endif(ENABLE_SRSUE) + + if(ENABLE_SRSENB) + message(STATUS "Building with srsENB") + add_subdirectory(srsenb) + else(ENABLE_SRSENB) + message(STATUS "srsENB build disabled") + endif(ENABLE_SRSENB) +else(RF_FOUND) + message(STATUS "srsUE and srsENB builds disabled due to missing RF driver") +endif(RF_FOUND) + +if(ENABLE_SRSEPC) + message(STATUS "Building with srsEPC") + add_subdirectory(srsepc) +else(ENABLE_SRSEPC) + message(STATUS "srsEPC build disabled") +endif(ENABLE_SRSEPC) diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..5921998 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,516 @@ +Copyright (C) 2013-2016 Software Radio Systems Limited. All rights reserved. + +The following copyright notices are for libraries used within srsLTE: + +----------------------------------------------------------- +FEC Library - Version 3.0.1 - August 7th, 2007 +----------------------------------------------------------- + +COPYRIGHT + +This package is copyright 2006 by Phil Karn, KA9Q. It may be used +under the terms of the GNU Lesser General Public License (LGPL). + + +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..d8e3160 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,14 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "srsLTE") +set(CTEST_NIGHTLY_START_TIME "00:00:00 GMT") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=srsLTE") +set(CTEST_DROP_SITE_CDASH TRUE) +set(VALGRIND_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --show-reachable=yes --vex-guest-max-insns=25") \ No newline at end of file diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in new file mode 100644 index 0000000..180c08d --- /dev/null +++ b/CTestCustom.cmake.in @@ -0,0 +1,15 @@ +SET(CTEST_CUSTOM_MEMCHECK_IGNORE + +# Ignore memcheck for plots. QT errors + + waterfallplot_test + scatterplot_test + realplot_test + complexplot_test + +# Ignore these to, they take too long + fft_normal + fft_extened + chest_test_all_cellids +) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ac29ae --- /dev/null +++ b/LICENSE @@ -0,0 +1,697 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. + + +The license terms used for the scard class (in pcsc_usim) derived from wpa_supplicant +------------------------------------------------------------------------------------- + +Modified BSD license (no advertisement clause): + +Copyright (c) 2002-2017, Jouni Malinen and contributors +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..88f2d5e --- /dev/null +++ b/README.md @@ -0,0 +1,204 @@ +srsLTE +======== + +[![Coverity Scan Build Status](https://scan.coverity.com/projects/10045/badge.svg)](https://scan.coverity.com/projects/10045) + +srsLTE is a free and open-source LTE software suite developed by SRS (www.softwareradiosystems.com). + +It includes: + * srsUE - a complete SDR LTE UE application featuring all layers from PHY to IP + * srsENB - a complete SDR LTE eNodeB application + * srsEPC - a light-weight LTE core network implementation with MME, HSS and S/P-GW + * a highly modular set of common libraries for PHY, MAC, RLC, PDCP, RRC, NAS, S1AP and GW layers. + +srsLTE is released under the AGPLv3 license and uses software from the OpenLTE project (http://sourceforge.net/projects/openlte) for some security functions and for RRC/NAS message parsing. + +Common Features +--------------- + + * LTE Release 8 compliant (with selected features of Release 9) + * FDD configuration + * Tested bandwidths: 1.4, 3, 5, 10, 15 and 20 MHz + * Transmission mode 1 (single antenna), 2 (transmit diversity), 3 (CCD) and 4 (closed-loop spatial multiplexing) + * Frequency-based ZF and MMSE equalizer + * Evolved multimedia broadcast and multicast service (eMBMS) + * Highly optimized Turbo Decoder available in Intel SSE4.1/AVX2 (+100 Mbps) and standard C (+25 Mbps) + * MAC, RLC, PDCP, RRC, NAS, S1AP and GW layers + * Detailed log system with per-layer log levels and hex dumps + * MAC layer wireshark packet capture + * Command-line trace metrics + * Detailed input configuration files + +srsUE Features +-------------- + + * Cell search and synchronization procedure for the UE + * Soft USIM supporting Milenage and XOR authentication + * Hard USIM support using PCSC framework + * Virtual network interface *tun_srsue* created upon network attach + * 150 Mbps DL in 20 MHz MIMO TM3/TM4 configuration in i7 Quad-Core CPU. + * 75 Mbps DL in 20 MHz SISO configuration in i7 Quad-Core CPU. + * 36 Mbps DL in 10 MHz SISO configuration in i5 Dual-Core CPU. + +srsUE has been fully tested and validated with the following network equipment: + * Amarisoft LTE100 eNodeB and EPC + * Nokia FlexiRadio family FSMF system module with 1800MHz FHED radio module and TravelHawk EPC simulator + * Huawei DBS3900 + * Octasic Flexicell LTE-FDD NIB + +srsENB Features +--------------- + + * Round Robin MAC scheduler with FAPI-like C++ API + * SR support + * Periodic and Aperiodic CQI feedback support + * Standard S1AP and GTP-U interfaces to the Core Network + * 150 Mbps DL in 20 MHz MIMO TM3/TM4 with commercial UEs + * 75 Mbps DL in SISO configuration with commercial UEs + * 50 Mbps UL in 20 MHz with commercial UEs + +srsENB has been tested and validated with the following handsets: + * LG Nexus 5 and 4 + * Motorola Moto G4 plus and G5 + * Huawei P9/P9lite, P10/P10lite, P20/P20lite + * Huawei dongles: E3276 and E398 + +srsEPC Features +--------------- + + * Single binary, light-weight LTE EPC implementation with: + * MME (Mobility Management Entity) with standard S1AP and GTP-U interface to eNB + * S/P-GW with standard SGi exposed as virtual network interface (TUN device) + * HSS (Home Subscriber Server) with configurable user database in CSV format + +Hardware +-------- + +The library currently supports the Ettus Universal Hardware Driver (UHD) and the bladeRF driver. Thus, any hardware supported by UHD or bladeRF can be used. There is no sampling rate conversion, therefore the hardware should support 30.72 MHz clock in order to work correctly with LTE sampling frequencies and decode signals from live LTE base stations. + +We have tested the following hardware: + * USRP B210 + * USRP B205mini + * USRP X300 + * limeSDR + * bladeRF + +Build Instructions +------------------ + +* Mandatory requirements: + * Common: + * cmake https://cmake.org/ + * libfftw http://www.fftw.org/ + * PolarSSL/mbedTLS https://tls.mbed.org + * srsUE: + * Boost: http://www.boost.org + * srsENB: + * Boost: http://www.boost.org + * lksctp: http://lksctp.sourceforge.net/ + * config: http://www.hyperrealm.com/libconfig/ + * srsEPC: + * Boost: http://www.boost.org + * lksctp: http://lksctp.sourceforge.net/ + * config: http://www.hyperrealm.com/libconfig/ + +For example, on Ubuntu 17.04, one can install the required libraries with: +``` +sudo apt-get install cmake libfftw3-dev libmbedtls-dev libboost-program-options-dev libconfig++-dev libsctp-dev +``` +Note that depending on your flavor and version of Linux, the actual package names may be different. + +* Optional requirements: + * srsgui: https://github.com/srslte/srsgui - for real-time plotting. + * libpcsclite-dev: https://pcsclite.apdu.fr/ - for accessing smart card readers + +* RF front-end driver: + * UHD: https://github.com/EttusResearch/uhd + * SoapySDR: https://github.com/pothosware/SoapySDR + * BladeRF: https://github.com/Nuand/bladeRF + +Download and build srsLTE: +``` +git clone https://github.com/srsLTE/srsLTE.git +cd srsLTE +mkdir build +cd build +cmake ../ +make +make test +``` + +Install srsLTE: + +``` +sudo make install +sudo srslte_install_configs.sh +``` + +This installs srsLTE and also copies the default srsLTE config files to +the user's home directory (~/.srs). + + +Execution Instructions +---------------------- + +The srsUE, srsENB and srsEPC applications include example configuration files +that should be copied (manually or by using the convenience script) and modified, +if needed, to meet the system configuration. +On many systems they should work out of the box. + +By default, all applications will search for config files in the user's home +directory (~/.srs) upon startup. + +Note that you have to execute the applications with root privileges to enable +real-time thread priorities and to permit creation of virtual network interfaces. + +srsENB and srsEPC can run on the same machine as a network-in-the-box configuration. +srsUE needs to run on a separate machine. + +If you have installed the software suite using ```sudo make install``` and +have installed the example config files using ```sudo srslte_install_configs.sh```, +you may just start all applications with their default parameters. + +### srsEPC + +On machine 1, run srsEPC as follows: + +``` +sudo srsepc +``` + +Using the default configuration, this creates a virtual network interface +named "srs_spgw_sgi" on machine 1 with IP 172.16.0.1. All connected UEs +will be assigned an IP in this network. + +### srsENB + +Also on machine 1, but in another console, run srsENB as follows: + +``` +sudo srsenb +``` + +### srsUE + +On machine 2, run srsUE as follows: + +``` +sudo srsue +``` + +Using the default configuration, this creates a virtual network interface +named "tun_srsue" on machine 2 with an IP in the network 172.16.0.x. +Assuming the UE has been assigned IP 172.16.0.2, you may now exchange +IP traffic with machine 1 over the LTE link. For example, run a ping to +the default SGi IP address: + +``` +ping 172.16.0.1 +``` + +Support +======== + +Mailing list: http://www.softwareradiosystems.com/mailman/listinfo/srslte-users diff --git a/cmake/modules/CheckCSourceRuns.cmake b/cmake/modules/CheckCSourceRuns.cmake new file mode 100644 index 0000000..5afeab6 --- /dev/null +++ b/cmake/modules/CheckCSourceRuns.cmake @@ -0,0 +1,107 @@ +#.rst: +# CheckCSourceRuns +# ---------------- +# +# Check if the given C source code compiles and runs. +# +# CHECK_C_SOURCE_RUNS( ) +# +# :: +# +# - source code to try to compile +# - variable to store the result +# (1 for success, empty for failure) +# Will be created as an internal cache variable. +# +# The following variables may be set before calling this macro to modify +# the way the check is run: +# +# :: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link +# CMAKE_REQUIRED_QUIET = execute quietly without messages + +#============================================================================= +# Copyright 2006-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + + +macro(CHECK_C_SOURCE_RUNS SOURCE VAR) + if(NOT DEFINED "${VAR}") + set(MACRO_CHECK_FUNCTION_DEFINITIONS + "-D${VAR} ${CMAKE_REQUIRED_FLAGS}") + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + else() + set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES) + endif() + file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c" + "${SOURCE}\n") + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR}") + endif() + try_run(${VAR}_EXITCODE ${VAR}_COMPILED + ${CMAKE_BINARY_DIR} + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH} + "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}" + COMPILE_OUTPUT_VARIABLE OUTPUT) + # if it did not compile make the return value fail code of 1 + if(NOT ${VAR}_COMPILED) + set(${VAR}_EXITCODE 1) + endif() + # if the return value was 0 then it worked + if("${${VAR}_EXITCODE}" EQUAL 0) + set(${VAR} 1 CACHE INTERNAL "Test ${VAR}") + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Success") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Performing C SOURCE FILE Test ${VAR} succeeded with the following output:\n" + "${OUTPUT}\n" + "Return value: ${${VAR}}\n" + "Source file was:\n${SOURCE}\n") + else() + if(CMAKE_CROSSCOMPILING AND "${${VAR}_EXITCODE}" MATCHES "FAILED_TO_RUN") + set(${VAR} "${${VAR}_EXITCODE}") + else() + set(${VAR} "" CACHE INTERNAL "Test ${VAR}") + endif() + + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VAR} - Failed") + endif() + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Performing C SOURCE FILE Test ${VAR} failed with the following output:\n" + "${OUTPUT}\n" + "Return value: ${${VAR}_EXITCODE}\n" + "Source file was:\n${SOURCE}\n") + + endif() + endif() +endmacro() + diff --git a/cmake/modules/CheckFunctionExists.c b/cmake/modules/CheckFunctionExists.c new file mode 100644 index 0000000..314263d --- /dev/null +++ b/cmake/modules/CheckFunctionExists.c @@ -0,0 +1,25 @@ +#ifdef CHECK_FUNCTION_EXISTS + +char CHECK_FUNCTION_EXISTS(); +#ifdef __CLASSIC_C__ +int main(){ + int ac; + char*av[]; +#else +int main(int ac, char*av[]){ + +#endif + float ac2 = sqrtf(rand()); + CHECK_FUNCTION_EXISTS(); + if(ac2 * ac > 1000) + { + return *av[0]; + } + return 0; +} + +#else /* CHECK_FUNCTION_EXISTS */ + +# error "CHECK_FUNCTION_EXISTS has to specify the function" + +#endif /* CHECK_FUNCTION_EXISTS */ diff --git a/cmake/modules/CheckFunctionExistsMath.cmake b/cmake/modules/CheckFunctionExistsMath.cmake new file mode 100644 index 0000000..82f371a --- /dev/null +++ b/cmake/modules/CheckFunctionExistsMath.cmake @@ -0,0 +1,67 @@ +# - Check if a C function can be linked +# CHECK_FUNCTION_EXISTS( ) +# +# Check that the is provided by libraries on the system and +# store the result in a . This does not verify that any +# system header file declares the function, only that it can be found +# at link time (considure using CheckSymbolExists). +# +# The following variables may be set before calling this macro to +# modify the way the check is run: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link + +#============================================================================= +# Copyright 2002-2011 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +MACRO(CHECK_FUNCTION_EXISTS_MATH FUNCTION VARIABLE) + IF(${VARIABLE} MATCHES "^${VARIABLE}$") + SET(MACRO_CHECK_FUNCTION_DEFINITIONS + "-DCHECK_FUNCTION_EXISTS=${FUNCTION} ${CMAKE_REQUIRED_FLAGS}") + MESSAGE(STATUS "Looking for ${FUNCTION}") + IF(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES + "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") + ELSE(CMAKE_REQUIRED_LIBRARIES) + SET(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES) + ENDIF(CMAKE_REQUIRED_LIBRARIES) + IF(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES + "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") + ELSE(CMAKE_REQUIRED_INCLUDES) + SET(CHECK_FUNCTION_EXISTS_ADD_INCLUDES) + ENDIF(CMAKE_REQUIRED_INCLUDES) + TRY_COMPILE(${VARIABLE} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/cmake/modules/CheckFunctionExists.c + COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} + "${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES}" + "${CHECK_FUNCTION_EXISTS_ADD_INCLUDES}" + OUTPUT_VARIABLE OUTPUT) + IF(${VARIABLE}) + SET(${VARIABLE} 1 CACHE INTERNAL "Have function ${FUNCTION}") + MESSAGE(STATUS "Looking for ${FUNCTION} - found") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + "Determining if the function ${FUNCTION} exists passed with the following output:\n" + "${OUTPUT}\n\n") + ELSE(${VARIABLE}) + MESSAGE(STATUS "Looking for ${FUNCTION} - not found") + SET(${VARIABLE} "" CACHE INTERNAL "Have function ${FUNCTION}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "Determining if the function ${FUNCTION} exists failed with the following output:\n" + "${OUTPUT}\n\n") + ENDIF(${VARIABLE}) + ENDIF(${VARIABLE} MATCHES "^${VARIABLE}$") +ENDMACRO(CHECK_FUNCTION_EXISTS_MATH) diff --git a/cmake/modules/FindFFTW3F.cmake b/cmake/modules/FindFFTW3F.cmake new file mode 100644 index 0000000..b699182 --- /dev/null +++ b/cmake/modules/FindFFTW3F.cmake @@ -0,0 +1,44 @@ +# - Try to find fftw3f - the single-precision version of FFTW3 +# Once done this will define +# FFTW3F_FOUND - System has fftw3f +# FFTW3F_INCLUDE_DIRS - The fftw3f include directories +# FFTW3F_LIBRARIES - The libraries needed to use fftw3f +# FFTW3F_DEFINITIONS - Compiler switches required for using fftw3f + +find_package(PkgConfig) +pkg_check_modules(PC_FFTW3F "fftw3f >= 3.0") +set(FFTW3F_DEFINITIONS ${PC_FFTW3F_CFLAGS_OTHER}) + +find_path(FFTW3F_INCLUDE_DIR + NAMES fftw3.h + HINTS ${PC_FFTW3F_INCLUDEDIR} ${PC_FFTW3F_INCLUDE_DIRS} $ENV{FFTW3_DIR}/include + PATHS /usr/local/include + /usr/include ) + +find_library(FFTW3F_STATIC_LIBRARY + NAMES fftw3f.a libfftw3f.a libfftw3f-3.a + HINTS ${PC_FFTW3F_LIBDIR} ${PC_FFTW3F_LIBRARY_DIRS} $ENV{FFTW3_DIR}/lib + PATHS /usr/local/lib + /usr/lib) + +find_library(FFTW3F_LIBRARY + NAMES fftw3f libfftw3f libfftw3f-3 + HINTS ${PC_FFTW3F_LIBDIR} ${PC_FFTW3F_LIBRARY_DIRS} $ENV{FFTW3_DIR}/lib + PATHS /usr/local/lib + /usr/lib) + +set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY} ) +set(FFTW3F_STATIC_LIBRARIES ${FFTW3F_STATIC_LIBRARY} ) +set(FFTW3F_INCLUDE_DIRS ${FFTW3F_INCLUDE_DIR} ) + +message(STATUS "FFTW3F LIBRARIES: " ${FFTW3F_LIBRARIES}) +message(STATUS "FFTW3F STATIC LIBRARIES: " ${FFTW3F_STATIC_LIBRARIES}) +message(STATUS "FFTW3F INCLUDE DIRS: " ${FFTW3F_INCLUDE_DIRS}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set FFTW3F_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(fftw3f DEFAULT_MSG + FFTW3F_LIBRARY FFTW3F_INCLUDE_DIR) + +mark_as_advanced(FFTW3F_INCLUDE_DIR FFTW3F_STATIC_LIBRARY FFTW3F_LIBRARY ) diff --git a/cmake/modules/FindLibConfig.cmake b/cmake/modules/FindLibConfig.cmake new file mode 100644 index 0000000..49bed9d --- /dev/null +++ b/cmake/modules/FindLibConfig.cmake @@ -0,0 +1,75 @@ +# Find the CUnit includes and library +# +# This module defines +# LIBCONFIG_INCLUDE_DIR, where to find cppunit include files, etc. +# LIBCONFIG_LIBRARIES, the libraries to link against to use CppUnit. +# LIBCONFIG_STATIC_LIBRARIY_PATH +# LIBCONFIG_FOUND, If false, do not try to use CppUnit. + +# also defined, but not for general use are +# LIBCONFIG_LIBRARY, where to find the CUnit library. + +#MESSAGE("Searching for libconfig library") + +FIND_PATH(LIBCONFIG_INCLUDE_DIR libconfig.h + /usr/local/include + /usr/include +) + +FIND_PATH(LIBCONFIGPP_INCLUDE_DIR libconfig.h++ + /usr/local/include + /usr/include +) + +FIND_LIBRARY(LIBCONFIG_LIBRARY config + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIGPP_LIBRARY config++ + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIG_STATIC_LIBRARY "libconfig${CMAKE_STATIC_LIBRARY_SUFFIX}" + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIGPP_STATIC_LIBRARY "libconfig++${CMAKE_STATIC_LIBRARY_SUFFIX}" + /usr/local/lib + /usr/lib +) + + +IF(LIBCONFIG_INCLUDE_DIR) + IF(LIBCONFIG_LIBRARY) + SET(LIBCONFIG_FOUND TRUE) + SET(LIBCONFIG_LIBRARIES ${LIBCONFIG_LIBRARY}) + SET(LIBCONFIG_STATIC_LIBRARY_PATH ${LIBCONFIG_STATIC_LIBRARY}) + ENDIF(LIBCONFIG_LIBRARY) +ENDIF(LIBCONFIG_INCLUDE_DIR) + +IF(LIBCONFIGPP_INCLUDE_DIR) + IF(LIBCONFIGPP_LIBRARY) + SET(LIBCONFIGPP_FOUND TRUE) + SET(LIBCONFIGPP_LIBRARIES ${LIBCONFIGPP_LIBRARY}) + SET(LIBCONFIGPP_STATIC_LIBRARY_PATH ${LIBCONFIGPP_STATIC_LIBRARY}) + ENDIF(LIBCONFIGPP_LIBRARY) +ENDIF(LIBCONFIGPP_INCLUDE_DIR) + +IF (LIBCONFIGPP_FOUND) + IF (NOT LibConfig_FIND_QUIETLY) + MESSAGE(STATUS "Found LibConfig++: ${LIBCONFIGPP_LIBRARIES}" ) + MESSAGE(STATUS "static LibConfig++ path: ${LIBCONFIGPP_STATIC_LIBRARY_PATH}") + MESSAGE(STATUS "Found LibConfig: ${LIBCONFIG_LIBRARIES}") + MESSAGE(STATUS "static LibConfig path: ${LIBCONFIG_STATIC_LIBRARY_PATH}") + ENDIF (NOT LibConfig_FIND_QUIETLY) +ELSE (LIBCONFIGPP_FOUND) + IF (LibConfig_FIND_REQUIRED) + MESSAGE(SEND_ERROR "Could NOT find LibConfig") + ENDIF (LibConfig_FIND_REQUIRED) +ENDIF (LIBCONFIGPP_FOUND) + +MARK_AS_ADVANCED(LIBCONFIG_INCLUDE_DIR LIBCONFIG_LIBRARY LIBCONFIG_STATIC_LIBRARY) +MARK_AS_ADVANCED(LIBCONFIGPP_INCLUDE_DIR LIBCONFIGPP_LIBRARY LIBCONFIGPP_STATIC_LIBRARY) diff --git a/cmake/modules/FindLimeSDR.cmake b/cmake/modules/FindLimeSDR.cmake new file mode 100644 index 0000000..0cfec6f --- /dev/null +++ b/cmake/modules/FindLimeSDR.cmake @@ -0,0 +1,28 @@ +if(NOT LIMESDR_FOUND) + pkg_check_modules (LIMESDR_PKG LimeSuite) + + find_path(LIMESDR_INCLUDE_DIRS + NAMES LimeSuite.h + PATHS ${LIMESDR_PKG_INCLUDE_DIRS} + /usr/include/lime + /usr/local/include/lime + ) + + find_library(LIMESDR_LIBRARIES + NAMES LimeSuite + PATHS ${LIMESDR_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +if(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + set(LIMESDR_FOUND TRUE CACHE INTERNAL "libLimeSuite found") + message(STATUS "Found libLimeSuite: ${LIMESDR_INCLUDE_DIRS}, ${LIMESDR_LIBRARIES}") +else(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + set(LIMESDR_FOUND FALSE CACHE INTERNAL "libLimeSuite found") + message(STATUS "libLimeSuite not found.") +endif(LIMESDR_INCLUDE_DIRS AND LIMESDR_LIBRARIES) + +mark_as_advanced(LIMESDR_LIBRARIES LIMESDR_INCLUDE_DIRS) + +endif(NOT LIMESDR_FOUND) diff --git a/cmake/modules/FindMKL.cmake b/cmake/modules/FindMKL.cmake new file mode 100644 index 0000000..138d4ca --- /dev/null +++ b/cmake/modules/FindMKL.cmake @@ -0,0 +1,53 @@ +# - Try to find mkl - the Intel Math Kernel Library +# Once done this will define +# MKL_FOUND - System has mkl +# MKL_INCLUDE_DIRS - The mkl include directories +# MKL_LIBRARIES - The libraries needed to use mkl +# MKL_DEFINITIONS - Compiler switches required for using mkl + +find_path(MKL_INCLUDE_DIR + NAMES mkl.h + HINTS $ENV{MKL_DIR}/include + PATHS) + +find_path(MKL_FFTW_INCLUDE_DIR + NAMES fftw3.h + HINTS $ENV{MKL_DIR}/include/fftw + PATHS) + +find_library(MKL_LIBRARIES + NAMES mkl_rt + HINTS $ENV{MKL_DIR}/lib/intel64 + PATHS) + +find_library(MKL_CORE + NAMES libmkl_core.a + HINTS $ENV{MKL_DIR}/lib/intel64 + PATHS) + +find_library(MKL_ILP + NAMES libmkl_intel_ilp64.a + HINTS $ENV{MKL_DIR}/lib/intel64 + PATHS) + +find_library(MKL_SEQ + NAMES libmkl_sequential.a + HINTS $ENV{MKL_DIR}/lib/intel64 + PATHS) + +set(MKL_STATIC_LIBRARIES -Wl,--start-group ${MKL_CORE} ${MKL_ILP} ${MKL_SEQ} -Wl,--end-group -lpthread -lm -ldl) +set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} ${MKL_FFTW_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set MKL_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(mkl DEFAULT_MSG + MKL_LIBRARIES MKL_CORE MKL_ILP MKL_SEQ MKL_INCLUDE_DIRS) + +if(MKL_FOUND) + MESSAGE(STATUS "Found MKL_INCLUDE_DIRS: ${MKL_INCLUDE_DIRS}" ) + MESSAGE(STATUS "Found MKL_LIBRARIES: ${MKL_LIBRARIES}" ) + MESSAGE(STATUS "Found MKL_STATIC_LIBRARIES: ${MKL_STATIC_LIBRARIES}" ) +endif(MKL_FOUND) + +mark_as_advanced(MKL_INCLUDE_DIR MKL_FFTW_INCLUDE_DIR MKL_LIBRARIES MKL_CORE MKL_ILP MKL_SEQ) diff --git a/cmake/modules/FindMbedTLS.cmake b/cmake/modules/FindMbedTLS.cmake new file mode 100644 index 0000000..2c9464e --- /dev/null +++ b/cmake/modules/FindMbedTLS.cmake @@ -0,0 +1,54 @@ +# - Try to find mbedtls +# +# Once done this will define +# MBEDTLS_FOUND - System has mbedtls +# MBEDTLS_INCLUDE_DIRS - The mbedtls include directories +# MBEDTLS_LIBRARIES - The mbedtls library + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_MBEDTLS mbedtls) + +#find Mbedtls +FIND_PATH( + MBEDTLS_INCLUDE_DIRS + NAMES mbedtls/md.h + HINTS $ENV{MBEDTLS_DIR}/include + ${PC_MBEDTLS_INCLUDEDIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + MBEDTLS_LIBRARIES + NAMES mbedcrypto + HINTS $ENV{MBEDTLS_DIR}/lib + ${PC_MBEDTLS_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +FIND_LIBRARY( + MBEDTLS_STATIC_LIBRARIES + NAMES libmbedcrypto.a + HINTS $ENV{MBEDTLS_DIR}/lib + ${PC_MBEDTLS_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +message(STATUS "MBEDTLS LIBRARIES: " ${MBEDTLS_LIBRARIES}) +message(STATUS "MBEDTLS STATIC LIBRARIES: " ${MBEDTLS_STATIC_LIBRARIES}) +message(STATUS "MBEDTLS INCLUDE DIRS: " ${MBEDTLS_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MBEDTLS DEFAULT_MSG MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) +MARK_AS_ADVANCED(MBEDTLS_LIBRARIES MBEDTLS_STATIC_LIBRARIES MBEDTLS_INCLUDE_DIRS) diff --git a/cmake/modules/FindPCSCLite.cmake b/cmake/modules/FindPCSCLite.cmake new file mode 100644 index 0000000..e335a7f --- /dev/null +++ b/cmake/modules/FindPCSCLite.cmake @@ -0,0 +1,48 @@ +# - Find PCSC-Lite +# Find the native PCSC-Lite includes and library +# +# PCSCLITE_INCLUDE_DIR - where to find winscard.h, wintypes.h, etc. +# PCSCLITE_LIBRARIES - List of libraries when using PCSC-Lite. +# PCSCLITE_FOUND - True if PCSC-Lite found. + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_PCSCLITE libpcsclite) + +IF(NOT PCSCLITE_FOUND) + +FIND_PATH(PCSCLITE_INCLUDE_DIR + NAMES winscard.h + HINTS /usr/include/PCSC + /usr/local/include/PCSC + ${PC_PCSCLITE_INCLUDEDIR} + ${PC_PCSCLITE_INCLUDE_DIRS} + ${PC_PCSCLITE_INCLUDE_DIRS}/PCSC + ${CMAKE_INSTALL_PREFIX}/include +) +FIND_LIBRARY(PCSCLITE_LIBRARY NAMES pcsclite libpcsclite PCSC + HINTS ${PC_PCSCLITE_LIBDIR} + ${PC_PCSCLITE_LIBRARY_DIRS} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +# handle the QUIETLY and REQUIRED arguments and set PCSCLITE_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCSCLITE DEFAULT_MSG PCSCLITE_LIBRARY PCSCLITE_INCLUDE_DIR) + +IF(PCSCLITE_FOUND) + SET(PCSCLITE_LIBRARIES ${PCSCLITE_LIBRARY}) +ELSE(PCSCLITE_FOUND) + SET(PCSCLITE_LIBRARIES ) +ENDIF(PCSCLITE_FOUND) + +message(STATUS "PCSC LIBRARIES: " ${PCSCLITE_LIBRARY}) +message(STATUS "PCSC INCLUDE DIRS: " ${PCSCLITE_INCLUDE_DIR}) + +MARK_AS_ADVANCED( PCSCLITE_LIBRARY PCSCLITE_INCLUDE_DIR ) +ENDIF(NOT PCSCLITE_FOUND) \ No newline at end of file diff --git a/cmake/modules/FindPolarssl.cmake b/cmake/modules/FindPolarssl.cmake new file mode 100644 index 0000000..58f21d1 --- /dev/null +++ b/cmake/modules/FindPolarssl.cmake @@ -0,0 +1,53 @@ +# - Try to find polarssl +# +# Once done this will define +# POLARSSL_FOUND - System has polarssl +# POLARSSL_INCLUDE_DIRS - The polarssl include directories +# POLARSSL_LIBRARIES - The polarssl library + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_POLARSSL polarssl) + +FIND_PATH( + POLARSSL_INCLUDE_DIRS + NAMES polarssl/version.h + HINTS $ENV{POLARSSL_DIR}/include + ${PC_POLARSSL_INCLUDEDIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + POLARSSL_LIBRARIES + NAMES polarssl + HINTS $ENV{POLARSSL_DIR}/lib + ${PC_POLARSSL_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +FIND_LIBRARY( + POLARSSL_STATIC_LIBRARIES + NAMES libpolarssl.a + HINTS $ENV{POLARSSL_DIR}/lib + ${PC_POLARSSL_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +message(STATUS "POLARSSL LIBRARIES: " ${POLARSSL_LIBRARIES}) +message(STATUS "POLARSSL STATIC LIBRARIES: " ${POLARSSL_STATIC_LIBRARIES}) +message(STATUS "POLARSSL INCLUDE DIRS: " ${POLARSSL_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(POLARSSL DEFAULT_MSG POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS) +MARK_AS_ADVANCED(POLARSSL_STATIC_LIBRARIES POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS) diff --git a/cmake/modules/FindSCTP.cmake b/cmake/modules/FindSCTP.cmake new file mode 100644 index 0000000..1ce75ed --- /dev/null +++ b/cmake/modules/FindSCTP.cmake @@ -0,0 +1,38 @@ +# - Try to find sctp +# +# Once done this will define +# SCTP_FOUND - System has mbedtls +# SCTP_INCLUDE_DIRS - The mbedtls include directories +# SCTP_LIBRARIES - The mbedtls library + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_SCTP sctp) + +#find Mbedtls +FIND_PATH( + SCTP_INCLUDE_DIRS + NAMES netinet/sctp.h + HINTS ${PC_SCTP_INCLUDEDIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + SCTP_LIBRARIES + NAMES sctp + HINTS ${PC_SCTP_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +message(STATUS "SCTP LIBRARIES: " ${SCTP_LIBRARIES}) +message(STATUS "SCTP INCLUDE DIRS: " ${SCTP_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARIES SCTP_INCLUDE_DIRS) +MARK_AS_ADVANCED(SCTP_LIBRARIES SCTP_INCLUDE_DIRS) diff --git a/cmake/modules/FindSRSGUI.cmake b/cmake/modules/FindSRSGUI.cmake new file mode 100644 index 0000000..42fff7f --- /dev/null +++ b/cmake/modules/FindSRSGUI.cmake @@ -0,0 +1,42 @@ +# - Try to find SRSGUI +# Once done this will define +# SRSGUI_FOUND - System has srsgui +# SRSGUI_INCLUDE_DIRS - The srsgui include directories +# SRSGUI_LIBRARIES - The srsgui library + +find_package(PkgConfig) +pkg_check_modules(PC_SRSGUI QUIET srsgui) +IF(NOT SRSGUI_FOUND) + +FIND_PATH( + SRSGUI_INCLUDE_DIRS + NAMES srsgui/srsgui.h + HINTS ${PC_SRSGUI_INCLUDEDIR} + ${PC_SRSGUI_INCLUDE_DIRS} + $ENV{SRSGUI_DIR}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + SRSGUI_LIBRARIES + NAMES srsgui + HINTS ${PC_SRSGUI_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + $ENV{SRSGUI_DIR}/lib + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +message(STATUS "SRSGUI LIBRARIES " ${SRSGUI_LIBRARIES}) +message(STATUS "SRSGUI INCLUDE DIRS " ${SRSGUI_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SRSGUI DEFAULT_MSG SRSGUI_LIBRARIES SRSGUI_INCLUDE_DIRS) +MARK_AS_ADVANCED(SRSGUI_LIBRARIES SRSGUI_INCLUDE_DIRS) + +ENDIF(NOT SRSGUI_FOUND) + diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake new file mode 100644 index 0000000..4c9673a --- /dev/null +++ b/cmake/modules/FindSSE.cmake @@ -0,0 +1,176 @@ +#if (NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|^i[3,9]86$") +# return() +#endif() + +include(CheckCSourceRuns) + +option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) +option(ENABLE_AVX "Enable compile-time AVX support." ON) +option(ENABLE_AVX2 "Enable compile-time AVX2 support." ON) +option(ENABLE_FMA "Enable compile-time FMA support." ON) +option(ENABLE_AVX512 "Enable compile-time AVX512 support." ON) + +if (ENABLE_SSE) + # + # Check compiler for SSE4_1 intrinsics + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-msse4.1") + check_c_source_runs(" + #include + #include + + int main() + { + __m128i a = _mm_setzero_si128(); + __m128i b = _mm_minpos_epu16(a); + return 0; + }" + HAVE_SSE) + endif() + + if (HAVE_SSE) + message(STATUS "SSE4.1 is enabled - target CPU must support it") + endif() + + if (ENABLE_AVX) + + # + # Check compiler for AVX intrinsics + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx") + check_c_source_runs(" + #include + int main() + { + __m256 a, b, c; + const float src[8] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; + float dst[8]; + a = _mm256_loadu_ps( src ); + b = _mm256_loadu_ps( src ); + c = _mm256_add_ps( a, b ); + _mm256_storeu_ps( dst, c ); + int i = 0; + for( i = 0; i < 8; i++ ){ + if( ( src[i] + src[i] ) != dst[i] ){ + return -1; + } + } + return 0; + }" + HAVE_AVX) + endif() + + if (HAVE_AVX) + message(STATUS "AVX is enabled - target CPU must support it") + endif() + endif() + + if (ENABLE_AVX2) + + # + # Check compiler for AVX intrinsics + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx2") + check_c_source_runs(" + #include + int main() + { + __m256i a, b, c; + const int src[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + int dst[8]; + a = _mm256_loadu_si256( (__m256i*)src ); + b = _mm256_loadu_si256( (__m256i*)src ); + c = _mm256_add_epi32( a, b ); + _mm256_storeu_si256( (__m256i*)dst, c ); + int i = 0; + for( i = 0; i < 8; i++ ){ + if( ( src[i] + src[i] ) != dst[i] ){ + return -1; + } + } + return 0; + }" + HAVE_AVX2) + endif() + + if (HAVE_AVX2) + message(STATUS "AVX2 is enabled - target CPU must support it") + endif() + endif() + + if (ENABLE_FMA) + + # + # Check compiler for AVX intrinsics + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mfma") + check_c_source_runs(" + #include + int main() + { + __m256 a, b, c, r; + const float src[8] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; + float dst[8]; + a = _mm256_loadu_ps( src ); + b = _mm256_loadu_ps( src ); + c = _mm256_loadu_ps( src ); + r = _mm256_fmadd_ps( a, b, c ); + _mm256_storeu_ps( dst, r ); + int i = 0; + for( i = 0; i < 8; i++ ){ + if( ( src[i] * src[i] + src[i] ) != dst[i] ){ + return -1; + } + } + return 0; + }" + HAVE_FMA) + endif() + + if (HAVE_FMA) + message(STATUS "FMA is enabled - target CPU must support it") + endif() + endif() + + if (ENABLE_AVX512) + + # + # Check compiler for AVX intrinsics + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx512f") + check_c_source_runs(" + #include + int main() + { + __m512i a, b, c; + const int src[16] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 , 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; + int dst[16]; + a = _mm512_loadu_si512( (__m512i*)src ); + b = _mm512_loadu_si512( (__m512i*)src ); + c = _mm512_add_epi32( a, b ); + _mm512_storeu_si512( (__m512i*)dst, c ); + int i = 0; + for( i = 0; i < 16; i++ ){ + if( ( src[i] + src[i] ) != dst[i] ){ + return -1; + } + } + return 0; + }" + HAVE_AVX512) + endif() + + if (HAVE_AVX512) + message(STATUS "AVX512 is enabled - target CPU must support it") + endif() + endif() + + +endif() + +mark_as_advanced(HAVE_SSE, HAVE_AVX, HAVE_AVX2, HAVE_FMA, HAVE_AVX512) diff --git a/cmake/modules/FindSoapySDR.cmake b/cmake/modules/FindSoapySDR.cmake new file mode 100644 index 0000000..75b69a8 --- /dev/null +++ b/cmake/modules/FindSoapySDR.cmake @@ -0,0 +1,32 @@ + +message(STATUS "FINDING SOAPY.") +if(NOT SOAPYSDR_FOUND) + pkg_check_modules (SOAPYSDR_PKG SoapySDR) + + find_path(SOAPYSDR_INCLUDE_DIRS + NAMES Device.h + PATHS ${SOAPYSDR_PKG_INCLUDE_DIRS} + /usr/include/SoapySDR + /usr/local/include/SoapySDR + ) + + find_library(SOAPYSDR_LIBRARIES + NAMES SoapySDR + PATHS ${LIMESDR_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + /usr/lib/arm-linux-gnueabihf + ) + + +if(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + set(SOAPYSDR_FOUND TRUE CACHE INTERNAL "libSOAPYSDR found") + message(STATUS "Found libSOAPYSDR: ${SOAPYSDR_INCLUDE_DIRS}, ${SOAPYSDR_LIBRARIES}") +else(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + set(SOAPYSDR_FOUND FALSE CACHE INTERNAL "libSOAPYSDR found") + message(STATUS "libSOAPYSDR not found.") +endif(SOAPYSDR_INCLUDE_DIRS AND SOAPYSDR_LIBRARIES) + +mark_as_advanced(SOAPYSDR_LIBRARIES SOAPYSDR_INCLUDE_DIRS) + +endif(NOT SOAPYSDR_FOUND) diff --git a/cmake/modules/FindUHD.cmake b/cmake/modules/FindUHD.cmake new file mode 100644 index 0000000..0c0eb74 --- /dev/null +++ b/cmake/modules/FindUHD.cmake @@ -0,0 +1,31 @@ +INCLUDE(FindPkgConfig) +#PKG_CHECK_MODULES(UHD uhd) +IF(NOT UHD_FOUND) + +FIND_PATH( + UHD_INCLUDE_DIRS + NAMES uhd.h + HINTS $ENV{UHD_DIR}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + UHD_LIBRARIES + NAMES uhd + HINTS $ENV{UHD_DIR}/lib + PATHS /usr/local/lib + /usr/lib + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +message(STATUS "UHD LIBRARIES " ${UHD_LIBRARIES}) +message(STATUS "UHD INCLUDE DIRS " ${UHD_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(UHD DEFAULT_MSG UHD_LIBRARIES UHD_INCLUDE_DIRS) +MARK_AS_ADVANCED(UHD_LIBRARIES UHD_INCLUDE_DIRS) + +ENDIF(NOT UHD_FOUND) diff --git a/cmake/modules/FindbladeRF.cmake b/cmake/modules/FindbladeRF.cmake new file mode 100644 index 0000000..b154a9c --- /dev/null +++ b/cmake/modules/FindbladeRF.cmake @@ -0,0 +1,27 @@ +if(NOT BLADERF_FOUND) + pkg_check_modules (BLADERF_PKG libbladeRF) + find_path(BLADERF_INCLUDE_DIRS NAMES libbladeRF.h + PATHS + ${BLADERF_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(BLADERF_LIBRARIES NAMES bladeRF + PATHS + ${BLADERF_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +if(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) + set(BLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found") + message(STATUS "Found libbladeRF: ${BLADERF_INCLUDE_DIRS}, ${BLADERF_LIBRARIES}") +else(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) + set(BLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found") + message(STATUS "libbladeRF not found.") +endif(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) + +mark_as_advanced(BLADERF_LIBRARIES BLADERF_INCLUDE_DIRS) + +endif(NOT BLADERF_FOUND) diff --git a/cmake/modules/SRSLTEPackage.cmake b/cmake/modules/SRSLTEPackage.cmake new file mode 100644 index 0000000..3fd26db --- /dev/null +++ b/cmake/modules/SRSLTEPackage.cmake @@ -0,0 +1,63 @@ +SET(CPACK_PACKAGE_DESCRIPTION "srsLTE") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LTE library for SDR.") +SET(CPACK_PACKAGE_NAME "srslte") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.6), libgcc1 (>= 1:4.1), libboost-dev (>= 1.35)") + +SET(CPACK_PACKAGE_CONTACT "Ismael Gomez ") +SET(CPACK_PACKAGE_VENDOR "Software Radio Systems Limited") +SET(CPACK_PACKAGE_VERSION_MAJOR ${SRSLTE_VERSION_MAJOR}) +SET(CPACK_PACKAGE_VERSION_MINOR ${SRSLTE_VERSION_MINOR}) +SET(CPACK_PACKAGE_VERSION_PATCH ${SRSLTE_VERSION_PATCH}) +SET(VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + +######################################################################## +# Setup additional defines for OS types +######################################################################## +IF(CMAKE_SYSTEM_NAME STREQUAL "Linux") + SET(LINUX TRUE) +ENDIF() + +IF(LINUX AND EXISTS "/etc/debian_version") + SET(DEBIAN TRUE) +ENDIF() + +IF(LINUX AND EXISTS "/etc/redhat-release") + SET(REDHAT TRUE) +ENDIF() + +######################################################################## +# Set generator type for recognized systems +######################################################################## +IF(CPACK_GENERATOR) + #already set +ELSEIF(APPLE) + SET(CPACK_GENERATOR PackageMaker) +ELSEIF(WIN32) + SET(CPACK_GENERATOR NSIS) +ELSEIF(DEBIAN) + SET(CPACK_GENERATOR DEB) +ELSEIF(REDHAT) + SET(CPACK_GENERATOR RPM) +ELSE() + SET(CPACK_GENERATOR TGZ) +ENDIF() + +######################################################################## +# Setup CPack Debian +######################################################################## +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-dev") + +######################################################################## +# Setup CPack RPM +######################################################################## +SET(CPACK_RPM_PACKAGE_REQUIRES "boost-devel") + +######################################################################## +# Setup CPack NSIS +######################################################################## +SET(CPACK_NSIS_MODIFY_PATH ON) + + +SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}-${CMAKE_SYSTEM_PROCESSOR}") +INCLUDE(CPack) + diff --git a/cmake/modules/SRSLTEVersion.cmake b/cmake/modules/SRSLTEVersion.cmake new file mode 100644 index 0000000..e79f7f8 --- /dev/null +++ b/cmake/modules/SRSLTEVersion.cmake @@ -0,0 +1,24 @@ +# +# Copyright 2013-2015 Software Radio Systems Limited +# +# This file is part of the srsLTE library. +# +# 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/. +# + +SET(SRSLTE_VERSION_MAJOR 18) +SET(SRSLTE_VERSION_MINOR 6) +SET(SRSLTE_VERSION_PATCH 1) +SET(SRSLTE_VERSION_STRING "${SRSLTE_VERSION_MAJOR}.${SRSLTE_VERSION_MINOR}.${SRSLTE_VERSION_PATCH}") diff --git a/cmake/modules/SRSLTE_install_configs.sh.in b/cmake/modules/SRSLTE_install_configs.sh.in new file mode 100755 index 0000000..c6d7318 --- /dev/null +++ b/cmake/modules/SRSLTE_install_configs.sh.in @@ -0,0 +1,55 @@ +#!/bin/bash + +# Auto-updated by CMake with actual install path +SRSLTE_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${DATA_DIR}" + +# Default folder where configs go +dest_folder="$HOME/.srs" + +install_file(){ + source_path="$SRSLTE_INSTALL_DIR/$1" + dest_path=$(echo "$dest_folder/$1" | sed 's/\.[^.]*$//') # Strip .example from filename + + # Check if config file already exists in location + if [ -f $dest_path ]; then + echo " - $dest_path already exists. Skipping it." + return + fi + + # Check if config file exists in source location + if [ -f $source_path ]; then + echo " - Installing $1 in $dest_path" + cp $source_path $dest_path + + # Set file ownership to user calling sudo + if [ $SUDO_USER ]; then + user=$SUDO_USER + chown $user:$user $dest_path + fi + else + echo " - $source_path doesn't exists. Skipping it." + fi + + return +} + +# Install all srsLTE config files +echo "Installing srsLTE configuration files:" + +# Make sure the target directory exists +if [ ! -d "$dest_folder" ]; then + echo " - Creating srsLTE config folder $dest_folder" + mkdir $dest_folder +fi + + +install_file "ue.conf.example" +install_file "enb.conf.example" +install_file "sib.conf.example" +install_file "rr.conf.example" +install_file "drb.conf.example" +install_file "epc.conf.example" +install_file "mbms.conf.example" +install_file "user_db.csv.example" + +echo "Done." diff --git a/cmake/modules/SRSLTEbuildinfo.cmake.in b/cmake/modules/SRSLTEbuildinfo.cmake.in new file mode 100644 index 0000000..d8715af --- /dev/null +++ b/cmake/modules/SRSLTEbuildinfo.cmake.in @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.6) + +execute_process( +COMMAND git rev-parse --abbrev-ref HEAD +WORKING_DIRECTORY "@CMAKE_SOURCE_DIR@" +OUTPUT_VARIABLE GIT_BRANCH +OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( +COMMAND git log -1 --format=%h +WORKING_DIRECTORY "@CMAKE_SOURCE_DIR@" +OUTPUT_VARIABLE GIT_COMMIT_HASH +OUTPUT_STRIP_TRAILING_WHITESPACE +) + +message(STATUS "Generating build_info.h") +configure_file( + @CMAKE_SOURCE_DIR@/lib/include/srslte/build_info.h.in + @CMAKE_BINARY_DIR@/lib/include/srslte/build_info.h +) diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 0000000..c869827 --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,26 @@ +if(POLICY CMP0007) + cmake_policy(SET CMP0007 OLD) +endif(POLICY CMP0007) + +if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +list(REVERSE files) +foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else (EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif (EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..51063a6 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,17 @@ +srslte (18.06.1-0ubuntu1) bionic; urgency=medium + + * Update to srsLTE 18.06.1 + + -- Andre Puschmann Wed, 18 Jul 2018 11:34:00 +0200 + +srslte (18.05~SNAPSHOT-0ubuntu2) bionic; urgency=medium + + * Update pkg deps + + -- Andre Puschmann Tue, 01 Jun 2018 15:10:00 +0200 + +srslte (18.05~SNAPSHOT-0ubuntu1) bionic; urgency=medium + + * Initial release of deb's + + -- Andre Puschmann Tue, 01 Jun 2018 14:10:00 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/config b/debian/config new file mode 100755 index 0000000..83d7550 --- /dev/null +++ b/debian/config @@ -0,0 +1,13 @@ +#!/bin/sh + +# Exit on error +set -e + +# Source debconf library. +. /usr/share/debconf/confmodule + +# Ask questions +db_input low srslte/install_configs_question || true + +# Show interface +db_go || true diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..e69e54d --- /dev/null +++ b/debian/control @@ -0,0 +1,35 @@ +Source: srslte +Section: universe/misc +Priority: optional +Maintainer: Andre Puschmann +Build-Depends: + debhelper (>= 9), + dh-exec, + cmake, + build-essential, + libfftw3-dev, + libmbedtls-dev, + libboost-program-options-dev, + libconfig++-dev, + libsctp-dev, + libuhd-dev, + uhd-host +Standards-Version: 4.1.1 +Homepage: http://www.softwareradiosystems.com +Vcs-Git: https://github.com/srsLTE/srsLTE.git +Vcs-Browser: https://github.com/srsLTE/srsLTE/ + +Package: srslte +Architecture: any +Depends: + libfftw3-3, + libboost-program-options1.55.0 | libboost-program-options1.62.0, + libmbedcrypto0 | libmbedcrypto1, + libconfig++9v5, + libsctp1, + uhd-host, + libuhd003 | libuhd003.010.003, + ${misc:Depends} +Description: This is srsLTE, a free and open-source LTE software suite. + This software allows you to run a full end-to-end, open-source LTE system. + It contains a UE, eNB and EPC implementation. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..6235644 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,81 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: srsLTE +Upstream-Contact: srsLTE packaging team +License: AGPL-3+ +Copyright: 2013-2018, Software Radio Systems Limited. +Source: https://www.github.com/srsLTE + +Files: * +Copyright: 2013-2018, Software Radio Systems Limited. +License: AGPL-3+ + +Files: lib/src/phy/fec/viterbi37_port.c + lib/src/phy/fec/viterbi37_sse.c + lib/src/phy/fec/parity.c +Copyright: 2009, Phil Karn, KA9Q +License: LGPL-2.1 + +Files: srsue/src/upper/pcsc_usim.cc + srsue/hdr/upper/pcsc_usim.h +Copyright: 2002-2014, Jouni Malinen +License: BSD-3-clause + + +License: AGPL-3+ + This program 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. + . + This program 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. + . + On Debian systems, the complete text of the AGPL 3 can be found in + /usr/share/doc/srslte/LICENSE + + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + 3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/debian/postinst b/debian/postinst new file mode 100755 index 0000000..2009e68 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,14 @@ +#!/bin/bash + +# Source debconf library. +. /usr/share/debconf/confmodule + +# Fetching configuration from debconf +db_get srslte/install_configs_question +ANSWER1=$RET + +if [ $ANSWER1 == "true" ]; then + srslte_install_configs.sh +fi + +#DEBHELPER# diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b1a2e7f --- /dev/null +++ b/debian/rules @@ -0,0 +1,18 @@ +#!/usr/bin/make -f +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +# This has to be exported to make some magic below work. +export DH_OPTIONS + +%: + dh $@ --parallel + +override_dh_shlibdeps: + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info + +override_dh_auto_configure: + dh_auto_configure --buildsystem=cmake -- $(extraopts) $(CEPH_EXTRA_CMAKE_ARGS) -DCMAKE_BUILD_TYPE=RelWithDebInfo + +override_dh_auto_test: + # skip executing tests diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/templates b/debian/templates new file mode 100644 index 0000000..97127f5 --- /dev/null +++ b/debian/templates @@ -0,0 +1,6 @@ +Template: srslte/install_configs_question +Type: boolean +Default: true +Description: Install configs? + This installs the default srsLTE configuration files to the user's + home directory (~/.srs) but keeps any existing config files. diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..6b638ce --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +add_subdirectory(include) +add_subdirectory(examples) +add_subdirectory(test) + +######################################################################## +# Install library headers +######################################################################## +INSTALL( DIRECTORY include/ + DESTINATION "${INCLUDE_DIR}" + FILES_MATCHING PATTERN "*.h" ) diff --git a/lib/examples/CMakeLists.txt b/lib/examples/CMakeLists.txt new file mode 100644 index 0000000..3577af0 --- /dev/null +++ b/lib/examples/CMakeLists.txt @@ -0,0 +1,86 @@ +# +# Copyright 2013-2017 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/. +# + + +################################################################# +# Applications +################################################################# + +add_executable(synch_file synch_file.c) +target_link_libraries(synch_file srslte_phy) + +################################################################# +# These can be compiled without UHD or graphics support +################################################################# + +if(RF_FOUND) + add_executable(pdsch_ue pdsch_ue.c) + target_link_libraries(pdsch_ue srslte_phy srslte_common srslte_rf pthread) + + add_executable(pdsch_enodeb pdsch_enodeb.c) + target_link_libraries(pdsch_enodeb srslte_phy srslte_common srslte_rf pthread) +else(RF_FOUND) + add_definitions(-DDISABLE_RF) + + add_executable(pdsch_ue pdsch_ue.c) + target_link_libraries(pdsch_ue srslte_common srslte_phy pthread) + + add_executable(pdsch_enodeb pdsch_enodeb.c) + target_link_libraries(pdsch_enodeb srslte_common srslte_phy pthread) +endif(RF_FOUND) + +find_package(SRSGUI) + +if(SRSGUI_FOUND) + include_directories(${SRSGUI_INCLUDE_DIRS}) + target_link_libraries(pdsch_ue ${SRSGUI_LIBRARIES}) +else(SRSGUI_FOUND) + add_definitions(-DDISABLE_GRAPHICS) +endif(SRSGUI_FOUND) + + +################################################################# +# These examples need the UHD driver +################################################################# + +if(RF_FOUND) + + add_executable(cell_search cell_search.c) + target_link_libraries(cell_search srslte_phy srslte_rf) + + add_executable(cell_measurement cell_measurement.c) + target_link_libraries(cell_measurement srslte_phy srslte_rf) + + add_executable(usrp_capture usrp_capture.c) + target_link_libraries(usrp_capture srslte_phy srslte_rf) + + add_executable(usrp_capture_sync usrp_capture_sync.c) + target_link_libraries(usrp_capture_sync srslte_phy srslte_rf) + + add_executable(usrp_txrx usrp_txrx.c) + target_link_libraries(usrp_txrx srslte_phy srslte_rf) + + message(STATUS " examples will be installed.") + +else(RF_FOUND) + message(STATUS " examples will NOT BE INSTALLED.") +endif(RF_FOUND) + + diff --git a/lib/examples/cell_measurement.c b/lib/examples/cell_measurement.c new file mode 100644 index 0000000..3185b74 --- /dev/null +++ b/lib/examples/cell_measurement.c @@ -0,0 +1,420 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_AGC_DEFAULT + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/rf/rf_utils.h" + +cell_search_cfg_t cell_detect_config = { + SRSLTE_DEFAULT_MAX_FRAMES_PBCH, + SRSLTE_DEFAULT_MAX_FRAMES_PSS, + SRSLTE_DEFAULT_NOF_VALID_PSS_FRAMES, + 0 +}; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + int nof_subframes; + bool disable_plots; + int force_N_id_2; + char *rf_args; + float rf_freq; + float rf_gain; +}prog_args_t; + +void args_default(prog_args_t *args) { + args->nof_subframes = -1; + args->force_N_id_2 = -1; // Pick the best + args->rf_args = ""; + args->rf_freq = -1.0; +#ifdef ENABLE_AGC_DEFAULT + args->rf_gain = -1; +#else + args->rf_gain = 50; +#endif +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [aglnv] -f rx_frequency (in Hz)\n", prog); + printf("\t-a RF args [Default %s]\n", args->rf_args); + printf("\t-g RF RX gain [Default %.2f dB]\n", args->rf_gain); + printf("\t-l Force N_id_2 [Default best]\n"); + printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +int parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "aglnvf")) != -1) { + switch (opt) { + case 'a': + args->rf_args = argv[optind]; + break; + case 'g': + args->rf_gain = atof(argv[optind]); + break; + case 'f': + args->rf_freq = atof(argv[optind]); + break; + case 'n': + args->nof_subframes = atoi(argv[optind]); + break; + case 'l': + args->force_N_id_2 = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(args, argv[0]); + return -1; + } + } + if (args->rf_freq < 0) { + usage(args, argv[0]); + return -1; + } + return 0; +} +/**********************************************************************/ + +/* TODO: Do something with the output data */ +uint8_t *data[SRSLTE_MAX_CODEWORDS]; + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + +int srslte_rf_recv_wrapper(void *h, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *q) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + + return srslte_rf_recv(h, data[0], nsamples, 1); +} + +enum receiver_state { DECODE_MIB, DECODE_SIB, MEASURE} state; + +#define MAX_SINFO 10 +#define MAX_NEIGHBOUR_CELLS 128 + +int main(int argc, char **argv) { + int ret; + cf_t *sf_buffer[SRSLTE_MAX_PORTS] = {NULL, NULL}; + prog_args_t prog_args; + srslte_cell_t cell; + int64_t sf_cnt; + srslte_ue_sync_t ue_sync; + srslte_ue_mib_t ue_mib; + srslte_rf_t rf; + srslte_ue_dl_t ue_dl; + srslte_ofdm_t fft; + srslte_chest_dl_t chest; + uint32_t nframes=0; + uint32_t nof_trials = 0; + uint32_t sfn = 0; // system frame number + int n; + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + int sfn_offset; + float rssi_utra=0,rssi=0, rsrp=0, rsrq=0, snr=0; + cf_t *ce[SRSLTE_MAX_PORTS]; + float cfo = 0; + bool acks[SRSLTE_MAX_CODEWORDS] = {false}; + + srslte_debug_handle_crash(argc, argv); + + if (parse_args(&prog_args, argc, argv)) { + exit(-1); + } + + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, prog_args.rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + if (prog_args.rf_gain > 0) { + srslte_rf_set_rx_gain(&rf, prog_args.rf_gain); + } else { + printf("Starting AGC thread...\n"); + if (srslte_rf_start_gain_thread(&rf, false)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_rx_gain(&rf, 50); + } + + sf_buffer[0] = srslte_vec_malloc(3*sizeof(cf_t)*SRSLTE_SF_LEN_PRB(100)); + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + data[i] = srslte_vec_malloc(sizeof(uint8_t) * 1500*8); + } + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + signal(SIGINT, sig_int_handler); + + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + + /* set receiver frequency */ + srslte_rf_set_rx_freq(&rf, (double) prog_args.rf_freq); + srslte_rf_rx_wait_lo_locked(&rf); + printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.rf_freq/1000000); + + cell_detect_config.init_agc = (prog_args.rf_gain<0); + + uint32_t ntrial=0; + do { + ret = rf_search_and_decode_mib(&rf, 1, &cell_detect_config, prog_args.force_N_id_2, &cell, &cfo); + if (ret < 0) { + fprintf(stderr, "Error searching for cell\n"); + exit(-1); + } else if (ret == 0 && !go_exit) { + printf("Cell not found after %d trials. Trying again (Press Ctrl+C to exit)\n", ntrial++); + } + } while (ret == 0 && !go_exit); + + if (go_exit) { + exit(0); + } + + /* set sampling frequency */ + int srate = srslte_sampling_freq_hz(cell.nof_prb); + if (srate != -1) { + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); + float srate_rf = srslte_rf_set_rx_srate(&rf, (double) srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb); + exit(-1); + } + + INFO("Stopping RF and flushing buffer...\n"); + srslte_rf_stop_rx_stream(&rf); + srslte_rf_flush_buffer(&rf); + + if (srslte_ue_sync_init_multi(&ue_sync, cell.nof_prb, cell.id==1000, srslte_rf_recv_wrapper, 1, (void*) &rf)) { + fprintf(stderr, "Error initiating ue_sync\n"); + return -1; + } + if (srslte_ue_sync_set_cell(&ue_sync, cell)) { + fprintf(stderr, "Error initiating ue_sync\n"); + return -1; + } + if (srslte_ue_dl_init(&ue_dl, sf_buffer, cell.nof_prb, 1)) { + fprintf(stderr, "Error initiating UE downlink processing module\n"); + return -1; + } + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + fprintf(stderr, "Error initiating UE downlink processing module\n"); + return -1; + } + if (srslte_ue_mib_init(&ue_mib, sf_buffer, cell.nof_prb)) { + fprintf(stderr, "Error initaiting UE MIB decoder\n"); + return -1; + } + if (srslte_ue_mib_set_cell(&ue_mib, cell)) { + fprintf(stderr, "Error initaiting UE MIB decoder\n"); + return -1; + } + + /* Configure downlink receiver for the SI-RNTI since will be the only one we'll use */ + srslte_ue_dl_set_rnti(&ue_dl, SRSLTE_SIRNTI); + + /* Initialize subframe counter */ + sf_cnt = 0; + + int sf_re = SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp); + + cf_t *sf_symbols = srslte_vec_malloc(sf_re * sizeof(cf_t)); + + for (int i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" + +#include "srslte/phy/rf/rf_utils.h" + + +#ifndef DISABLE_RF +#include "srslte/phy/rf/rf.h" +#endif + +#define MHZ 1000000 +#define SAMP_FREQ 1920000 +#define FLEN 9600 +#define FLEN_PERIOD 0.005 + +#define MAX_EARFCN 1000 + + +int band = -1; +int earfcn_start=-1, earfcn_end = -1; + + +cell_search_cfg_t cell_detect_config = { + SRSLTE_DEFAULT_MAX_FRAMES_PBCH, + SRSLTE_DEFAULT_MAX_FRAMES_PSS, + SRSLTE_DEFAULT_NOF_VALID_PSS_FRAMES, + 0 +}; + +struct cells { + srslte_cell_t cell; + float freq; + int dl_earfcn; + float power; +}; +struct cells results[1024]; + +float rf_gain = 70.0; +char *rf_args=""; + +void usage(char *prog) { + printf("Usage: %s [agsendtvb] -b band\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF gain [Default %.2f dB]\n", rf_gain); + printf("\t-s earfcn_start [Default All]\n"); + printf("\t-e earfcn_end [Default All]\n"); + printf("\t-n nof_frames_total [Default 100]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agsendvb")) != -1) { + switch(opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'b': + band = atoi(argv[optind]); + break; + case 's': + earfcn_start = atoi(argv[optind]); + break; + case 'e': + earfcn_end = atoi(argv[optind]); + break; + case 'n': + cell_detect_config.max_frames_pss = atoi(argv[optind]); + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (band == -1) { + usage(argv[0]); + exit(-1); + } +} + +int srslte_rf_recv_wrapper(void *h, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *t) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + void *ptr[SRSLTE_MAX_PORTS]; + for (int i=0;imin_rx_gain, + rf_info->max_rx_gain, + cell_detect_config.init_agc); + } + + for (freq=0;freq 0) { + for (int i=0;i<3;i++) { + if (found_cells[i].psr > 10.0) { + srslte_cell_t cell; + cell.id = found_cells[i].cell_id; + cell.cp = found_cells[i].cp; + int ret = rf_mib_decoder(&rf, 1, &cell_detect_config, &cell, NULL); + if (ret < 0) { + fprintf(stderr, "Error decoding MIB\n"); + exit(-1); + } + if (ret == SRSLTE_UE_MIB_FOUND) { + printf("Found CELL ID %d. %d PRB, %d ports\n", + cell.id, + cell.nof_prb, + cell.nof_ports); + if (cell.nof_ports > 0) { + memcpy(&results[n_found_cells].cell, &cell, sizeof(srslte_cell_t)); + results[n_found_cells].freq = channels[freq].fd; + results[n_found_cells].dl_earfcn = channels[freq].id; + results[n_found_cells].power = found_cells[i].peak; + n_found_cells++; + } + } + } + } + } + } + + printf("\n\nFound %d cells\n", n_found_cells); + for (int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "srslte/common/gen_mch_tables.h" +#include "srslte/srslte.h" + + +#define UE_CRNTI 0x1234 +#define M_CRNTI 0xFFFD + +#ifndef DISABLE_RF +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/common/phy_common.h" +srslte_rf_t rf; +#else +#warning Compiling pdsch_ue with no RF support +#endif + +char *output_file_name = NULL; + +#define LEFT_KEY 68 +#define RIGHT_KEY 67 +#define UP_KEY 65 +#define DOWN_KEY 66 + +srslte_cell_t cell = { + 25, // nof_prb + 1, // nof_ports + 0, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, // PHICH length + SRSLTE_PHICH_R_1 // PHICH resources +}; + +uint16_t c = -1; + +int net_port = -1; // -1 generates random dataThat means there is some problem sending samples to the device + +uint32_t cfi = 2; +uint32_t mcs_idx = 1, last_mcs_idx = 1; +int nof_frames = -1; + + +char mimo_type_str[32] = "single"; +uint32_t nof_tb = 1; +uint32_t multiplex_pmi = 0; +uint32_t multiplex_nof_layers = 1; +uint8_t mbsfn_sf_mask = 32; +int mbsfn_area_id = -1; +char *rf_args = ""; +float rf_amp = 0.8, rf_gain = 60.0, rf_freq = 2400000000; + +float output_file_snr = +INFINITY; + +bool null_file_sink=false; +srslte_filesink_t fsink; +srslte_ofdm_t ifft[SRSLTE_MAX_PORTS]; +srslte_ofdm_t ifft_mbsfn; +srslte_pbch_t pbch; +srslte_pcfich_t pcfich; +srslte_pdcch_t pdcch; +srslte_pdsch_t pdsch; +srslte_pdsch_cfg_t pdsch_cfg; +srslte_pmch_t pmch; +srslte_pdsch_cfg_t pmch_cfg; +srslte_softbuffer_tx_t *softbuffers[SRSLTE_MAX_CODEWORDS]; +srslte_regs_t regs; +srslte_ra_dl_dci_t ra_dl; +int rvidx[SRSLTE_MAX_CODEWORDS] = {0, 0}; + +cf_t *sf_buffer[SRSLTE_MAX_PORTS] = {NULL}, *output_buffer [SRSLTE_MAX_PORTS] = {NULL}; + + +int sf_n_re, sf_n_samples; + +pthread_t net_thread; +void *net_thread_fnc(void *arg); +sem_t net_sem; +bool net_packet_ready = false; +srslte_netsource_t net_source; +srslte_netsink_t net_sink; + + +int prbset_num = 1, last_prbset_num = 1; +int prbset_orig = 0; +//#define DATA_BUFF_SZ 1024*128 +//uint8_t data[8*DATA_BUFF_SZ], data2[DATA_BUFF_SZ]; +//uint8_t data_tmp[DATA_BUFF_SZ]; + + +#define DATA_BUFF_SZ 1024*1024 +uint8_t *data_mbms, *data[2], data2[DATA_BUFF_SZ]; +uint8_t data_tmp[DATA_BUFF_SZ]; + +void usage(char *prog) { + printf("Usage: %s [agmfoncvpuxb]\n", prog); +#ifndef DISABLE_RF + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-l RF amplitude [Default %.2f]\n", rf_amp); + printf("\t-g RF TX gain [Default %.2f dB]\n", rf_gain); + printf("\t-f RF TX frequency [Default %.1f MHz]\n", rf_freq / 1000000); +#else + printf("\t RF is disabled.\n"); +#endif + printf("\t-o output_file [Default use RF board]\n"); + printf("\t-m MCS index [Default %d]\n", mcs_idx); + printf("\t-n number of frames [Default %d]\n", nof_frames); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-p nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-M MBSFN area id [Default %d]\n", mbsfn_area_id); + printf("\t-x Transmission mode[single|diversity|cdd|multiplex] [Default %s]\n", mimo_type_str); + printf("\t-b Precoding Matrix Index (multiplex mode only)* [Default %d]\n", multiplex_pmi); + printf("\t-w Number of codewords/layers (multiplex mode only)* [Default %d]\n", multiplex_nof_layers); + printf("\t-u listen TCP/UDP port for input data (if mbsfn is active then the stream is over mbsfn only) (-1 is random) [Default %d]\n", net_port); + printf("\t-v [set srslte_verbose to debug, default none]\n"); + printf("\t-s output file SNR [Default %f]\n", output_file_snr); + printf("\n"); + printf("\t*: See 3GPP 36.212 Table 5.3.3.1.5-4 for more information\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "aglfmoncpvutxbwMsB")) != -1) { + + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'l': + rf_amp = atof(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 'o': + output_file_name = argv[optind]; + break; + case 'm': + mcs_idx = atoi(argv[optind]); + break; + case 'u': + net_port = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'p': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'x': + strncpy(mimo_type_str, argv[optind], 31); + mimo_type_str[31] = 0; + break; + case 'b': + multiplex_pmi = (uint32_t) atoi(argv[optind]); + break; + case 'w': + multiplex_nof_layers = (uint32_t) atoi(argv[optind]); + break; + case 'M': + mbsfn_area_id = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + case 's': + output_file_snr = atof(argv[optind]); + break; + case 'B': + mbsfn_sf_mask = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +#ifdef DISABLE_RF + if (!output_file_name) { + usage(argv[0]); + exit(-1); + } +#endif +} + +void base_init() { + int i; + + /* Select transmission mode */ + if (srslte_str2mimotype(mimo_type_str, &pdsch_cfg.mimo_type)) { + ERROR("Wrong transmission mode! Allowed modes: single, diversity, cdd and multiplex"); + exit(-1); + } + + /* Configure cell and PDSCH in function of the transmission mode */ + switch(pdsch_cfg.mimo_type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + cell.nof_ports = 1; + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + cell.nof_ports = 2; + break; + case SRSLTE_MIMO_TYPE_CDD: + cell.nof_ports = 2; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + cell.nof_ports = 2; + break; + default: + ERROR("Transmission mode not implemented."); + exit(-1); + } + + /* Allocate memory */ + for(i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + data[i] = srslte_vec_malloc(sizeof(uint8_t) * SOFTBUFFER_SIZE); + if (!data[i]) { + perror("malloc"); + exit(-1); + } + bzero(data[i], sizeof(uint8_t) * SOFTBUFFER_SIZE); + } + data_mbms = srslte_vec_malloc(sizeof(uint8_t) * SOFTBUFFER_SIZE); + + + /* init memory */ + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + sf_buffer[i] = srslte_vec_malloc(sizeof(cf_t) * sf_n_re); + if (!sf_buffer[i]) { + perror("malloc"); + exit(-1); + } + } + + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + output_buffer[i] = srslte_vec_malloc(sizeof(cf_t) * sf_n_samples); + if (!output_buffer[i]) { + perror("malloc"); + exit(-1); + } + bzero(output_buffer[i], sizeof(cf_t) * sf_n_samples); + } + + + /* open file or USRP */ + if (output_file_name) { + if (strcmp(output_file_name, "NULL")) { + if (srslte_filesink_init(&fsink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", output_file_name); + exit(-1); + } + null_file_sink = false; + } else { + null_file_sink = true; + } + } else { +#ifndef DISABLE_RF + printf("Opening RF device...\n"); + if (srslte_rf_open_multi(&rf, rf_args, cell.nof_ports)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } +#else + printf("Error RF not available. Select an output file\n"); + exit(-1); +#endif + } + + if (net_port > 0) { + if (srslte_netsource_init(&net_source, "127.0.0.1", net_port, SRSLTE_NETSOURCE_UDP)) { + fprintf(stderr, "Error creating input UDP socket at port %d\n", net_port); + exit(-1); + } + if (null_file_sink) { + if (srslte_netsink_init(&net_sink, "127.0.0.1", net_port+1, SRSLTE_NETSINK_TCP)) { + fprintf(stderr, "Error sink\n"); + exit(-1); + } + } + if (sem_init(&net_sem, 0, 1)) { + perror("sem_init"); + exit(-1); + } + } + + /* create ifft object */ + for (i = 0; i < cell.nof_ports; i++) { + if (srslte_ofdm_tx_init(&ifft[i], SRSLTE_CP_NORM, sf_buffer[i], output_buffer[i], cell.nof_prb)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + + srslte_ofdm_set_normalize(&ifft[i], true); + } + + if (srslte_ofdm_tx_init_mbsfn(&ifft_mbsfn, SRSLTE_CP_EXT, sf_buffer[0], output_buffer[0], cell.nof_prb)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + srslte_ofdm_set_non_mbsfn_region(&ifft_mbsfn, 2); + srslte_ofdm_set_normalize(&ifft_mbsfn, true); + + if (srslte_pbch_init(&pbch)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + if (srslte_pbch_set_cell(&pbch, cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + + + + + if (srslte_regs_init(®s, cell)) { + fprintf(stderr, "Error initiating regs\n"); + exit(-1); + } + if (srslte_pcfich_init(&pcfich, 1)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + if (srslte_pcfich_set_cell(&pcfich, ®s, cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + exit(-1); + } + + if (srslte_pdcch_init_enb(&pdcch, cell.nof_prb)) { + fprintf(stderr, "Error creating PDCCH object\n"); + exit(-1); + } + if (srslte_pdcch_set_cell(&pdcch, ®s, cell)) { + fprintf(stderr, "Error creating PDCCH object\n"); + exit(-1); + } + + if (srslte_pdsch_init_enb(&pdsch, cell.nof_prb)) { + fprintf(stderr, "Error creating PDSCH object\n"); + exit(-1); + } + if (srslte_pdsch_set_cell(&pdsch, cell)) { + fprintf(stderr, "Error creating PDSCH object\n"); + exit(-1); + } + + srslte_pdsch_set_rnti(&pdsch, UE_CRNTI); + + + if(mbsfn_area_id > -1){ + if (srslte_pmch_init(&pmch, cell.nof_prb)) { + fprintf(stderr, "Error creating PMCH object\n"); + } + srslte_pmch_set_area_id(&pmch, mbsfn_area_id); + } + + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + softbuffers[i] = calloc(sizeof(srslte_softbuffer_tx_t), 1); + if (!softbuffers[i]) { + fprintf(stderr, "Error allocating soft buffer\n"); + exit(-1); + } + + if (srslte_softbuffer_tx_init(softbuffers[i], cell.nof_prb)) { + fprintf(stderr, "Error initiating soft buffer\n"); + exit(-1); + } + } +} + + +void base_free() { + int i; + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + srslte_softbuffer_tx_free(softbuffers[i]); + if (softbuffers[i]) { + free(softbuffers[i]); + } + } + srslte_pdsch_free(&pdsch); + srslte_pdcch_free(&pdcch); + srslte_regs_free(®s); + srslte_pbch_free(&pbch); + if(mbsfn_area_id > -1){ + srslte_pmch_free(&pmch); + } + srslte_ofdm_tx_free(&ifft_mbsfn); + for (i = 0; i < cell.nof_ports; i++) { + srslte_ofdm_tx_free(&ifft[i]); + } + + for (i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (data[i]) { + free(data[i]); + } + } + + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (sf_buffer[i]) { + free(sf_buffer[i]); + } + + if (output_buffer[i]) { + free(output_buffer[i]); + } + } + if (output_file_name) { + if (!null_file_sink) { + srslte_filesink_free(&fsink); + } + } else { +#ifndef DISABLE_RF + srslte_rf_close(&rf); +#endif + } + + if (net_port > 0) { + srslte_netsource_free(&net_source); + sem_close(&net_sem); + } +} + + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } +} + + + +unsigned int +reverse(register unsigned int x) +{ + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + return((x >> 16) | (x << 16)); + +} + +uint32_t prbset_to_bitmask() { + uint32_t mask=0; + int nb = (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb)); + for (int i=0;i= prbset_orig && i < prbset_orig + prbset_num) { + mask = mask | (0x1<>(32-nb); +} + +int update_radl() { + + /* Configure cell and PDSCH in function of the transmission mode */ + switch(pdsch_cfg.mimo_type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + pdsch_cfg.nof_layers = 1; + nof_tb = 1; + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + pdsch_cfg.nof_layers = 2; + nof_tb = 1; + break; + case SRSLTE_MIMO_TYPE_CDD: + pdsch_cfg.nof_layers = 2; + nof_tb = 2; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + pdsch_cfg.nof_layers = multiplex_nof_layers; + nof_tb = multiplex_nof_layers; + break; + default: + ERROR("Transmission mode not implemented."); + exit(-1); + } + + bzero(&ra_dl, sizeof(srslte_ra_dl_dci_t)); + ra_dl.harq_process = 0; + ra_dl.mcs_idx = mcs_idx; + ra_dl.ndi = 0; + ra_dl.rv_idx = rvidx[0]; + ra_dl.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + ra_dl.type0_alloc.rbg_bitmask = prbset_to_bitmask(); + ra_dl.tb_en[0] = 1; + + if (nof_tb > 1) { + ra_dl.mcs_idx_1 = mcs_idx; + ra_dl.ndi_1 = 0; + ra_dl.rv_idx_1 = rvidx[1]; + ra_dl.tb_en[1] = 1; + } + + srslte_ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb); + srslte_ra_dl_grant_t dummy_grant; + srslte_ra_nbits_t dummy_nbits[SRSLTE_MAX_CODEWORDS]; + srslte_ra_dl_dci_to_grant(&ra_dl, cell.nof_prb, UE_CRNTI, &dummy_grant); + srslte_ra_dl_grant_to_nbits(&dummy_grant, cfi, cell, 0, dummy_nbits); + srslte_ra_dl_grant_fprint(stdout, &dummy_grant); + dummy_grant.sf_type = SRSLTE_SF_NORM; + if (pdsch_cfg.mimo_type != SRSLTE_MIMO_TYPE_SINGLE_ANTENNA) { + printf("\nTransmission mode key table:\n"); + printf(" Mode | 1TB | 2TB |\n"); + printf("----------+---------+-----+\n"); + printf("Diversity | x | |\n"); + printf(" CDD | | z |\n"); + printf("Multiplex | q,w,e,r | a,s |\n"); + printf("\n"); + printf("Type new MCS index (0-28) or mode key and press Enter: "); + } else { + printf("Type new MCS index (0-28) and press Enter: "); + } + fflush(stdout); + + return 0; +} + +/* Read new MCS from stdin */ +int update_control() { + char input[128]; + + fd_set set; + FD_ZERO(&set); + FD_SET(0, &set); + + struct timeval to; + to.tv_sec = 0; + to.tv_usec = 0; + + int n = select(1, &set, NULL, NULL, &to); + if (n == 1) { + // stdin ready + if (fgets(input, sizeof(input), stdin)) { + if(input[0] == 27) { + switch(input[2]) { + case RIGHT_KEY: + if (prbset_orig + prbset_num < (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb))) + prbset_orig++; + break; + case LEFT_KEY: + if (prbset_orig > 0) + prbset_orig--; + break; + case UP_KEY: + if (prbset_num < (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb))) + prbset_num++; + break; + case DOWN_KEY: + last_prbset_num = prbset_num; + if (prbset_num > 0) + prbset_num--; + break; + } + } else { + switch (input[0]) { + case 'q': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 0; + multiplex_nof_layers = 1; + break; + case 'w': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 1; + multiplex_nof_layers = 1; + break; + case 'e': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 2; + multiplex_nof_layers = 1; + break; + case 'r': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 3; + multiplex_nof_layers = 1; + break; + case 'a': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 0; + multiplex_nof_layers = 2; + break; + case 's': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + multiplex_pmi = 1; + multiplex_nof_layers = 2; + break; + case 'z': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_CDD; + break; + case 'x': + pdsch_cfg.mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + break; + default: + last_mcs_idx = mcs_idx; + mcs_idx = atoi(input); + } + } + bzero(input,sizeof(input)); + if (update_radl()) { + printf("Trying with last known MCS index\n"); + mcs_idx = last_mcs_idx; + prbset_num = last_prbset_num; + return update_radl(); + } + } + return 0; + } else if (n < 0) { + // error + perror("select"); + return -1; + } else { + return 0; + } +} + + +/** Function run in a separate thread to receive UDP data */ +void *net_thread_fnc(void *arg) { + int n; + int rpm = 0, wpm=0; + + do { + n = srslte_netsource_read(&net_source, &data2[rpm], DATA_BUFF_SZ-rpm); + if (n > 0) { + // FIXME: I assume that both transport blocks have same size in case of 2 tb are active + + int nbytes = 1 + (((mbsfn_area_id > -1)?(pmch_cfg.grant.mcs[0].tbs):(pdsch_cfg.grant.mcs[0].tbs + pdsch_cfg.grant.mcs[1].tbs)) - 1) / 8; + rpm += n; + INFO("received %d bytes. rpm=%d/%d\n",n,rpm,nbytes); + wpm = 0; + while (rpm >= nbytes) { + // wait for packet to be transmitted + sem_wait(&net_sem); + if(mbsfn_area_id > -1){ + memcpy(data_mbms, &data2[wpm], nbytes); + } + else{ + memcpy(data[0], &data2[wpm], nbytes / (size_t) 2); + memcpy(data[1], &data2[wpm], nbytes / (size_t) 2); + } + INFO("Sent %d/%d bytes ready\n", nbytes, rpm); + rpm -= nbytes; + wpm += nbytes; + net_packet_ready = true; + } + if (wpm > 0) { + INFO("%d bytes left in buffer for next packet\n", rpm); + memcpy(data2, &data2[wpm], rpm * sizeof(uint8_t)); + } + } else if (n == 0) { + rpm = 0; + } else { + fprintf(stderr, "Error receiving from network\n"); + exit(-1); + } + } while(n >= 0); + return NULL; +} + + +int main(int argc, char **argv) { + int nf=0, sf_idx=0, N_id_2=0; + cf_t pss_signal[SRSLTE_PSS_LEN]; + float sss_signal0[SRSLTE_SSS_LEN]; // for subframe 0 + float sss_signal5[SRSLTE_SSS_LEN]; // for subframe 5 + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + int i; + cf_t *sf_symbols[SRSLTE_MAX_PORTS]; + cf_t *slot1_symbols[SRSLTE_MAX_PORTS]; + srslte_dci_msg_t dci_msg; + srslte_dci_location_t locations[SRSLTE_NSUBFRAMES_X_FRAME][30]; + uint32_t sfn; + srslte_refsignal_t csr_refs; + srslte_refsignal_t mbsfn_refs; + + srslte_debug_handle_crash(argc, argv); + +#ifdef DISABLE_RF + if (argc < 3) { + usage(argv[0]); + exit(-1); + } +#endif + + parse_args(argc, argv); + + uint8_t mch_table[10]; + bzero(&mch_table[0], sizeof(uint8_t)*10); + if(mbsfn_area_id < -1) { + generate_mcch_table(mch_table, mbsfn_sf_mask); + } + N_id_2 = cell.id % 3; + sf_n_re = 2 * SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; + sf_n_samples = 2 * SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.nof_prb)); + + cell.phich_length = SRSLTE_PHICH_NORM; + cell.phich_resources = SRSLTE_PHICH_R_1; + sfn = 0; + + prbset_num = (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb)); + last_prbset_num = prbset_num; + + /* this *must* be called after setting slot_len_* */ + base_init(); + + /* Generate PSS/SSS signals */ + srslte_pss_generate(pss_signal, N_id_2); + srslte_sss_generate(sss_signal0, sss_signal5, cell.id); + + + /* Generate reference signals */ + if(srslte_refsignal_cs_init(&csr_refs, cell.nof_prb)) { + fprintf(stderr, "Error initializing equalizer\n"); + exit(-1); + } + if(mbsfn_area_id > -1) { + if(srslte_refsignal_mbsfn_init(&mbsfn_refs, cell.nof_prb)) { + fprintf(stderr, "Error initializing equalizer\n"); + exit(-1); + } + if (srslte_refsignal_mbsfn_set_cell(&mbsfn_refs, cell, mbsfn_area_id)) { + fprintf(stderr, "Error initializing MBSFNR signal\n"); + exit(-1); + } + + } + + if(srslte_refsignal_cs_set_cell(&csr_refs, cell)){ + fprintf(stderr, "Error setting cell\n"); + exit(-1); + } + + + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + sf_symbols[i] = sf_buffer[i%cell.nof_ports]; + slot1_symbols[i] = &sf_buffer[i%cell.nof_ports][SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)]; + } + + +#ifndef DISABLE_RF + + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + signal(SIGINT, sig_int_handler); + + if (!output_file_name) { + + int srate = srslte_sampling_freq_hz(cell.nof_prb); + if (srate != -1) { + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); + float srate_rf = srslte_rf_set_tx_srate(&rf, (double) srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb); + exit(-1); + } + printf("Set TX gain: %.1f dB\n", srslte_rf_set_tx_gain(&rf, rf_gain)); + printf("Set TX freq: %.2f MHz\n", + srslte_rf_set_tx_freq(&rf, rf_freq) / 1000000); + } +#endif + + if (update_radl(sf_idx)) { + exit(-1); + } + + if (net_port > 0) { + if (pthread_create(&net_thread, NULL, net_thread_fnc, NULL)) { + perror("pthread_create"); + exit(-1); + } + } + pmch_cfg.grant.mcs[0].tbs = 1096; + /* Initiate valid DCI locations */ + for (i=0;i -1){ + srslte_refsignal_mbsfn_put_sf(cell, 0,csr_refs.pilots[0][sf_idx], mbsfn_refs.pilots[0][sf_idx], sf_symbols[0]); + } else { + for (i = 0; i < cell.nof_ports; i++) { + srslte_refsignal_cs_put_sf(cell, (uint32_t) i, csr_refs.pilots[i / 2][sf_idx], sf_symbols[i]); + } + } + + srslte_pbch_mib_pack(&cell, sfn, bch_payload); + if (sf_idx == 0) { + srslte_pbch_encode(&pbch, bch_payload, slot1_symbols, nf%4); + } + + srslte_pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx); + + /* Update DL resource allocation from control port */ + if (update_control(sf_idx)) { + fprintf(stderr, "Error updating parameters from control port\n"); + } + + /* Transmit PDCCH + PDSCH only when there is data to send */ + if ((net_port > 0) && (mch_table[sf_idx] == 1 && mbsfn_area_id > -1)) { + send_data = net_packet_ready; + if (net_packet_ready) { + INFO("Transmitting packet from port\n"); + } + } else { + INFO("SF: %d, Generating %d random bits\n", sf_idx, pdsch_cfg.grant.mcs[0].tbs + pdsch_cfg.grant.mcs[1].tbs); + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + if (pdsch_cfg.grant.tb_en[tb]) { + for (i = 0; i < pdsch_cfg.grant.mcs[tb].tbs / 8; i++) { + data[tb][i] = (uint8_t) rand(); + } + } + } + /* Uncomment this to transmit on sf 0 and 5 only */ + if (sf_idx != 0 && sf_idx != 5) { + send_data = true; + } else { + send_data = false; + } + } + if (send_data) { + if(mch_table[sf_idx] == 0 || mbsfn_area_id < 0) { // PDCCH + PDSCH + srslte_dci_format_t dci_format; + switch(pdsch_cfg.mimo_type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + dci_format = SRSLTE_DCI_FORMAT1; + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + case SRSLTE_MIMO_TYPE_CDD: + dci_format = SRSLTE_DCI_FORMAT2A; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + dci_format = SRSLTE_DCI_FORMAT2; + if (multiplex_nof_layers == 1) { + ra_dl.pinfo = (uint8_t) (multiplex_pmi + 1); + } else { + ra_dl.pinfo = (uint8_t) multiplex_pmi; + } + break; + default: + fprintf(stderr, "Wrong MIMO configuration\n"); + exit(SRSLTE_ERROR); + } + /* Encode PDCCH */ + INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L); + srslte_dci_msg_pack_pdsch(&ra_dl, dci_format, &dci_msg, cell.nof_prb, cell.nof_ports, false); + if (srslte_pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], UE_CRNTI, sf_symbols, sf_idx, cfi)) { + fprintf(stderr, "Error encoding DCI message\n"); + exit(-1); + } + + /* Configure pdsch_cfg parameters */ + srslte_ra_dl_grant_t grant; + srslte_ra_dl_dci_to_grant(&ra_dl, cell.nof_prb, UE_CRNTI, &grant); + if (srslte_pdsch_cfg_mimo(&pdsch_cfg, cell, &grant, cfi, sf_idx, rvidx, pdsch_cfg.mimo_type, multiplex_pmi)) { + fprintf(stderr, "Error configuring PDSCH\n"); + exit(-1); + } + + /* Encode PDSCH */ + if (srslte_pdsch_encode(&pdsch, &pdsch_cfg, softbuffers, data, UE_CRNTI, sf_symbols)) { + fprintf(stderr, "Error encoding PDSCH\n"); + exit(-1); + } + if (net_port > 0 && net_packet_ready) { + if (null_file_sink) { + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + srslte_bit_pack_vector(data[tb], data_tmp, pdsch_cfg.grant.mcs[tb].tbs); + if (srslte_netsink_write(&net_sink, data_tmp, 1 + (pdsch_cfg.grant.mcs[tb].tbs - 1) / 8) < 0) { + fprintf(stderr, "Error sending data through UDP socket\n"); + } + } + } + if(mbsfn_area_id < 0){ + net_packet_ready = false; + sem_post(&net_sem); + } + } + }else{ // We're sending MCH on subframe 1 - PDCCH + PMCH + + /* Encode PDCCH */ + //INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L); + //srslte_dci_msg_pack_pdsch(&ra_dl, SRSLTE_DCI_FORMAT1, &dci_msg, cell.nof_prb, cell.nof_ports, false); + //if (srslte_pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], M_CRNTI, sf_symbols, sf_idx, cfi)) { + // fprintf(stderr, "Error encoding DCI message\n"); + // exit(-1); + // } + /* Configure pmch_cfg parameters */ + srslte_ra_dl_grant_t grant; + grant.tb_en[0] = true; + grant.tb_en[1] = false; + + grant.mcs[0].idx = 2; + grant.nof_prb = cell.nof_prb; + grant.sf_type = SRSLTE_SF_MBSFN; + srslte_dl_fill_ra_mcs(&grant.mcs[0], cell.nof_prb); + grant.Qm[0] = srslte_mod_bits_x_symbol(grant.mcs[0].mod); + for(int i = 0; i < 2; i++){ + for(int j = 0; j < grant.nof_prb; j++){ + grant.prb_idx[i][j] = true; + } + } + for(int i = 0; i < grant.mcs[0].tbs/8;i++) + { + data_mbms[i] = i%255; + } + + if (srslte_pmch_cfg(&pmch_cfg, cell, &grant, cfi, sf_idx)) { + fprintf(stderr, "Error configuring PMCH\n"); + exit(-1); + } + /* Encode PMCH */ + if (srslte_pmch_encode(&pmch, &pmch_cfg, softbuffers[0], data_mbms, mbsfn_area_id, sf_symbols)) { + fprintf(stderr, "Error encoding PDSCH\n"); + exit(-1); + } + if (net_port > 0 && net_packet_ready) { + if (null_file_sink) { + srslte_bit_pack_vector(data[0], data_tmp, pmch_cfg.grant.mcs[0].tbs); + if (srslte_netsink_write(&net_sink, data_tmp, 1+(pmch_cfg.grant.mcs[0].tbs-1)/8) < 0) { + fprintf(stderr, "Error sending data through UDP socket\n"); + } + } + net_packet_ready = false; + sem_post(&net_sem); + } + } + } + + /* Transform to OFDM symbols */ + if(mch_table[sf_idx] == 0 || mbsfn_area_id < 0){ + for (i = 0; i < cell.nof_ports; i++) { + srslte_ofdm_tx_sf(&ifft[i]); + } + }else{ + srslte_ofdm_tx_sf(&ifft_mbsfn); + } + + + /* send to file or usrp */ + if (output_file_name) { + if (!null_file_sink) { + /* Apply AWGN */ + if (output_file_snr != +INFINITY) { + float var = powf(10.0f, -(output_file_snr + 3.0f) / 20.0f); + for (int k = 0; k < cell.nof_ports; k++) { + srslte_ch_awgn_c(output_buffer[k], output_buffer[k], var, sf_n_samples); + } + } + srslte_filesink_write_multi(&fsink, (void**) output_buffer, sf_n_samples, cell.nof_ports); + } + usleep(1000); + } else { +#ifndef DISABLE_RF + float norm_factor = (float) cell.nof_prb/15/sqrtf(pdsch_cfg.grant.nof_prb); + for (i = 0; i < cell.nof_ports; i++) { + srslte_vec_sc_prod_cfc(output_buffer[i], rf_amp * norm_factor, output_buffer[i], SRSLTE_SF_LEN_PRB(cell.nof_prb)); + } + srslte_rf_send_multi(&rf, (void**) output_buffer, sf_n_samples, true, start_of_burst, false); + start_of_burst=false; +#endif + } + } + nf++; + sfn = (sfn + 1) % 1024; + } + + base_free(); + + printf("Done\n"); + exit(0); +} + + diff --git a/lib/examples/pdsch_ue.c b/lib/examples/pdsch_ue.c new file mode 100644 index 0000000..3b84afb --- /dev/null +++ b/lib/examples/pdsch_ue.c @@ -0,0 +1,1102 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "srslte/common/gen_mch_tables.h" +#include +#include "srslte/phy/io/filesink.h" +#include "srslte/srslte.h" + +#define ENABLE_AGC_DEFAULT + +#ifndef DISABLE_RF +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/rf/rf_utils.h" + +cell_search_cfg_t cell_detect_config = { + SRSLTE_DEFAULT_MAX_FRAMES_PBCH, + SRSLTE_DEFAULT_MAX_FRAMES_PSS, + SRSLTE_DEFAULT_NOF_VALID_PSS_FRAMES, + 0 +}; + +#else +#warning Compiling pdsch_ue with no RF support +#endif + +//#define STDOUT_COMPACT + +#ifndef DISABLE_GRAPHICS +#include "srsgui/srsgui.h" +void init_plots(); +pthread_t plot_thread; +sem_t plot_sem; +uint32_t plot_sf_idx=0; +bool plot_track = true; +bool enable_mbsfn_plot = false; +#endif +char *output_file_name; +#define PRINT_CHANGE_SCHEDULIGN + +//#define CORRECT_SAMPLE_OFFSET + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + int nof_subframes; + int cpu_affinity; + bool disable_plots; + bool disable_plots_except_constellation; + bool disable_cfo; + uint32_t time_offset; + int force_N_id_2; + uint16_t rnti; + char *input_file_name; + int file_offset_time; + float file_offset_freq; + uint32_t file_nof_prb; + uint32_t file_nof_ports; + uint32_t file_cell_id; + bool enable_cfo_ref; + bool average_subframe; + char *rf_args; + uint32_t rf_nof_rx_ant; + double rf_freq; + float rf_gain; + int net_port; + char *net_address; + int net_port_signal; + char *net_address_signal; + int decimate; + int32_t mbsfn_area_id; + uint8_t non_mbsfn_region; + uint8_t mbsfn_sf_mask; + int verbose; +}prog_args_t; + +void args_default(prog_args_t *args) { + args->disable_plots = false; + args->disable_plots_except_constellation = false; + args->nof_subframes = -1; + args->rnti = SRSLTE_SIRNTI; + args->force_N_id_2 = -1; // Pick the best + args->input_file_name = NULL; + args->disable_cfo = false; + args->time_offset = 0; + args->file_nof_prb = 25; + args->file_nof_ports = 1; + args->file_cell_id = 0; + args->file_offset_time = 0; + args->file_offset_freq = 0; + args->rf_args = ""; + args->rf_freq = -1.0; + args->rf_nof_rx_ant = 1; + args->enable_cfo_ref = false; + args->average_subframe = false; +#ifdef ENABLE_AGC_DEFAULT + args->rf_gain = -1.0; +#else + args->rf_gain = 50.0; +#endif + args->net_port = -1; + args->net_address = "127.0.0.1"; + args->net_port_signal = -1; + args->net_address_signal = "127.0.0.1"; + args->decimate = 0; + args->cpu_affinity = -1; + args->mbsfn_area_id = -1; + args->non_mbsfn_region = 2; + args->mbsfn_sf_mask = 32; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [agpPoOcildFRDnruMNv] -f rx_frequency (in Hz) | -i input_file\n", prog); +#ifndef DISABLE_RF + printf("\t-a RF args [Default %s]\n", args->rf_args); + printf("\t-A Number of RX antennas [Default %d]\n", args->rf_nof_rx_ant); +#ifdef ENABLE_AGC_DEFAULT + printf("\t-g RF fix RX gain [Default AGC]\n"); +#else + printf("\t-g Set RX gain [Default %.1f dB]\n", args->rf_gain); +#endif +#else + printf("\t RF is disabled.\n"); +#endif + printf("\t-i input_file [Default use RF board]\n"); + printf("\t-o offset frequency correction (in Hz) for input file [Default %.1f Hz]\n", args->file_offset_freq); + printf("\t-O offset samples for input file [Default %d]\n", args->file_offset_time); + printf("\t-p nof_prb for input file [Default %d]\n", args->file_nof_prb); + printf("\t-P nof_ports for input file [Default %d]\n", args->file_nof_ports); + printf("\t-c cell_id for input file [Default %d]\n", args->file_cell_id); + printf("\t-r RNTI in Hex [Default 0x%x]\n",args->rnti); + printf("\t-l Force N_id_2 [Default best]\n"); + printf("\t-C Disable CFO correction [Default %s]\n", args->disable_cfo?"Disabled":"Enabled"); + printf("\t-F Enable RS-based CFO correction [Default %s]\n", !args->enable_cfo_ref?"Disabled":"Enabled"); + printf("\t-R Average channel estimates on 1 ms [Default %s]\n", !args->average_subframe?"Disabled":"Enabled"); + printf("\t-t Add time offset [Default %d]\n", args->time_offset); +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); + printf("\t-D disable all but constellation plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif + printf("\t-y set the cpu affinity mask [Default %d] \n ",args->cpu_affinity); + printf("\t-n nof_subframes [Default %d]\n", args->nof_subframes); + printf("\t-s remote UDP port to send input signal (-1 does nothing with it) [Default %d]\n", args->net_port_signal); + printf("\t-S remote UDP address to send input signal [Default %s]\n", args->net_address_signal); + printf("\t-u remote TCP port to send data (-1 does nothing with it) [Default %d]\n", args->net_port); + printf("\t-U remote TCP address to send data [Default %s]\n", args->net_address); + printf("\t-M MBSFN area id [Default %d]\n", args->mbsfn_area_id); + printf("\t-N Non-MBSFN region [Default %d]\n", args->non_mbsfn_region); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "aAoglipPcOCtdDFRnvrfuUsSZyWMNB")) != -1) { + switch (opt) { + case 'i': + args->input_file_name = argv[optind]; + break; + case 'p': + args->file_nof_prb = atoi(argv[optind]); + break; + case 'P': + args->file_nof_ports = atoi(argv[optind]); + break; + case 'o': + args->file_offset_freq = atof(argv[optind]); + break; + case 'O': + args->file_offset_time = atoi(argv[optind]); + break; + case 'c': + args->file_cell_id = atoi(argv[optind]); + break; + case 'a': + args->rf_args = argv[optind]; + break; + case 'A': + args->rf_nof_rx_ant = atoi(argv[optind]); + break; + case 'g': + args->rf_gain = atof(argv[optind]); + break; + case 'C': + args->disable_cfo = true; + break; + case 'F': + args->enable_cfo_ref = true; + break; + case 'R': + args->average_subframe = true; + break; + case 't': + args->time_offset = atoi(argv[optind]); + break; + case 'f': + args->rf_freq = strtod(argv[optind], NULL); + break; + case 'n': + args->nof_subframes = atoi(argv[optind]); + break; + case 'r': + args->rnti = strtol(argv[optind], NULL, 16); + break; + case 'l': + args->force_N_id_2 = atoi(argv[optind]); + break; + case 'u': + args->net_port = atoi(argv[optind]); + break; + case 'U': + args->net_address = argv[optind]; + break; + case 's': + args->net_port_signal = atoi(argv[optind]); + break; + case 'S': + args->net_address_signal = argv[optind]; + break; + case 'd': + args->disable_plots = true; + break; + case 'D': + args->disable_plots_except_constellation = true; + break; + case 'v': + srslte_verbose++; + args->verbose = srslte_verbose; + break; + case 'Z': + args->decimate = atoi(argv[optind]); + break; + case 'y': + args->cpu_affinity = atoi(argv[optind]); + break; + case 'W': + output_file_name = argv[optind]; + break; + case 'M': + args->mbsfn_area_id = atoi(argv[optind]); + break; + case 'N': + args->non_mbsfn_region = atoi(argv[optind]); + break; + case 'B': + args->mbsfn_sf_mask = atoi(argv[optind]); + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_freq < 0 && args->input_file_name == NULL) { + usage(args, argv[0]); + exit(-1); + } +} +/**********************************************************************/ + +/* TODO: Do something with the output data */ +uint8_t *data[SRSLTE_MAX_CODEWORDS]; + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } else if (signo == SIGSEGV) { + exit(1); + } +} + +cf_t *sf_buffer[SRSLTE_MAX_PORTS] = {NULL}; + + +#ifndef DISABLE_RF +int srslte_rf_recv_wrapper(void *h, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *t) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + void *ptr[SRSLTE_MAX_PORTS]; + for (int i=0;i -1) { + enable_mbsfn_plot = true; + } +#endif + + for (int i = 0; i< SRSLTE_MAX_CODEWORDS; i++) { + data[i] = srslte_vec_malloc(sizeof(uint8_t)*1500*8); + if (!data[i]) { + ERROR("Allocating data"); + go_exit = true; + } + } + uint8_t mch_table[10]; + bzero(&mch_table[0], sizeof(uint8_t)*10); + if(prog_args.mbsfn_area_id < -1) { + generate_mcch_table(mch_table, prog_args.mbsfn_sf_mask); + } + if(prog_args.cpu_affinity > -1) { + + cpu_set_t cpuset; + pthread_t thread; + + thread = pthread_self(); + for(int i = 0; i < 8;i++){ + if(((prog_args.cpu_affinity >> i) & 0x01) == 1){ + printf("Setting pdsch_ue with affinity to core %d\n", i); + CPU_SET((size_t) i , &cpuset); + } + if(pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset)){ + fprintf(stderr, "Error setting main thread affinity to %d \n", prog_args.cpu_affinity); + exit(-1); + } + } + } + + if (prog_args.net_port > 0) { + if (srslte_netsink_init(&net_sink, prog_args.net_address, prog_args.net_port, SRSLTE_NETSINK_UDP)) { + fprintf(stderr, "Error initiating UDP socket to %s:%d\n", prog_args.net_address, prog_args.net_port); + exit(-1); + } + srslte_netsink_set_nonblocking(&net_sink); + } + if (prog_args.net_port_signal > 0) { + if (srslte_netsink_init(&net_sink_signal, prog_args.net_address_signal, + prog_args.net_port_signal, SRSLTE_NETSINK_UDP)) { + fprintf(stderr, "Error initiating UDP socket to %s:%d\n", prog_args.net_address_signal, prog_args.net_port_signal); + exit(-1); + } + srslte_netsink_set_nonblocking(&net_sink_signal); + } + +#ifndef DISABLE_RF + if (!prog_args.input_file_name) { + + printf("Opening RF device with %d RX antennas...\n", prog_args.rf_nof_rx_ant); + if (srslte_rf_open_multi(&rf, prog_args.rf_args, prog_args.rf_nof_rx_ant)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + /* Set receiver gain */ + if (prog_args.rf_gain > 0) { + srslte_rf_set_rx_gain(&rf, prog_args.rf_gain); + } else { + printf("Starting AGC thread...\n"); + if (srslte_rf_start_gain_thread(&rf, false)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_rx_gain(&rf, srslte_rf_get_rx_gain(&rf)); + cell_detect_config.init_agc = srslte_rf_get_rx_gain(&rf); + } + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + signal(SIGINT, sig_int_handler); + + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + + /* set receiver frequency */ + printf("Tunning receiver to %.3f MHz\n", (prog_args.rf_freq + prog_args.file_offset_freq)/1000000); + srslte_rf_set_rx_freq(&rf, prog_args.rf_freq + prog_args.file_offset_freq); + srslte_rf_rx_wait_lo_locked(&rf); + + + uint32_t ntrial=0; + do { + ret = rf_search_and_decode_mib(&rf, prog_args.rf_nof_rx_ant, &cell_detect_config, prog_args.force_N_id_2, &cell, &cfo); + if (ret < 0) { + fprintf(stderr, "Error searching for cell\n"); + exit(-1); + } else if (ret == 0 && !go_exit) { + printf("Cell not found after %d trials. Trying again (Press Ctrl+C to exit)\n", ntrial++); + } + } while (ret == 0 && !go_exit); + + if (go_exit) { + srslte_rf_close(&rf); + exit(0); + } + + /* set sampling frequency */ + int srate = srslte_sampling_freq_hz(cell.nof_prb); + if (srate != -1) { + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); + float srate_rf = srslte_rf_set_rx_srate(&rf, (double) srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + } else { + fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb); + exit(-1); + } + + INFO("Stopping RF and flushing buffer...\r"); + } +#endif + + /* If reading from file, go straight to PDSCH decoding. Otherwise, decode MIB first */ + if (prog_args.input_file_name) { + /* preset cell configuration */ + cell.id = prog_args.file_cell_id; + cell.cp = SRSLTE_CP_NORM; + cell.phich_length = SRSLTE_PHICH_NORM; + cell.phich_resources = SRSLTE_PHICH_R_1; + cell.nof_ports = prog_args.file_nof_ports; + cell.nof_prb = prog_args.file_nof_prb; + + if (srslte_ue_sync_init_file_multi(&ue_sync, prog_args.file_nof_prb, + prog_args.input_file_name, prog_args.file_offset_time, prog_args.file_offset_freq, prog_args.rf_nof_rx_ant)) { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + + } else { +#ifndef DISABLE_RF + if(prog_args.decimate) + { + if(prog_args.decimate > 4 || prog_args.decimate < 0) + { + printf("Invalid decimation factor, setting to 1 \n"); + } + else + { + decimate = prog_args.decimate; + //ue_sync.decimate = prog_args.decimate; + } + } + if (srslte_ue_sync_init_multi_decim(&ue_sync, + cell.nof_prb, + cell.id==1000, + srslte_rf_recv_wrapper, + prog_args.rf_nof_rx_ant, + (void*) &rf,decimate)) + { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + if (srslte_ue_sync_set_cell(&ue_sync, cell)) + { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } +#endif + } + + for (int i=0;i -1) { + srslte_ue_dl_set_mbsfn_area_id(&ue_dl, prog_args.mbsfn_area_id); + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, prog_args.non_mbsfn_region); + } + /* Initialize subframe counter */ + sf_cnt = 0; + + +#ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots) { + init_plots(cell); + sleep(1); + } +#endif + +#ifndef DISABLE_RF + if (!prog_args.input_file_name) { + srslte_rf_start_rx_stream(&rf, false); + } +#endif + + // Variables for measurements + uint32_t nframes=0; + uint8_t ri = 0, pmi = 0; + float rsrp0=0.0, rsrp1=0.0, rsrq=0.0, noise=0.0, enodebrate = 0.0, uerate = 0.0, procrate = 0.0, + sinr[SRSLTE_MAX_LAYERS][SRSLTE_MAX_CODEBOOKS], cn = 0.0; + bool decode_pdsch = false; + + for (int i = 0; i < SRSLTE_MAX_LAYERS; i++) { + bzero(sinr[i], sizeof(float)*SRSLTE_MAX_CODEBOOKS); + } + +#ifndef DISABLE_RF + if (prog_args.rf_gain < 0 && !prog_args.input_file_name) { + srslte_rf_info_t *rf_info = srslte_rf_get_info(&rf); + srslte_ue_sync_start_agc(&ue_sync, + srslte_rf_set_rx_gain_th_wrapper_, + rf_info->min_rx_gain, + rf_info->max_rx_gain, + cell_detect_config.init_agc); + } +#endif +#ifdef PRINT_CHANGE_SCHEDULIGN + srslte_ra_dl_dci_t old_dl_dci; + bzero(&old_dl_dci, sizeof(srslte_ra_dl_dci_t)); +#endif + + ue_sync.cfo_correct_enable_track = !prog_args.disable_cfo; + + srslte_pbch_decode_reset(&ue_mib.pbch); + + INFO("\nEntering main loop...\n\n"); + /* Main loop */ + while (!go_exit && (sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1)) { + bool acks [SRSLTE_MAX_CODEWORDS] = {false}; + char input[128]; + PRINT_LINE_INIT(); + + fd_set set; + FD_ZERO(&set); + FD_SET(0, &set); + + struct timeval to; + to.tv_sec = 0; + to.tv_usec = 0; + + /* Set default verbose level */ + srslte_verbose = prog_args.verbose; + int n = select(1, &set, NULL, NULL, &to); + if (n == 1) { + /* If a new line is detected set verbose level to Debug */ + if (fgets(input, sizeof(input), stdin)) { + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + ue_dl.pkt_errors = 0; + ue_dl.pkts_total = 0; + ue_dl.nof_detected = 0; + nof_trials = 0; + } + } + + ret = srslte_ue_sync_zerocopy_multi(&ue_sync, sf_buffer); + if (ret < 0) { + fprintf(stderr, "Error calling srslte_ue_sync_work()\n"); + } + +#ifdef CORRECT_SAMPLE_OFFSET + float sample_offset = (float) srslte_ue_sync_get_last_sample_offset(&ue_sync)+srslte_ue_sync_get_sfo(&ue_sync)/1000; + srslte_ue_dl_set_sample_offset(&ue_dl, sample_offset); +#endif + + /* srslte_ue_sync_get_buffer returns 1 if successfully read 1 aligned subframe */ + if (ret == 1) { + + uint32_t sfidx = srslte_ue_sync_get_sfidx(&ue_sync); + + switch (state) { + case DECODE_MIB: + if (sfidx == 0) { + n = srslte_ue_mib_decode(&ue_mib, bch_payload, NULL, &sfn_offset); + if (n < 0) { + fprintf(stderr, "Error decoding UE MIB\n"); + exit(-1); + } else if (n == SRSLTE_UE_MIB_FOUND) { + srslte_pbch_mib_unpack(bch_payload, &cell, &sfn); + srslte_cell_fprint(stdout, &cell, sfn); + printf("Decoded MIB. SFN: %d, offset: %d\n", sfn, sfn_offset); + sfn = (sfn + sfn_offset)%1024; + state = DECODE_PDSCH; + } + } + break; + case DECODE_PDSCH: + if (prog_args.rnti != SRSLTE_SIRNTI) { + decode_pdsch = true; + } else { + /* We are looking for SIB1 Blocks, search only in appropiate places */ + if ((sfidx == 5 && (sfn%2)==0) ||mch_table[sfidx] == 1) { + decode_pdsch = true; + } else { + decode_pdsch = false; + } + } + + gettimeofday(&t[1], NULL); + if (decode_pdsch) { + if(mch_table[sfidx] == 0 || prog_args.mbsfn_area_id < 0){ // Not an MBSFN subframe + if (cell.nof_ports == 1) { + /* Transmission mode 1 */ + n = srslte_ue_dl_decode(&ue_dl, data, 0, sfn*10+srslte_ue_sync_get_sfidx(&ue_sync), acks); + } else { + /* Transmission mode 2 */ + n = srslte_ue_dl_decode(&ue_dl, data, 1, sfn * 10 + srslte_ue_sync_get_sfidx(&ue_sync), + acks); + + if (n < 1) { + /* Transmission mode 3 */ + n = srslte_ue_dl_decode(&ue_dl, data, 2, sfn * 10 + srslte_ue_sync_get_sfidx(&ue_sync), + acks); + } + + if (n < 1) { + /* Transmission mode 4 */ + n = srslte_ue_dl_decode(&ue_dl, data, 3, sfn * 10 + srslte_ue_sync_get_sfidx(&ue_sync), + acks); + } + } + + // Feed-back ue_sync with chest_dl CFO estimation + if (sfidx == 5 && prog_args.enable_cfo_ref) { + srslte_ue_sync_set_cfo_ref(&ue_sync, srslte_chest_dl_get_cfo(&ue_dl.chest)); + } + + }else{ // MBSFN subframe + n = srslte_ue_dl_decode_mbsfn(&ue_dl, + data[0], + sfn*10+srslte_ue_sync_get_sfidx(&ue_sync)); + if(n>0){ + if(output_file_name){ + //srslte_filesink_init(&sink, output_file_name, SRSLTE_BYTE_BIN); + // srslte_filesink_write(&sink, data, n); + //srslte_filesink_free(&sink); + } + INFO("mbsfn PDU size is %d\n", n); + } + } + gettimeofday(&t[2], NULL); + get_time_interval(t); + if (n < 0) { + // fprintf(stderr, "Error decoding UE DL\n");fflush(stdout); + } else if (n > 0) { + + /* Send data if socket active */ + if (prog_args.net_port > 0) { + if(sfidx == 1) { + srslte_netsink_write(&net_sink, data[0], 1+(n-1)/8); + } else { + // FIXME: UDP Data transmission does not work + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + if (ue_dl.pdsch_cfg.grant.tb_en[tb]) { + srslte_netsink_write(&net_sink, data[tb], 1 + (ue_dl.pdsch_cfg.grant.mcs[tb].tbs - 1) / 8); + } + } + } + } + #ifdef PRINT_CHANGE_SCHEDULIGN + if (ue_dl.dl_dci.mcs_idx != old_dl_dci.mcs_idx || + memcmp(&ue_dl.dl_dci.type0_alloc, &old_dl_dci.type0_alloc, sizeof(srslte_ra_type0_t)) || + memcmp(&ue_dl.dl_dci.type1_alloc, &old_dl_dci.type1_alloc, sizeof(srslte_ra_type1_t)) || + memcmp(&ue_dl.dl_dci.type2_alloc, &old_dl_dci.type2_alloc, sizeof(srslte_ra_type2_t))) + { + memcpy(&old_dl_dci, &ue_dl.dl_dci, sizeof(srslte_ra_dl_dci_t)); + fflush(stdout); + printf("Format: %s\n", srslte_dci_format_string(ue_dl.dci_format)); + srslte_ra_pdsch_fprint(stdout, &old_dl_dci, cell.nof_prb); + srslte_ra_dl_grant_fprint(stdout, &ue_dl.pdsch_cfg.grant); + } + #endif + + } + + nof_trials++; + + uint32_t nof_bits = ((acks[0]?ue_dl.pdsch_cfg.grant.mcs[0].tbs:0) + (acks[1]?ue_dl.pdsch_cfg.grant.mcs[1].tbs:0)); + rsrq = SRSLTE_VEC_EMA(srslte_chest_dl_get_rsrq(&ue_dl.chest), rsrq, 0.1f); + rsrp0 = SRSLTE_VEC_EMA(srslte_chest_dl_get_rsrp_port(&ue_dl.chest, 0), rsrp0, 0.05f); + rsrp1 = SRSLTE_VEC_EMA(srslte_chest_dl_get_rsrp_port(&ue_dl.chest, 1), rsrp1, 0.05f); + noise = SRSLTE_VEC_EMA(srslte_chest_dl_get_noise_estimate(&ue_dl.chest), noise, 0.05f); + enodebrate = SRSLTE_VEC_EMA(nof_bits/1000.0f, enodebrate, 0.05f); + uerate = SRSLTE_VEC_EMA(nof_bits/1000.0f, uerate, 0.001f); + float elapsed = (float) t[0].tv_usec + t[0].tv_sec*1.0e+6f; + if (elapsed != 0.0f) { + procrate = SRSLTE_VEC_EMA(nof_bits/elapsed, procrate, 0.01f); + } + + nframes++; + if (isnan(rsrq)) { + rsrq = 0; + } + if (isnan(noise)) { + noise = 0; + } + if (isnan(rsrp0)) { + rsrp0 = 0; + } + if (isnan(rsrp1)) { + rsrp1 = 0; + } + } + + // Plot and Printf + if (sfidx == 5) { + float gain = prog_args.rf_gain; + if (gain < 0) { + gain = 10*log10(srslte_agc_get_gain(&ue_sync.agc)); + } + + /* Print transmission scheme */ + if (ue_dl.pdsch_cfg.mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + PRINT_LINE(" Tx scheme: %s (codebook_idx=%d)", srslte_mimotype2str(ue_dl.pdsch_cfg.mimo_type), + ue_dl.pdsch_cfg.codebook_idx); + } else { + PRINT_LINE(" Tx scheme: %s", srslte_mimotype2str(ue_dl.pdsch_cfg.mimo_type)); + } + + /* Print basic Parameters */ + PRINT_LINE(" nof layers: %d", ue_dl.pdsch_cfg.nof_layers); + PRINT_LINE("nof codewords: %d", SRSLTE_RA_DL_GRANT_NOF_TB(&ue_dl.pdsch_cfg.grant)); + PRINT_LINE(" CFO: %+7.2f Hz", srslte_ue_sync_get_cfo(&ue_sync)); + PRINT_LINE(" RSRP: %+5.1f dBm | %+5.1f dBm", 10 * log10(rsrp0), 10 * log10(rsrp1)); + PRINT_LINE(" SNR: %+5.1f dB | %+5.1f dB", 10 * log10(rsrp0 / noise), 10 * log10(rsrp1 / noise)); + PRINT_LINE(" Rb: %6.2f / %6.2f / %6.2f Mbps (net/maximum/processing)", uerate, enodebrate, procrate); + PRINT_LINE(" PDCCH-Miss: %5.2f%%", 100 * (1 - (float) ue_dl.nof_detected / nof_trials)); + PRINT_LINE(" PDSCH-BLER: %5.2f%%", (float) 100 * ue_dl.pdsch_pkt_errors / ue_dl.pdsch_pkts_total); + if(prog_args.mbsfn_area_id > -1){ + PRINT_LINE(" PMCH-BLER: %5.2f%%", (float) 100 * ue_dl.pmch_pkt_errors/ue_dl.pmch_pkts_total); + } + PRINT_LINE(" TB 0: mcs=%d; tbs=%d", ue_dl.pdsch_cfg.grant.mcs[0].idx, + ue_dl.pdsch_cfg.grant.mcs[0].tbs); + PRINT_LINE(" TB 1: mcs=%d; tbs=%d", ue_dl.pdsch_cfg.grant.mcs[1].idx, + ue_dl.pdsch_cfg.grant.mcs[1].tbs); + + /* MIMO: if tx and rx antennas are bigger than 1 */ + if (cell.nof_ports > 1 && ue_dl.pdsch.nof_rx_antennas > 1) { + /* Compute condition number */ + if (srslte_ue_dl_ri_select(&ue_dl, NULL, &cn)) { + /* Condition number calculation is not supported for the number of tx & rx antennas*/ + PRINT_LINE(" κ: NA"); + } else { + /* Print condition number */ + PRINT_LINE(" κ: %.1f dB (Condition number, 0 dB => Best)", cn); + } + } + PRINT_LINE(""); + + /* Spatial multiplex only */ + if (ue_dl.pdsch_cfg.mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + + /* Compute Rank Indicator (RI) and Precoding Matrix Indicator (PMI) */ + if (!srslte_ue_dl_ri_pmi_select(&ue_dl, &ri, &pmi, NULL)) { + for (uint32_t nl = 0; nl < SRSLTE_MAX_LAYERS; nl++) { + for (uint32_t cb = 0; cb < SRSLTE_MAX_CODEBOOKS; cb++) { + sinr[nl][cb] = SRSLTE_VEC_EMA(ue_dl.sinr[nl][cb], sinr[nl][cb], 0.5f); + } + } + + /* Print Multiplex stats */ + PRINT_LINE("SINR (dB) Vs RI and PMI:"); + PRINT_LINE(" | RI | 1 | 2 |"); + PRINT_LINE(" -------+-------+-------+"); + PRINT_LINE(" P | 0 | %5.2f%c| %5.2f%c|", 10 * log10(sinr[0][0]), (ri == 1 && pmi == 0) ? '*' : ' ', + 10 * log10(sinr[1][0]), (ri == 2 && pmi == 0) ? '*' : ' '); + PRINT_LINE(" M | 1 | %5.2f%c| %5.2f%c|", 10 * log10(sinr[0][1]), (ri == 1 && pmi == 1) ? '*' : ' ', + 10 * log10(sinr[1][1]), (ri == 2 && pmi == 1) ? '*' : ' '); + PRINT_LINE(" I | 2 | %5.2f%c|-------+ ", 10 * log10(sinr[0][2]), (ri == 1 && pmi == 2) ? '*' : ' '); + PRINT_LINE(" | 3 | %5.2f%c| ", 10 * log10(sinr[0][3]), (ri == 1 && pmi == 3) ? '*' : ' '); + PRINT_LINE(""); + } + } + PRINT_LINE("Press enter maximum printing debug log of 1 subframe."); + PRINT_LINE(""); + PRINT_LINE_RESET_CURSOR(); + + } + break; + } + if (sfidx == 9) { + sfn++; + if (sfn == 1024) { + sfn = 0; + PRINT_LINE_ADVANCE_CURSOR(); + ue_dl.pdsch_pkt_errors = 0; + ue_dl.pdsch_pkts_total = 0; + ue_dl.pmch_pkt_errors = 0; + ue_dl.pmch_pkts_total = 0; + /* + ue_dl.pkt_errors = 0; + ue_dl.pkts_total = 0; + ue_dl.nof_detected = 0; + nof_trials = 0; + */ + } + } + + #ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots) { + if ((sfn%3) == 0 && decode_pdsch) { + plot_sf_idx = srslte_ue_sync_get_sfidx(&ue_sync); + plot_track = true; + sem_post(&plot_sem); + } + } + #endif + } else if (ret == 0) { + printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r", + srslte_sync_get_peak_value(&ue_sync.sfind), + ue_sync.frame_total_cnt, ue_sync.state); + #ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots) { + plot_sf_idx = srslte_ue_sync_get_sfidx(&ue_sync); + plot_track = false; + sem_post(&plot_sem); + } + #endif + } + + sf_cnt++; + } // Main loop + +#ifndef DISABLE_GRAPHICS + if (!prog_args.disable_plots) { + if (!pthread_kill(plot_thread, 0)) { + pthread_kill(plot_thread, SIGHUP); + pthread_join(plot_thread, NULL); + } + } +#endif + srslte_ue_dl_free(&ue_dl); + srslte_ue_sync_free(&ue_sync); + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (data[i]) { + free(data[i]); + } + } + for (int i = 0; i < prog_args.rf_nof_rx_ant; i++) { + if (sf_buffer[i]) { + free(sf_buffer[i]); + } + } + +#ifndef DISABLE_RF + if (!prog_args.input_file_name) { + srslte_ue_mib_free(&ue_mib); + srslte_rf_close(&rf); + } +#endif + + printf("\nBye\n"); + exit(0); +} + + + + + + + + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + + +plot_real_t p_sync, pce; +plot_scatter_t pscatequal, pscatequal_pdcch, pscatequal_pmch; + +float tmp_plot[110*15*2048]; +float tmp_plot2[110*15*2048]; +float tmp_plot3[110*15*2048]; + +void *plot_thread_run(void *arg) { + int i; + uint32_t nof_re = SRSLTE_SF_LEN_RE(ue_dl.cell.nof_prb, ue_dl.cell.cp); + + + sdrgui_init(); + + plot_scatter_init(&pscatequal); + plot_scatter_setTitle(&pscatequal, "PDSCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal, -4, 4); + plot_scatter_setYAxisScale(&pscatequal, -4, 4); + + plot_scatter_addToWindowGrid(&pscatequal, (char*)"pdsch_ue", 0, 0); + + + if(enable_mbsfn_plot) { + plot_scatter_init(&pscatequal_pmch); + plot_scatter_setTitle(&pscatequal_pmch, "PMCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal_pmch, -4, 4); + plot_scatter_setYAxisScale(&pscatequal_pmch, -4, 4); + plot_scatter_addToWindowGrid(&pscatequal_pmch, (char*)"pdsch_ue", 0, 1); + } + + if (!prog_args.disable_plots_except_constellation) { + plot_real_init(&pce); + plot_real_setTitle(&pce, "Channel Response - Magnitude"); + plot_real_setLabels(&pce, "Index", "dB"); + plot_real_setYAxisScale(&pce, -40, 40); + + plot_real_init(&p_sync); + plot_real_setTitle(&p_sync, "PSS Cross-Corr abs value"); + plot_real_setYAxisScale(&p_sync, 0, 1); + + plot_scatter_init(&pscatequal_pdcch); + plot_scatter_setTitle(&pscatequal_pdcch, "PDCCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pscatequal_pdcch, -4, 4); + plot_scatter_setYAxisScale(&pscatequal_pdcch, -4, 4); + + plot_real_addToWindowGrid(&pce, (char*)"pdsch_ue", 0, (enable_mbsfn_plot)?2:1); + plot_real_addToWindowGrid(&pscatequal_pdcch, (char*)"pdsch_ue", 1, 0); + plot_real_addToWindowGrid(&p_sync, (char*)"pdsch_ue", 1, 1); + } + + while(1) { + sem_wait(&plot_sem); + + uint32_t nof_symbols = ue_dl.pdsch_cfg.nbits[0].nof_re; + uint32_t nof_symbols_pmch = ue_dl.pmch_cfg.nbits[0].nof_re; + if (!prog_args.disable_plots_except_constellation) { + for (i = 0; i < nof_re; i++) { + tmp_plot[i] = 20 * log10f(cabsf(ue_dl.sf_symbols[i])); + if (isinf(tmp_plot[i])) { + tmp_plot[i] = -80; + } + } + int sz = srslte_symbol_sz(ue_dl.cell.nof_prb); + bzero(tmp_plot2, sizeof(float)*sz); + int g = (sz - 12*ue_dl.cell.nof_prb)/2; + for (i = 0; i < 12*ue_dl.cell.nof_prb; i++) { + tmp_plot2[g+i] = 20 * log10(cabs(ue_dl.ce[0][i])); + if (isinf(tmp_plot2[g+i])) { + tmp_plot2[g+i] = -80; + } + } + plot_real_setNewData(&pce, tmp_plot2, sz); + + if (!prog_args.input_file_name) { + if (plot_track) { + srslte_pss_t *pss_obj = srslte_sync_get_cur_pss_obj(&ue_sync.strack); + int max = srslte_vec_max_fi(pss_obj->conv_output_avg, pss_obj->frame_size+pss_obj->fft_size-1); + srslte_vec_sc_prod_fff(pss_obj->conv_output_avg, + 1/pss_obj->conv_output_avg[max], + tmp_plot2, + pss_obj->frame_size+pss_obj->fft_size-1); + plot_real_setNewData(&p_sync, tmp_plot2, pss_obj->frame_size); + } else { + int max = srslte_vec_max_fi(ue_sync.sfind.pss.conv_output_avg, ue_sync.sfind.pss.frame_size+ue_sync.sfind.pss.fft_size-1); + srslte_vec_sc_prod_fff(ue_sync.sfind.pss.conv_output_avg, + 1/ue_sync.sfind.pss.conv_output_avg[max], + tmp_plot2, + ue_sync.sfind.pss.frame_size+ue_sync.sfind.pss.fft_size-1); + plot_real_setNewData(&p_sync, tmp_plot2, ue_sync.sfind.pss.frame_size); + } + + } + + plot_scatter_setNewData(&pscatequal_pdcch, ue_dl.pdcch.d, 36*ue_dl.pdcch.nof_cce[0]); + } + + plot_scatter_setNewData(&pscatequal, ue_dl.pdsch.d[0], nof_symbols); + + if(enable_mbsfn_plot) { + plot_scatter_setNewData(&pscatequal_pmch, ue_dl.pmch.d, nof_symbols_pmch); + } + + if (plot_sf_idx == 1) { + if (prog_args.net_port_signal > 0) { + srslte_netsink_write(&net_sink_signal, &sf_buffer[srslte_ue_sync_sf_len(&ue_sync)/7], + srslte_ue_sync_sf_len(&ue_sync)); + } + } + + } + + return NULL; +} + +void init_plots() { + + if (sem_init(&plot_sem, 0, 0)) { + perror("sem_init"); + exit(-1); + } + + pthread_attr_t attr; + struct sched_param param; + param.sched_priority = 0; + pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + pthread_attr_setschedparam(&attr, ¶m); + if (pthread_create(&plot_thread, NULL, plot_thread_run, NULL)) { + perror("pthread_create"); + exit(-1); + } +} + +#endif diff --git a/lib/examples/synch_file.c b/lib/examples/synch_file.c new file mode 100644 index 0000000..c73e8d9 --- /dev/null +++ b/lib/examples/synch_file.c @@ -0,0 +1,269 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name; +char *output_file_name="abs_corr.txt"; +int nof_frames=100, frame_length=9600, symbol_sz=128; +float corr_peak_threshold=25.0; +int out_N_id_2 = 0, force_N_id_2=-1; + +#define CFO_AUTO -9999.0 +float force_cfo = CFO_AUTO; + +void usage(char *prog) { + printf("Usage: %s [olntsNfcv] -i input_file\n", prog); + printf("\t-o output_file [Default %s]\n", output_file_name); + printf("\t-l frame_length [Default %d]\n", frame_length); + printf("\t-n number of frames [Default %d]\n", nof_frames); + printf("\t-t correlation threshold [Default %g]\n", corr_peak_threshold); + printf("\t-s symbol_sz [Default %d]\n", symbol_sz); + printf("\t-N out_N_id_2 [Default %d]\n", out_N_id_2); + printf("\t-f force_N_id_2 [Default %d]\n", force_N_id_2); + printf("\t-c force_cfo [Default disabled]\n"); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "ionltsNfcv")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'o': + output_file_name = argv[optind]; + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'l': + frame_length = atoi(argv[optind]); + break; + case 't': + corr_peak_threshold = atof(argv[optind]); + break; + case 's': + symbol_sz = atof(argv[optind]); + break; + case 'N': + out_N_id_2 = atoi(argv[optind]); + break; + case 'f': + force_N_id_2 = atoi(argv[optind]); + break; + case 'c': + force_cfo = atof(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + srslte_filesource_t fsrc; + srslte_filesink_t fsink; + srslte_pss_t pss[3]; // One for each N_id_2 + srslte_sss_t sss[3]; // One for each N_id_2 + srslte_cfo_t cfocorr; + int peak_pos[3]; + float *cfo; + float peak_value[3]; + int frame_cnt; + cf_t *input; + uint32_t m0, m1; + float m0_value, m1_value; + uint32_t N_id_2; + int sss_idx; + struct timeval tdata[3]; + int *exec_time; + + if (argc < 3) { + usage(argv[0]); + exit(-1); + } + + parse_args(argc,argv); + + gettimeofday(&tdata[1], NULL); + printf("Initializing...");fflush(stdout); + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + if (srslte_filesink_init(&fsink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", output_file_name); + exit(-1); + } + + input = malloc(frame_length*sizeof(cf_t)); + if (!input) { + perror("malloc"); + exit(-1); + } + cfo = malloc(nof_frames*sizeof(float)); + if (!cfo) { + perror("malloc"); + exit(-1); + } + exec_time = malloc(nof_frames*sizeof(int)); + if (!exec_time) { + perror("malloc"); + exit(-1); + } + + if (srslte_cfo_init(&cfocorr, frame_length)) { + fprintf(stderr, "Error initiating CFO\n"); + return -1; + } + + /* We have 2 options here: + * a) We create 3 pss objects, each initialized with a different N_id_2 + * b) We create 1 pss object which scans for each N_id_2 one after another. + * a) requries more memory but has less latency and is paralellizable. + */ + for (N_id_2=0;N_id_2<3;N_id_2++) { + if (srslte_pss_init(&pss[N_id_2], frame_length)) { + fprintf(stderr, "Error initializing PSS object\n"); + exit(-1); + } + if (srslte_pss_set_N_id_2(&pss[N_id_2], N_id_2)) { + fprintf(stderr, "Error initializing N_id_2\n"); + exit(-1); + } + if (srslte_sss_init(&sss[N_id_2], 128)) { + fprintf(stderr, "Error initializing SSS object\n"); + exit(-1); + } + if (srslte_sss_set_N_id_2(&sss[N_id_2], N_id_2)) { + fprintf(stderr, "Error initializing N_id_2\n"); + exit(-1); + } + } + gettimeofday(&tdata[2], NULL); + get_time_interval(tdata); + printf("done in %ld s %ld ms\n", tdata[0].tv_sec, tdata[0].tv_usec/1000); + + printf("\n\tFr.Cnt\tN_id_2\tN_id_1\tSubf\tPSS Peak/Avg\tIdx\tm0\tm1\tCFO\n"); + printf("\t===============================================================================\n"); + + /* read all file or nof_frames */ + frame_cnt = 0; + while (frame_length == srslte_filesource_read(&fsrc, input, frame_length) + && frame_cnt < nof_frames) { + + gettimeofday(&tdata[1], NULL); + if (force_cfo != CFO_AUTO) { + srslte_cfo_correct(&cfocorr, input, input, force_cfo/128); + } + + if (force_N_id_2 != -1) { + N_id_2 = force_N_id_2; + peak_pos[N_id_2] = srslte_pss_find_pss(&pss[N_id_2], input, &peak_value[N_id_2]); + } else { + for (N_id_2=0;N_id_2<3;N_id_2++) { + peak_pos[N_id_2] = srslte_pss_find_pss(&pss[N_id_2], input, &peak_value[N_id_2]); + } + float max_value=-99999; + N_id_2=-1; + int i; + for (i=0;i<3;i++) { + if (peak_value[i] > max_value) { + max_value = peak_value[i]; + N_id_2 = i; + } + } + } + + /* If peak detected */ + if (peak_value[N_id_2] > corr_peak_threshold) { + + sss_idx = peak_pos[N_id_2]-2*(symbol_sz+SRSLTE_CP_LEN(symbol_sz,SRSLTE_CP_NORM_LEN)); + if (sss_idx >= 0) { + srslte_sss_m0m1_diff(&sss[N_id_2], &input[sss_idx], + &m0, &m0_value, &m1, &m1_value); + + cfo[frame_cnt] = srslte_pss_cfo_compute(&pss[N_id_2], &input[peak_pos[N_id_2]-128]); + printf("\t%d\t%d\t%d\t%d\t%.3f\t\t%3d\t%d\t%d\t%.3f\n", + frame_cnt,N_id_2, srslte_sss_N_id_1(&sss[N_id_2], m0, m1), + srslte_sss_subframe(m0, m1), peak_value[N_id_2], + peak_pos[N_id_2], m0, m1, + cfo[frame_cnt]); + } + } + gettimeofday(&tdata[2], NULL); + get_time_interval(tdata); + exec_time[frame_cnt] = tdata[0].tv_usec; + frame_cnt++; + } + + int i; + float avg_time=0; + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/io/filesink.h" + +static bool keep_running = true; +char *output_file_name; +char *rf_args=""; +float rf_gain=40.0, rf_freq=-1.0, rf_rate=0.96e6; +int nof_samples = -1; +int nof_rx_antennas = 1; + +void int_handler(int dummy) { + keep_running = false; +} + +void usage(char *prog) { + printf("Usage: %s [agrnv] -f rx_frequency_hz -o output_file\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-r RF Rate [Default %.6f Hz]\n", rf_rate); + printf("\t-n nof_samples [Default %d]\n", nof_samples); + printf("\t-A nof_rx_antennas [Default %d]\n", nof_rx_antennas); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agrnvfoA")) != -1) { + switch (opt) { + case 'o': + output_file_name = argv[optind]; + break; + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'r': + rf_rate = atof(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 'n': + nof_samples = atoi(argv[optind]); + break; + case 'A': + nof_rx_antennas = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (rf_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + cf_t *buffer[SRSLTE_MAX_PORTS]; + int sample_count, n; + srslte_rf_t rf; + srslte_filesink_t sink; + uint32_t buflen; + + signal(SIGINT, int_handler); + + parse_args(argc, argv); + + buflen = 4800; + sample_count = 0; + + for (int i = 0; i < nof_rx_antennas; i++) { + buffer[i] = malloc(sizeof(cf_t) * buflen); + if (!buffer[i]) { + perror("malloc"); + exit(-1); + } + } + + srslte_filesink_init(&sink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN); + + printf("Opening RF device...\n"); + if (srslte_rf_open_multi(&rf, rf_args, nof_rx_antennas)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + + printf("Set RX freq: %.2f MHz\n", srslte_rf_set_rx_freq(&rf, rf_freq) / 1000000); + printf("Set RX gain: %.2f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + float srate = srslte_rf_set_rx_srate(&rf, rf_rate); + if (srate != rf_rate) { + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*rf_rate); + } else { + srslte_rf_set_master_clock_rate(&rf, rf_rate); + } + srate = srslte_rf_set_rx_srate(&rf, rf_rate); + if (srate != rf_rate) { + fprintf(stderr, "Errror setting samplign frequency %.2f MHz\n", rf_rate*1e-6); + exit(-1); + } + } + + printf("Correctly RX rate: %.2f MHz\n", srate*1e-6); + srslte_rf_rx_wait_lo_locked(&rf); + srslte_rf_start_rx_stream(&rf, false); + + + while((sample_count < nof_samples || nof_samples == -1) + && keep_running){ + n = srslte_rf_recv_with_time_multi(&rf, (void**) buffer, buflen, true, NULL, NULL); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + srslte_filesink_write_multi(&sink, (void**) buffer, buflen, nof_rx_antennas); + sample_count += buflen; + } + + for (int i = 0; i < nof_rx_antennas; i++) { + if (buffer[i]) { + free(buffer[i]); + } + } + + srslte_filesink_free(&sink); + srslte_rf_close(&rf); + + printf("Ok - wrote %d samples\n", sample_count); + exit(0); +} diff --git a/lib/examples/usrp_capture_sync.c b/lib/examples/usrp_capture_sync.c new file mode 100644 index 0000000..424521e --- /dev/null +++ b/lib/examples/usrp_capture_sync.c @@ -0,0 +1,218 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" + +static bool keep_running = true; +char *output_file_name = NULL; +char *rf_args=""; +float rf_gain=60.0, rf_freq=-1.0; +int nof_prb = 6; +int nof_subframes = -1; +int N_id_2 = -1; +uint32_t nof_rx_antennas = 1; + +void int_handler(int dummy) { + keep_running = false; +} + +void usage(char *prog) { + printf("Usage: %s [agrnv] -l N_id_2 -f rx_frequency_hz -o output_file\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-p nof_prb [Default %d]\n", nof_prb); + printf("\t-n nof_subframes [Default %d]\n", nof_subframes); + printf("\t-A nof_rx_antennas [Default %d]\n", nof_rx_antennas); + printf("\t-v verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "agpnvfolA")) != -1) { + switch (opt) { + case 'o': + output_file_name = argv[optind]; + break; + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 'n': + nof_subframes = atoi(argv[optind]); + break; + case 'l': + N_id_2 = atoi(argv[optind]); + break; + case 'A': + nof_rx_antennas = (uint32_t) atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (&rf_freq < 0 || N_id_2 == -1 || output_file_name == NULL) { + usage(argv[0]); + exit(-1); + } +} + +int srslte_rf_recv_wrapper(void *h, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *t) { + DEBUG(" ---- Receive %d samples ---- \n", nsamples); + return srslte_rf_recv_with_time_multi(h, (void**) data, nsamples, true, NULL, NULL); +} + +int main(int argc, char **argv) { + cf_t *buffer[SRSLTE_MAX_PORTS] = {NULL, NULL}; + int n; + srslte_rf_t rf; + srslte_filesink_t sink; + srslte_ue_sync_t ue_sync; + srslte_cell_t cell; + + signal(SIGINT, int_handler); + + parse_args(argc, argv); + + srslte_filesink_init(&sink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN); + + printf("Opening RF device...\n"); + if (srslte_rf_open_multi(&rf, rf_args, nof_rx_antennas)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + + for (int i = 0; i< SRSLTE_MAX_PORTS; i++) { + buffer[i] = srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(100)); + } + + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGINT); + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + + printf("Set RX freq: %.6f MHz\n", srslte_rf_set_rx_freq(&rf, rf_freq) / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + int srate = srslte_sampling_freq_hz(nof_prb); + if (srate != -1) { + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); + float srate_rf = srslte_rf_set_rx_srate(&rf, (double) srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + } else { + fprintf(stderr, "Invalid number of PRB %d\n", nof_prb); + exit(-1); + } + srslte_rf_rx_wait_lo_locked(&rf); + srslte_rf_start_rx_stream(&rf, false); + + cell.cp = SRSLTE_CP_NORM; + cell.id = N_id_2; + cell.nof_prb = nof_prb; + cell.nof_ports = 1; + + if (srslte_ue_sync_init_multi(&ue_sync, cell.nof_prb, cell.id==1000, srslte_rf_recv_wrapper, nof_rx_antennas, (void*) &rf)) { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + if (srslte_ue_sync_set_cell(&ue_sync, cell)) { + fprintf(stderr, "Error initiating ue_sync\n"); + exit(-1); + } + + uint32_t subframe_count = 0; + bool start_capture = false; + bool stop_capture = false; + while((subframe_count < nof_subframes || nof_subframes == -1) + && !stop_capture) + { + n = srslte_ue_sync_zerocopy_multi(&ue_sync, buffer); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + if (n == 1) { + if (!start_capture) { + if (srslte_ue_sync_get_sfidx(&ue_sync) == 9) { + start_capture = true; + } + } else { + printf("Writing to file %6d subframes...\r", subframe_count); + srslte_filesink_write_multi(&sink, (void**) buffer, SRSLTE_SF_LEN_PRB(nof_prb),nof_rx_antennas); + subframe_count++; + } + } + if (!keep_running) { + if (!start_capture || (start_capture && srslte_ue_sync_get_sfidx(&ue_sync) == 9)) { + stop_capture = true; + } + } + } + + srslte_filesink_free(&sink); + srslte_rf_close(&rf); + srslte_ue_sync_free(&ue_sync); + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (buffer[i]) { + free(buffer[i]); + } + } + + printf("\nOk - wrote %d subframes\n", subframe_count); + exit(0); +} diff --git a/lib/examples/usrp_txrx.c b/lib/examples/usrp_txrx.c new file mode 100644 index 0000000..42f610d --- /dev/null +++ b/lib/examples/usrp_txrx.c @@ -0,0 +1,201 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/rf/rf.h" +#include "srslte/srslte.h" + +uint32_t nof_prb = 25; +uint32_t nof_frames = 20; + +int time_adv_samples = 0; +float tone_offset_hz = 1e6; +float rf_rx_gain=40, srslte_rf_tx_gain=40, rf_freq=2.4e9; +char *rf_args=""; +char *output_filename = NULL; +char *input_filename = NULL; + +void usage(char *prog) { + printf("Usage: %s -o [rx_signal_file]\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-f RF TX/RX frequency [Default %.2f MHz]\n", rf_freq/1e6); + printf("\t-g RF RX gain [Default %.1f dB]\n", rf_rx_gain); + printf("\t-G RF TX gain [Default %.1f dB]\n", srslte_rf_tx_gain); + printf("\t-t Single tone offset (Hz) [Default %f]\n", tone_offset_hz); + printf("\t-T Time advance samples [Default %d]\n", time_adv_samples); + printf("\t-i File name to read signal from [Default single tone]\n"); + printf("\t-p Number of UL RB [Default %d]\n", nof_prb); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "ioafgGptT")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'o': + output_filename = argv[optind]; + break; + case 'i': + input_filename = argv[optind]; + break; + case 't': + tone_offset_hz = atof(argv[optind]); + break; + case 'T': + time_adv_samples = atoi(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 'g': + rf_rx_gain = atof(argv[optind]); + break; + case 'G': + srslte_rf_tx_gain = atof(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + if (!srslte_nofprb_isvalid(nof_prb)) { + fprintf(stderr, "Invalid number of UL RB %d\n", nof_prb); + exit(-1); + } + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!output_filename) { + usage(argv[0]); + exit(-1); + } + if (time_adv_samples < 0) { + printf("Time advance must be positive\n"); + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + uint32_t flen = srslte_sampling_freq_hz(nof_prb)/1000; + + cf_t *rx_buffer = malloc(sizeof(cf_t)*flen*nof_frames); + if (!rx_buffer) { + perror("malloc"); + exit(-1); + } + + cf_t *tx_buffer = malloc(sizeof(cf_t)*(flen+time_adv_samples)); + if (!tx_buffer) { + perror("malloc"); + exit(-1); + } + bzero(tx_buffer, sizeof(cf_t)*(flen+time_adv_samples)); + + cf_t *zeros = calloc(sizeof(cf_t),flen); + if (!zeros) { + perror("calloc"); + exit(-1); + } + + float time_adv_sec = (float) time_adv_samples/srslte_sampling_freq_hz(nof_prb); + + // Send through RF + srslte_rf_t rf; + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + + int srate = srslte_sampling_freq_hz(nof_prb); + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + srslte_rf_set_rx_srate(&rf, (double) srate); + srslte_rf_set_tx_srate(&rf, (double) srate); + + + printf("Subframe len: %d samples\n", flen); + printf("Time advance: %f us\n",time_adv_sec*1e6); + printf("Set TX/RX rate: %.2f MHz\n", (float) srate / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_rx_gain)); + printf("Set TX gain: %.1f dB\n", srslte_rf_set_tx_gain(&rf, srslte_rf_tx_gain)); + printf("Set TX/RX freq: %.2f MHz\n", srslte_rf_set_rx_freq(&rf, rf_freq) / 1000000); + srslte_rf_set_tx_freq(&rf, rf_freq); + + sleep(1); + + if (input_filename) { + srslte_vec_load_file(input_filename, &tx_buffer[time_adv_samples], flen*sizeof(cf_t)); + } else { + for (int i=0;i +#include "srslte/asn1/gtpc_msg.h" + + +namespace srslte{ + +/*GTP-C Version*/ +const uint8_t GTPC_V2 = 2; + +/**************************************************************************** + * GTP-C v2 Header + * Ref: 3GPP TS 29.274 v10.14.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version | P | T | S | S | S | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * m | If T=1, TEID (1st Octet) | + * m+1 | If T=1, TEID (2nd Octet) | + * m+2 | If T=1, TEID (3st Octet) | + * m+3 | If T=1, TEID (4st Octet) | + * n | Sequence | + * n+1 | Sequence | + * n+2 | Sequence | + * n+3 | Spare | + ***************************************************************************/ +typedef struct gtpc_header +{ + uint8_t version; + bool piggyback; + bool teid_present; + uint8_t type; + uint64_t teid; + uint64_t sequence; +}gtpc_header_t; + +/**************************************************************************** + * GTP-C v2 Payload + * Ref: 3GPP TS 29.274 v10.14.0 Section 5 + * + * Union that hold the different structures for the possible message types. + ***************************************************************************/ +typedef union gtpc_msg_choice +{ + struct gtpc_create_session_request create_session_request; + struct gtpc_create_session_response create_session_response; + struct gtpc_modify_bearer_request modify_bearer_request; + struct gtpc_modify_bearer_response modify_bearer_response; + struct gtpc_release_access_bearers_request release_access_bearers_request; + struct gtpc_release_access_bearers_response release_access_bearers_response; + struct gtpc_delete_session_request delete_session_request; + struct gtpc_delete_session_response delete_session_response; +}gtpc_msg_choice_t; + +/**************************************************************************** + * GTP-C v2 Message + * Ref: 3GPP TS 29.274 v10.14.0 + * + * This is the main structure to represent a GTP-C message. It is composed + * of one GTP-C header and one union of structures, which can hold + * all the possible GTP-C messages + ***************************************************************************/ +typedef struct gtpc_pdu +{ + struct gtpc_header header; + union gtpc_msg_choice choice; +}gtpc_pdu_t; +}//namespace +#endif // SRSLTE_GTPC_H diff --git a/lib/include/srslte/asn1/gtpc_ies.h b/lib/include/srslte/asn1/gtpc_ies.h new file mode 100644 index 0000000..2150215 --- /dev/null +++ b/lib/include/srslte/asn1/gtpc_ies.h @@ -0,0 +1,451 @@ +/* \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_GTPC_IES_H +#define SRSLTE_GTPC_IES_H + +#include "srslte/phy/io/netsource.h" + +namespace srslte +{ + +/**************************************************************** + * + * GTP-C IE Types + * Ref: TS 29.274 v10.14.0 Table 8.1-1 + * + ****************************************************************/ +enum gtpc_ie_type +{ +//const uint8_t GTPC_IE_TYPE_RESERVED = 0; + GTPC_IE_TYPE_IMSI = 1, + GTPC_IE_TYPE_CAUSE = 2, + GTPC_IE_TYPE_RECOVERY = 3, +//4 to 50 RESERVED_FOR_S101_INTERFACE + GTPC_IE_TYPE_STN_SR = 51, +//52 to 70 RESERVED_FOR_SV_INTERFACE + GTPC_IE_TYPE_APN = 71, + GTPC_IE_TYPE_AMBR = 72, + GTPC_IE_TYPE_EBI = 73, + GTPC_IE_TYPE_IP_ADDRESS = 74, + GTPC_IE_TYPE_MEI = 75, + GTPC_IE_TYPE_MSISDN = 76, + GTPC_IE_TYPE_INDICATION = 77, + GTPC_IE_TYPE_PCO = 78, + GTPC_IE_TYPE_PDN_ADDRESS_ALLOCATION = 79, + GTPC_IE_TYPE_BEARER_QOS = 80, + GTPC_IE_TYPE_FLOW_QOS = 81, + GTPC_IE_TYPE_RAT_TYPE = 82, + GTPC_IE_TYPE_SERVING_NETWORK = 83, + GTPC_IE_TYPE_BEARER_TFT = 84, + GTPC_IE_TYPE_TAD = 85, + GTPC_IE_TYPE_ULI = 86, + GTPC_IE_TYPE_F_TEID = 87, + GTPC_IE_TYPE_TMSI = 88, + GTPC_IE_TYPE_GLOBAL_CN_ID = 89, + GTPC_IE_TYPE_S103_PDN_DATA_FORWARDING_INFO = 90, + GTPC_IE_TYPE_S1_U_DATA_FORWARDING_INFO = 91, + GTPC_IE_TYPE_DELAY_VALUE = 92, + GTPC_IE_TYPE_BEARER_CONTEXT = 93, + GTPC_IE_TYPE_CHARGING_ID = 94, + GTPC_IE_TYPE_CHARGING_CHARACTERISTICS = 95, + GTPC_IE_TYPE_TRACE_INFORMATION = 96, + GTPC_IE_TYPE_BEARER_FLAGS = 97, +//98 Reserved + GTPC_IE_TYPE_PDN_TYPE = 99, + GTPC_IE_TYPE_PROCEDURE_TRANSACTION_ID = 100, + GTPC_IE_TYPE_DRX_PARAMETER = 101, +//102 Reserved + GTPC_IE_TYPE_MM_CONTEXT_GSM_KEY_AND_TRIPLETS = 103, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_USED_CIPHER_AND_QUINTUPLETS = 104, + GTPC_IE_TYPE_MM_CONTEXT_GSM_KEY_USED_CIPHER_AND_QUINTUPLETS = 105, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_AND_QUINTUPLETS = 106, + GTPC_IE_TYPE_MM_CONTEXT_EPS_SECURITY_CONTEXT_QUADRUPLETS_AND_QUINTUPLETS = 107, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_QUADRUPLETS_AND_QUINTUPLETS = 108, + GTPC_IE_TYPE_PDN_CONNECTION = 109, + GTPC_IE_TYPE_PDU_NUMBERS = 110, + GTPC_IE_TYPE_P_TMSI = 111, + GTPC_IE_TYPE_P_TMSI_SIGNATURE = 112, + GTPC_IE_TYPE_HOP_COUNTER = 113, + GTPC_IE_TYPE_UE_TIME_ZONE = 114, + GTPC_IE_TYPE_TRACE_REFERENCE = 115, + GTPC_IE_TYPE_COMPLETE_REQUEST_MESSAGE = 116, + GTPC_IE_TYPE_GUTI = 117, + GTPC_IE_TYPE_F_CONTAINER = 118, + GTPC_IE_TYPE_F_CAUSE = 119, + GTPC_IE_TYPE_SELECTED_PLMN_ID = 120, + GTPC_IE_TYPE_TARGET_IDENTIFICATION = 121, +//122 Reserved + GTPC_IE_TYPE_PACKET_FLOW_ID = 123, + GTPC_IE_TYPE_RAB_CONTEXT = 124, + GTPC_IE_TYPE_SOURCE_RNC_PDCP_CONTEXT_INFO = 125, + GTPC_IE_TYPE_UDP_SOURCE_PORT_NUMBER = 126, + GTPC_IE_TYPE_APN_RESTRICTION = 127, + GTPC_IE_TYPE_SELECTION_MODE = 128, + GTPC_IE_TYPE_SOURCE_IDENTIFICATION = 129, +//130 RESERVED + GTPC_IE_TYPE_CHANGE_REPORTING_ACTION = 131, + GTPC_IE_TYPE_FQ_CSID = 132, + GTPC_IE_TYPE_CHANNEL_NEEDED = 133, + GTPC_IE_TYPE_EMLPP_PRIORITY = 134, + GTPC_IE_TYPE_NODE_TYPE = 135, + GTPC_IE_TYPE_FQDN = 136, + GTPC_IE_TYPE_TI = 137, + GTPC_IE_TYPE_MBMS_SESSION_DURATION = 138, + GTPC_IE_TYPE_MBMS_SERVICE_AREA = 139, + GTPC_IE_TYPE_MBMS_SESSION_IDENTIFIER = 140, + GTPC_IE_TYPE_MBMS_FLOW_IDENTIFIER = 141, + GTPC_IE_TYPE_MBMS_IP_MULTICAST_DISTRIBUTION = 142, + GTPC_IE_TYPE_MBMS_DISTRIBUTION_ACKNOWLEDGE = 143, + GTPC_IE_TYPE_RFSP_INDEX = 144, + GTPC_IE_TYPE_UCI = 145, + GTPC_IE_TYPE_CSG_INFORMATION_REPORTING_ACTION = 146, + GTPC_IE_TYPE_CSG_ID = 147, + GTPC_IE_TYPE_CMI = 148, + GTPC_IE_TYPE_SERVICE_INDICATOR = 149, + GTPC_IE_TYPE_DETACH_TYPE = 150, + GTPC_IE_TYPE_LDN = 151, + GTPC_IE_TYPE_NODE_FEATURES = 152, + GTPC_IE_TYPE_MBMS_TIME_TO_DATA_TRANSFER = 153, + GTPC_IE_TYPE_THROTTLING =154, + GTPC_IE_TYPE_ARP = 155, + GTPC_IE_TYPE_EPC_TIMER = 156, + GTPC_IE_TYPE_SIGNALLING_PRIORITY_INDICATION = 157, + GTPC_IE_TYPE_TMGI = 158, + GTPC_IE_TYPE_ADDITIONAL_MM_CONTEXT_FOR_SRVCC = 159, + GTPC_IE_TYPE_ADDITIONAL_FLAGS_FOR_SRVCC = 160, +//161 RESERVED + GTPC_IE_TYPE_MDT_CONFIGURATION = 162, + GTPC_IE_TYPE_APCO = 163, +//164 RESERVED + GTPC_IE_TYPE_CHANGE_TO_REPORT_FLAGS = 165, +//168 TO 254 SPARE. FOR FUTURE USE. + GTPC_IE_TYPE_PRIVATE_EXTENSION = 255 +}; + +/**************************************************************** + * + * GTP-C IMSI IE + * Ref: TS 29.274 v10.14.0 Figure 8.3-1 + * + ****************************************************************/ +/* + * The IMSI should be kept as an uint64_t. + * The responsibility to convert from uint64_t to BCD coded is on + * the pack_imsi_ie function + */ + +/**************************************************************************** + * + * GTP-C Cause IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.4-1 and Table 8.4-1 + * + ***************************************************************************/ +enum gtpc_cause_value +{ + //Reserved + GTPC_CAUSE_VALUE_LOCAL_DETACH = 2, + GTPC_CAUSE_VALUE_COMPLETE_DETACH = 3, + GTPC_CAUSE_VALUE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP = 4, + GTPC_CAUSE_VALUE_ISR_DEACTIVATION = 5, + GTPC_CAUSE_VALUE_ERROR_INDICATION_RECEIVED_FROM_RNC_ENODEB_S4_SGSN = 6, + GTPC_CAUSE_VALUE_IMSI_DETACH_ONLY = 7, + GTPC_CAUSE_VALUE_REACTIVATION_REQUESTED = 8, + GTPC_CAUSE_VALUE_PDN_RECONNECTION_TO_THIS_APN_DISALLOWED = 9, + GTPC_CAUSE_VALUE_ACCESS_CHANGED_FROM_NON_3GPP_TO_3GPP = 10, + GTPC_CAUSE_VALUE_PDN_CONNECTION_INACTIVITY_TIMER_EXPIRES = 11, + //Spare. This value range shall be used by Cause values in an initial/request message. + GTPC_CAUSE_VALUE_REQUEST_ACCEPTED = 16, + GTPC_CAUSE_VALUE_REQUEST_ACCEPTED_PARTIALLY = 17, + GTPC_CAUSE_VALUE_NEW_PDN_TYPE_DUE_TO_NETWORK_PREFERENCE = 18, + GTPC_CAUSE_VALUE_NEW_PDN_TYPE_DUE_TO_SINGLE_ADDRESS_BEARER_ONLY = 19, + //20-63 Spare. + GTPC_CAUSE_VALUE_CONTEXT_NOT_FOUND = 64, + GTPC_CAUSE_VALUE_INVALID_MESSAGE_FORMAT = 65, + GTPC_CAUSE_VALUE_VERSION_NOT_SUPPORTED_BY_NEXT_PEER = 66, + GTPC_CAUSE_VALUE_INVALID_LENGTH = 67, + GTPC_CAUSE_VALUE_SERVICE_NOT_SUPPORTED = 68, + GTPC_CAUSE_VALUE_MANDATORY_IE_INCORRECT = 69, + GTPC_CAUSE_VALUE_MANDATORY_IE_MISSING = 70, + //71 Shall not be used. + GTPC_CAUSE_VALUE_SYSTEM_FAILURE = 72, + GTPC_CAUSE_VALUE_NO_RESOURCES_AVAILABLE = 73, + GTPC_CAUSE_VALUE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 74, + GTPC_CAUSE_VALUE_SYNTACTIC_ERROR_IN_THE_TFT_OPERATION = 75, + GTPC_CAUSE_VALUE_SEMANTIC_ERRORS_IN_PACKET_FILTER = 76, + GTPC_CAUSE_VALUE_SYNTACTIC_ERRORS_IN_PACKET_FILTER = 77, + GTPC_CAUSE_VALUE_MISSING_OR_UNKNOWN_APN = 78, + //79 Shall not be used. + GTPC_CAUSE_VALUE_GRE_KEY_NOT_FOUND = 80, + GTPC_CAUSE_VALUE_RELOCATION_FAILURE = 81, + GTPC_CAUSE_VALUE_DENIED_IN_RAT = 82, + GTPC_CAUSE_VALUE_PREFERRED_PDN_TYPE_NOT_SUPPORTED = 83, + GTPC_CAUSE_VALUE_ALL_DYNAMIC_ADDRESSES_ARE_OCCUPIED = 84, + GTPC_CAUSE_VALUE_UE_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED = 85, + GTPC_CAUSE_VALUE_PROTOCOL_TYPE_NOT_SUPPORTED = 86, + GTPC_CAUSE_VALUE_UE_NOT_RESPONDING = 87, + GTPC_CAUSE_VALUE_UE_REFUSES = 88, + GTPC_CAUSE_VALUE_SERVICE_DENIED = 89, + GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE = 90, + GTPC_CAUSE_VALUE_NO_MEMORY_AVAILABLE = 91, + GTPC_CAUSE_VALUE_USER_AUTHENTICATION_FAILED = 92, + GTPC_CAUSE_VALUE_APN_ACCESS_DENIED_NO_SUBSCRIPTION = 93, + GTPC_CAUSE_VALUE_REQUEST_REJECTED = 94, + GTPC_CAUSE_VALUE_P_TMSI_SIGNATURE_MISMATCH = 95, + GTPC_CAUSE_VALUE_IMSI_IMEI_NOT_KNOWN = 96, + GTPC_CAUSE_VALUE_SEMANTIC_ERROR_IN_THE_TAD_OPERATION = 97, + GTPC_CAUSE_VALUE_SYNTACTIC_ERROR_IN_THE_TAD_OPERATION = 98, + //99 Shall not be used. + GTPC_CAUSE_VALUE_REMOTE_PEER_NOT_RESPONDING = 100, + GTPC_CAUSE_VALUE_COLLISION_WITH_NETWORK_INITIATED_REQUEST = 101, + GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE_DUE_TO_SUSPENSION = 102, + GTPC_CAUSE_VALUE_CONDITIONAL_IE_MISSING = 103, + GTPC_CAUSE_VALUE_APN_RESTRICTION_TYPE_INCOMPATIBLE_WITH_CURRENTLY_ACTIVE_PDN_CONNECTION = 104, + GTPC_CAUSE_VALUE_INVALID_OVERALL_LENGTH_OF_THE_TRIGGERED_RESPONSE_MSG_AND_A_PIGGYBACKED_INITIAL_MSG = 105, + GTPC_CAUSE_VALUE_DATA_FORWARDING_NOT_SUPPORTED = 106, + GTPC_CAUSE_VALUE_INVALID_REPLY_FROM_REMOTE_PEER = 107, + GTPC_CAUSE_VALUE_FALLBACK_TO_GTPV1 = 108, + GTPC_CAUSE_VALUE_INVALID_PEER = 109, + GTPC_CAUSE_VALUE_TEMPORARILY_REJECTED_DUE_TO_HANDOVER_PROCEDURE_IN_PROGRESS = 110, + GTPC_CAUSE_VALUE_MODIFICATIONS_NOT_LIMITED_TO_S1_U_BEARERS = 111, + GTPC_CAUSE_VALUE_REQUEST_REJECTED_FOR_A_PMIPV6_REASON = 112, + GTPC_CAUSE_VALUE_APN_CONGESTION = 113, + GTPC_CAUSE_VALUE_BEARER_HANDLING_NOT_SUPPORTED = 114, + GTPC_CAUSE_VALUE_UE_ALREADY_RE_ATTACHED = 115, + GTPC_CAUSE_VALUE_MULTIPLE_PDN_CONNECTIONS_FOR_A_GIVEN_APN_NOT_ALLOWED = 116 + //117-239 Spare. For future use in a triggered/response message. + //240-255 Spare. For future use in an initial/request message. +}; + +struct gtpc_cause_ie +{ + enum gtpc_cause_value cause_value; + bool pce; + bool bce; + bool cs; + enum gtpc_ie_type offending_ie_type; + uint16_t length_of_offending_ie; + uint8_t offending_ie_instance; +}; + +/**************************************************************************** + * + * GTP-C Recovery IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.5-1 + * + ***************************************************************************/ +/* + * The Recovery (Restart Counter) IE should be kept as an uint8_t. + */ + +/**************************************************************************** + * + * GTP-C Access Point Name IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.6-1 + * + ***************************************************************************/ +/* + * APN IE should be kept as an null terminated string. + * This string will be kept in a char[MAX_APN_LENGTH] buffer. + */ +#define MAX_APN_LENGTH 1024 + +/**************************************************************************** + * + * GTP-C Aggregate Maximum bit-rate IE + * Ref: 3GPP TS 29.274 v10.14.0 Table 8.7-1 + * + ***************************************************************************/ +struct gtpc_ambr_ie +{ + uint32_t apn_ambr_uplink; + uint32_t apn_ambr_downlink; +}; + +/**************************************************************************** + * + * GTP-C EPS Bearer ID address IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.8-1 + * + ***************************************************************************/ +/* + * The EPS Bearer ID (EBI) IE should be kept as an uint8_t. + */ + +/**************************************************************************** + * + * GTP-C IP address IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.9-1 + * + ***************************************************************************/ +/* + * IP addresse IEs should the sockaddr_storage struct, which can hold IPv4 + * and IPv6 addresses. + */ + +//TODO +//TODO IEs between 8.10 and 8.13 missing +//TODO + +/**************************************************************************** + * + * GTP-C PDN Type IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.14-1 + * + ***************************************************************************/ +enum gtpc_pdn_type +{ + GTPC_PDN_TYPE_IPV4 = 1, + GTPC_PDN_TYPE_IPV6 = 2, + GTPC_PDN_TYPE_IPV4V6 = 3 +}; + +struct gtpc_pdn_address_allocation_ie +{ + enum gtpc_pdn_type pdn_type; + bool ipv4_present; + bool ipv6_present; + in_addr_t ipv4; + struct in6_addr ipv6; +}; + +/**************************************************************************** + * + * GTP-C Bearer Quality of Service IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.15-1 + * + ***************************************************************************/ +struct gtpc_bearer_qos_ie +{ + struct { + uint8_t pvi : 1; + uint8_t spare : 1; + uint8_t pl : 4; + uint8_t pci : 1; + uint8_t spare2 : 1; + } arp; + uint8_t qci; + uint8_t mbr_ul; + uint8_t mbr_dl; + uint8_t gbr_ul; + uint8_t gbr_dl; +}; + +//TODO +//TODO IEs between 8.16 and 8.17 missing +//TODO + +/**************************************************************************** + * + * GTP-C RAT Type IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.17-1 + * + ***************************************************************************/ + +enum gtpc_rat_type +{ + UTRAN = 1, + GERAN, + WLAN, + GAN, + HSPA_EVOLUTION, + EUTRAN, + Virtual +}; + +//TODO +//TODO IEs between 8.17 and 8.22 missing +//TODO + +/**************************************************************************** + * + * GTP-C Fully Qualified Tunnel End-point Identifier (F-TEID) IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.22-1 + * + ***************************************************************************/ +enum gtpc_interface_type +{ + S1_U_ENODEB_GTP_U_INTERFACE, + S1_U_SGW_GTP_U_INTERFACE, + S12_RNC_GTP_U_INTERFACE, + S12_SGW_GTP_U_INTERFACE, + S5_S8_SGW_GTP_U_INTERFACE, + S5_S8_PGW_GTP_U_INTERFACE, + S5_S8_SGW_GTP_C_INTERFACE, + S5_S8_PGW_GTP_C_INTERFACE, + S5_S8_SGW_PMIPV6_INTERFACE, //(the 32 bit GRE key is encoded in 32 bit TEID field and since alternate CoA is not used the control plane and user plane addresses are the same for PMIPv6) + S5_S8_PGW_PMIPV6_INTERFACE, //(the 32 bit GRE key is encoded in 32 bit TEID field and the control plane and user plane addresses are the same for PMIPv6) + S11_MME_GTP_C_INTERFACE, + S11_S4_SGW_GTP_C_INTERFACE, + S10_MME_GTP_C_INTERFACE, + S3_MME_GTP_C_INTERFACE, + S3_SGSN_GTP_C_INTERFACE, + S4_SGSN_GTP_U_INTERFACE, + S4_SGW_GTP_U_INTERFACE, + S4_SGSN_GTP_C_INTERFACE, + S16_SGSN_GTP_C_INTERFACE, + ENODEB_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, + ENODEB_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING, + RNC_GTP_U_INTERFACE_FOR_DATA_FORWARDING, + SGSN_GTP_U_INTERFACE_FOR_DATA_FORWARDING, + SGW_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, + SM_MBMS_GW_GTP_C_INTERFACE, + SN_MBMS_GW_GTP_C_INTERFACE, + SM_MME_GTP_C_INTERFACE, + SN_SGSN_GTP_C_INTERFACE, + SGW_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING, + SN_SGSN_GTP_U_INTERFACE, + S2B_EPDG_GTP_C_INTERFACE, + S2B_U_EPDG_GTP_U_INTERFACE, + S2B_PGW_GTP_C_INTERFACE, + S2B_U_PGW_GTP_U_INTERFACE +}; + + +typedef struct gtpc_f_teid_ie +{ + bool ipv4_present; + bool ipv6_present; + enum gtpc_interface_type interface_type; + uint32_t teid; + in_addr_t ipv4; + struct in6_addr ipv6; //FIXME +} gtp_fteid_t; + +//TODO +//TODO IEs between 8.22 and 8.28 missing +//TODO + +/**************************************************************************** + * + * GTP-C Bearer Context IE + * Ref: 3GPP TS 29.274 v10.14.0 Table 8.28-1 + * + ***************************************************************************/ +//The usage of this grouped IE is specific to the GTP-C message being sent. +//As such, each GTP-C message will define it's bearer context structures +//locally, according to the rules of TS 29.274 v10.14.0 Section 7. + +} //namespace +#endif // SRSLTE_GTPC_IES_H diff --git a/lib/include/srslte/asn1/gtpc_msg.h b/lib/include/srslte/asn1/gtpc_msg.h new file mode 100644 index 0000000..42b6ad3 --- /dev/null +++ b/lib/include/srslte/asn1/gtpc_msg.h @@ -0,0 +1,446 @@ +/* \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ +#ifndef SRSLTE_GTPC_MSG_H +#define SRSLTE_GTPC_MSG_H + +#include "srslte/asn1/gtpc_ies.h" + +namespace srslte{ + +/**************************************************************** + * + * GTP-C Message Types + * Ref: TS 29.274 v10.14.0 Table 6.1-1 + * + ****************************************************************/ +const uint8_t GTPC_MSG_TYPE_RESERVED = 0; +const uint8_t GTPC_MSG_TYPE_ECHO_REQUEST = 1; +const uint8_t GTPC_MSG_TYPE_ECHO_RESPONSE = 2; +const uint8_t GTPC_MSG_TYPE_VERSION_SUPPORT = 3; +//4-24 Reserved for S101 +//25-31 Reserved for Sv interface +//SGSN/MME/ePDG to PGW (S4/S11, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_CREATE_SESSION_REQUEST = 32; +const uint8_t GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE = 33; +const uint8_t GTPC_MSG_TYPE_DELETE_SESSION_REQUEST = 36; +const uint8_t GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE = 37; +//SGSN/MME to PGW (S4/S11, S5/S8) +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST = 34; +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE = 35; +const uint8_t GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST = 38; +const uint8_t GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE = 39; +//40 - 63 for future use +const uint8_t GTPC_MSG_TYPE_RESUME_NOTIFICATION = 164; +const uint8_t GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE = 165; +//Messages without explicit response +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND = 64; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION = 65; //(PGW to MME/SGSN/ePDG – S5/S8, S11/S4, S2b) +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_COMMAND = 66; //(MME/SGSN to PGW – S11/S4, S5/S8) +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION = 67; //(PGW to MME/SGSN – S5/S8, S11/S4)) +const uint8_t GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND = 68; //(MME/SGSN to PGW – S11/S4, S5/S8) +const uint8_t GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION = 69; //(PGW to MME/SGSN – S5/S8, S11/S4) +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION = 70; //(SGSN/MME to SGW – S4/S11) +const uint8_t GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION = 71; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION = 72; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_STOP_PAGING_INDICATION = 73; //(SGW to MME/SGSN – S11/S4) +//74-94 For future use +//P-GW to SGSN/MME/ePDG +const uint8_t GTPC_MSG_TYPE_CREATE_BEARER_REQUEST = 95; +const uint8_t GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE = 96; +const uint8_t GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST = 97; +const uint8_t GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE = 98; +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_REQUEST = 99; +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE = 100; +//PGW to MME, MME to PGW, SGW to PGW, SGW to MME, PGW to ePDG, ePDG to PGW (S5/S8, S11, S2b) +const uint8_t GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST = 101; +const uint8_t GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE = 102; +//103-127 For future use +//MME to MME, SGSN to MME, MME to SGSN, SGSN to SGSN (S3/S10/S16) +const uint8_t GTPC_MSG_TYPE_IDENTIFICATION_REQUEST = 128; +const uint8_t GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE = 129; +const uint8_t GTPC_MSG_TYPE_CONTEXT_REQUEST = 130; +const uint8_t GTPC_MSG_TYPE_CONTEXT_RESPONSE = 131; +const uint8_t GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE = 132; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST = 133; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE = 134; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION = 135; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE = 136; +const uint8_t GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION = 137; +const uint8_t GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE = 138; +const uint8_t GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST = 139; +const uint8_t GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE = 140; +const uint8_t GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL = 141; +//142 - 148 For future use +const uint8_t GTPC_MSG_TYPE_RAN_INFORMATION_RELAY = 152; +//SGSN to MME, MME to SGSN (S3) +const uint8_t GTPC_MSG_TYPE_DETACH_NOTIFICATION = 149; +const uint8_t GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE = 150; +const uint8_t GTPC_MSG_TYPE_CS_PAGING_INDICATION = 151; +const uint8_t GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION = 153; +const uint8_t GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE = 154; +const uint8_t GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION = 155; +const uint8_t GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE = 156; +//157 - 159 For future use +//GSN/MME to SGW, SGSN to MME (S4/S11/S3) SGSN to SGSN (S16), SGW to PGW (S5/S8) +const uint8_t GTPC_MSG_TYPE_SUSPEND_NOTIFICATION = 162; +const uint8_t GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE = 163; +//SGSN/MME to SGW (S4/S11) const uint8_t GTPC_IE_TYPE_ +const uint8_t GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST = 160; +const uint8_t GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE = 161; +const uint8_t GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST = 166; +const uint8_t GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE = 167; +const uint8_t GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST = 168; +const uint8_t GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE = 169; +const uint8_t GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST = 170; +const uint8_t GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE = 171; +//172 - 175 For future use +//SGW to SGSN/MME (S4/S11) +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION = 176; +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE = 177; +const uint8_t GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION = 179; +const uint8_t GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE = 180; +//SGW to SGSN (S4) +//178 Reserved. Allocated in earlier version of the specification. +//181 -189 For future use +//SGW to PGW, PGW to SGW (S5/S8) +const uint8_t GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST = 200; +const uint8_t GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE = 201; +//For future use +//MME to SGW (S11) +const uint8_t GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST = 211; +const uint8_t GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE = 212; +//For future use +//MBMS GW to MME/SGSN (Sm/Sn) +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST = 231; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE = 232; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST = 233; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE = 234; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST = 235; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE = 236; +//For future use +//Other +//240 - 255 For future use + +/**************************************************************************** + * + * GTP-C v2 Create Session Request + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.1-1 + * + ***************************************************************************/ + +struct gtpc_create_session_request +{ + bool imsi_present; + uint64_t imsi; // C + //bool msidn_present; + //uint64_t msisdn; // C + //bool mei_present; + //uint64_t mei; // C/CO + //bool user_location_info_present; + //struct gtpc_user_location_info_ie uli; // C/CO + //bool serving_network_present; + //struct gtpc_serving_network_ie serving_network; // C/CO + + enum gtpc_rat_type rat_type; // M + //bool indication_flags_present; + //struct indication_flags_ indication_flags; // C + + struct gtpc_f_teid_ie sender_f_teid; // M + bool pgw_addr_present; + struct gtpc_f_teid_ie pgw_addr; // C + + char apn[MAX_APN_LENGTH]; // M + //bool selection_mode_present; + //enum selection_mode_ selection_mode; // C/CO + //bool pdn_type_present; + //enum gtpc_pdn_type pdn_type; // C + //bool pdn_addr_alloc_present; + //struct pdn_addr_alloc_ pdn_addr_alloc; // C/CO + //bool max_apn_restriction_present; + //enum apn_restriction_ max_apn_restriction; // C + //bool apn_ambr_present; + //struct ambr_ apn_ambr; // C + //bool linked_eps_bearer_id_present; + //uint8_t linked_eps_bearer_id; // C + //bool pco_present; + //uint8_t pco; // C + + struct gtpc_bearer_context_created_ie //see TS 29.274 v10.14.0 Table 7.2.1-2 + { + uint8_t ebi; + bool tft_present; + bool s1_u_enodeb_f_teid_present; + struct gtpc_f_teid_ie s1_u_enodeb_f_teid; + bool s4_u_sgsn_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgsn_f_teid; + bool s5_s8_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_sgw_f_teid; + bool s5_s8_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_pgw_f_teid; + bool s12_rnc_f_teid_present; + struct gtpc_f_teid_ie s12_rnc_f_teid; + bool s2b_u_epdg_f_teid_present; + struct gtpc_f_teid_ie s2b_u_epdg_f_teid; + struct gtpc_bearer_qos_ie bearer_qos; // M + } eps_bearer_context_created; // M + //bool bearer_context_deleted_present; + //struct bearer_context_ bearer_context_deleted; // C + //bool trace_information_present; + //struct trace_infromation_ trace_information; // C + //bool recovery_present + //uint8_t recovery; // C + //bool mme_fq_csid_present; + //struct fq_csid_ mme_fq_csid; // C + //bool sgw_fq_csid_present; + //struct fq_csid_ sgw_fq_csid; // C + //bool epdg_fq_csid_present; + //struct fq_csid_ epdg_fq_csid; // C + //bool ue_time_zone_present; + //struct ue_time_zone_ ue_time_zone; // CO + //bool uci_present; + //struct uci_ uci; // CO + //bool charging_caracteristics_present; + //enum charging_characteristics_ charging_caracteristics; // O + //bool mme_ldn_present; + //uint8_t mme_ldn[LDN_MAX_SIZE]; // O + //bool sgw_ldn_present; + //uint8_t sgw_ldn[LDN_MAX_SIZE]; // O + //bool epgd_ldn_present; + //uint8_t epdg_ldn[LDN_MAX_SIZE]; // O + //bool signaling_priority_indication; + //enum signalling_priority_indication_ spi; // CO + //bool acpo_present; + //uint8_t apco; // CO + //bool ext; // O +}; + +/**************************************************************************** + * + * GTP-C v2 Create Session Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.2-1 + * + ***************************************************************************/ +struct gtpc_create_session_response +{ + struct gtpc_cause_ie cause; //M + //Change Reporting Action //C + //CSG Information Reporting Action //CO + bool sender_f_teid_present; + struct gtpc_f_teid_ie sender_f_teid; //C + //PGW S5/S8/S2b F-TEID //C + bool paa_present; + struct gtpc_pdn_address_allocation_ie paa; //C + //apn_restriction + //apn_ambr + //linked_eps_bearer_id + //pco + struct gtpc_bearer_context_created_ie + { + uint8_t ebi; + gtpc_cause_ie cause; + bool s1_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s1_u_sgw_f_teid; + bool s4_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgw_f_teid; + bool s5_s8_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_pgw_f_teid; + bool s12_sgw_f_teid_present; + struct gtpc_f_teid_ie s12_sgw_f_teid; + bool s2b_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s2b_u_pgw_f_teid; + bool bearer_level_qos_present; + struct gtpc_bearer_qos_ie bearer_level_qos; + //charging_id_present + //charging_id + //bearer_flags_present + //bearer_flags + } eps_bearer_context_created; //M + + /* + struct gtpc_bearer_context_removed_ie + { + uint8_t ebi; + // + } bearer_context_removed; //C + */ + //recovery; //C + //charging_gateway_name; //C + //charging_gateway_address; //C + //PGW-FQ-CSID //C + //SGW-FQ-CSID //C + //SGW LDN //O + //PGW LDN //O + //PGW Back-Off Time //O + //acpo //CO +}; + +/**************************************************************************** + * + * GTP-C v2 Modify Bearer Request + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.7-1, 7.2.7-2 and 7.2.7-3 + * + ***************************************************************************/ + +struct gtpc_modify_bearer_request +{ + //ME Identity (MEI)//C + //User Location Information (ULI)//C + //Serving Network //CO + //RAT Type //C/CO + //Indication Flags + //Sender F-TEID for Control Plane + //APN-AMBR + //Delay Downlink Packet Notification Request + struct gtpc_bearer_context_modified_ie + { + uint8_t ebi; + gtpc_cause_ie cause; + bool s1_u_enb_f_teid_present; + struct gtpc_f_teid_ie s1_u_enb_f_teid; + bool s5_s8_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_sgw_f_teid; + bool s12_rnc_f_teid_present; + struct gtpc_f_teid_ie s12_rnc_f_teid; + bool s4_u_sgsn_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgsn_f_teid; + } eps_bearer_context_to_modify; + //Bearer Contexts to be removed + //Recovery + //UE Time Zone + //MME-FQ-CSID + //SGW-FQ-CSID + //User CSG Information (UCI) + //MME/S4-SGSN LDN + //SGW LDN +}; + +/**************************************************************************** + * + * GTP-C v2 Modify Bearer Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.8-1 + * + ***************************************************************************/ + +struct gtpc_modify_bearer_response +{ + struct gtpc_cause_ie cause; + //MSISDN + //Linked EPS Bearer ID + //APN-AMBR + //APN Restriction + //Protocol Configuration Options + + struct gtpc_bearer_context_modified_ie + { + uint8_t ebi; + struct gtpc_cause_ie cause; + bool s1_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s1_u_sgw_f_teid; + bool s12_sgw_f_teid_present; + struct gtpc_f_teid_ie s12_sgw_f_teid; + bool s4_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgw_f_teid; + //charging id + //bearer flags + } eps_bearer_context_modified; + //Bearer Contexts marked for removal + //Change Reporting action + //CSG information reporting action + //Charging gateway name + //charging gateway address + //P-GW FQ-CSID + //S-GW FQ-CSID + //Recovery + //S-GW LDN + //P-GW LDN + //indication Flags + //ext +}; + +/**************************************************************************** + * + * GTP-C v2 Delete Session Resquest + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.9.1-1 + * + ***************************************************************************/ + +struct gtpc_delete_session_request +{ + struct gtpc_cause_ie cause; + //Linked EPS Bearer ID + //User Location Information + //Indication Flags + //Protocol Configuration Options + //Originating Node + //Private extension +}; + +/**************************************************************************** + * + * GTP-C v2 Delete Session Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.10.1-1 + * + ***************************************************************************/ + +struct gtpc_delete_session_response +{ + struct gtpc_cause_ie cause; + //Recovery + //Protocol Configuration Options + //Private extension +}; + +/**************************************************************************** + * + * GTP-C v2 Release Access Bearers Request + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.21.1-1 + * + ***************************************************************************/ +struct gtpc_release_access_bearers_request +{ + bool list_of_rabs_present; + //Linked EPS Bearer ID + bool originating_node_present; + //Indication Flags + //Private Extension +}; + + /**************************************************************************** + * + * GTP-C v2 Delete Session Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.22.1-1 + * + ***************************************************************************/ + + struct gtpc_release_access_bearers_response + { + struct gtpc_cause_ie cause; + //Recovery + //Private extension + }; + + + +} //namespace +#endif // SRSLTE_GTPC_MSG_H diff --git a/lib/include/srslte/asn1/liblte_common.h b/lib/include/srslte/asn1/liblte_common.h new file mode 100644 index 0000000..3b7eccf --- /dev/null +++ b/lib/include/srslte/asn1/liblte_common.h @@ -0,0 +1,180 @@ +/******************************************************************************* + + Copyright 2012-2014 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_common.h + + Description: Contains all the common definitions for the LTE library. + + Revision History + ---------- ------------- -------------------------------------------- + 02/26/2012 Ben Wojtowicz Created file. + 07/21/2013 Ben Wojtowicz Added a common message structure. + 06/15/2014 Ben Wojtowicz Split LIBLTE_MSG_STRUCT into bit and byte + aligned messages. + 08/03/2014 Ben Wojtowicz Commonized value_2_bits and bits_2_value. + 11/29/2014 Ben Wojtowicz Added liblte prefix to value_2_bits and + bits_2_value. + +*******************************************************************************/ + +#ifndef SRSLTE_LIBLTE_COMMON_H +#define SRSLTE_LIBLTE_COMMON_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include +#include +#include +#include + +/******************************************************************************* + DEFINES +*******************************************************************************/ + +// FIXME: This was chosen arbitrarily +#define LIBLTE_ASN1_OID_MAXSUBIDS 128 + +// Caution these values must match SRSLTE_ ones in common.h +#define LIBLTE_MAX_MSG_SIZE_BITS 102048 +#define LIBLTE_MAX_MSG_SIZE_BYTES 12756 +#define LIBLTE_MSG_HEADER_OFFSET 1020 + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; + +typedef enum{ + LIBLTE_SUCCESS = 0, + LIBLTE_ERROR_INVALID_INPUTS, + LIBLTE_ERROR_ENCODE_FAIL, + LIBLTE_ERROR_DECODE_FAIL, + LIBLTE_ERROR_INVALID_CRC, + LIBLTE_ERROR_N_ITEMS +}LIBLTE_ERROR_ENUM; +static const char liblte_error_text[LIBLTE_ERROR_N_ITEMS][64] = { + "Invalid inputs", + "Encode failure", + "Decode failure", +}; + +#define LIBLTE_STRING_LEN 128 + +typedef void* LIBLTE_ASN1_OPEN_TYPE_STRUCT; + +typedef struct { + uint32_t numids; // number of subidentifiers + uint32_t subid[LIBLTE_ASN1_OID_MAXSUBIDS]; // subidentifier values +} LIBLTE_ASN1_OID_STRUCT; + +typedef struct{ + bool data_valid; + bool data; +}LIBLTE_BOOL_MSG_STRUCT; + +typedef struct{ + uint32 N_bits; + uint8 header[LIBLTE_MSG_HEADER_OFFSET]; + uint8 msg[LIBLTE_MAX_MSG_SIZE_BITS]; +}LIBLTE_BIT_MSG_STRUCT; + +typedef struct{ + uint32 N_bytes; + uint8 header[LIBLTE_MSG_HEADER_OFFSET]; + uint8 msg[LIBLTE_MAX_MSG_SIZE_BYTES]; +}LIBLTE_BYTE_MSG_STRUCT; + + +/******************************************************************************* + DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + Name: liblte_value_2_bits + + Description: Converts a value to a bit string +*********************************************************************/ +void liblte_value_2_bits(uint32 value, + uint8 **bits, + uint32 N_bits); + +/********************************************************************* + Name: liblte_bits_2_value + + Description: Converts a bit string to a value +*********************************************************************/ +uint32 liblte_bits_2_value(uint8 **bits, + uint32 N_bits); + +/********************************************************************* + Name: liblte_pack + + Description: Pack a bit array into a byte array +*********************************************************************/ +void liblte_pack(LIBLTE_BIT_MSG_STRUCT *bits, + LIBLTE_BYTE_MSG_STRUCT *bytes); + +/********************************************************************* + Name: liblte_unpack + + Description: Unpack a byte array into a bit array +*********************************************************************/ +void liblte_unpack(LIBLTE_BYTE_MSG_STRUCT *bytes, + LIBLTE_BIT_MSG_STRUCT *bits); + +/********************************************************************* + Name: liblte_pack + + Description: Pack a bit array into a byte array +*********************************************************************/ +void liblte_pack(uint8_t *bits, uint32_t n_bits, uint8_t *bytes); + +/********************************************************************* + Name: liblte_unpack + + Description: Unpack a byte array into a bit array +*********************************************************************/ +void liblte_unpack(uint8_t *bytes, uint32_t n_bytes, uint8_t *bits); + +/********************************************************************* + Name: liblte_align_up + + Description: Aligns a pointer to a multibyte boundary +*********************************************************************/ +void liblte_align_up(uint8_t **ptr, uint32_t align); + +/********************************************************************* + Name: liblte_align_up_zero + + Description: Aligns a pointer to a multibyte boundary and zeros + bytes skipped +*********************************************************************/ +void liblte_align_up_zero(uint8_t **ptr, uint32_t align); + +#endif // SRSLTE_LIBLTE_COMMON_H diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h new file mode 100644 index 0000000..3d96408 --- /dev/null +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -0,0 +1,4044 @@ +/******************************************************************************* + + Copyright 2014-2015 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_mme.h + + Description: Contains all the definitions for the LTE Mobility Management + Entity library. + + Revision History + ---------- ------------- -------------------------------------------- + 06/15/2014 Ben Wojtowicz Created file. + 08/03/2014 Ben Wojtowicz Added more decoding/encoding. + 09/03/2014 Ben Wojtowicz Added more decoding/encoding. + 11/01/2014 Ben Wojtowicz Added more decoding/encoding. + 11/29/2014 Ben Wojtowicz Added more decoding/encoding. + 12/16/2014 Ben Wojtowicz Added more decoding/encoding. + 12/24/2014 Ben Wojtowicz Cleaned up the Time Zone and Time IE. + 02/15/2015 Ben Wojtowicz Added more decoding/encoding. + +*******************************************************************************/ + +#ifndef SRSLTE_LIBLTE_MME_H +#define SRSLTE_LIBLTE_MME_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "liblte_common.h" +#include + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + + +/******************************************************************************* + INFORMATION ELEMENT DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + IE Name: Additional Information + + Description: Provides additional information to upper layers in + relation to the generic NAS message transport + mechanism. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.0 +*********************************************************************/ +// Defines +#define LIBLTE_MME_ADDITIONAL_INFORMATION_MAX_N_OCTETS (LIBLTE_MAX_MSG_SIZE_BITS/2) +// Enums +// Structs +typedef struct{ + uint8 info[LIBLTE_MME_ADDITIONAL_INFORMATION_MAX_N_OCTETS]; + uint32 N_octets; +}LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_information_ie(LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT *add_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_information_ie(uint8 **ie_ptr, + LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT *add_info); + +/********************************************************************* + IE Name: Device Properties + + Description: Indicates if the UE is configured for NAS signalling + low priority. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.0A + 24.008 v10.2.0 Section 10.5.7.8 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_DEVICE_PROPERTIES_NOT_CONFIGURED_FOR_LOW_PRIORITY = 0, + LIBLTE_MME_DEVICE_PROPERTIES_CONFIGURED_FOR_LOW_PRIORITY, + LIBLTE_MME_DEVICE_PROPERTIES_N_ITEMS, +}LIBLTE_MME_DEVICE_PROPERTIES_ENUM; +static const char liblte_mme_device_properties_text[LIBLTE_MME_DEVICE_PROPERTIES_N_ITEMS][50] = {"Not configured for low priority", + "Configured for low priority"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_device_properties_ie(LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_props, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_device_properties_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_DEVICE_PROPERTIES_ENUM *device_props); + +/********************************************************************* + IE Name: EPS Bearer Context Status + + Description: Indicates the state of each EPS bearer context that + can be identified by an EPS bearer identity. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + bool ebi[16]; +}LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_bearer_context_status_ie(LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT *ebcs, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_bearer_context_status_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT *ebcs); + +/********************************************************************* + IE Name: Location Area Identification + + Description: Provides an unambiguous identification of location + areas within the area covered by the 3GPP system. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.2 + 24.008 v10.2.0 Section 10.5.1.3 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint16 mcc; + uint16 mnc; + uint16 lac; +}LIBLTE_MME_LOCATION_AREA_ID_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_location_area_id_ie(LIBLTE_MME_LOCATION_AREA_ID_STRUCT *lai, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_location_area_id_ie(uint8 **ie_ptr, + LIBLTE_MME_LOCATION_AREA_ID_STRUCT *lai); + +/********************************************************************* + IE Name: Mobile Identity + + Description: Provides either the IMSI, TMSI/P-TMSI/M-TMSI, IMEI, + IMEISV, or TMGI, associated with the optional MBMS + session identity. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.3 + 24.008 v10.2.0 Section 10.5.1.4 +*********************************************************************/ +// Defines +#define LIBLTE_MME_MOBILE_ID_TYPE_IMSI 0x1 +#define LIBLTE_MME_MOBILE_ID_TYPE_IMEI 0x2 +#define LIBLTE_MME_MOBILE_ID_TYPE_IMEISV 0x3 +#define LIBLTE_MME_MOBILE_ID_TYPE_TMSI 0x4 +#define LIBLTE_MME_MOBILE_ID_TYPE_TMGI 0x5 +// Enums +// Structs +typedef struct{ + uint8 type_of_id; + uint8 imsi[15]; + uint8 imei[15]; + uint8 imeisv[16]; + uint32 tmsi; +}LIBLTE_MME_MOBILE_ID_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mobile_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_id_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_ID_STRUCT *mobile_id); + +/********************************************************************* + IE Name: Mobile Station Classmark 2 + + Description: Provides the network with information concerning + aspects of both high and low priority of the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.4 + 24.008 v10.2.0 Section 10.5.1.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_REVISION_LEVEL_GSM_PHASE_1 = 0, + LIBLTE_MME_REVISION_LEVEL_GSM_PHASE_2, + LIBLTE_MME_REVISION_LEVEL_R99, + LIBLTE_MME_REVISION_LEVEL_RESERVED, + LIBLTE_MME_REVISION_LEVEL_N_ITEMS, +}LIBLTE_MME_REVISION_LEVEL_ENUM; +static const char liblte_mme_revision_level_text[LIBLTE_MME_REVISION_LEVEL_N_ITEMS][20] = {"GSM Phase 1", + "GSM Phase 2", + "R99", + "RESERVED"}; +typedef enum{ + LIBLTE_MME_RF_POWER_CAPABILITY_CLASS_1 = 0, + LIBLTE_MME_RF_POWER_CAPABILITY_CLASS_2, + LIBLTE_MME_RF_POWER_CAPABILITY_CLASS_3, + LIBLTE_MME_RF_POWER_CAPABILITY_CLASS_4, + LIBLTE_MME_RF_POWER_CAPABILITY_CLASS_5, + LIBLTE_MME_RF_POWER_CAPABILITY_N_ITEMS, +}LIBLTE_MME_RF_POWER_CAPABILITY_ENUM; +static const char liblte_mme_rf_power_capability_text[LIBLTE_MME_RF_POWER_CAPABILITY_N_ITEMS][20] = {"Class 1", + "Class 2", + "Class 3", + "Class 4", + "Class 5"}; +typedef enum{ + LIBLTE_MME_SS_SCREEN_INDICATOR_0 = 0, + LIBLTE_MME_SS_SCREEN_INDICATOR_1, + LIBLTE_MME_SS_SCREEN_INDICATOR_2, + LIBLTE_MME_SS_SCREEN_INDICATOR_3, + LIBLTE_MME_SS_SCREEN_INDICATOR_N_ITEMS, +}LIBLTE_MME_SS_SCREEN_INDICATOR_ENUM; +static const char liblte_mme_ss_screen_indicator_text[LIBLTE_MME_SS_SCREEN_INDICATOR_N_ITEMS][100] = {"Default Phase 1", + "Ellipsis Notation Phase 2", + "RESERVED", + "RESERVED"}; +// Structs +typedef struct{ + LIBLTE_MME_REVISION_LEVEL_ENUM rev_lev; + LIBLTE_MME_RF_POWER_CAPABILITY_ENUM rf_power_cap; + LIBLTE_MME_SS_SCREEN_INDICATOR_ENUM ss_screen_ind; + bool es_ind; + bool a5_1; + bool ps_cap; + bool sm_cap; + bool vbs; + bool vgcs; + bool fc; + bool cm3; + bool lcsva_cap; + bool ucs2; + bool solsa; + bool cmsp; + bool a5_3; + bool a5_2; +}LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_station_classmark_2_ie(LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT *ms_cm2, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_station_classmark_2_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT *ms_cm2); + +/********************************************************************* + IE Name: Mobile Station Classmark 3 + + Description: Provides the network with information concerning + aspects of the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.5 + 24.008 v10.2.0 Section 10.5.1.7 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_station_classmark_3_ie(LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT *ms_cm3, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_station_classmark_3_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT *ms_cm3); + +/********************************************************************* + IE Name: NAS Security Parameters From E-UTRA + + Description: Provides the UE with information that enables the UE + to create a mapped UMTS security context. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_parameters_from_eutra_ie(uint8 dl_nas_count, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_parameters_from_eutra_ie(uint8 **ie_ptr, + uint8 *dl_nas_count); + +/********************************************************************* + IE Name: NAS Security Parameters To E-UTRA + + Description: Provides the UE with parameters that enables the UE + to create a mapped EPS security context and take + this context into use after inter-system handover to + S1 mode. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.7 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA0 = 0, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_128_EIA1, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_128_EIA2, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA3, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA4, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA5, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA6, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_EIA7, + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_N_ITEMS, +}LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_ENUM; +static const char liblte_mme_type_of_integrity_algorithm_text[LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_N_ITEMS][20] = {"EIA0", + "128-EIA1", + "128-EIA2", + "EIA3", + "EIA4", + "EIA5", + "EIA6", + "EIA7"}; +typedef enum{ + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA0 = 0, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_128_EEA1, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_128_EEA2, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA3, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA4, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA5, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA6, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA7, + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_N_ITEMS, +}LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_ENUM; +static const char liblte_mme_type_of_ciphering_algorithm_text[LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_N_ITEMS][20] = {"EEA0", + "128-EEA1", + "128-EEA2", + "EEA3", + "EEA4", + "EEA5", + "EEA6", + "EEA7"}; +typedef enum{ + LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE = 0, + LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_MAPPED, + LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_N_ITEMS, +}LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_ENUM; +static const char liblte_mme_type_of_security_context_flag_text[LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_N_ITEMS][20] = {"Native", + "Mapped"}; +// Structs +typedef struct{ + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_ENUM eea; + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_ENUM eia; + LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_ENUM tsc_flag; + uint32 nonce_mme; + uint8 nas_ksi; +}LIBLTE_MME_NAS_SECURITY_PARAMETERS_TO_EUTRA_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_parameters_to_eutra_ie(LIBLTE_MME_NAS_SECURITY_PARAMETERS_TO_EUTRA_STRUCT *sec_params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_parameters_to_eutra_ie(uint8 **ie_ptr, + LIBLTE_MME_NAS_SECURITY_PARAMETERS_TO_EUTRA_STRUCT *sec_params); + +/********************************************************************* + IE Name: PLMN List + + Description: Provides a list of PLMN codes to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.8 + 24.008 v10.2.0 Section 10.5.1.13 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PLMN_LIST_MAX_SIZE 15 +// Enums +// Structs +typedef struct{ + uint32 N_plmns; + uint16 mcc[LIBLTE_MME_PLMN_LIST_MAX_SIZE]; + uint16 mnc[LIBLTE_MME_PLMN_LIST_MAX_SIZE]; +}LIBLTE_MME_PLMN_LIST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_plmn_list_ie(LIBLTE_MME_PLMN_LIST_STRUCT *plmn_list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_plmn_list_ie(uint8 **ie_ptr, + LIBLTE_MME_PLMN_LIST_STRUCT *plmn_list); + +/********************************************************************* + IE Name: Spare Half Octet + + Description: Used in the description of EMM and ESM messages when + an odd number of half octet type 1 information + elements are used. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.9 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions + +/********************************************************************* + IE Name: Supported Codec List + + Description: Provides the network with information about the + speech codecs supported by the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.10 + 24.008 v10.2.0 Section 10.5.4.32 +*********************************************************************/ +// Defines +#define LIBLTE_MME_MAX_N_SUPPORTED_CODECS (LIBLTE_MAX_MSG_SIZE_BITS/16) +// Enums +// Structs +typedef struct{ + uint8 sys_id; + uint16 codec_bitmap; +}LIBLTE_MME_SUPPORTED_CODEC_STRUCT; +typedef struct{ + LIBLTE_MME_SUPPORTED_CODEC_STRUCT supported_codec[LIBLTE_MME_MAX_N_SUPPORTED_CODECS]; + uint32 N_supported_codecs; +}LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_supported_codec_list_ie(LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT *supported_codec_list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_supported_codec_list_ie(uint8 **ie_ptr, + LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT *supported_codec_list); + +/********************************************************************* + IE Name: Additional Update Result + + Description: Provides additional information about the result of + a combined attached procedure or a combined tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.0A +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_NO_ADDITIONAL_INFO = 0, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_CS_FALLBACK_NOT_PREFERRED, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_SMS_ONLY, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_RESERVED, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_N_ITEMS, +}LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM; +static const char liblte_mme_additional_update_result_text[LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_N_ITEMS][100] = {"No Additional Information", + "CS Fallback Not Preferred", + "SMS Only", + "RESERVED"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_update_result_ie(LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM result, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_update_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM *result); + +/********************************************************************* + IE Name: Additional Update Type + + Description: Provides additional information about the type of + request for a combined attach or a combined tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.0B +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_NO_ADDITIONAL_INFO = 0, + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_SMS_ONLY, + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_N_ITEMS, +}LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM; +static const char liblte_mme_additional_update_type_text[LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_N_ITEMS][20] = {"No additional info", + "SMS Only"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_update_type_ie(LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM aut, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_update_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM *aut); + +/********************************************************************* + IE Name: Authentication Failure Parameter + + Description: Provides the network with the necessary information + to begin a re-authentication procedure in the case + of a 'Synch failure', following a UMTS or EPS + authentication challenge. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.1 + 24.008 v10.2.0 Section 10.5.3.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_failure_parameter_ie(uint8 *auth_fail_param, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_failure_parameter_ie(uint8 **ie_ptr, + uint8 *auth_fail_param); + +/********************************************************************* + IE Name: Authentication Parameter AUTN + + Description: Provides the UE with a means of authenticating the + network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.2 + 24.008 v10.2.0 Section 10.5.3.1.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_parameter_autn_ie(uint8 *autn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_autn_ie(uint8 **ie_ptr, + uint8 *autn); + +/********************************************************************* + IE Name: Authentication Parameter RAND + + Description: Provides the UE with a non-predictable number to be + used to calculate the authentication signature SRES + and the ciphering key Kc (for a GSM authentication + challenge), or the response RES and both the + ciphering key CK and the integrity key IK (for a + UMTS authentication challenge). + + Document Reference: 24.301 v10.2.0 Section 9.9.3.3 + 24.008 v10.2.0 Section 10.5.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_parameter_rand_ie(uint8 *rand_val, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_rand_ie(uint8 **ie_ptr, + uint8 *rand_val); + +/********************************************************************* + IE Name: Authentication Response Parameter + + Description: Provides the network with the authentication + response calculated in the USIM. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *res, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_response_parameter_ie(uint8 **ie_ptr, + uint8 *res); + +/********************************************************************* + IE Name: Ciphering Key Sequence Number + + Description: Makes it possible for the network to identify the + ciphering key Kc which is stored in the UE without + invoking the authentication procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.4A + 24.008 v10.2.0 Section 10.5.1.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ciphering_key_sequence_number_ie(uint8 key_seq, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ciphering_key_sequence_number_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *key_seq); + +/********************************************************************* + IE Name: CSFB Response + + Description: Indicates whether the UE accepts or rejects a paging + for CS fallback. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.5 +*********************************************************************/ +// Defines +#define LIBLTE_MME_CSFB_REJECTED_BY_THE_UE 0x0 +#define LIBLTE_MME_CSFB_ACCEPTED_BY_THE_UE 0x1 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_csfb_response_ie(uint8 csfb_resp, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_csfb_response_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *csfb_resp); + +/********************************************************************* + IE Name: Daylight Saving Time + + Description: Encodes the daylight saving time in steps of 1 hour. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.6 + 24.008 v10.2.0 Section 10.5.3.12 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_DAYLIGHT_SAVING_TIME_NO_ADJUSTMENT = 0, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_PLUS_ONE_HOUR, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_PLUS_TWO_HOURS, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_RESERVED, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_N_ITEMS, +}LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM; +static const char liblte_mme_daylight_saving_time_text[LIBLTE_MME_DAYLIGHT_SAVING_TIME_N_ITEMS][20] = {"No Adjustment", + "+1 Hour", + "+2 Hours", + "RESERVED"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_daylight_saving_time_ie(LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM dst, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_daylight_saving_time_ie(uint8 **ie_ptr, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM *dst); + +/********************************************************************* + IE Name: Detach Type + + Description: Indicates the type of detach. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_MME_SO_FLAG_NORMAL_DETACH 0 +#define LIBLTE_MME_SO_FLAG_SWITCH_OFF 1 +#define LIBLTE_MME_TOD_UL_EPS_DETACH 0x1 +#define LIBLTE_MME_TOD_UL_IMSI_DETACH 0x2 +#define LIBLTE_MME_TOD_UL_COMBINED_DETACH 0x3 +#define LIBLTE_MME_TOD_DL_REATTACH_REQUIRED 0x1 +#define LIBLTE_MME_TOD_DL_REATTACH_NOT_REQUIRED 0x2 +#define LIBLTE_MME_TOD_DL_IMSI_DETACH 0x3 +// Enums +// Structs +typedef struct{ + uint8 switch_off; + uint8 type_of_detach; +}LIBLTE_MME_DETACH_TYPE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_type_ie(LIBLTE_MME_DETACH_TYPE_STRUCT *detach_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_DETACH_TYPE_STRUCT *detach_type); + +/********************************************************************* + IE Name: DRX Parameter + + Description: Indicates whether the UE uses DRX mode or not. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.8 + 24.008 v10.2.0 Section 10.5.5.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_NON_DRX_TIMER_NO_NON_DRX_MODE = 0, + LIBLTE_MME_NON_DRX_TIMER_MAX_1S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_2S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_4S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_8S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_16S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_32S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_MAX_64S_NON_DRX_MODE, + LIBLTE_MME_NON_DRX_TIMER_N_ITEMS, +}LIBLTE_MME_NON_DRX_TIMER_ENUM; +static const char liblte_mme_non_drx_timer_text[LIBLTE_MME_NON_DRX_TIMER_N_ITEMS][100] = {"No Non-DRX Mode", + "Max 1s Non-DRX Mode", + "Max 2s Non-DRX Mode", + "Max 4s Non-DRX Mode", + "Max 8s Non-DRX Mode", + "Max 16s Non-DRX Mode", + "Max 32s Non-DRX Mode", + "Max 64s Non-DRX Mode"}; +// Structs +typedef struct{ + LIBLTE_MME_NON_DRX_TIMER_ENUM non_drx_timer; + uint8 split_pg_cycle_code; + uint8 drx_cycle_len_coeff_and_value; + bool split_on_ccch; +}LIBLTE_MME_DRX_PARAMETER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_drx_parameter_ie(LIBLTE_MME_DRX_PARAMETER_STRUCT *drx_param, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_drx_parameter_ie(uint8 **ie_ptr, + LIBLTE_MME_DRX_PARAMETER_STRUCT *drx_param); + +/********************************************************************* + IE Name: EMM Cause + + Description: Indicates the reason why an EMM request from the UE + is rejected by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.9 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EMM_CAUSE_IMSI_UNKNOWN_IN_HSS 0x02 +#define LIBLTE_MME_EMM_CAUSE_ILLEGAL_UE 0x03 +#define LIBLTE_MME_EMM_CAUSE_IMEI_NOT_ACCEPTED 0x05 +#define LIBLTE_MME_EMM_CAUSE_ILLEGAL_ME 0x06 +#define LIBLTE_MME_EMM_CAUSE_EPS_SERVICES_NOT_ALLOWED 0x07 +#define LIBLTE_MME_EMM_CAUSE_EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED 0x08 +#define LIBLTE_MME_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK 0x09 +#define LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED 0x0A +#define LIBLTE_MME_EMM_CAUSE_PLMN_NOT_ALLOWED 0x0B +#define LIBLTE_MME_EMM_CAUSE_TRACKING_AREA_NOT_ALLOWED 0x0C +#define LIBLTE_MME_EMM_CAUSE_ROAMING_NOT_ALLOWED_IN_THIS_TRACKING_AREA 0x0D +#define LIBLTE_MME_EMM_CAUSE_EPS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN 0x0E +#define LIBLTE_MME_EMM_CAUSE_NO_SUITABLE_CELLS_IN_TRACKING_AREA 0x0F +#define LIBLTE_MME_EMM_CAUSE_MSC_TEMPORARILY_NOT_REACHABLE 0x10 +#define LIBLTE_MME_EMM_CAUSE_NETWORK_FAILURE 0x11 +#define LIBLTE_MME_EMM_CAUSE_CS_DOMAIN_NOT_AVAILABLE 0x12 +#define LIBLTE_MME_EMM_CAUSE_ESM_FAILURE 0x13 +#define LIBLTE_MME_EMM_CAUSE_MAC_FAILURE 0x14 +#define LIBLTE_MME_EMM_CAUSE_SYNCH_FAILURE 0x15 +#define LIBLTE_MME_EMM_CAUSE_CONGESTION 0x16 +#define LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH 0x17 +#define LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED 0x18 +#define LIBLTE_MME_EMM_CAUSE_NOT_AUTHORIZED_FOR_THIS_CSG 0x19 +#define LIBLTE_MME_EMM_CAUSE_NON_EPS_AUTHENTICATION_UNACCEPTABLE 0x1A +#define LIBLTE_MME_EMM_CAUSE_CS_SERVICE_TEMPORARILY_NOT_AVAILABLE 0x27 +#define LIBLTE_MME_EMM_CAUSE_NO_EPS_BEARER_CONTEXT_ACTIVATED 0x28 +#define LIBLTE_MME_EMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE 0x5F +#define LIBLTE_MME_EMM_CAUSE_INVALID_MANDATORY_INFORMATION 0x60 +#define LIBLTE_MME_EMM_CAUSE_MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED 0x61 +#define LIBLTE_MME_EMM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE 0x62 +#define LIBLTE_MME_EMM_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED 0x63 +#define LIBLTE_MME_EMM_CAUSE_CONDITIONAL_IE_ERROR 0x64 +#define LIBLTE_MME_EMM_CAUSE_MESSAGE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE 0x65 +#define LIBLTE_MME_EMM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED 0x6F +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_cause_ie(uint8 emm_cause, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_cause_ie(uint8 **ie_ptr, + uint8 *emm_cause); + +/********************************************************************* + IE Name: EPS Attach Result + + Description: Specifies the result of an attach procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.10 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_ATTACH_RESULT_EPS_ONLY 0x1 +#define LIBLTE_MME_EPS_ATTACH_RESULT_COMBINED_EPS_IMSI_ATTACH 0x2 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_attach_result_ie(uint8 result, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_attach_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *result); + +/********************************************************************* + IE Name: EPS Attach Type + + Description: Indicates the type of the requested attach. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.11 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH 0x1 +#define LIBLTE_MME_EPS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH 0x2 +#define LIBLTE_MME_EPS_ATTACH_TYPE_EPS_EMERGENCY_ATTACH 0x6 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_attach_type_ie(uint8 attach_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_attach_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *attach_type); + +/********************************************************************* + IE Name: EPS Mobile Identity + + Description: Provides either the IMSI, the GUTI, or the IMEI. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.12 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI 0x1 +#define LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI 0x6 +#define LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMEI 0x3 +// Enums +// Structs +typedef struct{ + uint32 m_tmsi; + uint16 mcc; + uint16 mnc; + uint16 mme_group_id; + uint8 mme_code; +}LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT; +typedef struct{ + LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; + uint8 type_of_id; + uint8 imsi[15]; + uint8 imei[15]; +}LIBLTE_MME_EPS_MOBILE_ID_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_mobile_id_ie(LIBLTE_MME_EPS_MOBILE_ID_STRUCT *eps_mobile_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_mobile_id_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_MOBILE_ID_STRUCT *eps_mobile_id); + +/********************************************************************* + IE Name: EPS Network Feature Support + + Description: Indicates whether certain features are supported by + the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.12A +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_NOT_SUPPORTED 0 +#define LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_SUPPORTED 1 +// Enums +typedef enum{ + LIBLTE_MME_CS_LCS_NO_INFORMATION_AVAILABLE = 0, + LIBLTE_MME_CS_LCS_NOT_SUPPORTED, + LIBLTE_MME_CS_LCS_SUPPORTED, + LIBLTE_MME_CS_LCS_RESERVED, + LIBLTE_MME_CS_LCS_N_ITEMS, +}LIBLTE_MME_CS_LCS_ENUM; +static const char liblte_mme_cs_lcs_text[LIBLTE_MME_CS_LCS_N_ITEMS][100] = {"No Information Available", + "Not Supported", + "Supported", + "RESERVED"}; +// Structs +typedef struct{ + LIBLTE_MME_CS_LCS_ENUM cs_lcs; + bool esrps; + bool epc_lcs; + bool emc_bs; + bool ims_vops; +}LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_network_feature_support_ie(LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT *eps_nfs, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_network_feature_support_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT *eps_nfs); + +/********************************************************************* + IE Name: EPS Update Result + + Description: Specifies the result of the associated updating + procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.13 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_UPDATE_RESULT_TA_UPDATED 0x0 +#define LIBLTE_MME_EPS_UPDATE_RESULT_COMBINED_TA_LA_UPDATED 0x1 +#define LIBLTE_MME_EPS_UPDATE_RESULT_TA_UPDATED_AND_ISR_ACTIVATED 0x4 +#define LIBLTE_MME_EPS_UPDATE_RESULT_COMBINED_TA_LA_UPDATED_AND_ISR_ACTIVATED 0x5 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_update_result_ie(uint8 eps_update_res, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_update_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *eps_update_res); + +/********************************************************************* + IE Name: EPS Update Type + + Description: Specifies the area the updating procedure is + associated with. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.14 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_EPS_UPDATE_TYPE_TA_UPDATING = 0, + LIBLTE_MME_EPS_UPDATE_TYPE_COMBINED_TA_LA_UPDATING, + LIBLTE_MME_EPS_UPDATE_TYPE_COMBINED_TA_LA_UPDATING_WITH_IMSI_ATTACH, + LIBLTE_MME_EPS_UPDATE_TYPE_PERIODIC_UPDATING, + LIBLTE_MME_EPS_UPDATE_TYPE_N_ITEMS, +}LIBLTE_MME_EPS_UPDATE_TYPE_ENUM; +static const char liblte_mme_eps_update_type_text[LIBLTE_MME_EPS_UPDATE_TYPE_N_ITEMS][100] = {"TA Updating", + "Combined TA/LA Updating", + "Combined TA/LA Updating With IMSI Attach", + "Periodic Updating"}; +// Structs +typedef struct{ + LIBLTE_MME_EPS_UPDATE_TYPE_ENUM type; + bool active_flag; +}LIBLTE_MME_EPS_UPDATE_TYPE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_update_type_ie(LIBLTE_MME_EPS_UPDATE_TYPE_STRUCT *eps_update_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_update_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_EPS_UPDATE_TYPE_STRUCT *eps_update_type); + +/********************************************************************* + IE Name: ESM Message Container + + Description: Enables piggybacked transfer of a single ESM message + within an EMM message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.15 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *esm_msg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *esm_msg); + +/********************************************************************* + IE Name: GPRS Timer + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16 + 24.008 v10.2.0 Section 10.5.7.3 +*********************************************************************/ +// Defines +#define LIBLTE_MME_GPRS_TIMER_UNIT_2_SECONDS 0x0 +#define LIBLTE_MME_GPRS_TIMER_UNIT_1_MINUTE 0x1 +#define LIBLTE_MME_GPRS_TIMER_UNIT_6_MINUTES 0x2 +#define LIBLTE_MME_GPRS_TIMER_DEACTIVATED 0x7 +// Enums +// Structs +typedef struct{ + uint8 unit; + uint8 value; +}LIBLTE_MME_GPRS_TIMER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_ie(LIBLTE_MME_GPRS_TIMER_STRUCT *timer, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_ie(uint8 **ie_ptr, + LIBLTE_MME_GPRS_TIMER_STRUCT *timer); + +/********************************************************************* + IE Name: GPRS Timer 2 + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16A + 24.008 v10.2.0 Section 10.5.7.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_2_ie(uint8 value, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_2_ie(uint8 **ie_ptr, + uint8 *value); + +/********************************************************************* + IE Name: GPRS Timer 3 + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16B + 24.008 v10.2.0 Section 10.5.7.4A +*********************************************************************/ +// Defines +#define LIBLTE_MME_GPRS_TIMER_3_UNIT_10_MINUTES 0x0 +#define LIBLTE_MME_GPRS_TIMER_3_UNIT_1_HOUR 0x1 +#define LIBLTE_MME_GPRS_TIMER_3_UNIT_10_HOURS 0x2 +#define LIBLTE_MME_GPRS_TIMER_3_DEACTIVATED 0x7 +// Enums +// Structs +typedef struct{ + uint8 unit; + uint8 value; +}LIBLTE_MME_GPRS_TIMER_3_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_3_ie(LIBLTE_MME_GPRS_TIMER_3_STRUCT *timer, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_3_ie(uint8 **ie_ptr, + LIBLTE_MME_GPRS_TIMER_3_STRUCT *timer); + +/********************************************************************* + IE Name: Identity Type 2 + + Description: Specifies which identity is requested. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.17 + 24.008 v10.2.0 Section 10.5.5.9 +*********************************************************************/ +// Defines +#define LIBLTE_MME_ID_TYPE_2_IMSI 0x1 +#define LIBLTE_MME_ID_TYPE_2_IMEI 0x2 +#define LIBLTE_MME_ID_TYPE_2_IMEISV 0x3 +#define LIBLTE_MME_ID_TYPE_2_TMSI 0x4 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_type_2_ie(uint8 id_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_type_2_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *id_type); + +/********************************************************************* + IE Name: IMEISV Request + + Description: Indicates that the IMEISV shall be included by the + UE in the authentication and ciphering response + message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.18 + 24.008 v10.2.0 Section 10.5.5.10 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_IMEISV_NOT_REQUESTED = 0, + LIBLTE_MME_IMEISV_REQUESTED, + LIBLTE_MME_IMEISV_REQUEST_N_ITEMS, +}LIBLTE_MME_IMEISV_REQUEST_ENUM; +static const char liblte_mme_imeisv_request_text[LIBLTE_MME_IMEISV_REQUEST_N_ITEMS][20] = {"Not Requested", + "Requested"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_imeisv_request_ie(LIBLTE_MME_IMEISV_REQUEST_ENUM imeisv_req, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_imeisv_request_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_IMEISV_REQUEST_ENUM *imeisv_req); + +/********************************************************************* + IE Name: KSI And Sequence Number + + Description: Provides the network with the key set identifier + (KSI) value of the current EPS security context and + the 5 least significant bits of the NAS COUNT value + applicable for the message including this information + element. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.19 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 ksi; + uint8 seq_num; +}LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ksi_and_sequence_number_ie(LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT *ksi_and_seq_num, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ksi_and_sequence_number_ie(uint8 **ie_ptr, + LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT *ksi_and_seq_num); + +/********************************************************************* + IE Name: MS Network Capability + + Description: Provides the network with information concerning + aspects of the UE related to GPRS. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.20 + 24.008 v10.2.0 Section 10.5.5.12 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_SS_SCREENING_INDICATOR_PHASE_1 = 0, + LIBLTE_MME_SS_SCREENING_INDICATOR_PHASE_2, + LIBLTE_MME_SS_SCREENING_INDICATOR_RESERVED_1, + LIBLTE_MME_SS_SCREENING_INDICATOR_RESERVED_2, + LIBLTE_MME_SS_SCREENING_INDICATOR_N_ITEMS, +}LIBLTE_MME_SS_SCREENING_INDICATOR_ENUM; +static const char liblte_mme_ss_screening_indicator_text[LIBLTE_MME_SS_SCREENING_INDICATOR_N_ITEMS][20] = {"Phase 1", + "Phase 2", + "Reserved 1", + "Reserved 2"}; +// Structs +typedef struct{ + LIBLTE_MME_SS_SCREENING_INDICATOR_ENUM ss_screening; + bool gea[8]; + bool sm_cap_ded; + bool sm_cap_gprs; + bool ucs2; + bool solsa; + bool revision; + bool pfc; + bool lcsva; + bool ho_g2u_via_iu; + bool ho_g2e_via_s1; + bool emm_comb; + bool isr; + bool srvcc; + bool epc; + bool nf; +}LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ms_network_capability_ie(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT *ms_network_cap, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ms_network_capability_ie(uint8 **ie_ptr, + LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT *ms_network_cap); + +/********************************************************************* + IE Name: NAS Key Set Identifier + + Description: Provides the NAS key set identifier that is allocated + by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.21 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_ENUM tsc_flag; + uint8 nas_ksi; +}LIBLTE_MME_NAS_KEY_SET_ID_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_key_set_id_ie(LIBLTE_MME_NAS_KEY_SET_ID_STRUCT *nas_ksi, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_key_set_id_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT *nas_ksi); + +/********************************************************************* + IE Name: NAS Message Container + + Description: Encapsulates the SMS messages transferred between + the UE and the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.22 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *nas_msg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *nas_msg); + +/********************************************************************* + IE Name: NAS Security Algorithms + + Description: Indicates the algorithms to be used for ciphering + and integrity protection. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.23 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_ENUM type_of_eea; + LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_ENUM type_of_eia; +}LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_algorithms_ie(LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT *nas_sec_algs, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_algorithms_ie(uint8 **ie_ptr, + LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT *nas_sec_algs); + +/********************************************************************* + IE Name: Network Name + + Description: Passes a text string to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.24 + 24.008 v10.2.0 Section 10.5.3.5A +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_ADD_CI_DONT_ADD = 0, + LIBLTE_MME_ADD_CI_ADD, + LIBLTE_MME_ADD_CI_N_ITEMS, +}LIBLTE_MME_ADD_CI_ENUM; +static const char liblte_mme_add_ci_text[LIBLTE_MME_ADD_CI_N_ITEMS][20] = {"Don't add", + "Add"}; +// Structs +typedef struct{ + char name[LIBLTE_STRING_LEN]; + LIBLTE_MME_ADD_CI_ENUM add_ci; +}LIBLTE_MME_NETWORK_NAME_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_network_name_ie(LIBLTE_MME_NETWORK_NAME_STRUCT *net_name, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_network_name_ie(uint8 **ie_ptr, + LIBLTE_MME_NETWORK_NAME_STRUCT *net_name); + +/********************************************************************* + IE Name: Nonce + + Description: Transfers a 32-bit nonce value to support deriving + a new mapped EPS security context. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.25 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_nonce_ie(uint32 nonce, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_nonce_ie(uint8 **ie_ptr, + uint32 *nonce); + +/********************************************************************* + IE Name: Paging Identity + + Description: Indicates the identity used for paging for non-EPS + services. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.25A +*********************************************************************/ +// Defines +#define LIBLTE_MME_PAGING_IDENTITY_IMSI 0 +#define LIBLTE_MME_PAGING_IDENTITY_TMSI 1 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_paging_identity_ie(uint8 paging_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_paging_identity_ie(uint8 **ie_ptr, + uint8 *paging_id); + +/********************************************************************* + IE Name: P-TMSI Signature + + Description: Identifies a GMM context of a UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.26 + 24.008 v10.2.0 Section 10.5.5.8 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_p_tmsi_signature_ie(uint32 p_tmsi_signature, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_p_tmsi_signature_ie(uint8 **ie_ptr, + uint32 *p_tmsi_signature); + +/********************************************************************* + IE Name: Service Type + + Description: Specifies the purpose of the service request + procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.27 +*********************************************************************/ +// Defines +#define LIBLTE_MME_SERVICE_TYPE_MO_CSFB 0x0 +#define LIBLTE_MME_SERVICE_TYPE_MT_CSFB 0x1 +#define LIBLTE_MME_SERVICE_TYPE_MO_CSFB_EMERGENCY 0x2 +#define LIBLTE_MME_SERVICE_TYPE_PACKET_SERVICES 0x8 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_service_type_ie(uint8 value, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *value); + +/********************************************************************* + IE Name: Short MAC + + Description: Protects the integrity of a SERVICE REQUEST message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.28 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_short_mac_ie(uint16 short_mac, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_short_mac_ie(uint8 **ie_ptr, + uint16 *short_mac); + +/********************************************************************* + IE Name: Time Zone + + Description: Encodes the offset between universal time and local + time in steps of 15 minutes. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.29 + 24.008 v10.2.0 Section 10.5.3.8 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_time_zone_ie(uint8 tz, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_time_zone_ie(uint8 **ie_ptr, + uint8 *tz); + +/********************************************************************* + IE Name: Time Zone And Time + + Description: Encodes the offset between universal time and local + time in steps of 15 minutes and encodes the universal + time at which the IE may have been sent by the + network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.30 + 24.008 v10.2.0 Section 10.5.3.9 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint16 year; + uint8 month; + uint8 day; + uint8 hour; + uint8 minute; + uint8 second; + uint8 tz; +}LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_time_zone_and_time_ie(LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT *ttz, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_time_zone_and_time_ie(uint8 **ie_ptr, + LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT *ttz); + +/********************************************************************* + IE Name: TMSI Status + + Description: Indicates whether a valid TMSI is available in the + UE or not. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.31 + 24.008 v10.2.0 Section 10.5.5.4 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_TMSI_STATUS_NO_VALID_TMSI = 0, + LIBLTE_MME_TMSI_STATUS_VALID_TMSI, + LIBLTE_MME_TMSI_STATUS_N_ITEMS, +}LIBLTE_MME_TMSI_STATUS_ENUM; +static const char liblte_mme_tmsi_status_text[LIBLTE_MME_TMSI_STATUS_N_ITEMS][20] = {"No valid TMSI", + "Valid TMSI"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tmsi_status_ie(LIBLTE_MME_TMSI_STATUS_ENUM tmsi_status, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tmsi_status_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_TMSI_STATUS_ENUM *tmsi_status); + +/********************************************************************* + IE Name: Tracking Area Identity + + Description: Provides an unambiguous identification of tracking + areas within the area covered by the 3GPP system. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.32 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint16 mcc; + uint16 mnc; + uint16 tac; +}LIBLTE_MME_TRACKING_AREA_ID_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_id_ie(LIBLTE_MME_TRACKING_AREA_ID_STRUCT *tai, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_id_ie(uint8 **ie_ptr, + LIBLTE_MME_TRACKING_AREA_ID_STRUCT *tai); + +/********************************************************************* + IE Name: Tracking Area Identity List + + Description: Transfers a list of tracking areas from the network + to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.33 +*********************************************************************/ +// Defines +#define LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_MAX_SIZE 16 +// Enums +typedef enum{ + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ONE_PLMN_NON_CONSECUTIVE_TACS = 0, + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ONE_PLMN_CONSECUTIVE_TACS, + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_DIFFERENT_PLMNS, + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_N_ITEMS, +}LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ENUM; +static const char liblte_mme_tracking_area_identity_list_type_text[LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_N_ITEMS][100] = {"One PLMN, Non-Consecutive TACs", + "One PLMN, Consecutive TACs", + "Different PLMNs"}; +// Structs +typedef struct{ + LIBLTE_MME_TRACKING_AREA_ID_STRUCT tai[LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_MAX_SIZE]; + uint32 N_tais; +}LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_identity_list_ie(LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT *tai_list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_identity_list_ie(uint8 **ie_ptr, + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT *tai_list); + +/********************************************************************* + IE Name: UE Network Capability + + Description: Provides the network with information concerning + aspects of the UE related to EPS or interworking with + GPRS. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.34 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + bool eea[8]; + bool eia[8]; + bool uea[8]; + bool uea_present; + bool ucs2; + bool ucs2_present; + bool uia[8]; + bool uia_present; + bool lpp; + bool lpp_present; + bool lcs; + bool lcs_present; + bool onexsrvcc; + bool onexsrvcc_present; + bool nf; + bool nf_present; +}LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_network_capability_ie(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT *ue_network_cap, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_network_capability_ie(uint8 **ie_ptr, + LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT *ue_network_cap); + +/********************************************************************* + IE Name: UE Radio Capability Update Needed + + Description: Indicates whether the MME shall delete the stored + UE radio capability information, if any. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.35 +*********************************************************************/ +// Defines +#define LIBLTE_MME_URC_UPDATE_NOT_NEEDED 0 +#define LIBLTE_MME_URC_UPDATE_NEEDED 1 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_radio_capability_update_needed_ie(uint8 urc_update, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_radio_capability_update_needed_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *urc_update); + +/********************************************************************* + IE Name: UE Security Capability + + Description: Indicates which security algorithms are supported by + the UE in S1 mode. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.36 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + bool eea[8]; + bool eia[8]; + bool uea[8]; + bool uea_present; + bool uia[8]; + bool uia_present; + bool gea[8]; + bool gea_present; +}LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_security_capabilities_ie(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *ue_sec_cap, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_security_capabilities_ie(uint8 **ie_ptr, + LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *ue_sec_cap); + +/********************************************************************* + IE Name: Emergency Number List + + Description: Encodes emergency number(s) for use within the + country (as indicated by MCC) where the IE is + received. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.37 + 24.008 v10.2.0 Section 10.5.3.13 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EMERGENCY_NUMBER_LIST_MAX_SIZE 12 +#define LIBLTE_MME_EMERGENCY_NUMBER_MAX_NUM_DIGITS 92 +// Enums +typedef enum{ + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_POLICE = 0, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_AMBULANCE, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_FIRE, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_MANUALLY_INITIATED_ECALL, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_AUTOMATICALLY_INITIATED_ECALL, + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_N_ITEMS, +}LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_ENUM; +static const char liblte_mme_emergency_service_category_text[LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_N_ITEMS][100] = {"Police", + "Ambulance", + "Fire", + "Marine Guard", + "Mountain Rescue", + "Manually Initiated ECall", + "Automatically Initiated ECall"}; +// Structs +typedef struct{ + LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_ENUM emerg_service_cat; + uint32 N_emerg_num_digits; + uint8 emerg_num[LIBLTE_MME_EMERGENCY_NUMBER_MAX_NUM_DIGITS]; +}LIBLTE_MME_EMERGENCY_NUMBER_STRUCT; +typedef struct{ + LIBLTE_MME_EMERGENCY_NUMBER_STRUCT emerg_num[LIBLTE_MME_EMERGENCY_NUMBER_LIST_MAX_SIZE]; + uint32 N_emerg_nums; +}LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_emergency_number_list_ie(LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT *emerg_num_list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_emergency_number_list_ie(uint8 **ie_ptr, + LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT *emerg_num_list); + +/********************************************************************* + IE Name: CLI + + Description: Conveys information about the calling line for a + terminated call to a CS fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.38 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: SS Code + + Description: Conveys information related to a network initiated + supplementary service request to a CS fallback + capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.39 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_ss_code_ie(uint8 code, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_ss_code_ie(uint8 **ie_ptr, + uint8 *code); + +/********************************************************************* + IE Name: LCS Indicator + + Description: Indicates that the origin of the message is due to a + LCS request and the type of this request to a CS + fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.40 +*********************************************************************/ +// Defines +#define LIBLTE_MME_LCS_INDICATOR_MT_LR 0x01 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_lcs_indicator_ie(uint8 lcs_ind, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_lcs_indicator_ie(uint8 **ie_ptr, + uint8 *lcs_ind); + +/********************************************************************* + IE Name: LCS Client Identity + + Description: Conveys information related to the client of a LCS + request for a CS fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.41 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Generic Message Container Type + + Description: Specifies the type of message contained in the + generic message container IE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.42 +*********************************************************************/ +// Defines +#define LIBLTE_MME_GENERIC_MESSAGE_CONTAINER_TYPE_LPP 0x01 +#define LIBLTE_MME_GENERIC_MESSAGE_CONTAINER_TYPE_LOCATION_SERVICES 0x02 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_generic_message_container_type_ie(uint8 msg_cont_type, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_generic_message_container_type_ie(uint8 **ie_ptr, + uint8 *msg_cont_type); + +/********************************************************************* + IE Name: Generic Message Container + + Description: Encapsulates the application message transferred + between the UE and the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.43 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_generic_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_generic_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *msg); + +/********************************************************************* + IE Name: Voice Domain Preference and UE's Usage Setting + + Description: Provides the network with the UE's usage setting and + the voice domain preference for the E-UTRAN. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.44 + 24.008 v10.2.0 Section 10.5.5.28 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_UE_USAGE_SETTING_VOICE_CENTRIC = 0, + LIBLTE_MME_UE_USAGE_SETTING_DATA_CENTRIC, + LIBLTE_MME_UE_USAGE_SETTING_N_ITEMS, +}LIBLTE_MME_UE_USAGE_SETTING_ENUM; +static const char liblte_mme_ue_usage_setting_text[LIBLTE_MME_UE_USAGE_SETTING_N_ITEMS][20] = {"Voice Centric", + "Data Centric"}; +typedef enum{ + LIBLTE_MME_VOICE_DOMAIN_PREF_CS_ONLY = 0, + LIBLTE_MME_VOICE_DOMAIN_PREF_PS_ONLY, + LIBLTE_MME_VOICE_DOMAIN_PREF_CS_PREFFERED, + LIBLTE_MME_VOICE_DOMAIN_PREF_PS_PREFFERED, + LIBLTE_MME_VOICE_DOMAIN_PREF_N_ITEMS, +}LIBLTE_MME_VOICE_DOMAIN_PREF_ENUM; +static const char liblte_mme_voice_domain_pref_text[LIBLTE_MME_VOICE_DOMAIN_PREF_N_ITEMS][20] = {"CS Only", + "PS Only", + "CS Preffered", + "PS Preffered"}; +// Structs +typedef struct{ + LIBLTE_MME_UE_USAGE_SETTING_ENUM ue_usage_setting; + LIBLTE_MME_VOICE_DOMAIN_PREF_ENUM voice_domain_pref; +}LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_voice_domain_pref_and_ue_usage_setting_ie(LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT *voice_domain_pref_and_ue_usage_setting, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_voice_domain_pref_and_ue_usage_setting_ie(uint8 **ie_ptr, + LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT *voice_domain_pref_and_ue_usage_setting); + +/********************************************************************* + IE Name: GUTI Type + + Description: Indicates whether the GUTI included in the same + message in an information element of type EPS + mobility identity represents a native GUTI or a + mapped GUTI. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.45 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_GUTI_TYPE_NATIVE = 0, + LIBLTE_MME_GUTI_TYPE_MAPPED, + LIBLTE_MME_GUTI_TYPE_N_ITEMS, +}LIBLTE_MME_GUTI_TYPE_ENUM; +static const char liblte_mme_guti_type_text[LIBLTE_MME_GUTI_TYPE_N_ITEMS][20] = {"Native", + "Mapped"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_type_ie(LIBLTE_MME_GUTI_TYPE_ENUM guti_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_GUTI_TYPE_ENUM *guti_type); + +/********************************************************************* + IE Name: Access Point Name + + Description: Identifies the packet data network to which the GPRS + user wishes to connect and notifies the access point + of the packet data network that wishes to connect to + the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.1 + 24.008 v10.2.0 Section 10.5.6.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + char apn[LIBLTE_STRING_LEN]; +}LIBLTE_MME_ACCESS_POINT_NAME_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_access_point_name_ie(LIBLTE_MME_ACCESS_POINT_NAME_STRUCT *apn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_access_point_name_ie(uint8 **ie_ptr, + LIBLTE_MME_ACCESS_POINT_NAME_STRUCT *apn); + +/********************************************************************* + IE Name: APN Aggregate Maximum Bit Rate + + Description: Indicates the initial subscribed APN-AMBR when the + UE establishes a PDN connection or indicates the new + APN-AMBR if it is changed by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 apn_ambr_dl; + uint8 apn_ambr_ul; + uint8 apn_ambr_dl_ext; + uint8 apn_ambr_ul_ext; + uint8 apn_ambr_dl_ext2; + uint8 apn_ambr_ul_ext2; + bool ext_present; + bool ext2_present; +}LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_apn_aggregate_maximum_bit_rate_ie(LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT *apn_ambr, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_apn_aggregate_maximum_bit_rate_ie(uint8 **ie_ptr, + LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT *apn_ambr); + +/********************************************************************* + IE Name: Connectivity Type + + Description: Specifies the type of connectivity selected by the + network for the PDN connection. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.2A + 24.008 v10.2.0 Section 10.5.6.19 +*********************************************************************/ +// Defines +#define LIBLTE_MME_CONNECTIVITY_TYPE_NOT_INDICATED 0x0 +#define LIBLTE_MME_CONNECTIVITY_TYPE_LIPA_PDN 0x1 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_connectivity_type_ie(uint8 con_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_connectivity_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *con_type); + +/********************************************************************* + IE Name: EPS Quality Of Service + + Description: Specifies the QoS parameters for an EPS bearer + context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.3 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 qci; + uint8 mbr_ul; + uint8 mbr_dl; + uint8 gbr_ul; + uint8 gbr_dl; + uint8 mbr_ul_ext; + uint8 mbr_dl_ext; + uint8 gbr_ul_ext; + uint8 gbr_dl_ext; + bool br_present; + bool br_ext_present; +}LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_quality_of_service_ie(LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT *qos, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_quality_of_service_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT *qos); + +/********************************************************************* + IE Name: ESM Cause + + Description: Indicates the reason why a session management request + is rejected. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.4 +*********************************************************************/ +// Defines +#define LIBLTE_MME_ESM_CAUSE_OPERATOR_DETERMINED_BARRING 0x08 +#define LIBLTE_MME_ESM_CAUSE_INSUFFICIENT_RESOURCES 0x1A +#define LIBLTE_MME_ESM_CAUSE_UNKNOWN_OR_MISSING_APN 0x1B +#define LIBLTE_MME_ESM_CAUSE_UNKNOWN_PDN_TYPE 0x1C +#define LIBLTE_MME_ESM_CAUSE_USER_AUTHENTICATION_FAILED 0x1D +#define LIBLTE_MME_ESM_CAUSE_REQUEST_REJECTED_BY_SERVING_OR_PDN_GW 0x1E +#define LIBLTE_MME_ESM_CAUSE_REQUEST_REJECTED_UNSPECIFIED 0x1F +#define LIBLTE_MME_ESM_CAUSE_SERVICE_OPTION_NOT_SUPPORTED 0x20 +#define LIBLTE_MME_ESM_CAUSE_REQUESTED_SERVICE_OPTION_NOT_SUBSCRIBED 0x21 +#define LIBLTE_MME_ESM_CAUSE_SERVICE_OPTION_TEMPORARILY_OUT_OF_ORDER 0x22 +#define LIBLTE_MME_ESM_CAUSE_PTI_ALREADY_IN_USE 0x23 +#define LIBLTE_MME_ESM_CAUSE_REGULAR_DEACTIVATION 0x24 +#define LIBLTE_MME_ESM_CAUSE_EPS_QOS_NOT_ACCEPTED 0x25 +#define LIBLTE_MME_ESM_CAUSE_NETWORK_FAILURE 0x26 +#define LIBLTE_MME_ESM_CAUSE_REACTIVATION_REQUESTED 0x27 +#define LIBLTE_MME_ESM_CAUSE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION 0x29 +#define LIBLTE_MME_ESM_CAUSE_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION 0x2A +#define LIBLTE_MME_ESM_CAUSE_INVALID_EPS_BEARER_IDENTITY 0x2B +#define LIBLTE_MME_ESM_CAUSE_SEMANTIC_ERRORS_IN_PACKET_FILTERS 0x2C +#define LIBLTE_MME_ESM_CAUSE_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS 0x2D +#define LIBLTE_MME_ESM_CAUSE_UNUSED 0x2E +#define LIBLTE_MME_ESM_CAUSE_PTI_MISMATCH 0x2F +#define LIBLTE_MME_ESM_CAUSE_LAST_PDN_DISCONNECTION_NOT_ALLOWED 0x31 +#define LIBLTE_MME_ESM_CAUSE_PDN_TYPE_IPV4_ONLY_ALLOWED 0x32 +#define LIBLTE_MME_ESM_CAUSE_PDN_TYPE_IPV6_ONLY_ALLOWED 0x33 +#define LIBLTE_MME_ESM_CAUSE_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED 0x34 +#define LIBLTE_MME_ESM_CAUSE_ESM_INFORMATION_NOT_RECEIVED 0x35 +#define LIBLTE_MME_ESM_CAUSE_PDN_CONNECTION_DOES_NOT_EXIST 0x36 +#define LIBLTE_MME_ESM_CAUSE_MULTIPLE_PDN_CONNECTIONS_FOR_A_GIVEN_APN_NOT_ALLOWED 0x37 +#define LIBLTE_MME_ESM_CAUSE_COLLISION_WITH_NETWORK_INITIATED_REQUEST 0x38 +#define LIBLTE_MME_ESM_CAUSE_UNSUPPORTED_QCI_VALUE 0x3B +#define LIBLTE_MME_ESM_CAUSE_BEARER_HANDLING_NOT_SUPPORTED 0x3C +#define LIBLTE_MME_ESM_CAUSE_INVALID_PTI_VALUE 0x51 +#define LIBLTE_MME_ESM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE 0x5F +#define LIBLTE_MME_ESM_CAUSE_INVALID_MANDATORY_INFORMATION 0x60 +#define LIBLTE_MME_ESM_CAUSE_MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED 0x61 +#define LIBLTE_MME_ESM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE 0x62 +#define LIBLTE_MME_ESM_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED 0x63 +#define LIBLTE_MME_ESM_CAUSE_CONDITIONAL_IE_ERROR 0x64 +#define LIBLTE_MME_ESM_CAUSE_MESSAGE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE 0x65 +#define LIBLTE_MME_ESM_CAUSE_PROTOCOL_ERROR_UNSPECIFIED 0x6F +#define LIBLTE_MME_ESM_CAUSE_APN_RESTRICTION_VALUE_INCOMPATIBLE_WITH_ACTIVE_EPS_BEARER_CONTEXT 0x70 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_cause_ie(uint8 cause, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_cause_ie(uint8 **ie_ptr, + uint8 *cause); + +/********************************************************************* + IE Name: ESM Information Transfer Flag + + Description: Indicates whether ESM information, i.e. protocol + configuration options or APN or both, is to be + transferred security protected. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.5 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_NOT_REQUIRED = 0, + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_REQUIRED, + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_N_ITEMS, +}LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM; +static const char liblte_mme_esm_info_transfer_flag_text[LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_N_ITEMS][20] = {"Not Required", + "Required"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_info_transfer_flag_ie(LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM esm_info_transfer_flag, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_info_transfer_flag_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM *esm_info_transfer_flag); + +/********************************************************************* + IE Name: Linked EPS Bearer Identity + + Description: Identifies the default bearer that is associated + with a dedicated EPS bearer or identifies the EPS + bearer (default or dedicated) with which one or more + packet filters specified in a traffic flow aggregate + are associated. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.6 +*********************************************************************/ +// Defines +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_5 0x5 +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_6 0x6 +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_7 0x7 +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_8 0x8 +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_9 0x9 +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_10 0xA +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_11 0xB +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_12 0xC +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_13 0xD +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_14 0xE +#define LIBLTE_MME_LINKED_EPS_BEARER_IDENTITY_15 0xF +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_linked_eps_bearer_identity_ie(uint8 bearer_id, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_linked_eps_bearer_identity_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *bearer_id); + +/********************************************************************* + IE Name: LLC Service Access Point Identifier + + Description: Identifies the service access point that is used for + the GPRS data transfer at LLC layer. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.7 + 24.008 v10.2.0 Section 10.5.6.9 +*********************************************************************/ +// Defines +#define LIBLTE_MME_LLC_SAPI_NOT_ASSIGNED 0x0 +#define LIBLTE_MME_LLC_SAPI_3 0x3 +#define LIBLTE_MME_LLC_SAPI_5 0x5 +#define LIBLTE_MME_LLC_SAPI_9 0x9 +#define LIBLTE_MME_LLC_SAPI_11 0xB +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_llc_service_access_point_identifier_ie(uint8 llc_sapi, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_llc_service_access_point_identifier_ie(uint8 **ie_ptr, + uint8 *llc_sapi); + +/********************************************************************* + IE Name: Notification Indicator + + Description: Informs the UE about an event which is relevant for + the upper layer using an EPS bearer context or + having requested a procedure transaction. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.7A +*********************************************************************/ +// Defines +#define LIBLTE_MME_NOTIFICATION_INDICATOR_SRVCC_HO_CANCELLED_IMS_SESSION_REEST_REQ 0x01 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_notification_indicator_ie(uint8 notification_ind, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_notification_indicator_ie(uint8 **ie_ptr, + uint8 *notification_ind); + +/********************************************************************* + IE Name: Packet Flow Identifier + + Description: Indicates the packet flow identifier for a packet + flow context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.8 + 24.008 v10.2.0 Section 10.5.6.11 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PACKET_FLOW_ID_BEST_EFFORT 0x00 +#define LIBLTE_MME_PACKET_FLOW_ID_SIGNALLING 0x01 +#define LIBLTE_MME_PACKET_FLOW_ID_SMS 0x02 +#define LIBLTE_MME_PACKET_FLOW_ID_TOM8 0x03 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_packet_flow_identifier_ie(uint8 packet_flow_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_packet_flow_identifier_ie(uint8 **ie_ptr, + uint8 *packet_flow_id); + +/********************************************************************* + IE Name: PDN Address + + Description: Assigns an IPv4 address to the UE associated with a + packet data network and provides the UE with an + interface identifier to be used to build the IPv6 + link local address. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.9 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PDN_TYPE_IPV4 0x1 +#define LIBLTE_MME_PDN_TYPE_IPV6 0x2 +#define LIBLTE_MME_PDN_TYPE_IPV4V6 0x3 +// Enums +// Structs +typedef struct{ + uint8 pdn_type; + uint8 addr[12]; +}LIBLTE_MME_PDN_ADDRESS_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_address_ie(LIBLTE_MME_PDN_ADDRESS_STRUCT *pdn_addr, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_address_ie(uint8 **ie_ptr, + LIBLTE_MME_PDN_ADDRESS_STRUCT *pdn_addr); + +/********************************************************************* + IE Name: PDN Type + + Description: Indicates the IP version capability of the IP stack + associated with the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.10 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PDN_TYPE_IPV4 0x1 +#define LIBLTE_MME_PDN_TYPE_IPV6 0x2 +#define LIBLTE_MME_PDN_TYPE_IPV4V6 0x3 +#define LIBLTE_MME_PDN_TYPE_UNUSED 0x4 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_type_ie(uint8 pdn_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *pdn_type); + +/********************************************************************* + IE Name: Protocol Configuration Options + + Description: Transfers external network protocol options + associated with a PDP context activation and + transfers additional (protocol) data (e.g. + configuration parameters, error codes or messages/ + events) associated with an external protocol or an + application. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.11 + 24.008 v10.2.0 Section 10.5.6.3 +*********************************************************************/ +// Defines +#define LIBLTE_MME_MAX_PROTOCOL_CONFIG_OPTIONS 83 +#define LIBLTE_MME_MAX_PROTOCOL_CONFIG_LEN 248 +#define LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_LCP 0xC021 +#define LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_PAP 0xC023 +#define LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP 0xC223 +#define LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_IPCP 0x8021 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_P_CSCF_IPV6_ADDRESS_REQUEST 0x0001 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_IM_CN_SUBSYSTEM_SIGNALLING_FLAG 0x0002 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DNS_SERVER_IPV6_ADDRESS_REQUEST 0x0003 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_MS_SUPPORT_OF_NETWORK_REQUESTED_BEARER_CONTROL_INDICATOR 0x0005 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DSMIPV6_HOME_AGENT_ADDRESS_REQUEST 0x0007 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DSMIPV6_HOME_NETWORK_PREFIX_REQUEST 0x0008 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DSMIPV6_IPV4_HOME_AGENT_ADDRESS_REQUEST 0x0009 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_IP_ADDRESS_ALLOCATION_VIA_NAS_SIGNALLING 0x000A +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_IPV4_ADDRESS_ALLOCATION_VIA_DHCPV4 0x000B +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_P_CSCF_IPV4_ADDRESS_REQUEST 0x000C +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_DNS_SERVER_IPV4_ADDRESS_REQUEST 0x000D +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_MSISDN_REQUEST 0x000E +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_IFOM_SUPPORT_REQUEST 0x000F +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_UL_IPV4_LINK_MTU_REQUEST 0x0010 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_P_CSCF_IPV6_ADDRESS 0x0001 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_IM_CN_SUBSYSTEM_SIGNALLING_FLAG 0x0002 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DNS_SERVER_IPV6_ADDRESS 0x0003 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_POLICY_CONTROL_REJECTION_CODE 0x0004 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_SELECTED_BEARER_CONTROL_MODE 0x0005 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DSMIPV6_HOME_AGENT_ADDRESS 0x0007 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DSMIPV6_HOME_NETWORK_PREFIX 0x0008 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DSMIPV6_IPV4_HOME_AGENT_ADDRESS 0x0009 +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_P_CSCF_IPV4_ADDRESS 0x000C +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DNS_SERVER_IPV4_ADDRESS 0x000D +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_MSISDN 0x000E +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_IFOM_SUPPORT 0x000F +#define LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_IPV4_LINK_MTU 0x0010 +// Enums +// Structs +typedef struct{ + uint16 id; + uint8 len; + uint8 contents[LIBLTE_MME_MAX_PROTOCOL_CONFIG_LEN]; +}LIBLTE_MME_PROTOCOL_CONFIG_STRUCT; +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_STRUCT opt[LIBLTE_MME_MAX_PROTOCOL_CONFIG_OPTIONS]; + uint32 N_opts; +}LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_protocol_config_options_ie(LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT *protocol_cnfg_opts, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_protocol_config_options_ie(uint8 **ie_ptr, + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT *protocol_cnfg_opts); + +/********************************************************************* + IE Name: Quality Of Service + + Description: Specifies the QoS parameters for a PDP context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.12 + 24.008 v10.2.0 Section 10.5.6.5 +*********************************************************************/ +// Defines +#define LIBLTE_MME_QOS_DELAY_CLASS_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_DELAY_CLASS_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_DELAY_CLASS_1 0x1 +#define LIBLTE_MME_QOS_DELAY_CLASS_2 0x2 +#define LIBLTE_MME_QOS_DELAY_CLASS_3 0x3 +#define LIBLTE_MME_QOS_DELAY_CLASS_4 0x4 +#define LIBLTE_MME_QOS_DELAY_CLASS_RESERVED 0x7 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UNUSED 0x1 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UNACK_GTP_ACK_LLC_RLC_PROTECTED 0x2 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UNACK_GTP_LLC_ACK_RLC_PROTECTED 0x3 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UNACK_GTP_LLC_RLC_PROTECTED 0x4 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_UNACK_GTP_LLC_RLC_UNPROTECTED 0x5 +#define LIBLTE_MME_QOS_RELIABILITY_CLASS_RESERVED 0x7 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_1000BPS 0x1 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_2000BPS 0x2 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_4000BPS 0x3 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_8000BPS 0x4 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_16000BPS 0x5 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_32000BPS 0x6 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_64000BPS 0x7 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_128000BPS 0x8 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_UP_TO_256000BPS 0x9 +#define LIBLTE_MME_QOS_PEAK_THROUGHPUT_RESERVED 0xF +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_HIGH_PRIORITY 0x1 +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_NORMAL_PRIORITY 0x2 +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_LOW_PRIORITY 0x3 +#define LIBLTE_MME_QOS_PRECEDENCE_CLASS_RESERVED 0x7 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_UL_SUBSCRIBED 0x00 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_DL_RESERVED 0x00 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_100BPH 0x01 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_200BPH 0x02 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_500BPH 0x03 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_1000BPH 0x04 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_2000BPH 0x05 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_5000BPH 0x06 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_10000BPH 0x07 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_20000BPH 0x08 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_50000BPH 0x09 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_100000BPH 0x0A +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_200000BPH 0x0B +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_500000BPH 0x0C +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_1000000BPH 0x0D +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_2000000BPH 0x0E +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_5000000BPH 0x0F +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_10000000BPH 0x10 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_20000000BPH 0x11 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_50000000BPH 0x12 +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_RESERVED 0x1E +#define LIBLTE_MME_QOS_MEAN_THROUGHPUT_BEST_EFFORT 0x1F +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_CONVERSATIONAL 0x1 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_STREAMING 0x2 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_INTERACTIVE 0x3 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_BACKGROUND 0x4 +#define LIBLTE_MME_QOS_TRAFFIC_CLASS_RESERVED 0x7 +#define LIBLTE_MME_QOS_DELIVERY_ORDER_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_DELIVERY_ORDER_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_DELIVERY_ORDER_WITH_DELIVERY_ORDER_YES 0x1 +#define LIBLTE_MME_QOS_DELIVERY_ORDER_WITHOUT_DELIVERY_ORDER_NO 0x2 +#define LIBLTE_MME_QOS_DELIVERY_ORDER_RESERVED 0x3 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_NO_DETECT 0x1 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_DELIVERED 0x2 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_NOT_DELIVERED 0x3 +#define LIBLTE_MME_QOS_DELIVERY_OF_ERRONEOUS_SDU_RESERVED 0x7 +#define LIBLTE_MME_QOS_MAX_SDU_SIZE_UL_SUBSCRIBED 0x00 +#define LIBLTE_MME_QOS_MAX_SDU_SIZE_DL_RESERVED 0x00 +#define LIBLTE_MME_QOS_MAX_SDU_SIZE_RESERVED 0xFF +#define LIBLTE_MME_QOS_RESIDUAL_BER_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_RESIDUAL_BER_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_RESIDUAL_BER_5_E_NEG_2 0x1 +#define LIBLTE_MME_QOS_RESIDUAL_BER_1_E_NEG_2 0x2 +#define LIBLTE_MME_QOS_RESIDUAL_BER_5_E_NEG_3 0x3 +#define LIBLTE_MME_QOS_RESIDUAL_BER_4_E_NEG_3 0x4 +#define LIBLTE_MME_QOS_RESIDUAL_BER_1_E_NEG_3 0x5 +#define LIBLTE_MME_QOS_RESIDUAL_BER_1_E_NEG_4 0x6 +#define LIBLTE_MME_QOS_RESIDUAL_BER_1_E_NEG_5 0x7 +#define LIBLTE_MME_QOS_RESIDUAL_BER_1_E_NEG_6 0x8 +#define LIBLTE_MME_QOS_RESIDUAL_BER_6_E_NEG_8 0x9 +#define LIBLTE_MME_QOS_RESIDUAL_BER_RESERVED 0xF +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_2 0x1 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_7_E_NEG_3 0x2 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_3 0x3 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_4 0x4 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_5 0x5 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_6 0x6 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_1_E_NEG_1 0x7 +#define LIBLTE_MME_QOS_SDU_ERROR_RATIO_RESERVED 0xF +#define LIBLTE_MME_QOS_TRANSFER_DELAY_UL_SUBSCRIBED 0x00 +#define LIBLTE_MME_QOS_TRANSFER_DELAY_DL_RESERVED 0x00 +#define LIBLTE_MME_QOS_TRANSFER_DELAY_RESERVED 0x3F +#define LIBLTE_MME_QOS_TRAFFIC_HANDLING_PRIORITY_UL_SUBSCRIBED 0x0 +#define LIBLTE_MME_QOS_TRAFFIC_HANDLING_PRIORITY_DL_RESERVED 0x0 +#define LIBLTE_MME_QOS_TRAFFIC_HANDLING_PRIORITY_LEVEL_1 0x1 +#define LIBLTE_MME_QOS_TRAFFIC_HANDLING_PRIORITY_LEVEL_2 0x2 +#define LIBLTE_MME_QOS_TRAFFIC_HANDLING_PRIORITY_LEVEL_3 0x3 +#define LIBLTE_MME_QOS_SIGNALLING_INDICATOR_NOT_OPTIMIZED_FOR_SIGNALLING 0x0 +#define LIBLTE_MME_QOS_SIGNALLING_INDICATOR_OPTIMIZED_FOR_SIGNALLING 0x1 +#define LIBLTE_MME_QOS_SOURCE_STATISTICS_DESCRIPTOR_UNKNOWN 0x0 +#define LIBLTE_MME_QOS_SOURCE_STATISTICS_DESCRIPTOR_SPEECH 0x1 +// Enums +// Structs +typedef struct{ + uint8 delay_class; + uint8 reliability_class; + uint8 peak_throughput; + uint8 precedence_class; + uint8 mean_throughput; + uint8 traffic_class; + uint8 delivery_order; + uint8 delivery_of_erroneous_sdu; + uint8 max_sdu_size; + uint8 mbr_ul; + uint8 mbr_dl; + uint8 residual_ber; + uint8 sdu_error_ratio; + uint8 transfer_delay; + uint8 traffic_handling_prio; + uint8 gbr_ul; + uint8 gbr_dl; + uint8 signalling_ind; + uint8 source_stats_descriptor; + uint8 mbr_dl_ext; + uint8 gbr_dl_ext; + uint8 mbr_ul_ext; + uint8 gbr_ul_ext; + bool dl_ext_present; + bool ul_ext_present; +}LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_quality_of_service_ie(LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT *qos, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_quality_of_service_ie(uint8 **ie_ptr, + LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT *qos); + +/********************************************************************* + IE Name: Radio Priority + + Description: Specifies the priority level the UE shall use at the + lower layers for transmission of data related to a + PDP context or for mobile originated SMS + transmission. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.13 + 24.008 v10.2.0 Section 10.5.7.2 +*********************************************************************/ +// Defines +#define LIBLTE_MME_RADIO_PRIORITY_LEVEL_1 0x1 +#define LIBLTE_MME_RADIO_PRIORITY_LEVEL_2 0x2 +#define LIBLTE_MME_RADIO_PRIORITY_LEVEL_3 0x3 +#define LIBLTE_MME_RADIO_PRIORITY_LEVEL_4 0x4 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_radio_priority_ie(uint8 radio_prio, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_radio_priority_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *radio_prio); + +/********************************************************************* + IE Name: Request Type + + Description: Indicates whether the UE requests to establish a new + connectivity to a PDN or keep the connection(s) to + which it has connected via non-3GPP access. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.14 + 24.008 v10.2.0 Section 10.5.6.17 +*********************************************************************/ +// Defines +#define LIBLTE_MME_REQUEST_TYPE_INITIAL_REQUEST 0x1 +#define LIBLTE_MME_REQUEST_TYPE_HANDOVER 0x2 +#define LIBLTE_MME_REQUEST_TYPE_UNUSED 0x3 +#define LIBLTE_MME_REQUEST_TYPE_EMERGENCY 0x4 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_request_type_ie(uint8 req_type, + uint8 bit_offset, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_request_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *req_type); + +/********************************************************************* + IE Name: Traffic Flow Aggregate Description + + Description: Specifies the aggregate of one or more packet filters + and their related parameters and operations. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.15 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PACKET_FILTER_LIST_MAX_SIZE 15 +#define LIBLTE_MME_PACKET_FILTER_MAX_SIZE 20 +#define LIBLTE_MME_PARAMETER_LIST_MAX_SIZE 15 +#define LIBLTE_MME_PARAMETER_MAX_SIZE 20 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_IPV4_REMOTE_ADDRESS_TYPE 0x10 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_IPV6_REMOTE_ADDRESS_TYPE 0x20 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_PROTOCOL_ID_NEXT_HEADER_TYPE 0x30 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_SINGLE_LOCAL_PORT_TYPE 0x40 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_LOCAL_PORT_RANGE_TYPE 0x41 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_SINGLE_REMOTE_PORT_TYPE 0x50 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_REMOTE_PORT_RANGE_TYPE 0x51 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_SECURITY_PARAMETER_INDEX_TYPE 0x60 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_TYPE_OF_SERVICE_TRAFFIC_CLASS_TYPE 0x70 +#define LIBLTE_MME_TFT_PACKET_FILTER_COMPONENT_TYPE_ID_FLOW_LABEL_TYPE 0x80 +// Enums +typedef enum{ + LIBLTE_MME_TFT_OPERATION_CODE_SPARE = 0, + LIBLTE_MME_TFT_OPERATION_CODE_CREATE_NEW_TFT, + LIBLTE_MME_TFT_OPERATION_CODE_DELETE_EXISTING_TFT, + LIBLTE_MME_TFT_OPERATION_CODE_ADD_PACKET_FILTERS_TO_EXISTING_TFT, + LIBLTE_MME_TFT_OPERATION_CODE_REPLACE_PACKET_FILTERS_IN_EXISTING_TFT, + LIBLTE_MME_TFT_OPERATION_CODE_DELETE_PACKET_FILTERS_FROM_EXISTING_TFT, + LIBLTE_MME_TFT_OPERATION_CODE_NO_TFT_OPERATION, + LIBLTE_MME_TFT_OPERATION_CODE_RESERVED, + LIBLTE_MME_TFT_OPERATION_CODE_N_ITEMS, +}LIBLTE_MME_TFT_OPERATION_CODE_ENUM; +static const char liblte_mme_tft_operation_code_text[LIBLTE_MME_TFT_OPERATION_CODE_N_ITEMS][100] = {"SPARE", + "Create New TFT", + "Delete Existing TFT", + "Add Packet Filters to Existing TFT", + "Replace Packet Filters in Existing TFT", + "Delete Packet Filters from Existing TFT", + "No TFT Operation", + "RESERVED"}; +typedef enum{ + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_PRE_REL_7_TFT_FILTER = 0, + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_DOWNLINK_ONLY, + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_UPLINK_ONLY, + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_BIDIRECTIONAL, + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_N_ITEMS, +}LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_ENUM; +static const char liblte_mme_tft_packet_filter_direction_text[LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_N_ITEMS][100] = {"Pre Rel-7 TFT Filter", + "Downlink Only", + "Uplink Only", + "Bidirectional"}; +// Structs +typedef struct{ + LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_ENUM dir; + uint8 id; + uint8 eval_precedence; + uint8 filter[LIBLTE_MME_PACKET_FILTER_MAX_SIZE]; + uint8 filter_size; +}LIBLTE_MME_PACKET_FILTER_STRUCT; +typedef struct{ + uint8 id; + uint8 parameter[LIBLTE_MME_PARAMETER_MAX_SIZE]; + uint8 parameter_size; +}LIBLTE_MME_PARAMETER_STRUCT; +typedef struct{ + LIBLTE_MME_PACKET_FILTER_STRUCT packet_filter_list[LIBLTE_MME_PACKET_FILTER_LIST_MAX_SIZE]; + LIBLTE_MME_PARAMETER_STRUCT parameter_list[LIBLTE_MME_PARAMETER_LIST_MAX_SIZE]; + LIBLTE_MME_TFT_OPERATION_CODE_ENUM tft_op_code; + uint8 packet_filter_list_size; + uint8 parameter_list_size; +}LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT; +typedef LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_traffic_flow_aggregate_description_ie(LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT *tfad, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_traffic_flow_aggregate_description_ie(uint8 **ie_ptr, + LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT *tfad); + +/********************************************************************* + IE Name: Traffic Flow Template + + Description: Specifies the TFT parameters and operations for a + PDP context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.16 + 24.008 v10.2.0 Section 10.5.6.12 +*********************************************************************/ +// Defines +// Traffic Flow Template defines defined above +// Enums +// Traffic Flow Template enums defined above +// Structs +// Traffic Flow Template structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_traffic_flow_template_ie(LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *tft, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_traffic_flow_template_ie(uint8 **ie_ptr, + LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *tft); + +/********************************************************************* + IE Name: Transaction Identifier + + Description: Represents the corresponding PDP context in A/Gb + mode or Iu mode which is mapped from the EPS bearer + context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.17 + 24.008 v10.2.0 Section 10.5.6.7 +*********************************************************************/ +// Defines +#define LIBLTE_MME_TI_FLAG_SENT_FROM_ORIGINATOR 0 +#define LIBLTE_MME_TI_FLAG_SENT_TO_ORIGINATOR 1 +#define LIBLTE_MME_TI_VALUE_IS_GIVEN_BY_TIE 0x7 +// Enums +// Structs +typedef struct{ + uint8 ti_flag; + uint8 tio; + uint8 tie; +}LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_transaction_identifier_ie(LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT *trans_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_mme_unpack_transaction_identifier_ie(uint8 **ie_ptr, + LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT *trans_id); + +/******************************************************************************* + MESSAGE DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + Message Name: Message Header (Plain NAS Message) + + Description: Message header for plain NAS messages. + + Document Reference: 24.301 v10.2.0 Section 9.1 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT 0x2 +#define LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT 0x7 +#define LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS 0x0 +#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY 0x1 +#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED 0x2 +#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT 0x3 +#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT 0x4 +#define LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST 0xC +#define LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST 0x41 +#define LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT 0x42 +#define LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE 0x43 +#define LIBLTE_MME_MSG_TYPE_ATTACH_REJECT 0x44 +#define LIBLTE_MME_MSG_TYPE_DETACH_REQUEST 0x45 +#define LIBLTE_MME_MSG_TYPE_DETACH_ACCEPT 0x46 +#define LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST 0x48 +#define LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_ACCEPT 0x49 +#define LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_COMPLETE 0x4A +#define LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REJECT 0x4B +#define LIBLTE_MME_MSG_TYPE_EXTENDED_SERVICE_REQUEST 0x4C +#define LIBLTE_MME_MSG_TYPE_SERVICE_REJECT 0x4E +#define LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMMAND 0x50 +#define LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMPLETE 0x51 +#define LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST 0x52 +#define LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE 0x53 +#define LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT 0x54 +#define LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE 0x5C +#define LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST 0x55 +#define LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE 0x56 +#define LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND 0x5D +#define LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE 0x5E +#define LIBLTE_MME_MSG_TYPE_SECURITY_MODE_REJECT 0x5F +#define LIBLTE_MME_MSG_TYPE_EMM_STATUS 0x60 +#define LIBLTE_MME_MSG_TYPE_EMM_INFORMATION 0x61 +#define LIBLTE_MME_MSG_TYPE_DOWNLINK_NAS_TRANSPORT 0x62 +#define LIBLTE_MME_MSG_TYPE_UPLINK_NAS_TRANSPORT 0x63 +#define LIBLTE_MME_MSG_TYPE_CS_SERVICE_NOTIFICATION 0x64 +#define LIBLTE_MME_MSG_TYPE_DOWNLINK_GENERIC_NAS_TRANSPORT 0x68 +#define LIBLTE_MME_MSG_TYPE_UPLINK_GENERIC_NAS_TRANSPORT 0x69 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST 0xC1 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT 0xC2 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT 0xC3 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST 0xC5 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT 0xC6 +#define LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT 0xC7 +#define LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REQUEST 0xC9 +#define LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_ACCEPT 0xCA +#define LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REJECT 0xCB +#define LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST 0xCD +#define LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT 0xCE +#define LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REQUEST 0xD0 +#define LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REJECT 0xD1 +#define LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REQUEST 0xD2 +#define LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REJECT 0xD3 +#define LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REQUEST 0xD4 +#define LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REJECT 0xD5 +#define LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REQUEST 0xD6 +#define LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REJECT 0xD7 +#define LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST 0xD9 +#define LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE 0xDA +#define LIBLTE_MME_MSG_TYPE_NOTIFICATION 0xDB +#define LIBLTE_MME_MSG_TYPE_ESM_STATUS 0xE8 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_mme_parse_msg_sec_header(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 *pd, + uint8 *sec_hdr_type); +LIBLTE_ERROR_ENUM liblte_mme_parse_msg_header(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 *pd, + uint8 *msg_type); +LIBLTE_ERROR_ENUM liblte_mme_pack_security_protected_nas_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *sec_msg); + +/********************************************************************* + Message Name: Attach Accept + + Description: Sent by the network to the UE to indicate that the + corresponding attach request has been accepted. + + Document Reference: 24.301 v10.2.0 Section 8.2.1 +*********************************************************************/ +// Defines +#define LIBLTE_MME_GUTI_IEI 0x50 +#define LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI 0x13 +#define LIBLTE_MME_MS_IDENTITY_IEI 0x23 +#define LIBLTE_MME_EMM_CAUSE_IEI 0x53 +#define LIBLTE_MME_T3402_VALUE_IEI 0x17 +#define LIBLTE_MME_T3423_VALUE_IEI 0x59 +#define LIBLTE_MME_EQUIVALENT_PLMNS_IEI 0x4A +#define LIBLTE_MME_EMERGENCY_NUMBER_LIST_IEI 0x34 +#define LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI 0x64 +#define LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI 0xF +#define LIBLTE_MME_T3412_EXTENDED_VALUE_IEI 0x5E +// Enums +// Structs +typedef struct{ + LIBLTE_MME_GPRS_TIMER_STRUCT t3412; + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT tai_list; + LIBLTE_BYTE_MSG_STRUCT esm_msg; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT guti; + LIBLTE_MME_LOCATION_AREA_ID_STRUCT lai; + LIBLTE_MME_MOBILE_ID_STRUCT ms_id; + LIBLTE_MME_GPRS_TIMER_STRUCT t3402; + LIBLTE_MME_GPRS_TIMER_STRUCT t3423; + LIBLTE_MME_PLMN_LIST_STRUCT equivalent_plmns; + LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT emerg_num_list; + LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT eps_network_feature_support; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3412_ext; + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM additional_update_result; + uint8 eps_attach_result; + uint8 emm_cause; + bool guti_present; + bool lai_present; + bool ms_id_present; + bool emm_cause_present; + bool t3402_present; + bool t3423_present; + bool equivalent_plmns_present; + bool emerg_num_list_present; + bool eps_network_feature_support_present; + bool additional_update_result_present; + bool t3412_ext_present; +}LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_accept_msg(LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT *attach_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT *attach_accept); + +/********************************************************************* + Message Name: Attach Complete + + Description: Sent by the UE to the network in response to an + ATTACH ACCEPT message. + + Document Reference: 24.301 v10.2.0 Section 8.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT esm_msg; +}LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_complete_msg(LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT *attach_comp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT *attach_comp); + +/********************************************************************* + Message Name: Attach Reject + + Description: Sent by the network to the UE to indicate that the + corresponding attach request has been rejected. + + Document Reference: 24.301 v10.2.0 Section 8.2.3 +*********************************************************************/ +// Defines +#define LIBLTE_MME_ESM_MSG_CONTAINER_IEI 0x78 +#define LIBLTE_MME_T3446_VALUE_IEI 0x5F +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT esm_msg; + uint8 emm_cause; + uint8 t3446_value; + bool esm_msg_present; + bool t3446_value_present; +}LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_reject_msg(LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT *attach_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT *attach_rej); + +/********************************************************************* + Message Name: Attach Request + + Description: Sent by the UE to the network to perform an attach + procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.4 +*********************************************************************/ +// Defines +#define LIBLTE_MME_P_TMSI_SIGNATURE_IEI 0x19 +#define LIBLTE_MME_ADDITIONAL_GUTI_IEI 0x50 +#define LIBLTE_MME_LAST_VISITED_REGISTERED_TAI_IEI 0x52 +#define LIBLTE_MME_DRX_PARAMETER_IEI 0x5C +#define LIBLTE_MME_MS_NETWORK_CAPABILITY_IEI 0x31 +#define LIBLTE_MME_TMSI_STATUS_IEI 0x9 +#define LIBLTE_MME_MS_CLASSMARK_2_IEI 0x11 +#define LIBLTE_MME_MS_CLASSMARK_3_IEI 0x20 +#define LIBLTE_MME_SUPPORTED_CODEC_LIST_IEI 0x40 +#define LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_IEI 0xF +#define LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_IEI 0x5D +#define LIBLTE_MME_ATTACH_REQUEST_DEVICE_PROPERTIES_IEI 0xD +#define LIBLTE_MME_GUTI_TYPE_IEI 0xE +// Enums +// Structs +typedef struct{ + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT nas_ksi; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT eps_mobile_id; + LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap; + LIBLTE_BYTE_MSG_STRUCT esm_msg; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT additional_guti; + LIBLTE_MME_TRACKING_AREA_ID_STRUCT last_visited_registered_tai; + LIBLTE_MME_DRX_PARAMETER_STRUCT drx_param; + LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap; + LIBLTE_MME_LOCATION_AREA_ID_STRUCT old_lai; + LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT ms_cm2; + LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT ms_cm3; + LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT supported_codecs; + LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT voice_domain_pref_and_ue_usage_setting; + LIBLTE_MME_TMSI_STATUS_ENUM tmsi_status; + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM additional_update_type; + LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_properties; + LIBLTE_MME_GUTI_TYPE_ENUM old_guti_type; + uint32 old_p_tmsi_signature; + uint8 eps_attach_type; + bool old_p_tmsi_signature_present; + bool additional_guti_present; + bool last_visited_registered_tai_present; + bool drx_param_present; + bool ms_network_cap_present; + bool old_lai_present; + bool tmsi_status_present; + bool ms_cm2_present; + bool ms_cm3_present; + bool supported_codecs_present; + bool additional_update_type_present; + bool voice_domain_pref_and_ue_usage_setting_present; + bool device_properties_present; + bool old_guti_type_present; +}LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req); + +/********************************************************************* + Message Name: Authentication Failure + + Description: Sent by the UE to the network to indicate that + authentication of the network has failed. + + Document Reference: 24.301 v10.2.0 Section 8.2.5 +*********************************************************************/ +// Defines +#define LIBLTE_MME_AUTHENTICATION_FAILURE_PARAMETER_IEI 0x30 +// Enums +// Structs +typedef struct{ + uint8 emm_cause; + uint8 auth_fail_param[16]; + bool auth_fail_param_present; +}LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_failure_msg(LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT *auth_fail, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_failure_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT *auth_fail); + +/********************************************************************* + Message Name: Authentication Reject + + Description: Sent by the network to the UE to indicate that the + authentication procedure has failed and that the UE + shall abort all activities. + + Document Reference: 24.301 v10.2.0 Section 8.2.6 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_reject_msg(LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT *auth_reject, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT *auth_reject); + +/********************************************************************* + Message Name: Authentication Request + + Description: Sent by the network to the UE to initiate + authentication of the UE identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.7 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT nas_ksi; + uint8 autn[16]; + uint8 rand[16]; +}LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_request_msg(LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT *auth_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT *auth_req); + +/********************************************************************* + Message Name: Authentication Response + + Description: Sent by the UE to the network to deliver a calculated + authentication response to the network. + + Document Reference: 24.301 v10.2.0 Section 8.2.8 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 res[16]; + int res_len; +}LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_msg(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp); + +/********************************************************************* + Message Name: CS Service Notification + + Description: Sent by the network when a paging request with CS + call indicator was received via SGs for a UE, and a + NAS signalling connection is already established for + the UE. + + Document Reference: 24.301 v10.2.0 Section 8.2.9 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + Message Name: Detach Accept + + Description: Sent by the network to indicate that the detach + procedure has been completed. + + Document Reference: 24.301 v10.2.0 Section 8.2.10 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_accept_msg(LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT *detach_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT *detach_accept); + +/********************************************************************* + Message Name: Detach Request + + Description: Sent by the UE to request the release of an EMM + context. + + Document Reference: 24.301 v10.2.0 Section 8.2.11 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_DETACH_TYPE_STRUCT detach_type; + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT nas_ksi; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT eps_mobile_id; +}LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_request_msg(LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT *detach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT *detach_req); + +/********************************************************************* + Message Name: Downlink NAS Transport + + Description: Sent by the network to the UE in order to carry an + SMS message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.12 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT nas_msg; +}LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_nas_transport_msg(LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT *dl_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT *dl_nas_transport); + +/********************************************************************* + Message Name: EMM Information + + Description: Sent by the network at any time during EMM context is + established to send certain information to the UE. + + Document Reference: 24.301 v10.2.0 Section 8.2.13 +*********************************************************************/ +// Defines +#define LIBLTE_MME_FULL_NAME_FOR_NETWORK_IEI 0x43 +#define LIBLTE_MME_SHORT_NAME_FOR_NETWORK_IEI 0x45 +#define LIBLTE_MME_LOCAL_TIME_ZONE_IEI 0x46 +#define LIBLTE_MME_UNIVERSAL_TIME_AND_LOCAL_TIME_ZONE_IEI 0x47 +#define LIBLTE_MME_NETWORK_DAYLIGHT_SAVING_TIME_IEI 0x49 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_NETWORK_NAME_STRUCT full_net_name; + LIBLTE_MME_NETWORK_NAME_STRUCT short_net_name; + LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT utc_and_local_time_zone; + LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM net_dst; + uint8 local_time_zone; + bool full_net_name_present; + bool short_net_name_present; + bool local_time_zone_present; + bool utc_and_local_time_zone_present; + bool net_dst_present; +}LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_information_msg(LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT *emm_info, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_information_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT *emm_info); + +/********************************************************************* + Message Name: EMM Status + + Description: Sent by the UE or by the network at any time to + report certain error conditions. + + Document Reference: 24.301 v10.2.0 Section 8.2.14 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 emm_cause; +}LIBLTE_MME_EMM_STATUS_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_status_msg(LIBLTE_MME_EMM_STATUS_MSG_STRUCT *emm_status, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_status_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EMM_STATUS_MSG_STRUCT *emm_status); + +/********************************************************************* + Message Name: Extended Service Request + + Description: Sent by the UE to the network to initiate a CS + fallback or 1xCS fallback call or respond to a mobile + terminated CS fallback or 1xCS fallback request from + the network or to request the establishment of a NAS + signalling connection and of the radio and S1 bearers + for packet services, if the UE needs to provide + additional information that cannot be provided via a + SERVICE REQUEST message. + + Document Reference: 24.301 v10.2.0 Section 8.2.15 +*********************************************************************/ +// Defines +#define LIBLTE_MME_CSFB_RESPONSE_IEI 0xB +#define LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_IEI 0x57 +#define LIBLTE_MME_EXTENDED_SERVICE_REQUEST_DEVICE_PROPERTIES_IEI 0xD +// Enums +// Structs +typedef struct{ + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT nas_ksi; + LIBLTE_MME_MOBILE_ID_STRUCT m_tmsi; + LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT eps_bearer_context_status; + LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_props; + uint8 service_type; + uint8 csfb_resp; + bool csfb_resp_present; + bool eps_bearer_context_status_present; + bool device_props_present; +}LIBLTE_MME_EXTENDED_SERVICE_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_extended_service_request_msg(LIBLTE_MME_EXTENDED_SERVICE_REQUEST_MSG_STRUCT *ext_service_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_extended_service_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EXTENDED_SERVICE_REQUEST_MSG_STRUCT *ext_service_req); + +/********************************************************************* + Message Name: GUTI Reallocation Command + + Description: Sent by the network to the UE to reallocate a GUTI + and optionally provide a new TAI list. + + Document Reference: 24.301 v10.2.0 Section 8.2.16 +*********************************************************************/ +// Defines +#define LIBLTE_MME_TAI_LIST_IEI 0x54 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_EPS_MOBILE_ID_STRUCT guti; + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT tai_list; + bool tai_list_present; +}LIBLTE_MME_GUTI_REALLOCATION_COMMAND_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_reallocation_command_msg(LIBLTE_MME_GUTI_REALLOCATION_COMMAND_MSG_STRUCT *guti_realloc_cmd, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_reallocation_command_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_GUTI_REALLOCATION_COMMAND_MSG_STRUCT *guti_realloc_cmd); + +/********************************************************************* + Message Name: GUTI Reallocation Complete + + Description: Sent by the UE to the network to indicate that + reallocation of a GUTI has taken place. + + Document Reference: 24.301 v10.2.0 Section 8.2.17 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_MME_GUTI_REALLOCATION_COMPLETE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_reallocation_complete_msg(LIBLTE_MME_GUTI_REALLOCATION_COMPLETE_MSG_STRUCT *guti_realloc_complete, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_reallocation_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_GUTI_REALLOCATION_COMPLETE_MSG_STRUCT *guti_realloc_complete); + +/********************************************************************* + Message Name: Identity Request + + Description: Sent by the network to the UE to request the UE to + provide the specified identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.18 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 id_type; +}LIBLTE_MME_ID_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_request_msg(LIBLTE_MME_ID_REQUEST_MSG_STRUCT *id_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ID_REQUEST_MSG_STRUCT *id_req); + +/********************************************************************* + Message Name: Identity Response + + Description: Sent by the UE to the network in response to an + IDENTITY REQUEST message and provides the requested + identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.19 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_MOBILE_ID_STRUCT mobile_id; +}LIBLTE_MME_ID_RESPONSE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_response_msg(LIBLTE_MME_ID_RESPONSE_MSG_STRUCT *id_resp, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT *id_resp); + +/********************************************************************* + Message Name: Security Mode Command + + Description: Sent by the network to the UE to establish NAS + signalling security. + + Document Reference: 24.301 v10.2.0 Section 8.2.20 +*********************************************************************/ +// Defines +#define LIBLTE_MME_IMEISV_REQUEST_IEI 0xC +#define LIBLTE_MME_REPLAYED_NONCE_UE_IEI 0x55 +#define LIBLTE_MME_NONCE_MME_IEI 0x56 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT selected_nas_sec_algs; + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT nas_ksi; + LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT ue_security_cap; + LIBLTE_MME_IMEISV_REQUEST_ENUM imeisv_req; + uint32 nonce_ue; + uint32 nonce_mme; + bool imeisv_req_present; + bool nonce_ue_present; + bool nonce_mme_present; +}LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_command_msg(LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT *sec_mode_cmd, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_command_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT *sec_mode_cmd); + +/********************************************************************* + Message Name: Security Mode Complete + + Description: Sent by the UE to the network in response to a + SECURITY MODE COMMAND message. + + Document Reference: 24.301 v10.2.0 Section 8.2.21 +*********************************************************************/ +// Defines +#define LIBLTE_MME_IMEISV_IEI 0x23 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_MOBILE_ID_STRUCT imeisv; + bool imeisv_present; +}LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_complete_msg(LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT *sec_mode_comp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT *sec_mode_comp); + +/********************************************************************* + Message Name: Security Mode Reject + + Description: Sent by the UE to the network to indicate that the + corresponding security mode command has been + rejected. + + Document Reference: 24.301 v10.2.0 Section 8.2.22 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 emm_cause; +}LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_reject_msg(LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT *sec_mode_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT *sec_mode_rej); + +/********************************************************************* + Message Name: Service Reject + + Description: Sent by the network to the UE in order to reject the + service request procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.24 +*********************************************************************/ +// Defines +#define LIBLTE_MME_T3442_VALUE_IEI 0x5B +// Enums +// Structs +typedef struct{ + LIBLTE_MME_GPRS_TIMER_STRUCT t3442; + uint8 emm_cause; + uint8 t3446; + bool t3442_present; + bool t3446_present; +}LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_service_reject_msg(LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT *service_rej, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT *service_rej); + +/********************************************************************* + Message Name: Service Request + + Description: Sent by the UE to the network to request the + establishment of a NAS signalling connection and of + the radio and S1 bearers. + + Document Reference: 24.301 v10.2.0 Section 8.2.25 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT ksi_and_seq_num; + uint16 short_mac; +}LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_service_request_msg(LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT *service_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT *service_req); + +/********************************************************************* + Message Name: Tracking Area Update Accept + + Description: Sent by the network to the UE to provide the UE with + EPS mobility management related data in response to + a tracking area update request message. + + Document Reference: 24.301 v10.2.0 Section 8.2.26 +*********************************************************************/ +// Defines +#define LIBLTE_MME_T3412_VALUE_IEI 0x5A +// Enums +// Structs +typedef struct{ + LIBLTE_MME_GPRS_TIMER_STRUCT t3412; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT guti; + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT tai_list; + LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT eps_bearer_context_status; + LIBLTE_MME_LOCATION_AREA_ID_STRUCT lai; + LIBLTE_MME_MOBILE_ID_STRUCT ms_id; + LIBLTE_MME_GPRS_TIMER_STRUCT t3402; + LIBLTE_MME_GPRS_TIMER_STRUCT t3423; + LIBLTE_MME_PLMN_LIST_STRUCT equivalent_plmns; + LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT emerg_num_list; + LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT eps_network_feature_support; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3412_ext; + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM additional_update_result; + uint8 eps_update_result; + uint8 emm_cause; + bool t3412_present; + bool guti_present; + bool tai_list_present; + bool eps_bearer_context_status_present; + bool lai_present; + bool ms_id_present; + bool emm_cause_present; + bool t3402_present; + bool t3423_present; + bool equivalent_plmns_present; + bool emerg_num_list_present; + bool eps_network_feature_support_present; + bool additional_update_result_present; + bool t3412_ext_present; +}LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_accept_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT *ta_update_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT *ta_update_accept); + +/********************************************************************* + Message Name: Tracking Area Update Complete + + Description: Sent by the UE to the network in response to a + tracking area update accept message if a GUTI has + been changed or a new TMSI has been assigned. + + Document Reference: 24.301 v10.2.0 Section 8.2.27 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_MME_TRACKING_AREA_UPDATE_COMPLETE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_complete_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_COMPLETE_MSG_STRUCT *ta_update_complete, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_COMPLETE_MSG_STRUCT *ta_update_complete); + +/********************************************************************* + Message Name: Tracking Area Update Reject + + Description: Sent by the network to the UE in order to reject the + tracking area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.28 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 emm_cause; + uint8 t3446; + bool t3446_present; +}LIBLTE_MME_TRACKING_AREA_UPDATE_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_reject_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_REJECT_MSG_STRUCT *ta_update_rej, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_REJECT_MSG_STRUCT *ta_update_rej); + +/********************************************************************* + Message Name: Tracking Area Update Request + + Description: Sent by the UE to the network to initiate a tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.29 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + Message Name: Uplink NAS Transport + + Description: Sent by the UE to the network in order to carry an + SMS message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.30 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT nas_msg; +}LIBLTE_MME_UPLINK_NAS_TRANSPORT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_uplink_nas_transport_msg(LIBLTE_MME_UPLINK_NAS_TRANSPORT_MSG_STRUCT *ul_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_uplink_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_UPLINK_NAS_TRANSPORT_MSG_STRUCT *ul_nas_transport); + +/********************************************************************* + Message Name: Downlink Generic NAS Transport + + Description: Sent by the network to the UE in order to carry an + application message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.31 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT generic_msg_cont; + LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT add_info; + uint8 generic_msg_cont_type; + bool add_info_present; +}LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_generic_nas_transport_msg(LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *dl_generic_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_generic_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *dl_generic_nas_transport); + +/********************************************************************* + Message Name: Uplink Generic NAS Transport + + Description: Sent by the UE to the network in order to carry an + application protocol message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.32 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT generic_msg_cont; + LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT add_info; + uint8 generic_msg_cont_type; + bool add_info_present; +}LIBLTE_MME_UPLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_uplink_generic_nas_transport_msg(LIBLTE_MME_UPLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *ul_generic_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_uplink_generic_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_UPLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *ul_generic_nas_transport); + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge + activation of a dedicated EPS bearer context + associated with the same PDN address(es) and APN as + an already active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI 0x27 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_accept_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_ded_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_ded_eps_bearer_context_accept); + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Reject + + Description: Sent by the UE to the network to reject activation + of a dedicated EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_reject_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_ded_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_ded_eps_bearer_context_rej); + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Request + + Description: Sent by the network to the UE to request activation + of a dedicated EPS bearer context associated with + the same PDN address(es) and APN as an already + active default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.3 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT eps_qos; + LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT tft; + LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT transaction_id; + LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT negotiated_qos; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 linked_eps_bearer_id; + uint8 llc_sapi; + uint8 radio_prio; + uint8 packet_flow_id; + bool transaction_id_present; + bool negotiated_qos_present; + bool llc_sapi_present; + bool radio_prio_present; + bool packet_flow_id_present; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_request_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_ded_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_ded_eps_bearer_context_req); + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge + activation of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_accept_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_def_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_def_eps_bearer_context_accept); + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Reject + + Description: Sent by the UE to the network to reject activation + of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_reject_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_def_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_def_eps_bearer_context_rej); + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Request + + Description: Sent by the network to the UE to request activation + of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.6 +*********************************************************************/ +// Defines +#define LIBLTE_MME_TRANSACTION_IDENTIFIER_IEI 0x5D +#define LIBLTE_MME_QUALITY_OF_SERVICE_IEI 0x30 +#define LIBLTE_MME_LLC_SAPI_IEI 0x32 +#define LIBLTE_MME_RADIO_PRIORITY_IEI 0x8 +#define LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI 0x34 +#define LIBLTE_MME_APN_AMBR_IEI 0x5E +#define LIBLTE_MME_ESM_CAUSE_IEI 0x58 +#define LIBLTE_MME_CONNECTIVITY_TYPE_IEI 0xB +// Enums +// Structs +typedef struct{ + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT eps_qos; + LIBLTE_MME_ACCESS_POINT_NAME_STRUCT apn; + LIBLTE_MME_PDN_ADDRESS_STRUCT pdn_addr; + LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT transaction_id; + LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT negotiated_qos; + LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT apn_ambr; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 llc_sapi; + uint8 radio_prio; + uint8 packet_flow_id; + uint8 esm_cause; + uint8 connectivity_type; + bool transaction_id_present; + bool negotiated_qos_present; + bool llc_sapi_present; + bool radio_prio_present; + bool packet_flow_id_present; + bool apn_ambr_present; + bool esm_cause_present; + bool protocol_cnfg_opts_present; + bool connectivity_type_present; +}LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_request_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_def_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_def_eps_bearer_context_req); + +/********************************************************************* + Message Name: Bearer Resource Allocation Reject + + Description: Sent by the network to the UE to reject the + allocation of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_MME_T3496_VALUE_IEI 0x37 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3496; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; + bool t3496_present; +}LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_allocation_reject_msg(LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REJECT_MSG_STRUCT *bearer_res_alloc_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_allocation_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REJECT_MSG_STRUCT *bearer_res_alloc_rej); + +/********************************************************************* + Message Name: Bearer Resource Allocation Request + + Description: Sent by the UE to the network to request the + allocation of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.8 +*********************************************************************/ +// Defines +#define LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_DEVICE_PROPERTIES_IEI 0xC +// Enums +// Structs +typedef struct{ + LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT tfa; + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT req_tf_qos; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_properties; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 linked_eps_bearer_id; + bool protocol_cnfg_opts_present; + bool device_properties_present; +}LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_allocation_request_msg(LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_MSG_STRUCT *bearer_res_alloc_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_allocation_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_MSG_STRUCT *bearer_res_alloc_req); + +/********************************************************************* + Message Name: Bearer Resource Modification Reject + + Description: Sent by the network to the UE to reject the + modification of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.9 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3496; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; + bool t3496_present; +}LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_modification_reject_msg(LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REJECT_MSG_STRUCT *bearer_res_mod_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_modification_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REJECT_MSG_STRUCT *bearer_res_mod_rej); + +/********************************************************************* + Message Name: Bearer Resource Modification Request + + Description: Sent by the UE to the network to request the + modification of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.10 +*********************************************************************/ +// Defines +#define LIBLTE_MME_EPS_QUALITY_OF_SERVICE_IEI 0x5B +#define LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_DEVICE_PROPERTIES_IEI 0xC +// Enums +// Structs +typedef struct{ + LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT tfa; + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT req_tf_qos; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_properties; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 eps_bearer_id_for_packet_filter; + uint8 esm_cause; + bool req_tf_qos_present; + bool esm_cause_present; + bool protocol_cnfg_opts_present; + bool device_properties_present; +}LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_modification_request_msg(LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_MSG_STRUCT *bearer_res_mod_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_modification_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_MSG_STRUCT *bearer_res_mod_req); + +/********************************************************************* + Message Name: Deactivate EPS Bearer Context Accept + + Description: Sent by the UE to acknowledge deactivation of the + EPS bearer context requested in the corresponding + deactivate EPS bearer context request message. + + Document Reference: 24.301 v10.2.0 Section 8.3.11 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_accept_msg(LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *deact_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *deact_eps_bearer_context_accept); + +/********************************************************************* + Message Name: Deactivate EPS Bearer Context Request + + Description: Sent by the network to request deactivation of an + EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.12 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_request_msg(LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *deact_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *deact_eps_bearer_context_req); + +/********************************************************************* + Message Name: ESM Information Request + + Description: Sent by the network to the UE to request the UE to + provide ESM information, i.e. protocol configuration + options or APN or both. + + Document Reference: 24.301 v10.2.0 Section 8.3.13 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 eps_bearer_id; + uint8 proc_transaction_id; +}LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM srslte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req); + +/********************************************************************* + Message Name: ESM Information Response + + Description: Sent by the UE to the network in response to an ESM + INFORMATION REQUEST message and provides the + requested ESM information. + + Document Reference: 24.301 v10.2.0 Section 8.3.14 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_ACCESS_POINT_NAME_STRUCT apn; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + bool apn_present; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); +LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); + +/********************************************************************* + Message Name: ESM Status + + Description: Sent by the network or the UE to pass information on + the status of the indicated EPS bearer context and + report certain error conditions. + + Document Reference: 24.301 v10.2.0 Section 8.3.15 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; +}LIBLTE_MME_ESM_STATUS_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_status_msg(LIBLTE_MME_ESM_STATUS_MSG_STRUCT *esm_status, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_status_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_STATUS_MSG_STRUCT *esm_status); + +/********************************************************************* + Message Name: Modify EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge the + modification of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.16 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_accept_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *mod_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *mod_eps_bearer_context_accept); + +/********************************************************************* + Message Name: Modify EPS Bearer Context Reject + + Description: Sent by the UE or the network to reject a + modification of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.17 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_reject_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *mod_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *mod_eps_bearer_context_rej); + +/********************************************************************* + Message Name: Modify EPS Bearer Context Request + + Description: Sent by the network to the UE to request modification + of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.18 +*********************************************************************/ +// Defines +#define LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_IEI 0x36 +#define LIBLTE_MME_QUALITY_OF_SERVICE_IEI 0x30 +// Enums +// Structs +typedef struct{ + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT new_eps_qos; + LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT tft; + LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT new_qos; + LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT apn_ambr; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 negotiated_llc_sapi; + uint8 radio_prio; + uint8 packet_flow_id; + bool new_eps_qos_present; + bool tft_present; + bool new_qos_present; + bool negotiated_llc_sapi_present; + bool radio_prio_present; + bool packet_flow_id_present; + bool apn_ambr_present; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_request_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *mod_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *mod_eps_bearer_context_req); + +/********************************************************************* + Message Name: Notification + + Description: Sent by the network to inform the UE about events + which are relevant for the upper layer using an EPS + bearer context or having requested a procedure + transaction. + + Document Reference: 24.301 v10.2.0 Section 8.3.18A +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 notification_ind; +}LIBLTE_MME_NOTIFICATION_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_notification_msg(LIBLTE_MME_NOTIFICATION_MSG_STRUCT *notification, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_notification_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_NOTIFICATION_MSG_STRUCT *notification); + +/********************************************************************* + Message Name: PDN Connectivity Reject + + Description: Sent by the network to the UE to reject establishment + of a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.19 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3496; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; + bool t3496_present; +}LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_connectivity_reject_msg(LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT *pdn_con_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_connectivity_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT *pdn_con_rej); + +/********************************************************************* + Message Name: PDN Connectivity Request + + Description: Sent by the UE to the network to initiate + establishment of a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.20 +*********************************************************************/ +// Defines +#define LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_IEI 0xD +#define LIBLTE_MME_ACCESS_POINT_NAME_IEI 0x28 +#define LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_DEVICE_PROPERTIES_IEI 0xC +// Enums +// Structs +typedef struct{ + LIBLTE_MME_ACCESS_POINT_NAME_STRUCT apn; + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM esm_info_transfer_flag; + LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_properties; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 pdn_type; + uint8 request_type; + bool esm_info_transfer_flag_present; + bool apn_present; + bool protocol_cnfg_opts_present; + bool device_properties_present; +}LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_connectivity_request_msg(LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_connectivity_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req); + +/********************************************************************* + Message Name: PDN Disconnect Reject + + Description: Sent by the network to the UE to reject release of a + PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.21 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 esm_cause; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_PDN_DISCONNECT_REJECT_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_disconnect_reject_msg(LIBLTE_MME_PDN_DISCONNECT_REJECT_MSG_STRUCT *pdn_discon_rej, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_disconnect_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_DISCONNECT_REJECT_MSG_STRUCT *pdn_discon_rej); + +/********************************************************************* + Message Name: PDN Disconnect Request + + Description: Sent by the UE to the network to initiate release of + a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.22 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT protocol_cnfg_opts; + uint8 eps_bearer_id; + uint8 proc_transaction_id; + uint8 linked_eps_bearer_id; + bool protocol_cnfg_opts_present; +}LIBLTE_MME_PDN_DISCONNECT_REQUEST_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_disconnect_request_msg(LIBLTE_MME_PDN_DISCONNECT_REQUEST_MSG_STRUCT *pdn_discon_req, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_disconnect_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_DISCONNECT_REQUEST_MSG_STRUCT *pdn_discon_req); + +#endif // SRSLTE_LIBLTE_MME_H diff --git a/lib/include/srslte/asn1/liblte_rrc.h b/lib/include/srslte/asn1/liblte_rrc.h new file mode 100644 index 0000000..15afb3c --- /dev/null +++ b/lib/include/srslte/asn1/liblte_rrc.h @@ -0,0 +1,7143 @@ +/******************************************************************************* + + Copyright 2012-2014 Ben Wojtowicz + Copyright 2014 Andrew Murphy (SIB13 unpack) + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_rrc.h + + Description: Contains all the definitions for the LTE Radio Resource + Control Layer library. + + Revision History + ---------- ------------- -------------------------------------------- + 03/24/2012 Ben Wojtowicz Created file. + 04/21/2012 Ben Wojtowicz Added SIB1 parameters, IEs, and messages + 05/28/2012 Ben Wojtowicz Added SIB1 pack functionality + 08/19/2012 Ben Wojtowicz Added functionality to support SIB2, SIB3, + SIB4, and SIB8 packing and unpacking + 10/06/2012 Ben Wojtowicz Added more decoding/encoding. + 12/26/2012 Ben Wojtowicz Added text versions of some enums. + 03/03/2013 Ben Wojtowicz Added a test fill pattern, text and number + mappings for all enums, carrier_freqs_geran, + SIB5, SIB6, SIB7, and paging packing and + unpacking. + 07/21/2013 Ben Wojtowicz Using the common msg structure. + 03/26/2014 Ben Wojtowicz Added support for RRC Connection Request, + RRC Connection Reestablishment Request, + and UL CCCH Messages. + 05/04/2014 Ben Wojtowicz Added support for DL CCCH Messages. + 06/15/2014 Ben Wojtowicz Added support for UL DCCH Messages. + 08/03/2014 Ben Wojtowicz Added more decoding/encoding. + 09/19/2014 Andrew Murphy Added SIB13 unpack. + 11/01/2014 Ben Wojtowicz Added more decoding/encoding. + 11/09/2014 Ben Wojtowicz Added SIB13 pack. + 11/29/2014 Ben Wojtowicz Fixed a bug in RRC connection reestablishment + UE identity. + +*******************************************************************************/ + +#ifndef SRSLTE_LIBLTE_RRC_H +#define SRSLTE_LIBLTE_RRC_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "liblte_common.h" + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + +static const uint8 liblte_rrc_test_fill[8] = {1,0,1,0,0,1,0,1}; + +typedef void (*log_handler_t)(void *ctx, char *str); + +void liblte_rrc_log_register_handler(void *ctx, log_handler_t handler); + +/******************************************************************************* + INFORMATION ELEMENT DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + IE Name: MBSFN Notification Config + + Description: Specifies the MBMS notification related configuration + parameters, that are applicable for all MBSFN areas + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N2 = 0, + LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N4, + LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N_ITEMS, +}LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_ENUM; +static const char liblte_rrc_notification_repetition_coeff_r9_text[LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N_ITEMS][20] = {"2", "4"}; +static const uint8 liblte_rrc_notification_repetition_coeff_r9_num[LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N_ITEMS] = {2, 4}; +// Structs +typedef struct{ + LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_ENUM repetition_coeff; + uint8 offset; + uint8 sf_index; +}LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_notification_config_ie(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT *mbsfn_notification_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_notification_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT *mbsfn_notification_cnfg); + +/********************************************************************* + IE Name: MBSFN Area Info List + + Description: Contains the information required to acquire the MBMS + control information associated with one or more MBSFN + areas + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_MBSFN_AREAS 8 +// Enums +typedef enum{ + LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_S1 = 0, + LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_S2, + LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_N_ITEMS, +}LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_ENUM; +static const char liblte_rrc_non_mbsfn_region_length_text[LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_N_ITEMS][20] = {"1", "2"}; +static const uint8 liblte_rrc_non_mbsfn_region_length_num[LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_N_ITEMS] = {1, 2}; +typedef enum{ + LIBLTE_RRC_MCCH_REPETITION_PERIOD_RF32 = 0, + LIBLTE_RRC_MCCH_REPETITION_PERIOD_RF64, + LIBLTE_RRC_MCCH_REPETITION_PERIOD_RF128, + LIBLTE_RRC_MCCH_REPETITION_PERIOD_RF256, + LIBLTE_RRC_MCCH_REPETITION_PERIOD_N_ITEMS, +}LIBLTE_RRC_MCCH_REPETITION_PERIOD_ENUM; +static const char liblte_rrc_mcch_repetition_period_r9_text[LIBLTE_RRC_MCCH_REPETITION_PERIOD_N_ITEMS][20] = {"32", "64", "128", "256"}; +static const uint16 liblte_rrc_mcch_repetition_period_r9_num[LIBLTE_RRC_MCCH_REPETITION_PERIOD_N_ITEMS] = {32, 64, 128, 256}; +typedef enum{ + LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_512 = 0, + LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_1024, + LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_N_ITEMS, +}LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_ENUM; +static const char liblte_rrc_mcch_modification_period_r9_text[LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_N_ITEMS][20] = {"512", "1024"}; +static const uint16 liblte_rrc_mcch_modification_period_r9_num[LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_N_ITEMS] = {512, 1024}; +typedef enum{ + LIBLTE_RRC_MCCH_SIGNALLING_MCS_N2 = 0, + LIBLTE_RRC_MCCH_SIGNALLING_MCS_N7, + LIBLTE_RRC_MCCH_SIGNALLING_MCS_N13, + LIBLTE_RRC_MCCH_SIGNALLING_MCS_N19, + LIBLTE_RRC_MCCH_SIGNALLING_MCS_N_ITEMS, +}LIBLTE_RRC_MCCH_SIGNALLING_MCS_ENUM; +static const char liblte_rrc_mcch_signalling_mcs_r9_text[LIBLTE_RRC_MCCH_SIGNALLING_MCS_N_ITEMS][20] = {"2", "7", "13", "19"}; +static const uint8 liblte_rrc_mcch_signalling_mcs_r9_num[LIBLTE_RRC_MCCH_SIGNALLING_MCS_N_ITEMS] = {2, 7, 13, 19}; +// Structs +typedef struct{ + LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_ENUM non_mbsfn_region_length; + LIBLTE_RRC_MCCH_REPETITION_PERIOD_ENUM mcch_repetition_period_r9; + LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_ENUM mcch_modification_period_r9; + LIBLTE_RRC_MCCH_SIGNALLING_MCS_ENUM signalling_mcs_r9; + uint8 mbsfn_area_id_r9; + uint8 notification_indicator_r9; + uint8 mcch_offset_r9; + uint8 sf_alloc_info_r9; +}LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_info_ie(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *mbsfn_area_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *mbsfn_area_info); + +/********************************************************************* + IE Name: MBSFN Subframe Config + + Description: Defines subframes that are reserved for MBSFN in + downlink + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS 8 +// Enums +typedef enum{ + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N1 = 0, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N2, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N4, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N8, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N16, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N32, + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N_ITEMS, +}LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_ENUM; +static const char liblte_rrc_radio_frame_allocation_period_text[LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N_ITEMS][20] = { "1", "2", "4", "8", + "16", "32"}; +static const uint8 liblte_rrc_radio_frame_allocation_period_num[LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N_ITEMS] = {1, 2, 4, 8, 16, 32}; +typedef enum{ + LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE = 0, + LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR, + LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_N_ITEMS, +}LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ENUM; +static const char liblte_rrc_subframe_allocation_num_frames_text[LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_N_ITEMS][20] = {"1", "4"}; +static const uint8 liblte_rrc_subframe_allocation_num_frames_num[LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_N_ITEMS] = {1, 4}; +// Structs +typedef struct{ + LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_ENUM radio_fr_alloc_period; + LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ENUM subfr_alloc_num_frames; + uint32 subfr_alloc; + uint8 radio_fr_alloc_offset; +}LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_subframe_config_ie(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *mbsfn_subfr_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_subframe_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *mbsfn_subfr_cnfg); + +/********************************************************************* + IE Name: TMGI + + Description: Temporary Mobile Group Identity (PLMN + MBMS service ID) + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint16 mcc; + uint16 mnc; +}LIBLTE_RRC_PLMN_IDENTITY_STRUCT; +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id_r9; + uint8 plmn_index_r9; + bool plmn_id_explicit; + uint32 serviceid_r9; +}LIBLTE_RRC_TMGI_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_tmgi_r9_ie(LIBLTE_RRC_TMGI_R9_STRUCT *tmgi, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tmgi_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_TMGI_R9_STRUCT *tmgi); + +/********************************************************************* + IE Name: MBMS Session Info + + Description: Information about an individual MBMS session + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_TMGI_R9_STRUCT tmgi_r9; + uint8 sessionid_r9; + bool sessionid_r9_present; + uint8 logicalchannelid_r9; +}LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbms_session_info_r9_ie(LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbms_session_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info); + +/********************************************************************* + IE Name: PMCH Config + + Description: Contains configuration parameters of the sessions + carried by a PMCH + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines + +// Enums +typedef enum{ + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8 = 0, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF16, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF32, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF64, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF128, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF256, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF512, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024, + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS, +}LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM; +static const char liblte_rrc_mch_scheduling_period_r9_text[LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS][20] = {"8", "16", "32", "64", "128", "256", "512", "1024"}; +static const uint16 liblte_rrc_mch_scheduling_period_r9_num[LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_N_ITEMS] = {8, 16, 32, 64, 128, 256, 512, 1024}; + +// Structs +typedef struct{ + uint16 sf_alloc_end_r9; + uint8 datamcs_r9; + LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM mch_schedulingperiod_r9; +}LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_config_r9_ie(LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_config_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg); + +/********************************************************************* + IE Name: PMCH Info + + Description: Specifies configuration of PMCH of an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_SESSION_PER_PMCH 29 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT pmch_config_r9; + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT mbms_sessioninfolist_r9[LIBLTE_RRC_MAX_SESSION_PER_PMCH]; + uint8 mbms_sessioninfolist_r9_size; +}LIBLTE_RRC_PMCH_INFO_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_info_r9_ie(LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info); + +/********************************************************************* + IE Name: C-RNTI + + Description: Identifies a UE having a RRC connection within a cell + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_c_rnti_ie(uint16 rnti, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_c_rnti_ie(uint8 **ie_ptr, + uint16 *rnti); + +/********************************************************************* + IE Name: Dedicated Info CDMA2000 + + Description: Transfers UE specific CDMA2000 information between + the network and the UE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_dedicated_info_cdma2000_ie(LIBLTE_BYTE_MSG_STRUCT *ded_info_cdma2000, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dedicated_info_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *ded_info_cdma2000); + +/********************************************************************* + IE Name: Dedicated Info NAS + + Description: Transfers UE specific NAS layer information between + the network and the UE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_dedicated_info_nas_ie(LIBLTE_BYTE_MSG_STRUCT *ded_info_nas, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dedicated_info_nas_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *ded_info_nas); + +/********************************************************************* + IE Name: Filter Coefficient + + Description: Specifies the measurement filtering coefficient + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_FILTER_COEFFICIENT_FC0 = 0, + LIBLTE_RRC_FILTER_COEFFICIENT_FC1, + LIBLTE_RRC_FILTER_COEFFICIENT_FC2, + LIBLTE_RRC_FILTER_COEFFICIENT_FC3, + LIBLTE_RRC_FILTER_COEFFICIENT_FC4, + LIBLTE_RRC_FILTER_COEFFICIENT_FC5, + LIBLTE_RRC_FILTER_COEFFICIENT_FC6, + LIBLTE_RRC_FILTER_COEFFICIENT_FC7, + LIBLTE_RRC_FILTER_COEFFICIENT_FC8, + LIBLTE_RRC_FILTER_COEFFICIENT_FC9, + LIBLTE_RRC_FILTER_COEFFICIENT_FC11, + LIBLTE_RRC_FILTER_COEFFICIENT_FC13, + LIBLTE_RRC_FILTER_COEFFICIENT_FC15, + LIBLTE_RRC_FILTER_COEFFICIENT_FC17, + LIBLTE_RRC_FILTER_COEFFICIENT_FC19, + LIBLTE_RRC_FILTER_COEFFICIENT_SPARE1, + LIBLTE_RRC_FILTER_COEFFICIENT_N_ITEMS, +}LIBLTE_RRC_FILTER_COEFFICIENT_ENUM; +static const char liblte_rrc_filter_coefficient_text[LIBLTE_RRC_FILTER_COEFFICIENT_N_ITEMS][20] = { "0", "1", "2", "3", + "4", "5", "6", "7", + "8", "9", "11", "13", + "15", "17", "19", "SPARE"}; +static const int8 liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 19, -1}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_filter_coefficient_ie(LIBLTE_RRC_FILTER_COEFFICIENT_ENUM filter_coeff, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_filter_coefficient_ie(uint8 **ie_ptr, + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM *filter_coeff); + +/********************************************************************* + IE Name: MMEC + + Description: Identifies an MME within the scope of an MME group + within a PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mmec_ie(uint8 mmec, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mmec_ie(uint8 **ie_ptr, + uint8 *mmec); + +/********************************************************************* + IE Name: Neigh Cell Config + + Description: Provides the information related to MBSFN and TDD + UL/DL configuration of neighbor cells + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_neigh_cell_config_ie(uint8 neigh_cell_config, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_neigh_cell_config_ie(uint8 **ie_ptr, + uint8 *neigh_cell_config); + +/********************************************************************* + IE Name: Other Config + + Description: Contains configuration related to other configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_ENABLED = 0, + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_N_ITEMS, +}LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_ENUM; +static const char liblte_rrc_report_proximity_indication_eutra_r9_text[LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_N_ITEMS][20] = {"Enabled"}; +typedef enum{ + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_ENABLED = 0, + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_N_ITEMS, +}LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_ENUM; +static const char liblte_rrc_report_proximity_indication_utra_r9_text[LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_N_ITEMS][20] = {"Enabled"}; +// Structs +typedef struct{ + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_ENUM report_proximity_ind_eutra; + LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_ENUM report_proximity_ind_utra; + bool report_proximity_ind_eutra_present; + bool report_proximity_ind_utra_present; +}LIBLTE_RRC_REPORT_PROXIMITY_CONFIG_R9_STRUCT; +typedef struct{ + LIBLTE_RRC_REPORT_PROXIMITY_CONFIG_R9_STRUCT report_proximity_cnfg; + bool report_proximity_cnfg_present; +}LIBLTE_RRC_OTHER_CONFIG_R9_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_other_config_ie(LIBLTE_RRC_OTHER_CONFIG_R9_STRUCT *other_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_other_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_OTHER_CONFIG_R9_STRUCT *other_cnfg); + +/********************************************************************* + IE Name: RAND CDMA2000 (1xRTT) + + Description: Contains a random value, generated by the eNB, to be + passed to the CDMA2000 upper layers + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rand_cdma2000_1xrtt_ie(uint32 rand, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rand_cdma2000_1xrtt_ie(uint8 **ie_ptr, + uint32 *rand); + +/********************************************************************* + IE Name: RAT Type + + Description: Indicates the radio access technology (RAT), + including E-UTRA, of the requested/transferred UE + capabilities + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_RAT_TYPE_EUTRA = 0, + LIBLTE_RRC_RAT_TYPE_UTRA, + LIBLTE_RRC_RAT_TYPE_GERAN_CS, + LIBLTE_RRC_RAT_TYPE_GERAN_PS, + LIBLTE_RRC_RAT_TYPE_CDMA2000_1XRTT, + LIBLTE_RRC_RAT_TYPE_SPARE_3, + LIBLTE_RRC_RAT_TYPE_SPARE_2, + LIBLTE_RRC_RAT_TYPE_SPARE_1, + LIBLTE_RRC_RAT_TYPE_N_ITEMS, +}LIBLTE_RRC_RAT_TYPE_ENUM; +static const char liblte_rrc_rat_type_text[LIBLTE_RRC_RAT_TYPE_N_ITEMS][20] = { "EUTRA", "UTRA", "GERAN_CS", "GERAN_PS", + "CDMA2000_1XRTT", "SPARE", "SPARE", "SPARE"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rat_type_ie(LIBLTE_RRC_RAT_TYPE_ENUM rat_type, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rat_type_ie(uint8 **ie_ptr, + LIBLTE_RRC_RAT_TYPE_ENUM *rat_type); + +/********************************************************************* + IE Name: RRC Transaction Identifier + + Description: Identifies an RRC procedure along with the message + type + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_transaction_identifier_ie(uint8 rrc_transaction_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_transaction_identifier_ie(uint8 **ie_ptr, + uint8 *rrc_transaction_id); + +/********************************************************************* + IE Name: S-TMSI + + Description: Contains an S-Temporary Mobile Subscriber Identity, + a temporary UE identity provided by the EPC which + uniquely identifies the UE within the tracking area + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint32 m_tmsi; + uint8 mmec; +}LIBLTE_RRC_S_TMSI_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_s_tmsi_ie(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_s_tmsi_ie(uint8 **ie_ptr, + LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi); + +/********************************************************************* + IE Name: UE Capability RAT Container List + + Description: Contains list of containers, one for each RAT for + which UE capabilities are transferred + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: UE EUTRA Capability + + Description: Conveys the E-UTRA UE Radio Access Capability + Parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAXBANDS 64 + +// Enums +typedef enum{ + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_REL8 = 0, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_REL9, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE6, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE5, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE4, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE3, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE2, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_SPARE1, + LIBLTE_RRC_ACCESS_STRATUM_RELEASE_N_ITEMS +}LIBLTE_RRC_ACCESS_STRATUM_RELEASE_ENUM; +static const char liblte_rrc_access_stratum_release_text[LIBLTE_RRC_ACCESS_STRATUM_RELEASE_N_ITEMS][20] = { "rel8", "rel9", "spare6", "spare5", + "spare4", "spare3", "spare2", "spare1"}; + +typedef enum{ + LIBLTE_RRC_ROHC_PROFILES_0x0001, + LIBLTE_RRC_ROHC_PROFILES_0x0002, + LIBLTE_RRC_ROHC_PROFILES_0x0003, + LIBLTE_RRC_ROHC_PROFILES_0x0004, + LIBLTE_RRC_ROHC_PROFILES_0x0006, + LIBLTE_RRC_ROHC_PROFILES_0x0101, + LIBLTE_RRC_ROHC_PROFILES_0x0102, + LIBLTE_RRC_ROHC_PROFILES_0x0103, + LIBLTE_RRC_ROHC_PROFILES_0x0104, + LIBLTE_RRC_ROHC_PROFILES_NITEMS +}LIBLTE_RRC_ROHC_PROFILES_ENUM; +static const char liblte_rrc_rohc_profiles_text[LIBLTE_RRC_ROHC_PROFILES_NITEMS][20] = {"profile0x0001", + "profile0x0002", + "profile0x0003", + "profile0x0004", + "profile0x0006", + "profile0x0101", + "profile0x0102", + "profile0x0103", + "profile0x0104"}; + +typedef enum{ + LIBLTE_RRC_MAX_ROHC_CTXTS_CS2 = 0, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS4, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS8, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS12, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS16, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS24, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS32, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS48, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS64, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS128, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS256, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS512, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS1024, + LIBLTE_RRC_MAX_ROHC_CTXTS_CS16384, + LIBLTE_RRC_MAX_ROHC_CTXTS_SPARE1, + LIBLTE_RRC_MAX_ROHC_CTXTS_SPARE2, + LIBLTE_RRC_MAX_ROHC_CTXTS_N_ITEMS +}LIBLTE_RRC_MAX_ROHC_CTXTS_ENUM; +static const char liblte_rrc_max_rohc_ctxts_text[LIBLTE_RRC_MAX_ROHC_CTXTS_N_ITEMS][20] = { "cs2", "cs4", "cs8", "cs12", "cs16", "cs24", "cs32", + "cs48", "cs64", "cs128", "cs256", "cs512", "cs1024", + "cs16384", "spare2", "spare1"}; + +typedef enum{ + LIBLTE_RRC_BAND_1 = 0, + LIBLTE_RRC_BAND_2, + LIBLTE_RRC_BAND_3, + LIBLTE_RRC_BAND_4, + LIBLTE_RRC_BAND_5, + LIBLTE_RRC_BAND_6, + LIBLTE_RRC_BAND_7, + LIBLTE_RRC_BAND_8, + LIBLTE_RRC_BAND_9, + LIBLTE_RRC_BAND_10, + LIBLTE_RRC_BAND_11, + LIBLTE_RRC_BAND_12, + LIBLTE_RRC_BAND_13, + LIBLTE_RRC_BAND_14, + LIBLTE_RRC_BAND_17, + LIBLTE_RRC_BAND_18, + LIBLTE_RRC_BAND_19, + LIBLTE_RRC_BAND_20, + LIBLTE_RRC_BAND_21, + LIBLTE_RRC_BAND_22, + LIBLTE_RRC_BAND_23, + LIBLTE_RRC_BAND_24, + LIBLTE_RRC_BAND_25, + LIBLTE_RRC_BAND_33, + LIBLTE_RRC_BAND_34, + LIBLTE_RRC_BAND_35, + LIBLTE_RRC_BAND_36, + LIBLTE_RRC_BAND_37, + LIBLTE_RRC_BAND_38, + LIBLTE_RRC_BAND_39, + LIBLTE_RRC_BAND_40, + LIBLTE_RRC_BAND_41, + LIBLTE_RRC_BAND_42, + LIBLTE_RRC_BAND_43, + LIBLTE_RRC_BAND_N_ITEMS, +}LIBLTE_RRC_BAND_ENUM; +static const char liblte_rrc_band_text[LIBLTE_RRC_BAND_N_ITEMS][20] = { "1", "2", "3", "4", + "5", "6", "7", "8", + "9", "10", "11", "12", + "13", "14", "17", "18", + "19", "20", "21", "22", + "23", "24", "25", "33", + "34", "35", "36", "37", + "38", "39", "40", "41", + "42", "43"}; +static const uint8 liblte_rrc_band_num[LIBLTE_RRC_BAND_N_ITEMS] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}; + +// Structs +typedef struct{ + bool supported_rohc_profiles[9]; + LIBLTE_RRC_MAX_ROHC_CTXTS_ENUM max_rohc_ctxts; + bool max_rohc_ctxts_present; +}LIBLTE_RRC_PDCP_PARAMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdcp_params_ie(LIBLTE_RRC_PDCP_PARAMS_STRUCT *pdcp_params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdcp_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDCP_PARAMS_STRUCT *pdcp_params); + +typedef struct{ + bool tx_antenna_selection_supported; + bool specific_ref_sigs_supported; +}LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_phy_layer_params_ie(LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT *params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phy_layer_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT *params); + +typedef struct{ + uint8 band_eutra; + bool half_duplex; +}LIBLTE_RRC_SUPPORTED_BAND_EUTRA_STRUCT; + +typedef struct{ + LIBLTE_RRC_SUPPORTED_BAND_EUTRA_STRUCT supported_band_eutra[LIBLTE_RRC_MAXBANDS]; + uint32 N_supported_band_eutras; +}LIBLTE_RRC_RF_PARAMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_rf_params_ie(LIBLTE_RRC_RF_PARAMS_STRUCT *params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rf_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_RF_PARAMS_STRUCT *params); + +typedef struct{ + bool inter_freq_need_for_gaps[LIBLTE_RRC_MAXBANDS]; + uint32 N_inter_freq_need_for_gaps; +}LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_info_eutra_ie(LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT *info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_info_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT *info); + +typedef struct{ + LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT band_list_eutra[LIBLTE_RRC_MAXBANDS]; + uint32 N_band_list_eutra; +}LIBLTE_RRC_MEAS_PARAMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_params_ie(LIBLTE_RRC_MEAS_PARAMS_STRUCT *params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_PARAMS_STRUCT *params); + +typedef struct{ + // WARNING: hardcoding these options to not present + bool utra_fdd_present; + bool utra_tdd128_present; + bool utra_tdd384_present; + bool utra_tdd768_present; + bool geran_present; + bool cdma2000_hrpd_present; + bool cdma2000_1xrtt_present; +}LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_rrc_pack_inter_rat_params_ie(LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT *params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_inter_rat_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT *params); + +typedef struct{ + uint8 access_stratum_release; + uint8 ue_category; + LIBLTE_RRC_PDCP_PARAMS_STRUCT pdcp_params; + LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT phy_params; + LIBLTE_RRC_RF_PARAMS_STRUCT rf_params; + LIBLTE_RRC_MEAS_PARAMS_STRUCT meas_params; + uint32 feature_group_indicator; + LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT inter_rat_params; + bool feature_group_indicator_present; +}LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_eutra_capability_ie(LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *ue_eutra_capability, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_eutra_capability_ie(uint8 **ie_ptr, + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *ue_eutra_capability); + + +/********************************************************************* + IE Name: UE Timers and Constants + + Description: Contains timers and constants used by the UE in + either RRC_CONNECTED or RRC_IDLE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_T300_MS100 = 0, + LIBLTE_RRC_T300_MS200, + LIBLTE_RRC_T300_MS300, + LIBLTE_RRC_T300_MS400, + LIBLTE_RRC_T300_MS600, + LIBLTE_RRC_T300_MS1000, + LIBLTE_RRC_T300_MS1500, + LIBLTE_RRC_T300_MS2000, + LIBLTE_RRC_T300_N_ITEMS, +}LIBLTE_RRC_T300_ENUM; +static const char liblte_rrc_t300_text[LIBLTE_RRC_T300_N_ITEMS][20] = { "100", "200", "300", "400", + "600", "1000", "1500", "2000"}; +static const uint16 liblte_rrc_t300_num[LIBLTE_RRC_T300_N_ITEMS] = {100, 200, 300, 400, 600, 1000, 1500, 2000}; +typedef enum{ + LIBLTE_RRC_T301_MS100 = 0, + LIBLTE_RRC_T301_MS200, + LIBLTE_RRC_T301_MS300, + LIBLTE_RRC_T301_MS400, + LIBLTE_RRC_T301_MS600, + LIBLTE_RRC_T301_MS1000, + LIBLTE_RRC_T301_MS1500, + LIBLTE_RRC_T301_MS2000, + LIBLTE_RRC_T301_N_ITEMS, +}LIBLTE_RRC_T301_ENUM; +static const char liblte_rrc_t301_text[LIBLTE_RRC_T301_N_ITEMS][20] = { "100", "200", "300", "400", + "600", "1000", "1500", "2000"}; +static const uint16 liblte_rrc_t301_num[LIBLTE_RRC_T301_N_ITEMS] = {100, 200, 300, 400, 600, 1000, 1500, 2000}; +typedef enum{ + LIBLTE_RRC_T310_MS0 = 0, + LIBLTE_RRC_T310_MS50, + LIBLTE_RRC_T310_MS100, + LIBLTE_RRC_T310_MS200, + LIBLTE_RRC_T310_MS500, + LIBLTE_RRC_T310_MS1000, + LIBLTE_RRC_T310_MS2000, + LIBLTE_RRC_T310_N_ITEMS, +}LIBLTE_RRC_T310_ENUM; +static const char liblte_rrc_t310_text[LIBLTE_RRC_T310_N_ITEMS][20] = { "0", "50", "100", "200", + "500", "1000", "2000"}; +static const uint16 liblte_rrc_t310_num[LIBLTE_RRC_T310_N_ITEMS] = {0, 50, 100, 200, 500, 1000, 2000}; +typedef enum{ + LIBLTE_RRC_N310_N1 = 0, + LIBLTE_RRC_N310_N2, + LIBLTE_RRC_N310_N3, + LIBLTE_RRC_N310_N4, + LIBLTE_RRC_N310_N6, + LIBLTE_RRC_N310_N8, + LIBLTE_RRC_N310_N10, + LIBLTE_RRC_N310_N20, + LIBLTE_RRC_N310_N_ITEMS, +}LIBLTE_RRC_N310_ENUM; +static const char liblte_rrc_n310_text[LIBLTE_RRC_N310_N_ITEMS][20] = { "1", "2", "3", "4", + "6", "8", "10", "20"}; +static const uint8 liblte_rrc_n310_num[LIBLTE_RRC_N310_N_ITEMS] = {1, 2, 3, 4, 6, 8, 10, 20}; +typedef enum{ + LIBLTE_RRC_T311_MS1000 = 0, + LIBLTE_RRC_T311_MS3000, + LIBLTE_RRC_T311_MS5000, + LIBLTE_RRC_T311_MS10000, + LIBLTE_RRC_T311_MS15000, + LIBLTE_RRC_T311_MS20000, + LIBLTE_RRC_T311_MS30000, + LIBLTE_RRC_T311_N_ITEMS, +}LIBLTE_RRC_T311_ENUM; +static const char liblte_rrc_t311_text[LIBLTE_RRC_T311_N_ITEMS][20] = { "1000", "3000", "5000", "10000", + "15000", "20000", "30000"}; +static const uint16 liblte_rrc_t311_num[LIBLTE_RRC_T311_N_ITEMS] = {1000, 3000, 5000, 10000, 15000, 20000, 30000}; +typedef enum{ + LIBLTE_RRC_N311_N1 = 0, + LIBLTE_RRC_N311_N2, + LIBLTE_RRC_N311_N3, + LIBLTE_RRC_N311_N4, + LIBLTE_RRC_N311_N5, + LIBLTE_RRC_N311_N6, + LIBLTE_RRC_N311_N8, + LIBLTE_RRC_N311_N10, + LIBLTE_RRC_N311_N_ITEMS, +}LIBLTE_RRC_N311_ENUM; +static const char liblte_rrc_n311_text[LIBLTE_RRC_N311_N_ITEMS][20] = { "1", "2", "3", "4", + "5", "6", "8", "10"}; +static const uint8 liblte_rrc_n311_num[LIBLTE_RRC_N311_N_ITEMS] = {1, 2, 3, 4, 5, 6, 8, 10}; +// Structs +typedef struct{ + LIBLTE_RRC_T300_ENUM t300; + LIBLTE_RRC_T301_ENUM t301; + LIBLTE_RRC_T310_ENUM t310; + LIBLTE_RRC_N310_ENUM n310; + LIBLTE_RRC_T311_ENUM t311; + LIBLTE_RRC_N311_ENUM n311; +}LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_timers_and_constants_ie(LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT *ue_timers_and_constants, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_timers_and_constants_ie(uint8 **ie_ptr, + LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT *ue_timers_and_constants); + +/********************************************************************* + IE Name: Allowed Meas Bandwidth + + Description: Indicates the maximum allowed measurement bandwidth + on a carrier frequency as defined by the parameter + Transmission Bandwidth Configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW6 = 0, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW15, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW25, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW50, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW75, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_MBW100, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_N_ITEMS, +}LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM; +static const char liblte_rrc_allowed_meas_bandwidth_text[LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_N_ITEMS][20] = {"1.4", "3", "5", "10", + "15", "20"}; +static const double liblte_rrc_allowed_meas_bandwidth_num[LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_N_ITEMS] = {1.4, 3, 5, 10, 15, 20}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_allowed_meas_bandwidth_ie(LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM allowed_meas_bw, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_allowed_meas_bandwidth_ie(uint8 **ie_ptr, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM *allowed_meas_bw); + +/********************************************************************* + IE Name: Hysteresis + + Description: Used within the entry and leave condition of an + event triggered reporting condition + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_hysteresis_ie(uint8 hysteresis, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_hysteresis_ie(uint8 **ie_ptr, + uint8 *hysteresis); + +/********************************************************************* + IE Name: Location Info + + Description: Transfers location information available at the UE to + correlate measurements and UE position information + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Meas Config + + Description: Specifies measurements to be performed by the UE, + and covers intra-frequency, inter-frequency and + inter-RAT mobility as well as configuration of + measurement gaps + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_EXPLICIT_LIST_OF_ARFCNS 31 +#define LIBLTE_RRC_MAX_CELL_MEAS 32 +#define LIBLTE_RRC_MAX_OBJECT_ID 32 +#define LIBLTE_RRC_MAX_REPORT_CONFIG_ID 32 +#define LIBLTE_RRC_MAX_MEAS_ID 32 +// Enums +typedef enum{ + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC0 = 0, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC1, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC2, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC3, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC4, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC5, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC6, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC7, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC8, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC9, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC10, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC11, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC12, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC13, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC14, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC15, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC16, + LIBLTE_RRC_BAND_CLASS_CDMA2000_BC17, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE14, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE13, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE12, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE11, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE10, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE9, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE8, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE7, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE6, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE5, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE4, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE3, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE2, + LIBLTE_RRC_BAND_CLASS_CDMA2000_SPARE1, + LIBLTE_RRC_BAND_CLASS_CDMA2000_N_ITEMS, +}LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM; +static const char liblte_rrc_band_class_cdma2000_text[LIBLTE_RRC_BAND_CLASS_CDMA2000_N_ITEMS][20] = { "0", "1", "2", "3", + "4", "5", "6", "7", + "8", "9", "10", "11", + "12", "13", "14", "15", + "16", "17", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +static const int8 liblte_rrc_band_class_cdma2000_num[LIBLTE_RRC_BAND_CLASS_CDMA2000_N_ITEMS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_BAND_INDICATOR_GERAN_DCS1800 = 0, + LIBLTE_RRC_BAND_INDICATOR_GERAN_PCS1900, + LIBLTE_RRC_BAND_INDICATOR_GERAN_N_ITEMS, +}LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM; +static const char liblte_rrc_band_indicator_geran_text[LIBLTE_RRC_BAND_INDICATOR_GERAN_N_ITEMS][20] = {"DCS1800", "PCS1900"}; +typedef enum{ + LIBLTE_RRC_FOLLOWING_ARFCNS_EXPLICIT_LIST = 0, + LIBLTE_RRC_FOLLOWING_ARFCNS_EQUALLY_SPACED, + LIBLTE_RRC_FOLLOWING_ARFCNS_VARIABLE_BIT_MAP, + LIBLTE_RRC_FOLLOWING_ARFCNS_N_ITEMS, +}LIBLTE_RRC_FOLLOWING_ARFCNS_ENUM; +static const char liblte_rrc_following_arfcns_text[LIBLTE_RRC_FOLLOWING_ARFCNS_N_ITEMS][20] = {"Explicit List", "Equally Spaced", "Variable Bit Map"}; +typedef enum{ + LIBLTE_RRC_CDMA2000_TYPE_1XRTT = 0, + LIBLTE_RRC_CDMA2000_TYPE_HRPD, + LIBLTE_RRC_CDMA2000_TYPE_N_ITEMS, +}LIBLTE_RRC_CDMA2000_TYPE_ENUM; +static const char liblte_rrc_cdma2000_type_text[LIBLTE_RRC_CDMA2000_TYPE_N_ITEMS][20] = {"1xrtt", "hrpd"}; +typedef enum{ + LIBLTE_RRC_T_EVALUATION_S30 = 0, + LIBLTE_RRC_T_EVALUATION_S60, + LIBLTE_RRC_T_EVALUATION_S120, + LIBLTE_RRC_T_EVALUATION_S180, + LIBLTE_RRC_T_EVALUATION_S240, + LIBLTE_RRC_T_EVALUATION_SPARE3, + LIBLTE_RRC_T_EVALUATION_SPARE2, + LIBLTE_RRC_T_EVALUATION_SPARE1, + LIBLTE_RRC_T_EVALUATION_N_ITEMS, +}LIBLTE_RRC_T_EVALUATION_ENUM; +static const char liblte_rrc_t_evaluation_text[LIBLTE_RRC_T_EVALUATION_N_ITEMS][20] = { "30", "60", "120", "180", + "240", "SPARE", "SPARE", "SPARE"}; +static const int16 liblte_rrc_t_evaluation_num[LIBLTE_RRC_T_EVALUATION_N_ITEMS] = {30, 60, 120, 180, 240, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_T_HYST_NORMAL_S30 = 0, + LIBLTE_RRC_T_HYST_NORMAL_S60, + LIBLTE_RRC_T_HYST_NORMAL_S120, + LIBLTE_RRC_T_HYST_NORMAL_S180, + LIBLTE_RRC_T_HYST_NORMAL_S240, + LIBLTE_RRC_T_HYST_NORMAL_SPARE3, + LIBLTE_RRC_T_HYST_NORMAL_SPARE2, + LIBLTE_RRC_T_HYST_NORMAL_SPARE1, + LIBLTE_RRC_T_HYST_NORMAL_N_ITEMS, +}LIBLTE_RRC_T_HYST_NORMAL_ENUM; +static const char liblte_rrc_t_hyst_normal_text[LIBLTE_RRC_T_HYST_NORMAL_N_ITEMS][20] = { "30", "60", "120", "180", + "240", "SPARE", "SPARE", "SPARE"}; +static const int16 liblte_rrc_t_hyst_normal_num[LIBLTE_RRC_T_HYST_NORMAL_N_ITEMS] = {30, 60, 120, 180, 240, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N4 = 0, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N8, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N12, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N16, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N24, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N32, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N48, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N64, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N84, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N96, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N128, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N168, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N252, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N504, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_SPARE2, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_SPARE1, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N1, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_N_ITEMS, +}LIBLTE_RRC_PHYS_CELL_ID_RANGE_ENUM; +static const char liblte_rrc_phys_cell_id_range_text[LIBLTE_RRC_PHYS_CELL_ID_RANGE_N_ITEMS][20] = { "4", "8", "12", "16", + "24", "32", "48", "64", + "84", "96", "128", "168", + "252", "504", "SPARE", "SPARE", + "1"}; +static const int16 liblte_rrc_phys_cell_id_range_num[LIBLTE_RRC_PHYS_CELL_ID_RANGE_N_ITEMS] = {4, 8, 12, 16, 24, 32, 48, 64, 84, 96, 128, 168, 252, 504, -1, -1, 1}; +typedef enum{ + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N24 = 0, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N22, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N20, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N18, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N16, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N14, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N12, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N10, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N8, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N6, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N5, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N4, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N3, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N2, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_N1, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_0, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_1, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_2, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_3, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_4, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_5, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_6, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_8, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_10, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_12, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_14, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_16, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_18, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_20, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_22, + LIBLTE_RRC_Q_OFFSET_RANGE_DB_24, + LIBLTE_RRC_Q_OFFSET_RANGE_N_ITEMS, +}LIBLTE_RRC_Q_OFFSET_RANGE_ENUM; +static const char liblte_rrc_q_offset_range_text[LIBLTE_RRC_Q_OFFSET_RANGE_N_ITEMS][20] = {"-24", "-22", "-20", "-18", + "-16", "-14", "-12", "-10", + "-8", "-6", "-5", "-4", + "-3", "-2", "-1", "0", + "1", "2", "3", "4", + "5", "6", "8", "10", + "12", "14", "16", "18", + "20", "22", "24"}; +static const int8 liblte_rrc_q_offset_range_num[LIBLTE_RRC_Q_OFFSET_RANGE_N_ITEMS] = {-24, -22, -20, -18, -16, -14, -12, -10, -8, -6, -5, -4, -3, -2, -1, 0, + 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}; +typedef enum{ + LIBLTE_RRC_SSSF_MEDIUM_0DOT25 = 0, + LIBLTE_RRC_SSSF_MEDIUM_0DOT5, + LIBLTE_RRC_SSSF_MEDIUM_0DOT75, + LIBLTE_RRC_SSSF_MEDIUM_1DOT0, + LIBLTE_RRC_SSSF_MEDIUM_N_ITEMS, +}LIBLTE_RRC_SSSF_MEDIUM_ENUM; +static const char liblte_rrc_sssf_medium_text[LIBLTE_RRC_SSSF_MEDIUM_N_ITEMS][20] = {"0.25", "0.5", "0.75", "1.0"}; +static const double liblte_rrc_sssf_medium_num[LIBLTE_RRC_SSSF_MEDIUM_N_ITEMS] = {0.25, 0.5, 0.75, 1.0}; +typedef enum{ + LIBLTE_RRC_SSSF_HIGH_0DOT25 = 0, + LIBLTE_RRC_SSSF_HIGH_0DOT5, + LIBLTE_RRC_SSSF_HIGH_0DOT75, + LIBLTE_RRC_SSSF_HIGH_1DOT0, + LIBLTE_RRC_SSSF_HIGH_N_ITEMS, +}LIBLTE_RRC_SSSF_HIGH_ENUM; +static const char liblte_rrc_sssf_high_text[LIBLTE_RRC_SSSF_HIGH_N_ITEMS][20] = {"0.25", "0.5", "0.75", "1.0"}; +static const double liblte_rrc_sssf_high_num[LIBLTE_RRC_SSSF_HIGH_N_ITEMS] = {0.25, 0.5, 0.75, 1.0}; +typedef enum{ + LIBLTE_RRC_GAP_OFFSET_TYPE_GP0 = 0, + LIBLTE_RRC_GAP_OFFSET_TYPE_GP1, + LIBLTE_RRC_GAP_OFFSET_TYPE_N_ITEMS, +}LIBLTE_RRC_GAP_OFFSET_TYPE_ENUM; +static const char liblte_rrc_gap_offset_type_text[LIBLTE_RRC_GAP_OFFSET_TYPE_N_ITEMS][20] = {"GP0", + "GP1"}; +typedef enum{ + LIBLTE_RRC_UTRA_SYSTEM_TYPE_FDD = 0, + LIBLTE_RRC_UTRA_SYSTEM_TYPE_TDD, + LIBLTE_RRC_UTRA_SYSTEM_TYPE_N_ITEMS, +}LIBLTE_RRC_UTRA_SYSTEM_TYPE_ENUM; +static const char liblte_rrc_utra_system_type_text[LIBLTE_RRC_UTRA_SYSTEM_TYPE_N_ITEMS][20] = {"FDD", + "TDD"}; +typedef enum{ + LIBLTE_RRC_MEAS_OBJECT_TYPE_EUTRA = 0, + LIBLTE_RRC_MEAS_OBJECT_TYPE_UTRA, + LIBLTE_RRC_MEAS_OBJECT_TYPE_GERAN, + LIBLTE_RRC_MEAS_OBJECT_TYPE_CDMA2000, + LIBLTE_RRC_MEAS_OBJECT_TYPE_N_ITEMS, +}LIBLTE_RRC_MEAS_OBJECT_TYPE_ENUM; +static const char liblte_rrc_meas_object_type_text[LIBLTE_RRC_MEAS_OBJECT_TYPE_N_ITEMS][20] = {"EUTRA", + "UTRA", + "GERAN", + "CDMA2000"}; +typedef enum{ + LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_CPICH_RSCP = 0, + LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_CPICH_ECNO, + LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_N_ITEMS, +}LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_ENUM; +static const char liblte_rrc_meas_quantity_utra_fdd_text[LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_N_ITEMS][20] = {"CPICH RSCP", + "CPICH Ec/No"}; +typedef enum{ + LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_PCCPCH_RSCP = 0, + LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_N_ITEMS, +}LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_ENUM; +static const char liblte_rrc_meas_quantity_utra_tdd_text[LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_N_ITEMS][20] = {"PCCPCH RSCP"}; +typedef enum{ + LIBLTE_RRC_MEAS_QUANTITY_GERAN_RSSI = 0, + LIBLTE_RRC_MEAS_QUANTITY_GERAN_N_ITEMS, +}LIBLTE_RRC_MEAS_QUANTITY_GERAN_ENUM; +static const char liblte_rrc_meas_quantity_geran_text[LIBLTE_RRC_MEAS_QUANTITY_GERAN_N_ITEMS][20] = {"RSSI"}; +typedef enum{ + LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_PILOT_STRENGTH = 0, + LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_PILOT_PN_PHASE_AND_STRENGTH, + LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_N_ITEMS, +}LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_ENUM; +static const char liblte_rrc_meas_quantity_cdma2000_text[LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_N_ITEMS][100] = {"Pilot Strength", + "Pilot PN Phase and Strength"}; +typedef enum{ + LIBLTE_RRC_REPORT_INTERVAL_MS120 = 0, + LIBLTE_RRC_REPORT_INTERVAL_MS240, + LIBLTE_RRC_REPORT_INTERVAL_MS480, + LIBLTE_RRC_REPORT_INTERVAL_MS640, + LIBLTE_RRC_REPORT_INTERVAL_MS1024, + LIBLTE_RRC_REPORT_INTERVAL_MS2048, + LIBLTE_RRC_REPORT_INTERVAL_MS5120, + LIBLTE_RRC_REPORT_INTERVAL_MS10240, + LIBLTE_RRC_REPORT_INTERVAL_MIN1, + LIBLTE_RRC_REPORT_INTERVAL_MIN6, + LIBLTE_RRC_REPORT_INTERVAL_MIN12, + LIBLTE_RRC_REPORT_INTERVAL_MIN30, + LIBLTE_RRC_REPORT_INTERVAL_MIN60, + LIBLTE_RRC_REPORT_INTERVAL_SPARE3, + LIBLTE_RRC_REPORT_INTERVAL_SPARE2, + LIBLTE_RRC_REPORT_INTERVAL_SPARE1, + LIBLTE_RRC_REPORT_INTERVAL_N_ITEMS, +}LIBLTE_RRC_REPORT_INTERVAL_ENUM; +static const char liblte_rrc_report_interval_text[LIBLTE_RRC_REPORT_INTERVAL_N_ITEMS][20] = { "120", "240", "480", "640", + "1024", "2048", "5120", "10240", + "60000", "360000", "720000", "1800000", + "3600000", "SPARE", "SPARE", "SPARE"}; +static const int32 liblte_rrc_report_interval_num[LIBLTE_RRC_REPORT_INTERVAL_N_ITEMS] = {120, 240, 480, 640, 1024, 2048, 5120, 10240, 60000, 360000, 720000, 1800000, 3600000, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_TIME_TO_TRIGGER_MS0 = 0, + LIBLTE_RRC_TIME_TO_TRIGGER_MS40, + LIBLTE_RRC_TIME_TO_TRIGGER_MS64, + LIBLTE_RRC_TIME_TO_TRIGGER_MS80, + LIBLTE_RRC_TIME_TO_TRIGGER_MS100, + LIBLTE_RRC_TIME_TO_TRIGGER_MS128, + LIBLTE_RRC_TIME_TO_TRIGGER_MS160, + LIBLTE_RRC_TIME_TO_TRIGGER_MS256, + LIBLTE_RRC_TIME_TO_TRIGGER_MS320, + LIBLTE_RRC_TIME_TO_TRIGGER_MS480, + LIBLTE_RRC_TIME_TO_TRIGGER_MS512, + LIBLTE_RRC_TIME_TO_TRIGGER_MS640, + LIBLTE_RRC_TIME_TO_TRIGGER_MS1024, + LIBLTE_RRC_TIME_TO_TRIGGER_MS1280, + LIBLTE_RRC_TIME_TO_TRIGGER_MS2560, + LIBLTE_RRC_TIME_TO_TRIGGER_MS5120, + LIBLTE_RRC_TIME_TO_TRIGGER_N_ITEMS, +}LIBLTE_RRC_TIME_TO_TRIGGER_ENUM; +static const char liblte_rrc_time_to_trigger_text[LIBLTE_RRC_TIME_TO_TRIGGER_N_ITEMS][20] = { "0", "40", "64", "80", + "100", "128", "160", "256", + "320", "480", "512", "640", + "1024", "1280", "2560", "5120"}; +static const uint16 liblte_rrc_time_to_trigger_num[LIBLTE_RRC_TIME_TO_TRIGGER_N_ITEMS] = {0, 40, 64, 80, 100, 128, 160, 256, 320, 480, 512, 640, 1024, 1280, 2560, 5120}; +typedef enum{ + LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP = 0, + LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRQ, + LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_N_ITEMS, +}LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM; +static const char liblte_rrc_threshold_eutra_type_text[LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_N_ITEMS][20] = {"RSRP", + "RSRQ"}; +typedef enum{ + LIBLTE_RRC_EVENT_ID_EUTRA_A1 = 0, + LIBLTE_RRC_EVENT_ID_EUTRA_A2, + LIBLTE_RRC_EVENT_ID_EUTRA_A3, + LIBLTE_RRC_EVENT_ID_EUTRA_A4, + LIBLTE_RRC_EVENT_ID_EUTRA_A5, + LIBLTE_RRC_EVENT_ID_EUTRA_A6, + LIBLTE_RRC_EVENT_ID_EUTRA_N_ITEMS, +}LIBLTE_RRC_EVENT_ID_EUTRA_ENUM; +static const char liblte_rrc_event_id_eutra_text[LIBLTE_RRC_EVENT_ID_EUTRA_N_ITEMS][20] = {"A1", "A2", "A3", + "A4", "A5", "A6"}; +typedef enum{ + LIBLTE_RRC_PURPOSE_EUTRA_REPORT_STRONGEST_CELL = 0, + LIBLTE_RRC_PURPOSE_EUTRA_REPORT_CGI, + LIBLTE_RRC_PURPOSE_EUTRA_N_ITEMS, +}LIBLTE_RRC_PURPOSE_EUTRA_ENUM; +static const char liblte_rrc_purpose_eutra_text[LIBLTE_RRC_PURPOSE_EUTRA_N_ITEMS][100] = {"Report Strongest Cell", + "Report CGI"}; +typedef enum{ + LIBLTE_RRC_TRIGGER_TYPE_EUTRA_EVENT = 0, + LIBLTE_RRC_TRIGGER_TYPE_EUTRA_PERIODICAL, + LIBLTE_RRC_TRIGGER_TYPE_EUTRA_N_ITEMS, +}LIBLTE_RRC_TRIGGER_TYPE_EUTRA_ENUM; +static const char liblte_rrc_trigger_type_eutra_text[LIBLTE_RRC_TRIGGER_TYPE_EUTRA_N_ITEMS][20] = {"Event", + "Periodical"}; +typedef enum{ + LIBLTE_RRC_TRIGGER_QUANTITY_RSRP = 0, + LIBLTE_RRC_TRIGGER_QUANTITY_RSRQ, + LIBLTE_RRC_TRIGGER_QUANTITY_N_ITEMS, +}LIBLTE_RRC_TRIGGER_QUANTITY_ENUM; +static const char liblte_rrc_trigger_quantity_text[LIBLTE_RRC_TRIGGER_QUANTITY_N_ITEMS][20] = {"RSRP", + "RSRQ"}; +typedef enum{ + LIBLTE_RRC_REPORT_QUANTITY_SAME_AS_TRIGGER_QUANTITY = 0, + LIBLTE_RRC_REPORT_QUANTITY_BOTH, + LIBLTE_RRC_REPORT_QUANTITY_N_ITEMS, +}LIBLTE_RRC_REPORT_QUANTITY_ENUM; +static const char liblte_rrc_report_quantity_text[LIBLTE_RRC_REPORT_QUANTITY_N_ITEMS][100] = {"Same As Trigger Quantity", + "Both"}; +typedef enum{ + LIBLTE_RRC_REPORT_AMOUNT_R1 = 0, + LIBLTE_RRC_REPORT_AMOUNT_R2, + LIBLTE_RRC_REPORT_AMOUNT_R4, + LIBLTE_RRC_REPORT_AMOUNT_R8, + LIBLTE_RRC_REPORT_AMOUNT_R16, + LIBLTE_RRC_REPORT_AMOUNT_R32, + LIBLTE_RRC_REPORT_AMOUNT_R64, + LIBLTE_RRC_REPORT_AMOUNT_INFINITY, + LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS, +}LIBLTE_RRC_REPORT_AMOUNT_ENUM; +static const char liblte_rrc_report_amount_text[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS][20] = { "r1", "r2", "r4", "r8", + "r16", "r32", "r64", "INFINITY"}; + +static const int8 liblte_rrc_report_amount_num[LIBLTE_RRC_REPORT_AMOUNT_N_ITEMS] = {1, 2, 4, 8, 16, 32, 64, -1}; + +typedef enum{ + LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP = 0, + LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ECNO, + LIBLTE_RRC_THRESHOLD_UTRA_TYPE_N_ITEMS, +}LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ENUM; +static const char liblte_rrc_threshold_utra_type_text[LIBLTE_RRC_THRESHOLD_UTRA_TYPE_N_ITEMS][20] = {"RSCP", + "Ec/No"}; +typedef enum{ + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_UTRA = 0, + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_GERAN, + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_CDMA2000, + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_N_ITEMS, +}LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_ENUM; +static const char liblte_rrc_threshold_inter_rat_type_text[LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_N_ITEMS][20] = {"UTRA", + "GERAN", + "CDMA2000"}; +typedef enum{ + LIBLTE_RRC_EVENT_ID_INTER_RAT_B1 = 0, + LIBLTE_RRC_EVENT_ID_INTER_RAT_B2, + LIBLTE_RRC_EVENT_ID_INTER_RAT_N_ITEMS, +}LIBLTE_RRC_EVENT_ID_INTER_RAT_ENUM; +static const char liblte_rrc_event_id_inter_rat_text[LIBLTE_RRC_EVENT_ID_INTER_RAT_N_ITEMS][20] = {"B1", + "B2"}; +typedef enum{ + LIBLTE_RRC_PURPOSE_INTER_RAT_REPORT_STRONGEST_CELLS = 0, + LIBLTE_RRC_PURPOSE_INTER_RAT_REPORT_STRONGEST_CELLS_FOR_SON, + LIBLTE_RRC_PURPOSE_INTER_RAT_REPORT_CGI, + LIBLTE_RRC_PURPOSE_INTER_RAT_N_ITEMS, +}LIBLTE_RRC_PURPOSE_INTER_RAT_ENUM; +static const char liblte_rrc_purpose_inter_rat_text[LIBLTE_RRC_PURPOSE_INTER_RAT_N_ITEMS][100] = {"Report Strongest Cells", + "Report Strongest Cells for SON", + "Report CGI"}; +typedef enum{ + LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_EVENT = 0, + LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_PERIODICAL, + LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_N_ITEMS, +}LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_ENUM; +static const char liblte_rrc_trigger_type_inter_rat_text[LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_N_ITEMS][20] = {"Event", + "Periodical"}; +typedef enum{ + LIBLTE_RRC_REPORT_CONFIG_TYPE_EUTRA = 0, + LIBLTE_RRC_REPORT_CONFIG_TYPE_INTER_RAT, + LIBLTE_RRC_REPORT_CONFIG_TYPE_N_ITEMS, +}LIBLTE_RRC_REPORT_CONFIG_TYPE_ENUM; +static const char liblte_rrc_report_config_type_text[LIBLTE_RRC_REPORT_CONFIG_TYPE_N_ITEMS][20] = {"EUTRA", + "Inter RAT"}; +// Structs +typedef struct{ + LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM bandclass; + uint16 arfcn; +}LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT; +typedef struct{ + uint8 arfcn_spacing; + uint8 number_of_arfcns; +}LIBLTE_RRC_EQUALLY_SPACED_ARFCNS_STRUCT; +typedef struct{ + LIBLTE_RRC_EQUALLY_SPACED_ARFCNS_STRUCT equally_spaced_arfcns; + LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM band_indicator; + LIBLTE_RRC_FOLLOWING_ARFCNS_ENUM following_arfcns; + uint16 starting_arfcn; + uint16 explicit_list_of_arfcns[LIBLTE_RRC_MAX_EXPLICIT_LIST_OF_ARFCNS]; + uint16 variable_bit_map_of_arfcns; + uint8 explicit_list_of_arfcns_size; +}LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT; +typedef struct{ + uint32 N_cell_idx; + uint8 cell_idx[LIBLTE_RRC_MAX_CELL_MEAS]; +}LIBLTE_RRC_CELL_INDEX_LIST_STRUCT; +typedef struct{ + LIBLTE_RRC_T_EVALUATION_ENUM t_eval; + LIBLTE_RRC_T_HYST_NORMAL_ENUM t_hyst_normal; + uint8 n_cell_change_medium; + uint8 n_cell_change_high; +}LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT; +typedef struct{ + LIBLTE_RRC_PHYS_CELL_ID_RANGE_ENUM range; + uint16 start; +}LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT; +typedef struct{ + uint8 ncc; + uint8 bcc; +}LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT; +typedef struct{ + uint8 pre_reg_zone_id; + uint8 secondary_pre_reg_zone_id_list[2]; + uint8 secondary_pre_reg_zone_id_list_size; + bool pre_reg_allowed; + bool pre_reg_zone_id_present; +}LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT; +typedef struct{ + LIBLTE_RRC_SSSF_MEDIUM_ENUM sf_medium; + LIBLTE_RRC_SSSF_HIGH_ENUM sf_high; +}LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT; +typedef struct{ + LIBLTE_RRC_GAP_OFFSET_TYPE_ENUM gap_offset_type; + uint8 gap_offset; + bool setup_present; +}LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT; +typedef struct{ + uint8 meas_id; + uint8 meas_obj_id; + uint8 rep_cnfg_id; +}LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_STRUCT meas_id_list[LIBLTE_RRC_MAX_MEAS_ID]; + uint32 N_meas_id; +}LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT; +typedef struct{ + uint16 pci; + uint8 cell_idx; +}LIBLTE_RRC_CELLS_TO_ADD_MOD_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT carrier_freq; + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT cells_to_remove_list; + LIBLTE_RRC_CELLS_TO_ADD_MOD_CDMA2000_STRUCT cells_to_add_mod_list[LIBLTE_RRC_MAX_CELL_MEAS]; + LIBLTE_RRC_CDMA2000_TYPE_ENUM cdma2000_type; + uint32 N_cells_to_add_mod; + uint16 cell_for_which_to_rep_cgi; + uint8 search_win_size; + int8 offset_freq; + bool search_win_size_present; + bool cells_to_remove_list_present; + bool cell_for_which_to_rep_cgi_present; +}LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM cell_offset; + uint16 pci; + uint8 cell_idx; +}LIBLTE_RRC_CELLS_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT pci_range; + uint8 cell_idx; +}LIBLTE_RRC_BLACK_CELLS_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT cells_to_remove_list; + LIBLTE_RRC_CELLS_TO_ADD_MOD_STRUCT cells_to_add_mod_list[LIBLTE_RRC_MAX_CELL_MEAS]; + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT black_cells_to_remove_list; + LIBLTE_RRC_BLACK_CELLS_TO_ADD_MOD_STRUCT black_cells_to_add_mod_list[LIBLTE_RRC_MAX_CELL_MEAS]; + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM allowed_meas_bw; + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM offset_freq; + uint32 N_cells_to_add_mod; + uint32 N_black_cells_to_add_mod; + uint16 carrier_freq; + uint16 cell_for_which_to_rep_cgi; + uint8 neigh_cell_cnfg; + bool offset_freq_not_default; + bool presence_ant_port_1; + bool cells_to_remove_list_present; + bool black_cells_to_remove_list_present; + bool cell_for_which_to_rep_cgi_present; +}LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT carrier_freqs; + LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT cell_for_which_to_rep_cgi; + int8 offset_freq; + uint8 ncc_permitted; + bool cell_for_which_to_rep_cgi_present; +}LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT; +typedef struct{ + uint16 pci; + uint8 cell_idx; +}LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_FDD_STRUCT; +typedef struct{ + uint8 cell_idx; + uint8 pci; +}LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_TDD_STRUCT; +typedef struct{ + LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_FDD_STRUCT cells_fdd[LIBLTE_RRC_MAX_CELL_MEAS]; + LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_TDD_STRUCT cells_tdd[LIBLTE_RRC_MAX_CELL_MEAS]; + LIBLTE_RRC_UTRA_SYSTEM_TYPE_ENUM type; + uint32 N_cells; +}LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_UTRA_SYSTEM_TYPE_ENUM type; + uint16 pci_fdd; + uint8 pci_tdd; +}LIBLTE_RRC_CELLS_FOR_WHICH_TO_REPORT_CGI_STRUCT; +typedef struct{ + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT cells_to_remove_list; + LIBLTE_RRC_CELLS_TO_ADD_MOD_LIST_UTRA_STRUCT cells_to_add_mod_list; + LIBLTE_RRC_CELLS_FOR_WHICH_TO_REPORT_CGI_STRUCT cells_for_which_to_rep_cgi; + uint16 carrier_freq; + int8 offset_freq; + bool cells_to_remove_list_present; + bool cells_to_add_mod_list_present; + bool cells_for_which_to_rep_cgi_present; +}LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT meas_obj_eutra; + LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT meas_obj_utra; + LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT meas_obj_geran; + LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT meas_obj_cdma2000; + LIBLTE_RRC_MEAS_OBJECT_TYPE_ENUM meas_obj_type; + uint8 meas_obj_id; +}LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_STRUCT meas_obj_list[LIBLTE_RRC_MAX_OBJECT_ID]; + uint32 N_meas_obj; +}LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT; +typedef struct{ + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM fc_rsrp; + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM fc_rsrq; + bool fc_rsrp_not_default; + bool fc_rsrq_not_default; +}LIBLTE_RRC_QUANTITY_CONFIG_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_ENUM mq_fdd; + LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_ENUM mq_tdd; + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM fc; + bool fc_not_default; +}LIBLTE_RRC_QUANTITY_CONFIG_UTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_QUANTITY_GERAN_ENUM mq; + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM fc; + bool fc_not_default; +}LIBLTE_RRC_QUANTITY_CONFIG_GERAN_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_ENUM mq; +}LIBLTE_RRC_QUANTITY_CONFIG_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_QUANTITY_CONFIG_EUTRA_STRUCT qc_eutra; + LIBLTE_RRC_QUANTITY_CONFIG_UTRA_STRUCT qc_utra; + LIBLTE_RRC_QUANTITY_CONFIG_GERAN_STRUCT qc_geran; + LIBLTE_RRC_QUANTITY_CONFIG_CDMA2000_STRUCT qc_cdma2000; + bool qc_eutra_present; + bool qc_utra_present; + bool qc_geran_present; + bool qc_cdma2000_present; +}LIBLTE_RRC_QUANTITY_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM type; + uint8 range; +}LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra; +}LIBLTE_RRC_EVENT_A1_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra; +}LIBLTE_RRC_EVENT_A2_STRUCT; +typedef struct{ + int8 offset; + bool report_on_leave; +}LIBLTE_RRC_EVENT_A3_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra; +}LIBLTE_RRC_EVENT_A4_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra1; + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra2; +}LIBLTE_RRC_EVENT_A5_STRUCT; +typedef struct{ + int8 offset; + bool report_on_leave; +}LIBLTE_RRC_EVENT_A6_STRUCT; +typedef struct{ + LIBLTE_RRC_EVENT_A1_STRUCT event_a1; + LIBLTE_RRC_EVENT_A2_STRUCT event_a2; + LIBLTE_RRC_EVENT_A3_STRUCT event_a3; + LIBLTE_RRC_EVENT_A4_STRUCT event_a4; + LIBLTE_RRC_EVENT_A5_STRUCT event_a5; + LIBLTE_RRC_EVENT_A6_STRUCT event_a6; + LIBLTE_RRC_EVENT_ID_EUTRA_ENUM event_id; + LIBLTE_RRC_TIME_TO_TRIGGER_ENUM time_to_trigger; + uint8 hysteresis; +}LIBLTE_RRC_EVENT_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_PURPOSE_EUTRA_ENUM purpose; +}LIBLTE_RRC_PERIODICAL_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_EVENT_EUTRA_STRUCT event; + LIBLTE_RRC_PERIODICAL_EUTRA_STRUCT periodical; + LIBLTE_RRC_TRIGGER_TYPE_EUTRA_ENUM trigger_type; + LIBLTE_RRC_TRIGGER_QUANTITY_ENUM trigger_quantity; + LIBLTE_RRC_REPORT_QUANTITY_ENUM report_quantity; + LIBLTE_RRC_REPORT_INTERVAL_ENUM report_interval; + LIBLTE_RRC_REPORT_AMOUNT_ENUM report_amount; + uint8 max_report_cells; +}LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ENUM type; + int8 value; +}LIBLTE_RRC_THRESHOLD_UTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_UTRA_STRUCT utra; + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_ENUM type; + uint8 geran; + uint8 cdma2000; +}LIBLTE_RRC_EVENT_B1_STRUCT; +typedef struct{ + LIBLTE_RRC_THRESHOLD_UTRA_STRUCT utra; + LIBLTE_RRC_THRESHOLD_EUTRA_STRUCT eutra; + LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_ENUM type2; + uint8 geran; + uint8 cdma2000; +}LIBLTE_RRC_EVENT_B2_STRUCT; +typedef struct{ + LIBLTE_RRC_EVENT_B1_STRUCT event_b1; + LIBLTE_RRC_EVENT_B2_STRUCT event_b2; + LIBLTE_RRC_EVENT_ID_INTER_RAT_ENUM event_id; + LIBLTE_RRC_TIME_TO_TRIGGER_ENUM time_to_trigger; + uint8 hysteresis; +}LIBLTE_RRC_EVENT_INTER_RAT_STRUCT; +typedef struct{ + LIBLTE_RRC_PURPOSE_INTER_RAT_ENUM purpose; +}LIBLTE_RRC_PERIODICAL_INTER_RAT_STRUCT; +typedef struct{ + LIBLTE_RRC_EVENT_INTER_RAT_STRUCT event; + LIBLTE_RRC_PERIODICAL_INTER_RAT_STRUCT periodical; + LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_ENUM trigger_type; + LIBLTE_RRC_REPORT_INTERVAL_ENUM report_interval; + LIBLTE_RRC_REPORT_AMOUNT_ENUM report_amount; + uint8 max_report_cells; +}LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT; +typedef struct{ + LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT rep_cnfg_eutra; + LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT rep_cnfg_inter_rat; + LIBLTE_RRC_REPORT_CONFIG_TYPE_ENUM rep_cnfg_type; + uint8 rep_cnfg_id; +}LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_STRUCT rep_cnfg_list[LIBLTE_RRC_MAX_REPORT_CONFIG_ID]; + uint32 N_rep_cnfg; +}LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT; +typedef struct{ + LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT mob_state_params; + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT time_to_trig_sf; +}LIBLTE_RRC_SPEED_STATE_PARAMS_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT meas_obj_to_add_mod_list; + LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT rep_cnfg_to_add_mod_list; + LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT meas_id_to_add_mod_list; + LIBLTE_RRC_QUANTITY_CONFIG_STRUCT quantity_cnfg; + LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT meas_gap_cnfg; + LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT pre_reg_info_hrpd; + LIBLTE_RRC_SPEED_STATE_PARAMS_STRUCT speed_state_params; + uint32 N_meas_obj_to_remove; + uint32 N_rep_cnfg_to_remove; + uint32 N_meas_id_to_remove; + uint8 meas_obj_to_remove_list[LIBLTE_RRC_MAX_OBJECT_ID]; + uint8 rep_cnfg_to_remove_list[LIBLTE_RRC_MAX_REPORT_CONFIG_ID]; + uint8 meas_id_to_remove_list[LIBLTE_RRC_MAX_MEAS_ID]; + uint8 s_meas; + bool meas_obj_to_add_mod_list_present; + bool rep_cnfg_to_add_mod_list_present; + bool meas_id_to_add_mod_list_present; + bool quantity_cnfg_present; + bool meas_gap_cnfg_present; + bool s_meas_present; + bool pre_reg_info_hrpd_present; + bool speed_state_params_present; +}LIBLTE_RRC_MEAS_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_config_ie(LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_cnfg); + +/********************************************************************* + IE Name: Meas Gap Config + + Description: Specifies the measurement gap configuration and + controls setup/release of measurement gaps + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Meas Gap Config enum defined above +// Structs +// Meas Gap Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_gap_config_ie(LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT *meas_gap_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_gap_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT *meas_gap_cnfg); + +/********************************************************************* + IE Name: Meas ID + + Description: Identifies a measurement configuration, i.e. linking + of a measurement object and a reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_id_ie(uint8 meas_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_id_ie(uint8 **ie_ptr, + uint8 *meas_id); + +/********************************************************************* + IE Name: Meas Id To Add Mod List + + Description: Concerns a list of measurement identities to add or + modify, with for each entry the meas ID, the + associated meas object ID and the associated report + config ID + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Meas ID To Add Mod List structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_id_to_add_mod_list_ie(LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_id_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT *list); + +/********************************************************************* + IE Name: Meas Object CDMA2000 + + Description: Specifies information applicable for inter-RAT + CDMA2000 neighboring cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Meas Object CDMA2000 structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_cdma2000_ie(LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT *meas_obj_cdma2000, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT *meas_obj_cdma2000); + +/********************************************************************* + IE Name: Meas Object EUTRA + + Description: Specifies information applicable for intra-frequency + or inter-frequency E-UTRA cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Meas Object EUTRA structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_eutra_ie(LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *meas_obj_eutra, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *meas_obj_eutra); + +/********************************************************************* + IE Name: Meas Object GERAN + + Description: Specifies information applicable for inter-RAT + GERAN neighboring frequencies + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Meas Object GERAN struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_geran_ie(LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT *meas_obj_geran, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT *meas_obj_geran); + +/********************************************************************* + IE Name: Meas Object ID + + Description: Identifies a measurement object configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_id_ie(uint8 meas_object_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_id_ie(uint8 **ie_ptr, + uint8 *meas_object_id); + +/********************************************************************* + IE Name: Meas Object To Add Mod List + + Description: Concerns a list of measurement objects to add or + modify + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Meas Object To Add Mod List enum defined above +// Structs +// Meas Object To Add Mod List structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_to_add_mod_list_ie(LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT *list); + +/********************************************************************* + IE Name: Meas Object UTRA + + Description: Specifies information applicable for inter-RAT UTRA + neighboring cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Meas Object UTRA define defined above +// Enums +// Meas Object UTRA enum defined above +// Structs +// Meas Object UTRA structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_utra_ie(LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT *meas_obj_utra, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_utra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT *meas_obj_utra); + +/********************************************************************* + IE Name: Meas Results + + Description: Covers measured results for intra-frequency, + inter-frequency and inter-RAT mobility + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Quantity Config + + Description: Specifies the measurement quantities and layer 3 + filtering coefficients for E-UTRA and inter-RAT + measurements + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Quantity Config enums defined above +// Structs +// Quantity Config structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_quantity_config_ie(LIBLTE_RRC_QUANTITY_CONFIG_STRUCT *qc, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_quantity_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_QUANTITY_CONFIG_STRUCT *qc); + +/********************************************************************* + IE Name: Report Config EUTRA + + Description: Specifies criteria for triggering of an E-UTRA + measurement reporting event + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Report Config EUTRA enums defined above +// Structs +// Report Config EUTRA structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_eutra_ie(LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *rep_cnfg_eutra, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *rep_cnfg_eutra); + +/********************************************************************* + IE Name: Report Config ID + + Description: Identifies a measurement reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_id_ie(uint8 report_cnfg_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_id_ie(uint8 **ie_ptr, + uint8 *report_cnfg_id); + +/********************************************************************* + IE Name: Report Config Inter RAT + + Description: Specifies criteria for triggering of an inter-RAT + measurement reporting event + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Report Config Inter RAT enums defined above +// Structs +// Report Config Inter RAT structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_inter_rat_ie(LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT *rep_cnfg_inter_rat, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_inter_rat_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT *rep_cnfg_inter_rat); + +/********************************************************************* + IE Name: Report Config To Add Mod List + + Description: Concerns a list of reporting configurations to add + or modify + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Report Config To Add Mod List enum defined above +// Structs +// Report Config To Add Mod List structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_to_add_mod_list_ie(LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT *list); + +/********************************************************************* + IE Name: Report Interval + + Description: Indicates the interval between periodic reports + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Report Interval enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_interval_ie(LIBLTE_RRC_REPORT_INTERVAL_ENUM report_int, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_interval_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_INTERVAL_ENUM *report_int); + +/********************************************************************* + IE Name: RSRP Range + + Description: Specifies the value range used in RSRP measurements + and thresholds + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rsrp_range_ie(uint8 rsrp_range, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rsrp_range_ie(uint8 **ie_ptr, + uint8 *rsrp_range); + +/********************************************************************* + IE Name: RSRQ Range + + Description: Specifies the value range used in RSRQ measurements + and thresholds + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rsrq_range_ie(uint8 rsrq_range, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rsrq_range_ie(uint8 **ie_ptr, + uint8 *rsrq_range); + +/********************************************************************* + IE Name: Time To Trigger + + Description: Specifies the value range used for the time to + trigger parameter, which concerns the time during + which specific criteria for the event needs to be + met in order to trigger a measurement report + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// Defines +// Enums +// Time To Trigger enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_time_to_trigger_ie(LIBLTE_RRC_TIME_TO_TRIGGER_ENUM time_to_trigger, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_time_to_trigger_ie(uint8 **ie_ptr, + LIBLTE_RRC_TIME_TO_TRIGGER_ENUM *time_to_trigger); + +/********************************************************************* + IE Name: Additional Spectrum Emission + + Description: FIXME + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_additional_spectrum_emission_ie(uint8 add_spect_em, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_additional_spectrum_emission_ie(uint8 **ie_ptr, + uint8 *add_spect_em); + +/********************************************************************* + IE Name: ARFCN value CDMA2000 + + Description: Indicates the CDMA2000 carrier frequency within + a CDMA2000 band + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_cdma2000_ie(uint16 arfcn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_cdma2000_ie(uint8 **ie_ptr, + uint16 *arfcn); + +/********************************************************************* + IE Name: ARFCN value EUTRA + + Description: Indicates the ARFCN applicable for a downlink, + uplink, or bi-directional (TDD) E-UTRA carrier + frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_eutra_ie(uint16 arfcn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_eutra_ie(uint8 **ie_ptr, + uint16 *arfcn); + +/********************************************************************* + IE Name: ARFCN value GERAN + + Description: Specifies the ARFCN value applicable for a GERAN + BCCH carrier frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_geran_ie(uint16 arfcn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_geran_ie(uint8 **ie_ptr, + uint16 *arfcn); + +/********************************************************************* + IE Name: ARFCN value UTRA + + Description: Indicates the ARFCN applicable for a downlink (Nd, + FDD) or bi-directional (Nt, TDD) UTRA carrier + frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_utra_ie(uint16 arfcn, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_utra_ie(uint8 **ie_ptr, + uint16 *arfcn); + +/********************************************************************* + IE Name: Band Class CDMA2000 + + Description: Defines the CDMA2000 band in which the CDMA2000 + carrier frequency can be found + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Band Class CDMA2000 enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_class_cdma2000_ie(LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM bc_cdma2000, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_class_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM *bc_cdma2000); + +/********************************************************************* + IE Name: Band Indicator GERAN + + Description: Indicates how to interpret an associated GERAN + carrier ARFCN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Band Indicator GERAN enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_indicator_geran_ie(LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM bi_geran, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_indicator_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM *bi_geran); + +/********************************************************************* + IE Name: Carrier Freq CDMA2000 + + Description: Provides the CDMA2000 carrier information + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Carrier Freq CDMA2000 struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freq_cdma2000_ie(LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT *carrier_freq, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freq_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT *carrier_freq); + +/********************************************************************* + IE Name: Carrier Freq GERAN + + Description: Provides an unambiguous carrier frequency description + of a GERAN cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM band_indicator; + uint16 arfcn; +}LIBLTE_RRC_CARRIER_FREQ_GERAN_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freq_geran_ie(LIBLTE_RRC_CARRIER_FREQ_GERAN_STRUCT *carrier_freq, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freq_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQ_GERAN_STRUCT *carrier_freq); + +/********************************************************************* + IE Name: Carrier Freqs GERAN + + Description: Provides one or more GERAN ARFCN values, which + represent a list of GERAN BCCH carrier frequencies + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Carrier Freqs GERAN define defined above +// Enums +// Carrier Freqs GERAN enum defined above +// Structs +// Carrier Freqs GERAN structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freqs_geran_ie(LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT *carrier_freqs, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freqs_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT *carrier_freqs); + +/********************************************************************* + IE Name: CDMA2000 Type + + Description: Describes the type of CDMA2000 network + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// CDMA2000 Type enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cdma2000_type_ie(LIBLTE_RRC_CDMA2000_TYPE_ENUM cdma2000_type, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cdma2000_type_ie(uint8 **ie_ptr, + LIBLTE_RRC_CDMA2000_TYPE_ENUM *cdma2000_type); + +/********************************************************************* + IE Name: Cell Identity + + Description: Unambiguously identifies a cell within a PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_identity_ie(uint32 cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_identity_ie(uint8 **ie_ptr, + uint32 *cell_id); + +/********************************************************************* + IE Name: Cell Index List + + Description: Concerns a list of cell indecies, which may be used + for different purposes + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Cell Index List struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_index_list_ie(LIBLTE_RRC_CELL_INDEX_LIST_STRUCT *cell_idx_list, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_index_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT *cell_idx_list); + +/********************************************************************* + IE Name: Cell Reselection Priority + + Description: Contains the absolute priority of the concerned + carrier frequency/set of frequencies (GERAN)/ + bandclass (CDMA2000), as used by the cell + reselection procedure + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_reselection_priority_ie(uint8 cell_resel_prio, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_reselection_priority_ie(uint8 **ie_ptr, + uint8 *cell_resel_prio); + +/********************************************************************* + IE Name: CSFB Registration Param 1xRTT + + Description: Indicates whether or not the UE shall perform a + CDMA2000 1xRTT pre-registration if the UE does not + have a valid/current pre-registration + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_POWER_DOWN_REG_R9_TRUE = 0, + LIBLTE_RRC_POWER_DOWN_REG_R9_N_ITEMS, +}LIBLTE_RRC_POWER_DOWN_REG_R9_ENUM; +static const char liblte_rrc_power_down_reg_r9_text[LIBLTE_RRC_POWER_DOWN_REG_R9_N_ITEMS][20] = {"TRUE"}; +// Structs +typedef struct{ + uint16 sid; + uint16 nid; + uint16 reg_zone; + uint8 reg_period; + uint8 total_zone; + uint8 zone_timer; + bool multiple_sid; + bool multiple_nid; + bool home_reg; + bool foreign_sid_reg; + bool foreign_nid_reg; + bool param_reg; + bool power_up_reg; +}LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT; +typedef struct{ + LIBLTE_RRC_POWER_DOWN_REG_R9_ENUM power_down_reg; +}LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_V920_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_registration_param_1xrtt_ie(LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT *csfb_reg_param, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_registration_param_1xrtt_ie(uint8 **ie_ptr, + LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT *csfb_reg_param); +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_registration_param_1xrtt_v920_ie(LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_V920_STRUCT *csfb_reg_param, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_registration_param_1xrtt_v920_ie(uint8 **ie_ptr, + LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_V920_STRUCT *csfb_reg_param); + +/********************************************************************* + IE Name: Cell Global ID EUTRA + + Description: Specifies the Evolved Cell Global Identifier (ECGI), + the globally unique identity of a cell in E-UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; + uint32 cell_id; +}LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_eutra_ie(LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT *cell_global_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT *cell_global_id); + +/********************************************************************* + IE Name: Cell Global ID UTRA + + Description: Specifies the global UTRAN Cell Identifier, the + globally unique identity of a cell in UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; + uint32 cell_id; +}LIBLTE_RRC_CELL_GLOBAL_ID_UTRA_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_utra_ie(LIBLTE_RRC_CELL_GLOBAL_ID_UTRA_STRUCT *cell_global_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_utra_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_UTRA_STRUCT *cell_global_id); + +/********************************************************************* + IE Name: Cell Global ID GERAN + + Description: Specifies the Cell Global Identity (CGI), the + globally unique identity of a cell in GERAN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; + uint16 lac; + uint16 cell_id; +}LIBLTE_RRC_CELL_GLOBAL_ID_GERAN_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_geran_ie(LIBLTE_RRC_CELL_GLOBAL_ID_GERAN_STRUCT *cell_global_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_GERAN_STRUCT *cell_global_id); + +/********************************************************************* + IE Name: Cell Global ID CDMA2000 + + Description: Specifies the Cell Global Identity (CGI), the + globally unique identity of a cell in CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint64 onexrtt; + uint32 hrpd[4]; +}LIBLTE_RRC_CELL_GLOBAL_ID_CDMA2000_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_cdma2000_ie(LIBLTE_RRC_CELL_GLOBAL_ID_CDMA2000_STRUCT *cell_global_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_CDMA2000_STRUCT *cell_global_id); + +/********************************************************************* + IE Name: CSG Identity + + Description: Identifies a Closed Subscriber Group + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_CSG_IDENTITY_NOT_PRESENT 0xFFFFFFFF +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_csg_identity_ie(uint32 csg_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csg_identity_ie(uint8 **ie_ptr, + uint32 *csg_id); + +/********************************************************************* + IE Name: Mobility Control Info + + Description: Includes parameters relevant for network controlled + mobility to/within E-UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_ANTENNA_PORTS_COUNT_AN1 = 0, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_AN2, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_AN4, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_SPARE1, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_N_ITEMS, +}LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM; +static const char liblte_rrc_antenna_ports_count_text[LIBLTE_RRC_ANTENNA_PORTS_COUNT_N_ITEMS][20] = {"an1", "an2", "an4", "SPARE"}; +typedef enum{ + LIBLTE_RRC_PHICH_DURATION_NORMAL = 0, + LIBLTE_RRC_PHICH_DURATION_EXTENDED, + LIBLTE_RRC_PHICH_DURATION_N_ITEMS, +}LIBLTE_RRC_PHICH_DURATION_ENUM; +static const char liblte_rrc_phich_duration_text[LIBLTE_RRC_PHICH_DURATION_N_ITEMS][20] = {"Normal", "Extended"}; +typedef enum{ + LIBLTE_RRC_PHICH_RESOURCE_1_6 = 0, + LIBLTE_RRC_PHICH_RESOURCE_1_2, + LIBLTE_RRC_PHICH_RESOURCE_1, + LIBLTE_RRC_PHICH_RESOURCE_2, + LIBLTE_RRC_PHICH_RESOURCE_N_ITEMS, +}LIBLTE_RRC_PHICH_RESOURCE_ENUM; +static const char liblte_rrc_phich_resource_text[LIBLTE_RRC_PHICH_RESOURCE_N_ITEMS][20] = {"1/6", "1/2", "1", "2"}; +static const double liblte_rrc_phich_resource_num[LIBLTE_RRC_PHICH_RESOURCE_N_ITEMS] = {0.16666667, 0.5, 1, 2}; +typedef enum{ + LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS1 = 0, + LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2, + LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS3, + LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS, +}LIBLTE_RRC_DELTA_PUCCH_SHIFT_ENUM; +static const char liblte_rrc_delta_pucch_shift_text[LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS][20] = {"1", "2", "3"}; +static const uint8 liblte_rrc_delta_pucch_shift_num[LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS] = {1, 2, 3}; +typedef enum{ + LIBLTE_RRC_HOPPING_MODE_INTER_SUBFRAME = 0, + LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME, + LIBLTE_RRC_HOOPPING_MODE_N_ITEMS, +}LIBLTE_RRC_HOPPING_MODE_ENUM; +static const char liblte_rrc_hopping_mode_text[LIBLTE_RRC_HOOPPING_MODE_N_ITEMS][20] = {"inter-subframe","intra-subframe"}; +typedef enum{ + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N4 = 0, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N8, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N12, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N16, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N20, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N24, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N28, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N32, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N36, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N40, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N44, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N48, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N52, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N56, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N60, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N64, + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N_ITEMS, +}LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_ENUM; +static const char liblte_rrc_number_of_ra_preambles_text[LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N_ITEMS][20] = { "4", "8", "12", "16", + "20", "24", "28", "32", + "36", "40", "44", "48", + "52", "56", "60", "64"}; +static const uint8 liblte_rrc_number_of_ra_preambles_num[LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N_ITEMS] = {4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64}; +typedef enum{ + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N4 = 0, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N8, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N12, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N16, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N20, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N24, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N28, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N32, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N36, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N40, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N44, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N48, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N52, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N56, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N60, + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N_ITEMS, +}LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_ENUM; +static const char liblte_rrc_size_of_ra_preambles_group_a_text[LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N_ITEMS][20] = { "4", "8", "12", "16", + "20", "24", "28", "32", + "36", "40", "44", "48", + "52", "56", "60"}; +static const uint8 liblte_rrc_size_of_ra_preambles_group_a_num[LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N_ITEMS] = {4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60}; +typedef enum{ + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_B56 = 0, + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_B144, + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_B208, + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_B256, + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_N_ITEMS, +}LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_ENUM; +static const char liblte_rrc_message_size_group_a_text[LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_N_ITEMS][20] = {"56", "144", "208", "256"}; +static const uint16 liblte_rrc_message_size_group_a_num[LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_N_ITEMS] = {56, 144, 208, 256}; +typedef enum{ + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_MINUS_INFINITY = 0, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB0, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB5, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB8, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB10, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB12, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB15, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_DB18, + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_N_ITEMS, +}LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_ENUM; +static const char liblte_rrc_message_power_offset_group_b_text[LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_N_ITEMS][20] = {"-INFINITY", "0", "5", "8", + "10", "12", "15", "18"}; +static const int liblte_rrc_message_power_offset_group_b_num[LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_N_ITEMS] = {-1, 0, 5, 8, + 10, 12, 15, 18}; +typedef enum{ + LIBLTE_RRC_POWER_RAMPING_STEP_DB0 = 0, + LIBLTE_RRC_POWER_RAMPING_STEP_DB2, + LIBLTE_RRC_POWER_RAMPING_STEP_DB4, + LIBLTE_RRC_POWER_RAMPING_STEP_DB6, + LIBLTE_RRC_POWER_RAMPING_STEP_N_ITEMS, +}LIBLTE_RRC_POWER_RAMPING_STEP_ENUM; +static const char liblte_rrc_power_ramping_step_text[LIBLTE_RRC_POWER_RAMPING_STEP_N_ITEMS][20] = {"0", "2", "4", "6"}; +static const uint8 liblte_rrc_power_ramping_step_num[LIBLTE_RRC_POWER_RAMPING_STEP_N_ITEMS] = {0, 2, 4, 6}; +typedef enum{ + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N120 = 0, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N118, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N116, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N114, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N112, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N110, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N108, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N106, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N104, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N102, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N100, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N98, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N96, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N94, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N92, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N90, + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_N_ITEMS, +}LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_ENUM; +static const char liblte_rrc_preamble_initial_received_target_power_text[LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_N_ITEMS][20] = {"-120", "-118", "-116", "-114", + "-112", "-110", "-108", "-106", + "-104", "-102", "-100", "-98", + "-96", "-94", "-92", "-90"}; +static const int8 liblte_rrc_preamble_initial_received_target_power_num[LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_N_ITEMS] = {-120, -118, -116, -114, -112, -110, -108, -106, + -104, -102, -100, -98, -96, -94, -92, -90}; +typedef enum{ + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N3 = 0, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N4, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N5, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N6, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N7, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N8, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N10, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N20, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N50, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N100, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N200, + LIBLTE_RRC_PREAMBLE_TRANS_MAX_N_ITEMS, +}LIBLTE_RRC_PREAMBLE_TRANS_MAX_ENUM; +static const char liblte_rrc_preamble_trans_max_text[LIBLTE_RRC_PREAMBLE_TRANS_MAX_N_ITEMS][20] = { "3", "4", "5", "6", + "7", "8", "10", "20", + "50", "100", "200"}; +static const uint8 liblte_rrc_preamble_trans_max_num[LIBLTE_RRC_PREAMBLE_TRANS_MAX_N_ITEMS] = {3, 4, 5, 6, 7, 8, 10, 20, 50, 100, 200}; +typedef enum{ + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF2 = 0, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF3, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF4, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF5, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF6, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF7, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF8, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF10, + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_N_ITEMS, +}LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_ENUM; +static const char liblte_rrc_ra_response_window_size_text[LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_N_ITEMS][20] = { "2", "3", "4", "5", + "6", "7", "8", "10"}; +static const uint8 liblte_rrc_ra_response_window_size_num[LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_N_ITEMS] = {2, 3, 4, 5, 6, 7, 8, 10}; +typedef enum{ + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF8 = 0, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF16, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF24, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF32, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF40, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF48, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF56, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF64, + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_N_ITEMS, +}LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_ENUM; +static const char liblte_rrc_mac_contention_resolution_timer_text[LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_N_ITEMS][20] = { "8", "16", "24", "32", + "40", "48", "56", "64"}; +static const uint8 liblte_rrc_mac_contention_resolution_timer_num[LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_N_ITEMS] = {8, 16, 24, 32, 40, 48, 56, 64}; +typedef enum{ + LIBLTE_RRC_UL_CP_LENGTH_1 = 0, + LIBLTE_RRC_UL_CP_LENGTH_2, + LIBLTE_RRC_UL_CP_LENGTH_N_ITEMS, +}LIBLTE_RRC_UL_CP_LENGTH_ENUM; +static const char liblte_rrc_ul_cp_length_text[LIBLTE_RRC_UL_CP_LENGTH_N_ITEMS][20] = {"Normal", "Extended"}; +typedef enum{ + LIBLTE_RRC_SRS_BW_CONFIG_0 = 0, + LIBLTE_RRC_SRS_BW_CONFIG_1, + LIBLTE_RRC_SRS_BW_CONFIG_2, + LIBLTE_RRC_SRS_BW_CONFIG_3, + LIBLTE_RRC_SRS_BW_CONFIG_4, + LIBLTE_RRC_SRS_BW_CONFIG_5, + LIBLTE_RRC_SRS_BW_CONFIG_6, + LIBLTE_RRC_SRS_BW_CONFIG_7, + LIBLTE_RRC_SRS_BW_CONFIG_N_ITEMS, +}LIBLTE_RRC_SRS_BW_CONFIG_ENUM; +static const char liblte_rrc_srs_bw_config_text[LIBLTE_RRC_SRS_BW_CONFIG_N_ITEMS][20] = {"0", "1", "2", "3", + "4", "5", "6", "7"}; +static const uint8 liblte_rrc_srs_bw_config_num[LIBLTE_RRC_SRS_BW_CONFIG_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6, 7}; +typedef enum{ + LIBLTE_RRC_SRS_SUBFR_CONFIG_0 = 0, + LIBLTE_RRC_SRS_SUBFR_CONFIG_1, + LIBLTE_RRC_SRS_SUBFR_CONFIG_2, + LIBLTE_RRC_SRS_SUBFR_CONFIG_3, + LIBLTE_RRC_SRS_SUBFR_CONFIG_4, + LIBLTE_RRC_SRS_SUBFR_CONFIG_5, + LIBLTE_RRC_SRS_SUBFR_CONFIG_6, + LIBLTE_RRC_SRS_SUBFR_CONFIG_7, + LIBLTE_RRC_SRS_SUBFR_CONFIG_8, + LIBLTE_RRC_SRS_SUBFR_CONFIG_9, + LIBLTE_RRC_SRS_SUBFR_CONFIG_10, + LIBLTE_RRC_SRS_SUBFR_CONFIG_11, + LIBLTE_RRC_SRS_SUBFR_CONFIG_12, + LIBLTE_RRC_SRS_SUBFR_CONFIG_13, + LIBLTE_RRC_SRS_SUBFR_CONFIG_14, + LIBLTE_RRC_SRS_SUBFR_CONFIG_15, + LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS, +}LIBLTE_RRC_SRS_SUBFR_CONFIG_ENUM; +static const char liblte_rrc_srs_subfr_config_text[LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS][20] = { "0", "1", "2", "3", + "4", "5", "6", "7", + "8", "9", "10", "11", + "12", "13", "14", "15"}; +static const uint8 liblte_rrc_srs_subfr_config_num[LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; +typedef enum{ + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_0 = 0, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_1, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_2, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_3, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_4, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_5, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_6, + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_N_ITEMS, +}LIBLTE_RRC_SUBFRAME_ASSIGNMENT_ENUM; +static const char liblte_rrc_subframe_assignment_text[LIBLTE_RRC_SUBFRAME_ASSIGNMENT_N_ITEMS][20] = {"0", "1", "2", "3", + "4", "5", "6"}; +static const uint8 liblte_rrc_subframe_assignment_num[LIBLTE_RRC_SUBFRAME_ASSIGNMENT_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6}; +typedef enum{ + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_0 = 0, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_1, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_2, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_3, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_4, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_5, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_6, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_7, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_8, + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_N_ITEMS, +}LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_ENUM; +static const char liblte_rrc_special_subframe_patterns_text[LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_N_ITEMS][20] = {"0", "1", "2", "3", + "4", "5", "6", "7", + "8"}; +static const uint8 liblte_rrc_special_subframe_patterns_num[LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; +typedef enum{ + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_0 = 0, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_04, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_05, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_06, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_07, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_08, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_09, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_1, + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS, +}LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_ENUM; +static const char liblte_rrc_ul_power_control_alpha_text[LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS][20] = {"0.0", "0.4", "0.5", "0.6", + "0.7", "0.8", "0.9", "1.0"}; +static const double liblte_rrc_ul_power_control_alpha_num[LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS] = {0.0, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; +typedef enum{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_NEG_2 = 0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_2, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS, +}LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_ENUM; +static const char liblte_rrc_delta_f_pucch_format_1_text[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS][20] = {"-2", "0", "2"}; +static const int8 liblte_rrc_delta_f_pucch_format_1_num[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS] = {-2, 0, 2}; +typedef enum{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_1 = 0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_3, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_5, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS, +}LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_ENUM; +static const char liblte_rrc_delta_f_pucch_format_1b_text[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS][20] = {"1", "3", "5"}; +static const uint8 liblte_rrc_delta_f_pucch_format_1b_num[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS] = {1, 3, 5}; +typedef enum{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_NEG_2 = 0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_1, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_2, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS, +}LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_ENUM; +static const char liblte_rrc_delta_f_pucch_format_2_text[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS][20] = {"-2", "0", "1", "2"}; +static const int8 liblte_rrc_delta_f_pucch_format_2_num[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS] = {-2, 0, 1, 2}; +typedef enum{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_NEG_2 = 0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_2, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS, +}LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_ENUM; +static const char liblte_rrc_delta_f_pucch_format_2a_text[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS][20] = {"-2", "0", "2"}; +static const int8 liblte_rrc_delta_f_pucch_format_2a_num[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS] = {-2, 0, 2}; +typedef enum{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_NEG_2 = 0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_0, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_2, + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS, +}LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_ENUM; +static const char liblte_rrc_delta_f_pucch_format_2b_text[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS][20] = {"-2", "0", "2"}; +static const int8 liblte_rrc_delta_f_pucch_format_2b_num[LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS] = {-2, 0, 2}; +typedef enum{ + LIBLTE_RRC_BANDWIDTH_N6 = 0, + LIBLTE_RRC_BANDWIDTH_N15, + LIBLTE_RRC_BANDWIDTH_N25, + LIBLTE_RRC_BANDWIDTH_N50, + LIBLTE_RRC_BANDWIDTH_N75, + LIBLTE_RRC_BANDWIDTH_N100, + LIBLTE_RRC_BANDWIDTH_SPARE10, + LIBLTE_RRC_BANDWIDTH_SPARE9, + LIBLTE_RRC_BANDWIDTH_SPARE8, + LIBLTE_RRC_BANDWIDTH_SPARE7, + LIBLTE_RRC_BANDWIDTH_SPARE6, + LIBLTE_RRC_BANDWIDTH_SPARE5, + LIBLTE_RRC_BANDWIDTH_SPARE4, + LIBLTE_RRC_BANDWIDTH_SPARE3, + LIBLTE_RRC_BANDWIDTH_SPARE2, + LIBLTE_RRC_BANDWIDTH_SPARE1, + LIBLTE_RRC_BANDWIDTH_N_ITEMS, +}LIBLTE_RRC_BANDWIDTH_ENUM; +static const char liblte_rrc_bandwidth_text[LIBLTE_RRC_BANDWIDTH_N_ITEMS][20] = { "1.4", "3", "5", "10", + "15", "20", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_T304_MS50 = 0, + LIBLTE_RRC_T304_MS100, + LIBLTE_RRC_T304_MS150, + LIBLTE_RRC_T304_MS200, + LIBLTE_RRC_T304_MS500, + LIBLTE_RRC_T304_MS1000, + LIBLTE_RRC_T304_MS2000, + LIBLTE_RRC_T304_SPARE, + LIBLTE_RRC_T304_N_ITEMS, +}LIBLTE_RRC_T304_ENUM; +static const char liblte_rrc_t304_text[LIBLTE_RRC_T304_N_ITEMS][20] = { "50", "100", "150", "200", + "500", "1000", "2000", "SPARE"}; +static const int32 liblte_rrc_t304_num[LIBLTE_RRC_T304_N_ITEMS] = {50, 100, 150, 200, 500, 1000, 2000, -1}; +// Structs +typedef struct{ + uint8 p_b; + int8 rs_power; +}LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT; +typedef struct{ + LIBLTE_RRC_PHICH_DURATION_ENUM dur; + LIBLTE_RRC_PHICH_RESOURCE_ENUM res; +}LIBLTE_RRC_PHICH_CONFIG_STRUCT; +typedef struct{ + uint8 prach_config_index; + uint8 zero_correlation_zone_config; + uint8 prach_freq_offset; + bool high_speed_flag; +}LIBLTE_RRC_PRACH_CONFIG_INFO_STRUCT; +typedef struct{ + LIBLTE_RRC_PRACH_CONFIG_INFO_STRUCT prach_cnfg_info; + uint16 root_sequence_index; +}LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT; +typedef struct{ + LIBLTE_RRC_PRACH_CONFIG_INFO_STRUCT prach_cnfg_info; + uint16 root_sequence_index; + bool prach_cnfg_info_present; +}LIBLTE_RRC_PRACH_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_DELTA_PUCCH_SHIFT_ENUM delta_pucch_shift; + uint16 n1_pucch_an; + uint8 n_rb_cqi; + uint8 n_cs_an; +}LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT; +typedef struct{ + uint8 group_assignment_pusch; + uint8 cyclic_shift; + bool group_hopping_enabled; + bool sequence_hopping_enabled; +}LIBLTE_RRC_UL_RS_PUSCH_STRUCT; +typedef struct{ + LIBLTE_RRC_UL_RS_PUSCH_STRUCT ul_rs; + LIBLTE_RRC_HOPPING_MODE_ENUM hopping_mode; + uint8 n_sb; + uint8 pusch_hopping_offset; + bool enable_64_qam; +}LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT; +typedef struct{ + LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_ENUM size_of_ra; + LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_ENUM msg_size; + LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_ENUM msg_pwr_offset_group_b; + bool present; +}LIBLTE_RRC_PREAMBLES_GROUP_A_STRUCT; +typedef struct{ + LIBLTE_RRC_PREAMBLES_GROUP_A_STRUCT preambles_group_a_cnfg; + LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_ENUM num_ra_preambles; + LIBLTE_RRC_POWER_RAMPING_STEP_ENUM pwr_ramping_step; + LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_ENUM preamble_init_rx_target_pwr; + LIBLTE_RRC_PREAMBLE_TRANS_MAX_ENUM preamble_trans_max; + LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_ENUM ra_resp_win_size; + LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_ENUM mac_con_res_timer; + uint8 max_harq_msg3_tx; +}LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT; +typedef struct{ + uint8 preamble_index; + uint8 prach_mask_index; +}LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT; +typedef struct{ + LIBLTE_RRC_SRS_BW_CONFIG_ENUM bw_cnfg; + LIBLTE_RRC_SRS_SUBFR_CONFIG_ENUM subfr_cnfg; + bool ack_nack_simul_tx; + bool max_up_pts; + bool max_up_pts_present; + bool present; +}LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT; +typedef struct{ + LIBLTE_RRC_SUBFRAME_ASSIGNMENT_ENUM sf_assignment; + LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_ENUM special_sf_patterns; +}LIBLTE_RRC_TDD_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_ENUM format_1; + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_ENUM format_1b; + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_ENUM format_2; + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_ENUM format_2a; + LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_ENUM format_2b; +}LIBLTE_RRC_DELTA_FLIST_PUCCH_STRUCT; +typedef struct{ + LIBLTE_RRC_DELTA_FLIST_PUCCH_STRUCT delta_flist_pucch; + LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_ENUM alpha; + int8 p0_nominal_pusch; + int8 p0_nominal_pucch; + int8 delta_preamble_msg3; +}LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT; +typedef struct{ + LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT rach_cnfg; + LIBLTE_RRC_PRACH_CONFIG_STRUCT prach_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg; + LIBLTE_RRC_PHICH_CONFIG_STRUCT phich_cnfg; + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; + LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT ul_pwr_ctrl; + LIBLTE_RRC_TDD_CONFIG_STRUCT tdd_cnfg; + LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM ant_info; + LIBLTE_RRC_UL_CP_LENGTH_ENUM ul_cp_length; + int8 p_max; + bool rach_cnfg_present; + bool pdsch_cnfg_present; + bool phich_cnfg_present; + bool pucch_cnfg_present; + bool srs_ul_cnfg_present; + bool ul_pwr_ctrl_present; + bool ant_info_present; + bool p_max_present; + bool tdd_cnfg_present; +}LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT; +typedef struct{ + uint16 dl_carrier_freq; + uint16 ul_carrier_freq; + bool ul_carrier_freq_present; +}LIBLTE_RRC_CARRIER_FREQ_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_BANDWIDTH_ENUM dl_bw; + LIBLTE_RRC_BANDWIDTH_ENUM ul_bw; + bool ul_bw_present; +}LIBLTE_RRC_CARRIER_BANDWIDTH_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_CARRIER_FREQ_EUTRA_STRUCT carrier_freq_eutra; + LIBLTE_RRC_CARRIER_BANDWIDTH_EUTRA_STRUCT carrier_bw_eutra; + LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT rr_cnfg_common; + LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT rach_cnfg_ded; + LIBLTE_RRC_T304_ENUM t304; + uint16 target_pci; + uint16 new_ue_id; + uint8 add_spect_em; + bool carrier_freq_eutra_present; + bool carrier_bw_eutra_present; + bool add_spect_em_present; + bool rach_cnfg_ded_present; +}LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mobility_control_info_ie(LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT *mob_ctrl_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mobility_control_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT *mob_ctrl_info); + +/********************************************************************* + IE Name: Mobility Parameters CDMA2000 (1xRTT) + + Description: Contains the parameters provided to the UE for + handover and (enhanced) CSFB to 1xRTT support + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Mobility State Parameters + + Description: Contains parameters to determine UE mobility state + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Mobility State Parameters enums defined above +// Structs +// Mobility State Parameters struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mobility_state_parameters_ie(LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT *mobility_state_params, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mobility_state_parameters_ie(uint8 **ie_ptr, + LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT *mobility_state_params); + +/********************************************************************* + IE Name: Phys Cell ID + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_ie(uint16 phys_cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_ie(uint8 **ie_ptr, + uint16 *phys_cell_id); + +/********************************************************************* + IE Name: Phys Cell ID Range + + Description: Encodes either a single or a range of physical cell + identities + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Phys Cell ID Range enum defined above +// Structs +// Phys Cell ID Range struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_range_ie(LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT *phys_cell_id_range, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_range_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT *phys_cell_id_range); + +/********************************************************************* + IE Name: Phys Cell ID Range UTRA FDD List + + Description: Encodes one or more of Phys Cell ID Range UTRA FDD + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Phys Cell ID CDMA2000 + + Description: Identifies the PN offset that represents the + "Physical cell identity" in CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_PN_OFFSET 511 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_cdma2000_ie(uint16 phys_cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_cdma2000_ie(uint8 **ie_ptr, + uint16 *phys_cell_id); + +/********************************************************************* + IE Name: Phys Cell ID GERAN + + Description: Contains the Base Station Identity Code (BSIC) + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Phys Cell ID GERAN struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_geran_ie(LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT *phys_cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT *phys_cell_id); + +/********************************************************************* + IE Name: Phys Cell ID UTRA FDD + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_utra_fdd_ie(uint16 phys_cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_utra_fdd_ie(uint8 **ie_ptr, + uint16 *phys_cell_id); + +/********************************************************************* + IE Name: Phys Cell ID UTRA TDD + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_utra_tdd_ie(uint8 phys_cell_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_utra_tdd_ie(uint8 **ie_ptr, + uint8 *phys_cell_id); + +/********************************************************************* + IE Name: PLMN Identity + + Description: Identifies a Public Land Mobile Network + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MCC_NOT_PRESENT 0xFFFF +// Enums +// Structs +// PLMN Identity struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_plmn_identity_ie(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *plmn_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_plmn_identity_ie(uint8 **ie_ptr, + LIBLTE_RRC_PLMN_IDENTITY_STRUCT *plmn_id); + +/********************************************************************* + IE Name: Pre Registration Info HRPD + + Description: FIXME + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Pre Registration Info HRPD struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pre_registration_info_hrpd_ie(LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT *pre_reg_info_hrpd, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pre_registration_info_hrpd_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT *pre_reg_info_hrpd); + +/********************************************************************* + IE Name: Q Qual Min + + Description: Indicates for cell selection/re-selection the + required minimum received RSRQ level in the (E-UTRA) + cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_qual_min_ie(int8 q_qual_min, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_qual_min_ie(uint8 **ie_ptr, + int8 *q_qual_min); + +/********************************************************************* + IE Name: Q Rx Lev Min + + Description: Indicates the required minimum received RSRP level in + the (E-UTRA) cell for cell selection/re-selection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_rx_lev_min_ie(int16 q_rx_lev_min, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_rx_lev_min_ie(uint8 **ie_ptr, + int16 *q_rx_lev_min); + +/********************************************************************* + IE Name: Q Offset Range + + Description: Indicates a cell or frequency specific offset to be + applied when evaluating candidates for cell + reselection or when evaluating triggering conditions + for measurement reporting + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Q Offset Range enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_offset_range_ie(LIBLTE_RRC_Q_OFFSET_RANGE_ENUM q_offset_range, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_offset_range_ie(uint8 **ie_ptr, + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM *q_offset_range); + +/********************************************************************* + IE Name: Q Offset Range Inter RAT + + Description: Indicates a frequency specific offset to be applied + when evaluating triggering conditions for + measurement reporting + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_offset_range_inter_rat_ie(int8 q_offset_range_inter_rat, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_offset_range_inter_rat_ie(uint8 **ie_ptr, + int8 *q_offset_range_inter_rat); + +/********************************************************************* + IE Name: Reselection Threshold + + Description: Indicates an RX level threshold for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_reselection_threshold_ie(uint8 resel_thresh, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_reselection_threshold_ie(uint8 **ie_ptr, + uint8 *resel_thresh); + +/********************************************************************* + IE Name: Reselection Threshold Q + + Description: Indicates a quality level threshold for cell + reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_reselection_threshold_q_ie(uint8 resel_thresh_q, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_reselection_threshold_q_ie(uint8 **ie_ptr, + uint8 *resel_thresh_q); + +/********************************************************************* + IE Name: S Cell Index + + Description: Contains a short identity, used to identify an + SCell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_s_cell_index_ie(uint8 s_cell_idx, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_s_cell_index_ie(uint8 **ie_ptr, + uint8 *s_cell_idx); + +/********************************************************************* + IE Name: Serv Cell Index + + Description: Contains a short identity, used to identify a + serving cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_serv_cell_index_ie(uint8 serv_cell_idx, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_serv_cell_index_ie(uint8 **ie_ptr, + uint8 *serv_cell_idx); + +/********************************************************************* + IE Name: Speed State Scale Factors + + Description: Contains factors, to be applied when the UE is in + medium or high speed state, used for scaling a + mobility control related parameter + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Speed State Scale Factors enums defined above +// Structs +// Speed State Scale Factors struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_speed_state_scale_factors_ie(LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT *speed_state_scale_factors, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_speed_state_scale_factors_ie(uint8 **ie_ptr, + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT *speed_state_scale_factors); + +/********************************************************************* + IE Name: System Info List GERAN + + Description: Contains system information of a GERAN cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: System Time Info CDMA2000 + + Description: Informs the UE about the absolute time in the current + cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint64 system_time; + bool system_time_async; + bool cdma_eutra_sync; +}LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_system_time_info_cdma2000_ie(LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT *sys_time_info_cdma2000, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_system_time_info_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT *sys_time_info_cdma2000); + +/********************************************************************* + IE Name: Tracking Area Code + + Description: Identifies a tracking area within the scope of a + PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_tracking_area_code_ie(uint16 tac, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tracking_area_code_ie(uint8 **ie_ptr, + uint16 *tac); + +/********************************************************************* + IE Name: T Reselection + + Description: Contains the timer T_reselection_rat for E-UTRA, + UTRA, GERAN, or CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_t_reselection_ie(uint8 t_resel, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_t_reselection_ie(uint8 **ie_ptr, + uint8 *t_resel); + +/********************************************************************* + IE Name: Next Hop Chaining Count + + Description: Updates the Kenb key and corresponds to parameter + NCC + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_next_hop_chaining_count_ie(uint8 next_hop_chaining_count, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_next_hop_chaining_count_ie(uint8 **ie_ptr, + uint8 *next_hop_chaining_count); + +/********************************************************************* + IE Name: Security Algorithm Config + + Description: Configures AS integrity protection algorithm (SRBs) + and AS ciphering algorithm (SRBs and DRBs) + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_CIPHERING_ALGORITHM_EEA0 = 0, + LIBLTE_RRC_CIPHERING_ALGORITHM_EEA1, + LIBLTE_RRC_CIPHERING_ALGORITHM_EEA2, + LIBLTE_RRC_CIPHERING_ALGORITHM_SPARE5, + LIBLTE_RRC_CIPHERING_ALGORITHM_SPARE4, + LIBLTE_RRC_CIPHERING_ALGORITHM_SPARE3, + LIBLTE_RRC_CIPHERING_ALGORITHM_SPARE2, + LIBLTE_RRC_CIPHERING_ALGORITHM_SPARE1, + LIBLTE_RRC_CIPHERING_ALGORITHM_N_ITEMS, +}LIBLTE_RRC_CIPHERING_ALGORITHM_ENUM; +static const char liblte_rrc_ciphering_algorithm_text[LIBLTE_RRC_CIPHERING_ALGORITHM_N_ITEMS][20] = { "EEA0", "EEA1", "EEA2", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_EIA0_V920 = 0, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_EIA1, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_EIA2, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_SPARE5, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_SPARE4, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_SPARE3, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_SPARE2, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_SPARE1, + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_N_ITEMS, +}LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_ENUM; +static const char liblte_rrc_integrity_prot_algorithm_text[LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_N_ITEMS][20] = { "EIA0", "EIA1", "EIA2", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +// Structs +typedef struct{ + LIBLTE_RRC_CIPHERING_ALGORITHM_ENUM cipher_alg; + LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_ENUM int_alg; +}LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_algorithm_config_ie(LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT *sec_alg_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_algorithm_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT *sec_alg_cnfg); + +/********************************************************************* + IE Name: Short MAC I + + Description: Identifies and verifies the UE at RRC connection + re-establishment + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_short_mac_i_ie(uint16 short_mac_i, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_short_mac_i_ie(uint8 **ie_ptr, + uint16 *short_mac_i); + +/********************************************************************* + IE Name: Antenna Info + + Description: Specifies the common and the UE specific antenna + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Antenna Ports Count enum defined above +typedef enum{ + LIBLTE_RRC_TRANSMISSION_MODE_1 = 0, + LIBLTE_RRC_TRANSMISSION_MODE_2, + LIBLTE_RRC_TRANSMISSION_MODE_3, + LIBLTE_RRC_TRANSMISSION_MODE_4, + LIBLTE_RRC_TRANSMISSION_MODE_5, + LIBLTE_RRC_TRANSMISSION_MODE_6, + LIBLTE_RRC_TRANSMISSION_MODE_7, + LIBLTE_RRC_TRANSMISSION_MODE_8, + LIBLTE_RRC_TRANSMISSION_MODE_N_ITEMS, +}LIBLTE_RRC_TRANSMISSION_MODE_ENUM; +static const char liblte_rrc_transmission_mode_text[LIBLTE_RRC_TRANSMISSION_MODE_N_ITEMS][20] = {"1", "2", "3", "4", + "5", "6", "7", "8"}; +static const uint8 liblte_rrc_transmission_mode_num[LIBLTE_RRC_TRANSMISSION_MODE_N_ITEMS] = {1, 2, 3, 4, 5, 6, 7, 8}; +typedef enum{ + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM3 = 0, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM3, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM4, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM4, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM5, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM5, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM6, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM6, + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N_ITEMS, +}LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_CHOICE_ENUM; +static const char liblte_rrc_codebook_subset_restriction_choice_text[LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N_ITEMS][20] = {"n2_tm3", "n4_tm3", "n2_tm4", "n4_tm4", + "n2_tm5", "n4_tm5", "n2_tm6", "n4_tm6"}; +typedef enum{ + LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_CLOSED_LOOP = 0, + LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_OPEN_LOOP, + LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_N_ITEMS, +}LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_ENUM; +static const char liblte_rrc_ue_tx_antenna_selection_text[LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_N_ITEMS][20] = {"closed_loop", "open_loop"}; +// Structs +typedef struct{ + LIBLTE_RRC_TRANSMISSION_MODE_ENUM tx_mode; + LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_CHOICE_ENUM codebook_subset_restriction_choice; + LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_ENUM ue_tx_antenna_selection_setup; + uint64 codebook_subset_restriction; + bool codebook_subset_restriction_present; + bool ue_tx_antenna_selection_setup_present; +}LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_antenna_info_common_ie(LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM antenna_ports_cnt, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_antenna_info_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM *antenna_ports_cnt); +LIBLTE_ERROR_ENUM liblte_rrc_pack_antenna_info_dedicated_ie(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *antenna_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_antenna_info_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *antenna_info); + +/********************************************************************* + IE Name: CQI Report Config + + Description: Specifies the CQI reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM12 = 0, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM20, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM22, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_SPARE3, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_SPARE2, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_SPARE1, + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_N_ITEMS, +}LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_ENUM; +static const char liblte_rrc_cqi_report_mode_aperiodic_text[LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_N_ITEMS][20] = { "rm12", "rm20", "rm22", "rm30", + "rm31", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI = 0, + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_SUBBAND_CQI, + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_N_ITEMS, +}LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_ENUM; +static const char liblte_rrc_cqi_format_indicator_periodic_text[LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_N_ITEMS][20] = {"wideband_cqi", "subband_cqi"}; +// Structs +typedef struct{ + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_ENUM format_ind_periodic; + uint32 pucch_resource_idx; + uint32 pmi_cnfg_idx; + uint32 ri_cnfg_idx; + uint32 format_ind_periodic_subband_k; + bool ri_cnfg_idx_present; + bool simult_ack_nack_and_cqi; +}LIBLTE_RRC_CQI_REPORT_PERIODIC_STRUCT; +typedef struct{ + LIBLTE_RRC_CQI_REPORT_PERIODIC_STRUCT report_periodic; + LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_ENUM report_mode_aperiodic; + int32 nom_pdsch_rs_epre_offset; + bool report_mode_aperiodic_present; + bool report_periodic_present; + bool report_periodic_setup_present; +}LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_cqi_report_config_ie(LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT *cqi_report_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cqi_report_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT *cqi_report_cnfg); + +/********************************************************************* + IE Name: Cross Carrier Scheduling Config + + Description: Specifies the configuration when the cross carrier + scheduling is used in a cell + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: CSI RS Config + + Description: Specifies the CSI (Channel State Information) + reference signal configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: DRB Identity + + Description: Identifies a DRB used by a UE + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_drb_identity_ie(uint8 drb_id, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_drb_identity_ie(uint8 **ie_ptr, + uint8 *drb_id); + +/********************************************************************* + IE Name: Logical Channel Config + + Description: Configures the logical channel parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_0 = 0, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_8, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_16, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_32, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_64, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_128, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_KBPS_256, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_INFINITY, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE8, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE7, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE6, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE5, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE4, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE3, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE2, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_SPARE1, + LIBLTE_RRC_PRIORITIZED_BIT_RATE_N_ITEMS, +}LIBLTE_RRC_PRIORITIZED_BIT_RATE_ENUM; +static const char liblte_rrc_prioritized_bit_rate_text[LIBLTE_RRC_PRIORITIZED_BIT_RATE_N_ITEMS][20] = { "0", "8", "16", "32", + "64", "128", "256", "INFINITY", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +static const int liblte_rrc_prioritized_bit_rate_num[LIBLTE_RRC_PRIORITIZED_BIT_RATE_N_ITEMS] = { 0 , 8 , 16 , 32 , + 64 , 128 , 256 , -1 , + -1 , -1 , -1 , -1 , + -1 , -1 , -1 , -1 }; +typedef enum{ + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS50 = 0, + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS100, + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS150, + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS300, + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS500, + LIBLTE_RRC_BUCKET_SIZE_DURATION_MS1000, + LIBLTE_RRC_BUCKET_SIZE_DURATION_SPARE2, + LIBLTE_RRC_BUCKET_SIZE_DURATION_SPARE1, + LIBLTE_RRC_BUCKET_SIZE_DURATION_N_ITEMS, +}LIBLTE_RRC_BUCKET_SIZE_DURATION_ENUM; +static const char liblte_rrc_bucket_size_duration_text[LIBLTE_RRC_BUCKET_SIZE_DURATION_N_ITEMS][20] = { "50", "100", "150", "300", + "500", "1000", "SPARE", "SPARE"}; +static const int16 liblte_rrc_bucket_size_duration_num[LIBLTE_RRC_BUCKET_SIZE_DURATION_N_ITEMS] = {50, 100, 150, 300, 500, 1000, -1, -1}; +typedef enum{ + LIBLTE_RRC_LOGICAL_CHANNEL_SR_MASK_R9_SETUP = 0, + LIBLTE_RRC_LOGICAL_CHANNEL_SR_MASK_R9_N_ITEMS, +}LIBLTE_RRC_LOGICAL_CHANNEL_SR_MASK_R9_ENUM; +static const char liblte_rrc_logical_channel_sr_mask_r9_text[LIBLTE_RRC_LOGICAL_CHANNEL_SR_MASK_R9_N_ITEMS][20] = {"SETUP"}; +// Structs +typedef struct{ + LIBLTE_RRC_PRIORITIZED_BIT_RATE_ENUM prioritized_bit_rate; + LIBLTE_RRC_BUCKET_SIZE_DURATION_ENUM bucket_size_duration; + uint8 priority; + uint8 log_chan_group; + bool log_chan_group_present; +}LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT; +typedef struct{ + LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT ul_specific_params; + LIBLTE_RRC_LOGICAL_CHANNEL_SR_MASK_R9_ENUM log_chan_sr_mask; + bool ul_specific_params_present; + bool log_chan_sr_mask_present; +}LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_logical_channel_config_ie(LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT *log_chan_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_logical_channel_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT *log_chan_cnfg); + +/********************************************************************* + IE Name: MAC Main Config + + Description: Specifies the MAC main configuration for signalling + and data radio bearers + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_MAX_HARQ_TX_N1 = 0, + LIBLTE_RRC_MAX_HARQ_TX_N2, + LIBLTE_RRC_MAX_HARQ_TX_N3, + LIBLTE_RRC_MAX_HARQ_TX_N4, + LIBLTE_RRC_MAX_HARQ_TX_N5, + LIBLTE_RRC_MAX_HARQ_TX_N6, + LIBLTE_RRC_MAX_HARQ_TX_N7, + LIBLTE_RRC_MAX_HARQ_TX_N8, + LIBLTE_RRC_MAX_HARQ_TX_N10, + LIBLTE_RRC_MAX_HARQ_TX_N12, + LIBLTE_RRC_MAX_HARQ_TX_N16, + LIBLTE_RRC_MAX_HARQ_TX_N20, + LIBLTE_RRC_MAX_HARQ_TX_N24, + LIBLTE_RRC_MAX_HARQ_TX_N28, + LIBLTE_RRC_MAX_HARQ_TX_SPARE2, + LIBLTE_RRC_MAX_HARQ_TX_SPARE1, + LIBLTE_RRC_MAX_HARQ_TX_N_ITEMS, +}LIBLTE_RRC_MAX_HARQ_TX_ENUM; +static const char liblte_rrc_max_harq_tx_text[LIBLTE_RRC_MAX_HARQ_TX_N_ITEMS][20] = { "1", "2", "3", "4", + "5", "6", "7", "8", + "10", "12", "16", "20", + "24", "28", "SPARE", "SPARE"}; +static const int8 liblte_rrc_max_harq_tx_num[LIBLTE_RRC_MAX_HARQ_TX_N_ITEMS] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 20, 24, 28, -1, -1}; +typedef enum{ + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF5 = 0, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF10, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF16, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF20, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF32, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF40, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF64, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF80, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF128, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF160, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF320, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF640, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF1280, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SF2560, + LIBLTE_RRC_PERIODIC_BSR_TIMER_INFINITY, + LIBLTE_RRC_PERIODIC_BSR_TIMER_SPARE, + LIBLTE_RRC_PERIODIC_BSR_TIMER_N_ITEMS, +}LIBLTE_RRC_PERIODIC_BSR_TIMER_ENUM; +static const char liblte_rrc_periodic_bsr_timer_text[LIBLTE_RRC_PERIODIC_BSR_TIMER_N_ITEMS][20] = { "sf5", "sf10", "sf16", "sf20", + "sf32", "sf40", "sf64", "sf80", + "sf128", "sf160", "sf320", "sf640", + "sf1280", "sf2560", "INFINITY", "SPARE"}; +static const int32 liblte_rrc_periodic_bsr_timer_num[LIBLTE_RRC_PERIODIC_BSR_TIMER_N_ITEMS] = { 5, 10, 16, 20, 32, 40, 64, 80, 128, 160, 320, 640, + 1280, 2560, -1, -1}; +typedef enum{ + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF320 = 0, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF640, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF1280, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF2560, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF5120, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF10240, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SPARE2, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SPARE1, + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_N_ITEMS, +}LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_ENUM; +static const char liblte_rrc_retransmission_bsr_timer_text[LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_N_ITEMS][20] = { "sf320", "sf640", "sf1280", "sf2560", + "sf5120", "sf10240", "SPARE", "SPARE"}; +static const int32 liblte_rrc_retransmission_bsr_timer_num[LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_N_ITEMS] = { 320, 640, 1280, 2560, 5120, 10240, -1, -1}; +typedef enum{ + LIBLTE_RRC_ON_DURATION_TIMER_PSF1 = 0, + LIBLTE_RRC_ON_DURATION_TIMER_PSF2, + LIBLTE_RRC_ON_DURATION_TIMER_PSF3, + LIBLTE_RRC_ON_DURATION_TIMER_PSF4, + LIBLTE_RRC_ON_DURATION_TIMER_PSF5, + LIBLTE_RRC_ON_DURATION_TIMER_PSF6, + LIBLTE_RRC_ON_DURATION_TIMER_PSF8, + LIBLTE_RRC_ON_DURATION_TIMER_PSF10, + LIBLTE_RRC_ON_DURATION_TIMER_PSF20, + LIBLTE_RRC_ON_DURATION_TIMER_PSF30, + LIBLTE_RRC_ON_DURATION_TIMER_PSF40, + LIBLTE_RRC_ON_DURATION_TIMER_PSF50, + LIBLTE_RRC_ON_DURATION_TIMER_PSF60, + LIBLTE_RRC_ON_DURATION_TIMER_PSF80, + LIBLTE_RRC_ON_DURATION_TIMER_PSF100, + LIBLTE_RRC_ON_DURATION_TIMER_PSF200, + LIBLTE_RRC_ON_DURATION_TIMER_N_ITEMS, +}LIBLTE_RRC_ON_DURATION_TIMER_ENUM; +static const char liblte_rrc_on_duration_timer_text[LIBLTE_RRC_ON_DURATION_TIMER_N_ITEMS][20] = { "psf1", "psf2", "psf3", "psf4", + "psf5", "psf6", "psf8", "psf10", + "psf20", "psf30", "psf40", "psf50", + "psf60", "psf80", "psf100", "psf200"}; +typedef enum{ + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF1 = 0, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF2, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF3, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF4, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF5, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF6, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF8, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF10, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF20, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF30, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF40, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF50, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF60, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF80, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF100, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF200, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF300, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF500, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF750, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF1280, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF1920, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_PSF2560, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE10, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE9, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE8, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE7, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE6, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE5, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE4, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE3, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE2, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_SPARE1, + LIBLTE_RRC_DRX_INACTIVITY_TIMER_N_ITEMS, +}LIBLTE_RRC_DRX_INACTIVITY_TIMER_ENUM; +static const char liblte_rrc_drx_inactivity_timer_text[LIBLTE_RRC_DRX_INACTIVITY_TIMER_N_ITEMS][20] = { "psf1", "psf2", "psf3", "psf4", + "psf5", "psf6", "psf8", "psf10", + "psf20", "psf30", "psf40", "psf50", + "psf60", "psf80", "psf100", "psf200", + "psf300", "psf500", "psf750", "psf1280", + "psf1920", "psf2560", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF1 = 0, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF2, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF4, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF6, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF8, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF16, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF24, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_PSF33, + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_N_ITEMS, +}LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_ENUM; +static const char liblte_rrc_drx_retransmission_timer_text[LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_N_ITEMS][20] = { "psf1", "psf2", "psf4", "psf6", + "psf8", "psf16", "psf24", "psf33"}; +typedef enum{ + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF10 = 0, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF20, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF32, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF40, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF64, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF80, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF128, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF160, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF256, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF320, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF512, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF640, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1024, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1280, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2048, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2560, + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_N_ITEMS, +}LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_CHOICE_ENUM; +static const char liblte_rrc_long_drx_cycle_start_offset_choice_text[LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_N_ITEMS][20] = { "sf10", "sf20", "sf32", "sf40", + "sf64", "sf80", "sf128", "sf160", + "sf256", "sf320", "sf512", "sf640", + "sf1024", "sf1280", "sf2048", "sf2560"}; +typedef enum{ + LIBLTE_RRC_SHORT_DRX_CYCLE_SF2 = 0, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF5, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF8, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF10, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF16, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF20, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF32, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF40, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF64, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF80, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF128, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF160, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF256, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF320, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF512, + LIBLTE_RRC_SHORT_DRX_CYCLE_SF640, + LIBLTE_RRC_SHORT_DRX_CYCLE_N_ITEMS, +}LIBLTE_RRC_SHORT_DRX_CYCLE_ENUM; +static const char liblte_rrc_short_drx_cycle_text[LIBLTE_RRC_SHORT_DRX_CYCLE_N_ITEMS][20] = { "sf2", "sf5", "sf8", "sf10", + "sf16", "sf20", "sf32", "sf40", + "sf64", "sf80", "sf128", "sf160", + "sf256", "sf320", "sf512", "sf640"}; +typedef enum{ + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF500 = 0, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF750, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF1280, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF1920, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF2560, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF5120, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_SF10240, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS, +}LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM; +static const char liblte_rrc_time_alignment_timer_text[LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS][20] = { "sf500", "sf750", "sf1280", "sf1920", + "sf2560", "sf5120", "sf10240", "INFINITY"}; +static const int liblte_rrc_time_alignment_timer_num[LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS] = { 500, 750, 1280, 1920, 2560, 5120, 10240, -1}; +typedef enum{ + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF10 = 0, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF20, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF50, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF100, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF200, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF500, + LIBLTE_RRC_PERIODIC_PHR_TIMER_SF1000, + LIBLTE_RRC_PERIODIC_PHR_TIMER_INFINITY, + LIBLTE_RRC_PERIODIC_PHR_TIMER_N_ITEMS, +}LIBLTE_RRC_PERIODIC_PHR_TIMER_ENUM; +static const char liblte_rrc_periodic_phr_timer_text[LIBLTE_RRC_PERIODIC_PHR_TIMER_N_ITEMS][20] = { "sf10", "sf20", "sf50", "sf100", + "sf200", "sf500", "sf1000", "INFINITY"}; +static int liblte_rrc_periodic_phr_timer_num[LIBLTE_RRC_PERIODIC_PHR_TIMER_N_ITEMS] = {10, 20, 50, 100, 200, 500, 1000, -1}; + +typedef enum{ + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF0 = 0, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF10, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF20, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF50, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF100, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF200, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF500, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_SF1000, + LIBLTE_RRC_PROHIBIT_PHR_TIMER_N_ITEMS, +}LIBLTE_RRC_PROHIBIT_PHR_TIMER_ENUM; +static const char liblte_rrc_prohibit_phr_timer_text[LIBLTE_RRC_PROHIBIT_PHR_TIMER_N_ITEMS][20] = { "sf0", "sf10", "sf20", "sf50", + "sf100", "sf200", "sf500", "sf1000"}; + +static int liblte_rrc_prohibit_phr_timer_num[LIBLTE_RRC_PROHIBIT_PHR_TIMER_N_ITEMS] = {0, 10, 20, 50, 100, 200, 500, 1000}; + +typedef enum{ + LIBLTE_RRC_DL_PATHLOSS_CHANGE_DB1 = 0, + LIBLTE_RRC_DL_PATHLOSS_CHANGE_DB3, + LIBLTE_RRC_DL_PATHLOSS_CHANGE_DB6, + LIBLTE_RRC_DL_PATHLOSS_CHANGE_INFINITY, + LIBLTE_RRC_DL_PATHLOSS_CHANGE_N_ITEMS, +}LIBLTE_RRC_DL_PATHLOSS_CHANGE_ENUM; +static const char liblte_rrc_dl_pathloss_change_text[LIBLTE_RRC_DL_PATHLOSS_CHANGE_N_ITEMS][20] = {"1dB", "3dB", "6dB", "INFINITY"}; + +static int liblte_rrc_dl_pathloss_change_num[LIBLTE_RRC_DL_PATHLOSS_CHANGE_N_ITEMS] = {1, 3, 6, -1}; + +// Structs +typedef struct{ + LIBLTE_RRC_MAX_HARQ_TX_ENUM max_harq_tx; + LIBLTE_RRC_PERIODIC_BSR_TIMER_ENUM periodic_bsr_timer; + LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_ENUM retx_bsr_timer; + bool tti_bundling; + bool max_harq_tx_present; + bool periodic_bsr_timer_present; +}LIBLTE_RRC_ULSCH_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_ON_DURATION_TIMER_ENUM on_duration_timer; + LIBLTE_RRC_DRX_INACTIVITY_TIMER_ENUM drx_inactivity_timer; + LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_ENUM drx_retx_timer; + LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_CHOICE_ENUM long_drx_cycle_start_offset_choice; + LIBLTE_RRC_SHORT_DRX_CYCLE_ENUM short_drx_cycle; + uint32 long_drx_cycle_start_offset; + uint32 short_drx_cycle_timer; + bool setup_present; + bool short_drx_present; +}LIBLTE_RRC_DRX_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_PERIODIC_PHR_TIMER_ENUM periodic_phr_timer; + LIBLTE_RRC_PROHIBIT_PHR_TIMER_ENUM prohibit_phr_timer; + LIBLTE_RRC_DL_PATHLOSS_CHANGE_ENUM dl_pathloss_change; + bool setup_present; +}LIBLTE_RRC_PHR_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_ULSCH_CONFIG_STRUCT ulsch_cnfg; + LIBLTE_RRC_DRX_CONFIG_STRUCT drx_cnfg; + LIBLTE_RRC_PHR_CONFIG_STRUCT phr_cnfg; + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM time_alignment_timer; + bool ulsch_cnfg_present; + bool drx_cnfg_present; + bool phr_cnfg_present; +}LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mac_main_config_ie(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_main_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mac_main_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_main_cnfg); + +/********************************************************************* + IE Name: PDCP Config + + Description: Sets the configurable PDCP parameters for data + radio bearers + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_DISCARD_TIMER_MS50 = 0, + LIBLTE_RRC_DISCARD_TIMER_MS100, + LIBLTE_RRC_DISCARD_TIMER_MS150, + LIBLTE_RRC_DISCARD_TIMER_MS300, + LIBLTE_RRC_DISCARD_TIMER_MS500, + LIBLTE_RRC_DISCARD_TIMER_MS750, + LIBLTE_RRC_DISCARD_TIMER_MS1500, + LIBLTE_RRC_DISCARD_TIMER_INFINITY, + LIBLTE_RRC_DISCARD_TIMER_N_ITEMS, +}LIBLTE_RRC_DISCARD_TIMER_ENUM; +static const char liblte_rrc_discard_timer_text[LIBLTE_RRC_DISCARD_TIMER_N_ITEMS][20] = { "ms50", "ms100", "ms150", "ms300", + "ms500", "ms750", "ms1500", "INFINITY"}; +static const int32 liblte_rrc_discard_timer_num[LIBLTE_RRC_DISCARD_TIMER_N_ITEMS] = { 50, 100, 150, 300, 500, 750, 1500, -1}; +typedef enum{ + LIBLTE_RRC_PDCP_SN_SIZE_7_BITS = 0, + LIBLTE_RRC_PDCP_SN_SIZE_12_BITS, + LIBLTE_RRC_PDCP_SN_SIZE_N_ITEMS, +}LIBLTE_RRC_PDCP_SN_SIZE_ENUM; +static const char liblte_rrc_pdcp_sn_size_text[LIBLTE_RRC_PDCP_SN_SIZE_N_ITEMS][20] = {"7-bits", "12-bits"}; + +static const int8 liblte_rrc_pdcp_sn_size_num[LIBLTE_RRC_PDCP_SN_SIZE_N_ITEMS] = {7, 12}; + +// Structs +typedef struct{ + LIBLTE_RRC_DISCARD_TIMER_ENUM discard_timer; + LIBLTE_RRC_PDCP_SN_SIZE_ENUM rlc_um_pdcp_sn_size; + uint32 hdr_compression_max_cid; + bool hdr_compression_rohc; + bool hdr_compression_profile_0001; + bool hdr_compression_profile_0002; + bool hdr_compression_profile_0003; + bool hdr_compression_profile_0004; + bool hdr_compression_profile_0006; + bool hdr_compression_profile_0101; + bool hdr_compression_profile_0102; + bool hdr_compression_profile_0103; + bool hdr_compression_profile_0104; + bool discard_timer_present; + bool rlc_am_status_report_required_present; + bool rlc_am_status_report_required; + bool rlc_um_pdcp_sn_size_present; +}LIBLTE_RRC_PDCP_CONFIG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdcp_config_ie(LIBLTE_RRC_PDCP_CONFIG_STRUCT *pdcp_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdcp_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDCP_CONFIG_STRUCT *pdcp_cnfg); + +/********************************************************************* + IE Name: PDSCH Config + + Description: Specifies the common and the UE specific PDSCH + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_N6 = 0, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_N4_DOT_77, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_N3, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_N1_DOT_77, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_0, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_1, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_2, + LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_3, + LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS, +}LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM; +static const char liblte_rrc_pdsch_config_p_a_text[LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS][20] = { "-6", "-4.77", "-3", "-1.77", + "0", "1", "2", "3"}; +static const float liblte_rrc_pdsch_config_p_a_num[LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS] = {-6, -4.77f, -3, -1.77f, 0, 1, 2, 3}; +// Structs +// PDSCH Config Common struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdsch_config_common_ie(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT *pdsch_config, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdsch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT *pdsch_config); +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdsch_config_dedicated_ie(LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM p_a, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdsch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM *p_a); + +/********************************************************************* + IE Name: PHICH Config + + Description: Specifies the PHICH configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// PHICH Config enums defined above +// Structs +// PHICH Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_phich_config_ie(LIBLTE_RRC_PHICH_CONFIG_STRUCT *phich_config, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phich_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHICH_CONFIG_STRUCT *phich_config); + +/********************************************************************* + IE Name: Physical Config Dedicated + + Description: Specifies the UE specific physical channel + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_N2 = 0, + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_N4, + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_N6, + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_SPARE1, + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_N_ITEMS, +}LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_ENUM; +static const char liblte_rrc_ack_nack_repetition_factor_text[LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_N_ITEMS][20] = {"n2", "n4", "n6", "SPARE"}; +typedef enum{ + LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_BUNDLING = 0, + LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_MULTIPLEXING, + LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_N_ITEMS, +}LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_ENUM; +static const char liblte_rrc_tdd_ack_nack_feedback_mode_text[LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_N_ITEMS][20] = {"bundling", "multiplexing"}; +typedef enum{ + LIBLTE_RRC_DSR_TRANS_MAX_N4 = 0, + LIBLTE_RRC_DSR_TRANS_MAX_N8, + LIBLTE_RRC_DSR_TRANS_MAX_N16, + LIBLTE_RRC_DSR_TRANS_MAX_N32, + LIBLTE_RRC_DSR_TRANS_MAX_N64, + LIBLTE_RRC_DSR_TRANS_MAX_SPARE3, + LIBLTE_RRC_DSR_TRANS_MAX_SPARE2, + LIBLTE_RRC_DSR_TRANS_MAX_SPARE1, + LIBLTE_RRC_DSR_TRANS_MAX_N_ITEMS, +}LIBLTE_RRC_DSR_TRANS_MAX_ENUM; +static const char liblte_rrc_dsr_trans_max_text[LIBLTE_RRC_DSR_TRANS_MAX_N_ITEMS][20] = { "n4", "n8", "n16", "n32", + "n64", "SPARE", "SPARE", "SPARE"}; +static const int32 liblte_rrc_dsr_trans_max_num[LIBLTE_RRC_DSR_TRANS_MAX_N_ITEMS] = {4, 8, 16, 32, 64, -1, -1, -1}; + +typedef enum{ + LIBLTE_RRC_DELTA_MCS_ENABLED_EN0 = 0, + LIBLTE_RRC_DELTA_MCS_ENABLED_EN1, + LIBLTE_RRC_DELTA_MCS_ENABLED_N_ITEMS, +}LIBLTE_RRC_DELTA_MCS_ENABLED_ENUM; +static const char liblte_rrc_delta_mcs_enabled_text[LIBLTE_RRC_DELTA_MCS_ENABLED_N_ITEMS][20] = {"en0", "en1"}; +typedef enum{ + LIBLTE_RRC_TPC_INDEX_FORMAT_3 = 0, + LIBLTE_RRC_TPC_INDEX_FORMAT_3A, + LIBLTE_RRC_TPC_INDEX_N_ITEMS, +}LIBLTE_RRC_TPC_INDEX_ENUM; +static const char liblte_rrc_tpc_index_text[LIBLTE_RRC_TPC_INDEX_N_ITEMS][20] = {"format_3", "format_3a"}; +typedef enum{ + LIBLTE_RRC_SRS_BANDWIDTH_BW0 = 0, + LIBLTE_RRC_SRS_BANDWIDTH_BW1, + LIBLTE_RRC_SRS_BANDWIDTH_BW2, + LIBLTE_RRC_SRS_BANDWIDTH_BW3, + LIBLTE_RRC_SRS_BANDWIDTH_N_ITEMS, +}LIBLTE_RRC_SRS_BANDWIDTH_ENUM; +static const char liblte_rrc_srs_bandwidth_text[LIBLTE_RRC_SRS_BANDWIDTH_N_ITEMS][20] = {"bw0", "bw1", "bw2", "bw3"}; +typedef enum{ + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_HBW0 = 0, + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_HBW1, + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_HBW2, + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_HBW3, + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_N_ITEMS, +}LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_ENUM; +static const char liblte_rrc_srs_hopping_bandwidth_text[LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_N_ITEMS][20] = {"hbw0", "hbw1", "hbw2", "hbw3"}; +typedef enum{ + LIBLTE_RRC_CYCLIC_SHIFT_CS0 = 0, + LIBLTE_RRC_CYCLIC_SHIFT_CS1, + LIBLTE_RRC_CYCLIC_SHIFT_CS2, + LIBLTE_RRC_CYCLIC_SHIFT_CS3, + LIBLTE_RRC_CYCLIC_SHIFT_CS4, + LIBLTE_RRC_CYCLIC_SHIFT_CS5, + LIBLTE_RRC_CYCLIC_SHIFT_CS6, + LIBLTE_RRC_CYCLIC_SHIFT_CS7, + LIBLTE_RRC_CYCLIC_SHIFT_N_ITEMS, +}LIBLTE_RRC_CYCLIC_SHIFT_ENUM; +static const char liblte_rrc_cyclic_shift_text[LIBLTE_RRC_CYCLIC_SHIFT_N_ITEMS][20] = {"cs0", "cs1", "cs2", "cs3", + "cs4", "cs5", "cs6", "cs7"}; +// Structs +typedef struct{ + LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_ENUM ack_nack_repetition_factor; + LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_ENUM tdd_ack_nack_feedback_mode; + uint32 ack_nack_repetition_n1_pucch_an; + bool tdd_ack_nack_feedback_mode_present; + bool ack_nack_repetition_setup_present; +}LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT; +typedef struct{ + uint8 beta_offset_ack_idx; + uint8 beta_offset_ri_idx; + uint8 beta_offset_cqi_idx; +}LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT; +typedef struct{ + LIBLTE_RRC_DELTA_MCS_ENABLED_ENUM delta_mcs_en; + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM filter_coeff; + uint32 p_srs_offset; + int32 p0_ue_pusch; + int32 p0_ue_pucch; + bool accumulation_en; + bool filter_coeff_present; +}LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT; +typedef struct{ + LIBLTE_RRC_TPC_INDEX_ENUM tpc_idx_choice; + uint32 tpc_rnti; + uint32 tpc_idx; + bool setup_present; +}LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_SRS_BANDWIDTH_ENUM srs_bandwidth; + LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_ENUM srs_hopping_bandwidth; + LIBLTE_RRC_CYCLIC_SHIFT_ENUM cyclic_shift; + uint32 freq_domain_pos; + uint32 srs_cnfg_idx; + uint32 tx_comb; + bool setup_present; + bool duration; +}LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT; +typedef struct{ + LIBLTE_RRC_DSR_TRANS_MAX_ENUM dsr_trans_max; + uint32 sr_pucch_resource_idx; + uint32 sr_cnfg_idx; + bool setup_present; +}LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT pucch_cnfg_ded; + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cnfg_ded; + LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT ul_pwr_ctrl_ded; + LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT tpc_pdcch_cnfg_pucch; + LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT tpc_pdcch_cnfg_pusch; + LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT cqi_report_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT srs_ul_cnfg_ded; + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT antenna_info_explicit_value; + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT sched_request_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM pdsch_cnfg_ded; + bool pdsch_cnfg_ded_present; + bool pucch_cnfg_ded_present; + bool pusch_cnfg_ded_present; + bool ul_pwr_ctrl_ded_present; + bool tpc_pdcch_cnfg_pucch_present; + bool tpc_pdcch_cnfg_pusch_present; + bool cqi_report_cnfg_present; + bool srs_ul_cnfg_ded_present; + bool antenna_info_present; + bool antenna_info_default_value; + bool sched_request_cnfg_present; +}LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_physical_config_dedicated_ie(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg_ded, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_physical_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg_ded); + +/********************************************************************* + IE Name: P Max + + Description: Limits the UE's uplink transmission power on a + carrier frequency and is used to calculate the + parameter P Compensation + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_p_max_ie(int8 p_max, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_p_max_ie(uint8 **ie_ptr, + int8 *p_max); + +/********************************************************************* + IE Name: PRACH Config + + Description: Specifies the PRACH configuration in the system + information and in the mobility control information + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// PRACH Config structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_sib_ie(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *prach_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_sib_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *prach_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_ie(LIBLTE_RRC_PRACH_CONFIG_STRUCT *prach_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRACH_CONFIG_STRUCT *prach_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_scell_r10_ie(uint8 prach_cnfg_idx, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_scell_r10_ie(uint8 **ie_ptr, + uint8 *prach_cnfg_idx); + +/********************************************************************* + IE Name: Presence Antenna Port 1 + + Description: Indicates whether all the neighboring cells use + antenna port 1 + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_presence_antenna_port_1_ie(bool presence_ant_port_1, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_presence_antenna_port_1_ie(uint8 **ie_ptr, + bool *presence_ant_port_1); + +/********************************************************************* + IE Name: PUCCH Config + + Description: Specifies the common and the UE specific PUCCH + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// PUCCH Config enum defined above +// Structs +// PUCCH Config structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pucch_config_common_ie(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT *pucch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pucch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT *pucch_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_pucch_config_dedicated_ie(LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT *pucch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pucch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT *pucch_cnfg); + +/********************************************************************* + IE Name: PUSCH Config + + Description: Specifies the common and the UE specific PUSCH + configuration and the reference signal configuration + for PUSCH and PUCCH + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// PUSCH Config enum defined above +// Structs +// PUSCH Config structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pusch_config_common_ie(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT *pusch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pusch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT *pusch_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_pusch_config_dedicated_ie(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT *pusch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pusch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT *pusch_cnfg); + +/********************************************************************* + IE Name: RACH Config Common + + Description: Specifies the generic random access parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// RACH Config Common enums defined above +// Structs +// RACH Config Common structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rach_config_common_ie(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rach_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cnfg); + +/********************************************************************* + IE Name: RACH Config Dedicated + + Description: Specifies the dedicated random access parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// RACH Config Dedicated struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rach_config_dedicated_ie(LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT *rach_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rach_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT *rach_cnfg); + +/********************************************************************* + IE Name: Radio Resource Config Common + + Description: Specifies the common radio resource configurations + in the system information and in the mobility control + information, including random access parameters + and static physical layer parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N2 = 0, + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N4, + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N8, + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N16, + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N_ITEMS, +}LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_ENUM; +static const char liblte_rrc_modification_period_coeff_text[LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N_ITEMS][20] = {"2", "4", "8", "16"}; +static const uint8 liblte_rrc_modification_period_coeff_num[LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N_ITEMS] = {2, 4, 8, 16}; +typedef enum{ + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF32 = 0, + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF64, + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF128, + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF256, + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_N_ITEMS, +}LIBLTE_RRC_DEFAULT_PAGING_CYCLE_ENUM; +static const char liblte_rrc_default_paging_cycle_text[LIBLTE_RRC_DEFAULT_PAGING_CYCLE_N_ITEMS][20] = {"32", "64", "128", "256"}; +static const uint16 liblte_rrc_default_paging_cycle_num[LIBLTE_RRC_DEFAULT_PAGING_CYCLE_N_ITEMS] = {32, 64, 128, 256}; +typedef enum{ + LIBLTE_RRC_NB_FOUR_T = 0, + LIBLTE_RRC_NB_TWO_T, + LIBLTE_RRC_NB_ONE_T, + LIBLTE_RRC_NB_HALF_T, + LIBLTE_RRC_NB_QUARTER_T, + LIBLTE_RRC_NB_ONE_EIGHTH_T, + LIBLTE_RRC_NB_ONE_SIXTEENTH_T, + LIBLTE_RRC_NB_ONE_THIRTY_SECOND_T, + LIBLTE_RRC_NB_N_ITEMS, +}LIBLTE_RRC_NB_ENUM; +static const char liblte_rrc_nb_text[LIBLTE_RRC_NB_N_ITEMS][20] = { "4", "2", "1", "1/2", + "1/4", "1/8", "1/16", "1/32"}; +static const double liblte_rrc_nb_num[LIBLTE_RRC_NB_N_ITEMS] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125, 0.0625, 0.03125}; +// Structs +typedef struct{ + LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_ENUM modification_period_coeff; +}LIBLTE_RRC_BCCH_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_DEFAULT_PAGING_CYCLE_ENUM default_paging_cycle; + LIBLTE_RRC_NB_ENUM nB; +}LIBLTE_RRC_PCCH_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT rach_cnfg; + LIBLTE_RRC_BCCH_CONFIG_STRUCT bcch_cnfg; + LIBLTE_RRC_PCCH_CONFIG_STRUCT pcch_cnfg; + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT prach_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg; + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; + LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT ul_pwr_ctrl; + LIBLTE_RRC_UL_CP_LENGTH_ENUM ul_cp_length; +}LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT; +// RR Config Common struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_common_sib_ie(LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT *rr_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_common_sib_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT *rr_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_common_ie(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *rr_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *rr_cnfg); + +/********************************************************************* + IE Name: Radio Resource Config Dedicated + + Description: Sets up/Modifies/Releases RBs, modifies the MAC + main configuration, modifies the SPS configuration + and modifies dedicated physical configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_DRB 11 +// Enums +typedef enum{ + LIBLTE_RRC_T_POLL_RETRANSMIT_MS5 = 0, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS10, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS15, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS20, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS25, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS30, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS35, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS40, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS45, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS50, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS55, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS60, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS65, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS70, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS75, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS80, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS85, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS90, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS95, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS100, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS105, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS110, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS115, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS120, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS125, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS130, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS135, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS140, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS145, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS150, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS155, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS160, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS165, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS170, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS175, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS180, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS185, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS190, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS195, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS200, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS205, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS210, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS215, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS220, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS225, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS230, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS235, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS240, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS245, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS250, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS300, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS350, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS400, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS450, + LIBLTE_RRC_T_POLL_RETRANSMIT_MS500, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE9, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE8, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE7, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE6, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE5, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE4, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE3, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE2, + LIBLTE_RRC_T_POLL_RETRANSMIT_SPARE1, + LIBLTE_RRC_T_POLL_RETRANSMIT_N_ITEMS, +}LIBLTE_RRC_T_POLL_RETRANSMIT_ENUM; +static const char liblte_rrc_t_poll_retransmit_text[LIBLTE_RRC_T_POLL_RETRANSMIT_N_ITEMS][20] = { "5ms", "10ms", "15ms", "20ms", + "25ms", "30ms", "35ms", "40ms", + "45ms", "50ms", "55ms", "60ms", + "65ms", "70ms", "75ms", "80ms", + "85ms", "90ms", "95ms", "100ms", + "105ms", "110ms", "115ms", "120ms", + "125ms", "130ms", "135ms", "140ms", + "145ms", "150ms", "155ms", "160ms", + "165ms", "170ms", "175ms", "180ms", + "185ms", "190ms", "195ms", "200ms", + "205ms", "210ms", "215ms", "220ms", + "225ms", "230ms", "235ms", "240ms", + "245ms", "250ms", "300ms", "350ms", + "400ms", "450ms", "500ms", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +static const int32 liblte_rrc_t_poll_retransmit_num[LIBLTE_RRC_T_POLL_RETRANSMIT_N_ITEMS] = { 5, 10, 15, 20, + 25, 30, 35, 40, + 45, 50, 55, 60, + 65, 70, 75, 80, + 85, 90, 95, 100, + 105, 110, 115, 120, + 125, 130, 135, 140, + 145, 150, 155, 160, + 165, 170, 175, 180, + 185, 190, 195, 200, + 205, 210, 215, 220, + 225, 230, 235, 240, + 245, 250, 300, 350, + 400, 450, 500, -1, + -1, -1, -1, -1, + -1, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_POLL_PDU_P4 = 0, + LIBLTE_RRC_POLL_PDU_P8, + LIBLTE_RRC_POLL_PDU_P16, + LIBLTE_RRC_POLL_PDU_P32, + LIBLTE_RRC_POLL_PDU_P64, + LIBLTE_RRC_POLL_PDU_P128, + LIBLTE_RRC_POLL_PDU_P256, + LIBLTE_RRC_POLL_PDU_INFINITY, + LIBLTE_RRC_POLL_PDU_N_ITEMS, +}LIBLTE_RRC_POLL_PDU_ENUM; +static const char liblte_rrc_poll_pdu_text[LIBLTE_RRC_POLL_PDU_N_ITEMS][20] = { "p4", "p8", "p16", "p32", + "p64", "p128", "p256", "INFINITY"}; +static const int32 liblte_rrc_poll_pdu_num[LIBLTE_RRC_POLL_PDU_N_ITEMS] = { 4, 8, 16, 32, + 64, 128, 256, -1}; +typedef enum{ + LIBLTE_RRC_POLL_BYTE_KB25 = 0, + LIBLTE_RRC_POLL_BYTE_KB50, + LIBLTE_RRC_POLL_BYTE_KB75, + LIBLTE_RRC_POLL_BYTE_KB100, + LIBLTE_RRC_POLL_BYTE_KB125, + LIBLTE_RRC_POLL_BYTE_KB250, + LIBLTE_RRC_POLL_BYTE_KB375, + LIBLTE_RRC_POLL_BYTE_KB500, + LIBLTE_RRC_POLL_BYTE_KB750, + LIBLTE_RRC_POLL_BYTE_KB1000, + LIBLTE_RRC_POLL_BYTE_KB1250, + LIBLTE_RRC_POLL_BYTE_KB1500, + LIBLTE_RRC_POLL_BYTE_KB2000, + LIBLTE_RRC_POLL_BYTE_KB3000, + LIBLTE_RRC_POLL_BYTE_INFINITY, + LIBLTE_RRC_POLL_BYTE_SPARE1, + LIBLTE_RRC_POLL_BYTE_N_ITEMS, +}LIBLTE_RRC_POLL_BYTE_ENUM; +static const char liblte_rrc_poll_byte_text[LIBLTE_RRC_POLL_BYTE_N_ITEMS][20] = { "25kB", "50kB", "75kB", "100kB", + "125kB", "250kB", "375kB", "500kB", + "750kB", "1000kB", "1250kB", "1500kB", + "2000kB", "3000kB", "INFINITY", "SPARE"}; +static const int32 liblte_rrc_poll_byte_num[LIBLTE_RRC_POLL_BYTE_N_ITEMS] = { 25, 50, 75, 100, + 125, 250, 375, 500, + 750, 1000, 1250, 1500, + 2000, 3000, -1, -1}; +typedef enum{ + LIBLTE_RRC_MAX_RETX_THRESHOLD_T1 = 0, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T2, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T3, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T4, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T6, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T8, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T16, + LIBLTE_RRC_MAX_RETX_THRESHOLD_T32, + LIBLTE_RRC_MAX_RETX_THRESHOLD_N_ITEMS, +}LIBLTE_RRC_MAX_RETX_THRESHOLD_ENUM; +static const char liblte_rrc_max_retx_threshold_text[LIBLTE_RRC_MAX_RETX_THRESHOLD_N_ITEMS][20] = { "t1", "t2", "t3", "t4", + "t6", "t8", "t16", "t32"}; +static const uint32_t liblte_rrc_max_retx_threshold_num[LIBLTE_RRC_MAX_RETX_THRESHOLD_N_ITEMS] = { 1, 2, 3, 4, + 6, 8, 16, 32}; +typedef enum{ + LIBLTE_RRC_T_REORDERING_MS0 = 0, + LIBLTE_RRC_T_REORDERING_MS5, + LIBLTE_RRC_T_REORDERING_MS10, + LIBLTE_RRC_T_REORDERING_MS15, + LIBLTE_RRC_T_REORDERING_MS20, + LIBLTE_RRC_T_REORDERING_MS25, + LIBLTE_RRC_T_REORDERING_MS30, + LIBLTE_RRC_T_REORDERING_MS35, + LIBLTE_RRC_T_REORDERING_MS40, + LIBLTE_RRC_T_REORDERING_MS45, + LIBLTE_RRC_T_REORDERING_MS50, + LIBLTE_RRC_T_REORDERING_MS55, + LIBLTE_RRC_T_REORDERING_MS60, + LIBLTE_RRC_T_REORDERING_MS65, + LIBLTE_RRC_T_REORDERING_MS70, + LIBLTE_RRC_T_REORDERING_MS75, + LIBLTE_RRC_T_REORDERING_MS80, + LIBLTE_RRC_T_REORDERING_MS85, + LIBLTE_RRC_T_REORDERING_MS90, + LIBLTE_RRC_T_REORDERING_MS95, + LIBLTE_RRC_T_REORDERING_MS100, + LIBLTE_RRC_T_REORDERING_MS110, + LIBLTE_RRC_T_REORDERING_MS120, + LIBLTE_RRC_T_REORDERING_MS130, + LIBLTE_RRC_T_REORDERING_MS140, + LIBLTE_RRC_T_REORDERING_MS150, + LIBLTE_RRC_T_REORDERING_MS160, + LIBLTE_RRC_T_REORDERING_MS170, + LIBLTE_RRC_T_REORDERING_MS180, + LIBLTE_RRC_T_REORDERING_MS190, + LIBLTE_RRC_T_REORDERING_MS200, + LIBLTE_RRC_T_REORDERING_SPARE1, + LIBLTE_RRC_T_REORDERING_N_ITEMS, +}LIBLTE_RRC_T_REORDERING_ENUM; +static const char liblte_rrc_t_reordering_text[LIBLTE_RRC_T_REORDERING_N_ITEMS][20] = { "ms0", "ms5", "ms10", "ms15", + "ms20", "ms25", "ms30", "ms35", + "ms40", "ms45", "ms50", "ms55", + "ms60", "ms65", "ms70", "ms75", + "ms80", "ms85", "ms90", "ms95", + "ms100", "ms110", "ms120", "ms130", + "ms140", "ms150", "ms160", "ms170", + "ms180", "ms190", "ms200", "SPARE"}; +static const int32 liblte_rrc_t_reordering_num[LIBLTE_RRC_T_REORDERING_N_ITEMS] = { 0, 5, 10, 15, + 20, 25, 30, 35, + 40, 45, 50, 55, + 60, 65, 70, 75, + 80, 85, 90, 95, + 100, 110, 120, 130, + 140, 150, 160, 170, + 180, 190, 200, -1}; +typedef enum{ + LIBLTE_RRC_RLC_MODE_AM = 0, + LIBLTE_RRC_RLC_MODE_UM_BI, + LIBLTE_RRC_RLC_MODE_UM_UNI_UL, + LIBLTE_RRC_RLC_MODE_UM_UNI_DL, + LIBLTE_RRC_RLC_MODE_N_ITEMS, +}LIBLTE_RRC_RLC_MODE_ENUM; +static const char liblte_rrc_rlc_mode_text[LIBLTE_RRC_RLC_MODE_N_ITEMS][20] = {"AM", + "UM BI", + "UM UNI UL", + "UM UNI DL"}; +typedef enum{ + LIBLTE_RRC_T_STATUS_PROHIBIT_MS0 = 0, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS5, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS10, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS15, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS20, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS25, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS30, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS35, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS40, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS45, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS50, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS55, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS60, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS65, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS70, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS75, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS80, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS85, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS90, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS95, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS100, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS105, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS110, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS115, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS120, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS125, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS130, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS135, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS140, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS145, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS150, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS155, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS160, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS165, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS170, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS175, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS180, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS185, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS190, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS195, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS200, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS205, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS210, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS215, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS220, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS225, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS230, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS235, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS240, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS245, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS250, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS300, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS350, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS400, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS450, + LIBLTE_RRC_T_STATUS_PROHIBIT_MS500, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE8, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE7, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE6, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE5, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE4, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE3, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE2, + LIBLTE_RRC_T_STATUS_PROHIBIT_SPARE1, + LIBLTE_RRC_T_STATUS_PROHIBIT_N_ITEMS, +}LIBLTE_RRC_T_STATUS_PROHIBIT_ENUM; +static const char liblte_rrc_t_status_prohibit_text[LIBLTE_RRC_T_STATUS_PROHIBIT_N_ITEMS][20] = { "ms0", "ms5", "ms10", "ms15", + "ms20", "ms25", "ms30", "ms35", + "ms40", "ms45", "ms50", "ms55", + "ms60", "ms65", "ms70", "ms75", + "ms80", "ms85", "ms90", "ms95", + "ms100", "ms105", "ms110", "ms115", + "ms120", "ms125", "ms130", "ms135", + "ms140", "ms145", "ms150", "ms155", + "ms160", "ms165", "ms170", "ms175", + "ms180", "ms185", "ms190", "ms195", + "ms200", "ms205", "ms210", "ms215", + "ms220", "ms225", "ms230", "ms235", + "ms240", "ms245", "ms250", "ms300", + "ms350", "ms400", "ms450", "ms500", + "SPARE", "SPARE", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +static const int32 liblte_rrc_t_status_prohibit_num[LIBLTE_RRC_T_STATUS_PROHIBIT_N_ITEMS] = { 0, 5, 10, 15, + 20, 25, 30, 35, + 40, 45, 50, 55, + 60, 65, 70, 75, + 80, 85, 90, 95, + 100, 105, 110, 115, + 120, 125, 130, 135, + 140, 145, 150, 155, + 160, 165, 170, 175, + 180, 185, 190, 195, + 200, 205, 210, 215, + 220, 225, 230, 235, + 240, 245, 250, 300, + 350, 400, 450, 500, + -1, -1, -1, -1, + -1, -1, -1, -1}; +typedef enum{ + LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5 = 0, + LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10, + LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS, +}LIBLTE_RRC_SN_FIELD_LENGTH_ENUM; +static const char liblte_rrc_sn_field_length_text[LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS][20] = {"size5", "size10"}; +static const uint8 liblte_rrc_sn_field_length_num[LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS] = {5, 10}; +typedef enum{ + LIBLTE_RRC_SPS_INTERVAL_DL_SF10 = 0, + LIBLTE_RRC_SPS_INTERVAL_DL_SF20, + LIBLTE_RRC_SPS_INTERVAL_DL_SF32, + LIBLTE_RRC_SPS_INTERVAL_DL_SF40, + LIBLTE_RRC_SPS_INTERVAL_DL_SF64, + LIBLTE_RRC_SPS_INTERVAL_DL_SF80, + LIBLTE_RRC_SPS_INTERVAL_DL_SF128, + LIBLTE_RRC_SPS_INTERVAL_DL_SF160, + LIBLTE_RRC_SPS_INTERVAL_DL_SF320, + LIBLTE_RRC_SPS_INTERVAL_DL_SF640, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE6, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE5, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE4, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE3, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE2, + LIBLTE_RRC_SPS_INTERVAL_DL_SPARE1, + LIBLTE_RRC_SPS_INTERVAL_DL_N_ITEMS, +}LIBLTE_RRC_SPS_INTERVAL_DL_ENUM; +static const char liblte_rrc_sps_interval_dl_text[LIBLTE_RRC_SPS_INTERVAL_DL_N_ITEMS][20] = { "sf10", "sf20", "sf32", "sf40", + "sf64", "sf80", "sf128", "sf160", + "sf320", "sf640", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_SPS_INTERVAL_UL_SF10 = 0, + LIBLTE_RRC_SPS_INTERVAL_UL_SF20, + LIBLTE_RRC_SPS_INTERVAL_UL_SF32, + LIBLTE_RRC_SPS_INTERVAL_UL_SF40, + LIBLTE_RRC_SPS_INTERVAL_UL_SF64, + LIBLTE_RRC_SPS_INTERVAL_UL_SF80, + LIBLTE_RRC_SPS_INTERVAL_UL_SF128, + LIBLTE_RRC_SPS_INTERVAL_UL_SF160, + LIBLTE_RRC_SPS_INTERVAL_UL_SF320, + LIBLTE_RRC_SPS_INTERVAL_UL_SF640, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE6, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE5, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE4, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE3, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE2, + LIBLTE_RRC_SPS_INTERVAL_UL_SPARE1, + LIBLTE_RRC_SPS_INTERVAL_UL_N_ITEMS, +}LIBLTE_RRC_SPS_INTERVAL_UL_ENUM; +static const char liblte_rrc_sps_interval_ul_text[LIBLTE_RRC_SPS_INTERVAL_UL_N_ITEMS][20] = { "sf10", "sf20", "sf32", "sf40", + "sf64", "sf80", "sf128", "sf160", + "sf320", "sf640", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +typedef enum{ + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_E2 = 0, + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_E3, + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_E4, + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_E8, + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_N_ITEMS, +}LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_ENUM; +static const char liblte_rrc_implicit_release_after_text[LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_N_ITEMS][20] = {"e2", "e3", "e4", "e8"}; +typedef enum{ + LIBLTE_RRC_TWO_INTERVALS_CONFIG_TRUE = 0, + LIBLTE_RRC_TWO_INTERVALS_CONFIG_N_ITEMS, +}LIBLTE_RRC_TWO_INTERVALS_CONFIG_ENUM; +static const char liblte_rrc_two_intervals_config_text[LIBLTE_RRC_TWO_INTERVALS_CONFIG_N_ITEMS][20] = {"TRUE"}; +// Structs +typedef struct{ + LIBLTE_RRC_T_POLL_RETRANSMIT_ENUM t_poll_retx; + LIBLTE_RRC_POLL_PDU_ENUM poll_pdu; + LIBLTE_RRC_POLL_BYTE_ENUM poll_byte; + LIBLTE_RRC_MAX_RETX_THRESHOLD_ENUM max_retx_thresh; +}LIBLTE_RRC_UL_AM_RLC_STRUCT; +typedef struct{ + LIBLTE_RRC_T_REORDERING_ENUM t_reordering; + LIBLTE_RRC_T_STATUS_PROHIBIT_ENUM t_status_prohibit; +}LIBLTE_RRC_DL_AM_RLC_STRUCT; +typedef struct{ + LIBLTE_RRC_SN_FIELD_LENGTH_ENUM sn_field_len; +}LIBLTE_RRC_UL_UM_RLC_STRUCT; +typedef struct{ + LIBLTE_RRC_SN_FIELD_LENGTH_ENUM sn_field_len; + LIBLTE_RRC_T_REORDERING_ENUM t_reordering; +}LIBLTE_RRC_DL_UM_RLC_STRUCT; +typedef struct{ + LIBLTE_RRC_UL_AM_RLC_STRUCT ul_am_rlc; + LIBLTE_RRC_DL_AM_RLC_STRUCT dl_am_rlc; + LIBLTE_RRC_UL_UM_RLC_STRUCT ul_um_bi_rlc; + LIBLTE_RRC_DL_UM_RLC_STRUCT dl_um_bi_rlc; + LIBLTE_RRC_UL_UM_RLC_STRUCT ul_um_uni_rlc; + LIBLTE_RRC_DL_UM_RLC_STRUCT dl_um_uni_rlc; + LIBLTE_RRC_RLC_MODE_ENUM rlc_mode; +}LIBLTE_RRC_RLC_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_RLC_CONFIG_STRUCT rlc_explicit_cnfg; + LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT lc_explicit_cnfg; + uint32 srb_id; + bool rlc_cnfg_present; + bool rlc_default_cnfg_present; + bool lc_cnfg_present; + bool lc_default_cnfg_present; +}LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_PDCP_CONFIG_STRUCT pdcp_cnfg; + LIBLTE_RRC_RLC_CONFIG_STRUCT rlc_cnfg; + LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT lc_cnfg; + uint32 eps_bearer_id; + uint32 lc_id; + uint8 drb_id; + bool eps_bearer_id_present; + bool pdcp_cnfg_present; + bool rlc_cnfg_present; + bool lc_id_present; + bool lc_cnfg_present; +}LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT; +typedef struct{ + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT explicit_value; + bool default_value; +}LIBLTE_RRC_MAC_MAIN_CONFIG_CHOICE_STRUCT; +typedef struct{ + LIBLTE_RRC_SPS_INTERVAL_DL_ENUM sps_interval_dl; + uint32 n1_pucch_an_persistent_list[4]; + uint32 n1_pucch_an_persistent_list_size; + uint8 N_sps_processes; + bool setup_present; +}LIBLTE_RRC_SPS_CONFIG_DL_STRUCT; +typedef struct{ + LIBLTE_RRC_SPS_INTERVAL_UL_ENUM sps_interval_ul; + LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_ENUM implicit_release_after; + LIBLTE_RRC_TWO_INTERVALS_CONFIG_ENUM two_intervals_cnfg; + int32 p0_nominal_pusch; + int32 p0_ue_pusch; + bool setup_present; + bool p0_persistent_present; + bool two_intervals_cnfg_present; +}LIBLTE_RRC_SPS_CONFIG_UL_STRUCT; +typedef struct{ + LIBLTE_RRC_SPS_CONFIG_DL_STRUCT sps_cnfg_dl; + LIBLTE_RRC_SPS_CONFIG_UL_STRUCT sps_cnfg_ul; + uint16 sps_c_rnti; + bool sps_c_rnti_present; + bool sps_cnfg_dl_present; + bool sps_cnfg_ul_present; +}LIBLTE_RRC_SPS_CONFIG_STRUCT; +typedef struct{ + LIBLTE_RRC_T301_ENUM t301; + LIBLTE_RRC_T310_ENUM t310; + LIBLTE_RRC_N310_ENUM n310; + LIBLTE_RRC_T311_ENUM t311; + LIBLTE_RRC_N311_ENUM n311; +}LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT; +typedef struct{ + LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT srb_to_add_mod_list[2]; + LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT drb_to_add_mod_list[LIBLTE_RRC_MAX_DRB]; + LIBLTE_RRC_MAC_MAIN_CONFIG_CHOICE_STRUCT mac_main_cnfg; + LIBLTE_RRC_SPS_CONFIG_STRUCT sps_cnfg; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT phy_cnfg_ded; + LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT rlf_timers_and_constants; + uint32 srb_to_add_mod_list_size; + uint32 drb_to_add_mod_list_size; + uint32 drb_to_release_list_size; + uint8 drb_to_release_list[LIBLTE_RRC_MAX_DRB]; + bool mac_main_cnfg_present; + bool sps_cnfg_present; + bool phy_cnfg_ded_present; + bool rlf_timers_and_constants_present; +}LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_dedicated_ie(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *rr_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *rr_cnfg); + +/********************************************************************* + IE Name: RLC Config + + Description: Specifies the RLC configuration of SRBs and DRBs + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// RLC Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rlc_config_ie(LIBLTE_RRC_RLC_CONFIG_STRUCT *rlc_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rlc_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_RLC_CONFIG_STRUCT *rlc_cnfg); + +/********************************************************************* + IE Name: RLF Timers and Constants + + Description: Contains UE specific timers and constants applicable + for UEs in RRC_CONNECTED + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// RLF Timers and Constants struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rlf_timers_and_constants_ie(LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT *rlf_timers_and_constants, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rlf_timers_and_constants_ie(uint8 **ie_ptr, + LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT *rlf_timers_and_constants); + +/********************************************************************* + IE Name: RN Subframe Config + + Description: Specifies the subframe configuration for an RN + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: Scheduling Request Config + + Description: Specifies the scheduling request related parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Scheduling Request Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_scheduling_request_config_ie(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sched_request_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_scheduling_request_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sched_request_cnfg); + +/********************************************************************* + IE Name: Sounding RS UL Config + + Description: Specifies the uplink Sounding RS configuration for + periodic and aperiodic sounding + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Sounding RS UL Config enums defined above +// Structs +// Sounding RS UL Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_srs_ul_config_common_ie(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT *srs_ul_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_srs_ul_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT *srs_ul_cnfg); +LIBLTE_ERROR_ENUM liblte_rrc_pack_srs_ul_config_dedicated_ie(LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT *srs_ul_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_srs_ul_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT *srs_ul_cnfg); + +/********************************************************************* + IE Name: SPS Config + + Description: Specifies the semi-persistent scheduling + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// SPS Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sps_config_ie(LIBLTE_RRC_SPS_CONFIG_STRUCT *sps_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sps_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SPS_CONFIG_STRUCT *sps_cnfg); + +/********************************************************************* + IE Name: TDD Config + + Description: Specifies the TDD specific physical channel + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// TDD Config enums defined above +// Structs +// TDD Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_tdd_config_ie(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tdd_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd_cnfg); + +/********************************************************************* + IE Name: Time Alignment Timer + + Description: Controls how long the UE is considered uplink time + aligned + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Time Alignment Timer enum defined above +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_time_alignment_timer_ie(LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM time_alignment_timer, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_time_alignment_timer_ie(uint8 **ie_ptr, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM *time_alignment_timer); + +/********************************************************************* + IE Name: TPC PDCCH Config + + Description: Specifies the RNTIs and indecies for PUCCH and PUSCH + power control + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// TPC PDCCH Config struct defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_tpc_pdcch_config_ie(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT *tpc_pdcch_cnfg, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tpc_pdcch_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT *tpc_pdcch_cnfg); + +/********************************************************************* + IE Name: UL Antenna Info + + Description: Specifies the UL antenna configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_TM1 = 0, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_TM2, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE6, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE5, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE4, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE3, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE2, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_SPARE1, + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_N_ITEMS, +}LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_ENUM; +static const char liblte_rrc_ul_transmission_mode_r10_text[LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_N_ITEMS][20] = { "TM1", "TM2", "SPARE", "SPARE", + "SPARE", "SPARE", "SPARE", "SPARE"}; +// Structs +typedef struct{ + LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_ENUM ul_tx_mode; + bool four_ant_port_activated; +}LIBLTE_RRC_UL_ANTENNA_INFO_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_antenna_info_ie(LIBLTE_RRC_UL_ANTENNA_INFO_STRUCT *ul_ant_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_antenna_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_ANTENNA_INFO_STRUCT *ul_ant_info); + +/********************************************************************* + IE Name: Uplink Power Control + + Description: Specifies the parameters for uplink power control in + the system information and in the dedicated + signalling + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// Defines +// Enums +// Uplink Power Control enums defined above +// Structs +// Uplink Power Control structs defined above +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_power_control_common_ie(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT *ul_pwr_ctrl, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_power_control_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT *ul_pwr_ctrl); +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_power_control_dedicated_ie(LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT *ul_pwr_ctrl, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_power_control_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT *ul_pwr_ctrl); + +/********************************************************************* + IE Name: System Information Block Type 2 + + Description: Contains radio resource configuration that is common + for all UEs + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_AC_BARRING_FACTOR_P00 = 0, + LIBLTE_RRC_AC_BARRING_FACTOR_P05, + LIBLTE_RRC_AC_BARRING_FACTOR_P10, + LIBLTE_RRC_AC_BARRING_FACTOR_P15, + LIBLTE_RRC_AC_BARRING_FACTOR_P20, + LIBLTE_RRC_AC_BARRING_FACTOR_P25, + LIBLTE_RRC_AC_BARRING_FACTOR_P30, + LIBLTE_RRC_AC_BARRING_FACTOR_P40, + LIBLTE_RRC_AC_BARRING_FACTOR_P50, + LIBLTE_RRC_AC_BARRING_FACTOR_P60, + LIBLTE_RRC_AC_BARRING_FACTOR_P70, + LIBLTE_RRC_AC_BARRING_FACTOR_P75, + LIBLTE_RRC_AC_BARRING_FACTOR_P80, + LIBLTE_RRC_AC_BARRING_FACTOR_P85, + LIBLTE_RRC_AC_BARRING_FACTOR_P90, + LIBLTE_RRC_AC_BARRING_FACTOR_P95, + LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS, +}LIBLTE_RRC_AC_BARRING_FACTOR_ENUM; +static const char liblte_rrc_ac_barring_factor_text[LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS][20] = {"0.00", "0.05", "0.10", "0.15", + "0.20", "0.25", "0.30", "0.40", + "0.50", "0.60", "0.70", "0.75", + "0.80", "0.85", "0.90", "0.95"}; +static const double liblte_rrc_ac_barring_factor_num[LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS] = {0.00, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.40, + 0.50, 0.60, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95}; +typedef enum{ + LIBLTE_RRC_AC_BARRING_TIME_S4 = 0, + LIBLTE_RRC_AC_BARRING_TIME_S8, + LIBLTE_RRC_AC_BARRING_TIME_S16, + LIBLTE_RRC_AC_BARRING_TIME_S32, + LIBLTE_RRC_AC_BARRING_TIME_S64, + LIBLTE_RRC_AC_BARRING_TIME_S128, + LIBLTE_RRC_AC_BARRING_TIME_S256, + LIBLTE_RRC_AC_BARRING_TIME_S512, + LIBLTE_RRC_AC_BARRING_TIME_N_ITEMS, +}LIBLTE_RRC_AC_BARRING_TIME_ENUM; +static const char liblte_rrc_ac_barring_time_text[LIBLTE_RRC_AC_BARRING_TIME_N_ITEMS][20] = { "4", "8", "16", "32", + "64", "128", "256", "512"}; +static const uint16 liblte_rrc_ac_barring_time_num[LIBLTE_RRC_AC_BARRING_TIME_N_ITEMS] = {4, 8, 16, 32, 64, 128, 256, 512}; +typedef enum{ + LIBLTE_RRC_UL_BW_N6 = 0, + LIBLTE_RRC_UL_BW_N15, + LIBLTE_RRC_UL_BW_N25, + LIBLTE_RRC_UL_BW_N50, + LIBLTE_RRC_UL_BW_N75, + LIBLTE_RRC_UL_BW_N100, + LIBLTE_RRC_UL_BW_N_ITEMS, +}LIBLTE_RRC_UL_BW_ENUM; +static const char liblte_rrc_ul_bw_text[LIBLTE_RRC_UL_BW_N_ITEMS][20] = {"1.4", "3", "5", "10", + "15", "20"}; +static const double liblte_rrc_ul_bw_num[LIBLTE_RRC_UL_BW_N_ITEMS] = {1.4, 3, 5, 10, 15, 20}; +// Structs +typedef struct{ + LIBLTE_RRC_AC_BARRING_FACTOR_ENUM factor; + LIBLTE_RRC_AC_BARRING_TIME_ENUM time; + uint8 for_special_ac; + bool enabled; +}LIBLTE_RRC_AC_BARRING_CONFIG_STRUCT; +typedef struct{ + uint16 value; + bool present; +}LIBLTE_RRC_ARFCN_VALUE_EUTRA_STRUCT; +typedef struct{ + LIBLTE_RRC_UL_BW_ENUM bw; + bool present; +}LIBLTE_RRC_UL_BW_STRUCT; +typedef struct{ + LIBLTE_RRC_AC_BARRING_CONFIG_STRUCT ac_barring_for_mo_signalling; + LIBLTE_RRC_AC_BARRING_CONFIG_STRUCT ac_barring_for_mo_data; + LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT rr_config_common_sib; + LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT ue_timers_and_constants; + LIBLTE_RRC_ARFCN_VALUE_EUTRA_STRUCT arfcn_value_eutra; + LIBLTE_RRC_UL_BW_STRUCT ul_bw; + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg_list[LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS]; + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM time_alignment_timer; + uint32 mbsfn_subfr_cnfg_list_size; + uint8 additional_spectrum_emission; + bool ac_barring_for_emergency; + bool ac_barring_info_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_2_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_2_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2); + +/********************************************************************* + IE Name: System Information Block Type 3 + + Description: Contains cell reselection information common for + intra-frequency, inter-frequency, and/or inter-RAT + cell re-selection as well as intra-frequency cell + re-selection information other than neighboring + cell related + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_Q_HYST_DB_0 = 0, + LIBLTE_RRC_Q_HYST_DB_1, + LIBLTE_RRC_Q_HYST_DB_2, + LIBLTE_RRC_Q_HYST_DB_3, + LIBLTE_RRC_Q_HYST_DB_4, + LIBLTE_RRC_Q_HYST_DB_5, + LIBLTE_RRC_Q_HYST_DB_6, + LIBLTE_RRC_Q_HYST_DB_8, + LIBLTE_RRC_Q_HYST_DB_10, + LIBLTE_RRC_Q_HYST_DB_12, + LIBLTE_RRC_Q_HYST_DB_14, + LIBLTE_RRC_Q_HYST_DB_16, + LIBLTE_RRC_Q_HYST_DB_18, + LIBLTE_RRC_Q_HYST_DB_20, + LIBLTE_RRC_Q_HYST_DB_22, + LIBLTE_RRC_Q_HYST_DB_24, + LIBLTE_RRC_Q_HYST_N_ITEMS, +}LIBLTE_RRC_Q_HYST_ENUM; +static const char liblte_rrc_q_hyst_text[LIBLTE_RRC_Q_HYST_N_ITEMS][20] = { "0", "1", "2", "3", + "4", "5", "6", "8", + "10", "12", "14", "16", + "18", "20", "22", "24"}; +static const uint8 liblte_rrc_q_hyst_num[LIBLTE_RRC_Q_HYST_N_ITEMS] = {0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}; +typedef enum{ + LIBLTE_RRC_SF_MEDIUM_DB_N6 = 0, + LIBLTE_RRC_SF_MEDIUM_DB_N4, + LIBLTE_RRC_SF_MEDIUM_DB_N2, + LIBLTE_RRC_SF_MEDIUM_DB_0, + LIBLTE_RRC_SF_MEDIUM_N_ITEMS, +}LIBLTE_RRC_SF_MEDIUM_ENUM; +static const char liblte_rrc_sf_medium_text[LIBLTE_RRC_SF_MEDIUM_N_ITEMS][20] = {"-6", "-4", "-2", "0"}; +static const int8 liblte_rrc_sf_medium_num[LIBLTE_RRC_SF_MEDIUM_N_ITEMS] = {-6, -4, -2, 0}; +typedef enum{ + LIBLTE_RRC_SF_HIGH_DB_N6 = 0, + LIBLTE_RRC_SF_HIGH_DB_N4, + LIBLTE_RRC_SF_HIGH_DB_N2, + LIBLTE_RRC_SF_HIGH_DB_0, + LIBLTE_RRC_SF_HIGH_N_ITEMS, +}LIBLTE_RRC_SF_HIGH_ENUM; +static const char liblte_rrc_sf_high_text[LIBLTE_RRC_SF_HIGH_N_ITEMS][20] = {"-6", "-4", "-2", "0"}; +static const int8 liblte_rrc_sf_high_num[LIBLTE_RRC_SF_HIGH_N_ITEMS] = {-6, -4, -2, 0}; +// Structs +typedef struct{ + LIBLTE_RRC_SF_MEDIUM_ENUM medium; + LIBLTE_RRC_SF_HIGH_ENUM high; +}LIBLTE_RRC_Q_HYST_SF_STRUCT; +typedef struct{ + LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT mobility_state_params; + LIBLTE_RRC_Q_HYST_SF_STRUCT q_hyst_sf; + bool present; +}LIBLTE_RRC_SPEED_STATE_RESELECTION_PARS_STRUCT; +typedef struct{ + LIBLTE_RRC_SPEED_STATE_RESELECTION_PARS_STRUCT speed_state_resel_params; + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT t_resel_eutra_sf; + LIBLTE_RRC_Q_HYST_ENUM q_hyst; + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM allowed_meas_bw; + int16 q_rx_lev_min; + uint8 s_non_intra_search; + uint8 thresh_serving_low; + uint8 cell_resel_prio; + uint8 s_intra_search; + uint8 neigh_cell_cnfg; + uint8 t_resel_eutra; + int8 p_max; + bool s_non_intra_search_present; + bool presence_ant_port_1; + bool p_max_present; + bool s_intra_search_present; + bool allowed_meas_bw_present; + bool t_resel_eutra_sf_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_3_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_3_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3); + +/********************************************************************* + IE Name: System Information Block Type 4 + + Description: Contains the neighboring cell related information + relevant only for intra-frequency cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_CELL_INTRA 16 +#define LIBLTE_RRC_MAX_CELL_BLACK 16 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM q_offset_range; + uint16 phys_cell_id; +}LIBLTE_RRC_INTRA_FREQ_NEIGH_CELL_INFO_STRUCT; +typedef struct{ + LIBLTE_RRC_INTRA_FREQ_NEIGH_CELL_INFO_STRUCT intra_freq_neigh_cell_list[LIBLTE_RRC_MAX_CELL_INTRA]; + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT intra_freq_black_cell_list[LIBLTE_RRC_MAX_CELL_BLACK]; + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT csg_phys_cell_id_range; + uint32 intra_freq_neigh_cell_list_size; + uint32 intra_freq_black_cell_list_size; + bool csg_phys_cell_id_range_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_4_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_4_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4); + +/********************************************************************* + IE Name: System Information Block Type 5 + + Description: Contains information relevant only for + inter-frequency cell reselection, i.e. information + about other E-UTRA frequencies and inter-frequency + neighboring cells relevant for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_FREQ 8 +#define LIBLTE_RRC_MAX_CELL_INTER 16 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM q_offset_cell; + uint16 phys_cell_id; +}LIBLTE_RRC_INTER_FREQ_NEIGH_CELL_STRUCT; +typedef struct{ + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT t_resel_eutra_sf; + LIBLTE_RRC_INTER_FREQ_NEIGH_CELL_STRUCT inter_freq_neigh_cell_list[LIBLTE_RRC_MAX_CELL_INTER]; + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT inter_freq_black_cell_list[LIBLTE_RRC_MAX_CELL_BLACK]; + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM allowed_meas_bw; + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM q_offset_freq; + uint16 dl_carrier_freq; + int16 q_rx_lev_min; + uint8 t_resel_eutra; + uint8 threshx_high; + uint8 threshx_low; + uint8 cell_resel_prio; + uint8 neigh_cell_cnfg; + uint8 inter_freq_neigh_cell_list_size; + uint8 inter_freq_black_cell_list_size; + int8 p_max; + bool presence_ant_port_1; + bool p_max_present; + bool t_resel_eutra_sf_present; + bool cell_resel_prio_present; +}LIBLTE_RRC_INTER_FREQ_CARRIER_FREQ_INFO_STRUCT; +typedef struct{ + LIBLTE_RRC_INTER_FREQ_CARRIER_FREQ_INFO_STRUCT inter_freq_carrier_freq_list[LIBLTE_RRC_MAX_FREQ]; + uint32 inter_freq_carrier_freq_list_size; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_5_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT *sib5, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_5_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT *sib5); + +/********************************************************************* + IE Name: System Information Block Type 6 + + Description: Contains information relevant only for inter-RAT + cell reselection, i.e. information about UTRA + frequencies and UTRA neighboring cells relevant for + cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_UTRA_FDD_CARRIER 16 +#define LIBLTE_RRC_MAX_UTRA_TDD_CARRIER 16 +// Enums +// Structs +typedef struct{ + uint16 carrier_freq; + uint8 cell_resel_prio; + uint8 threshx_high; + uint8 threshx_low; + int8 q_rx_lev_min; + int8 p_max_utra; + int8 q_qual_min; + bool cell_resel_prio_present; +}LIBLTE_RRC_CARRIER_FREQ_UTRA_FDD_STRUCT; +typedef struct{ + uint16 carrier_freq; + uint8 cell_resel_prio; + uint8 threshx_high; + uint8 threshx_low; + int8 q_rx_lev_min; + int8 p_max_utra; + bool cell_resel_prio_present; +}LIBLTE_RRC_CARRIER_FREQ_UTRA_TDD_STRUCT; +typedef struct{ + LIBLTE_RRC_CARRIER_FREQ_UTRA_FDD_STRUCT carrier_freq_list_utra_fdd[LIBLTE_RRC_MAX_UTRA_FDD_CARRIER]; + LIBLTE_RRC_CARRIER_FREQ_UTRA_TDD_STRUCT carrier_freq_list_utra_tdd[LIBLTE_RRC_MAX_UTRA_TDD_CARRIER]; + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT t_resel_utra_sf; + uint8 t_resel_utra; + uint8 carrier_freq_list_utra_fdd_size; + uint8 carrier_freq_list_utra_tdd_size; + bool t_resel_utra_sf_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_6_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT *sib6, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_6_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT *sib6); + +/********************************************************************* + IE Name: System Information Block Type 7 + + Description: Contains information relevant only for inter-RAT + cell reselection, i.e. information about GERAN + frequencies relevant for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_GNFG 16 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT carrier_freqs; + uint8 cell_resel_prio; + uint8 ncc_permitted; + uint8 p_max_geran; + uint8 threshx_high; + uint8 threshx_low; + int8 q_rx_lev_min; + bool cell_resel_prio_present; + bool p_max_geran_present; +}LIBLTE_RRC_CARRIER_FREQS_INFO_LIST_GERAN_STRUCT; +typedef struct{ + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT t_resel_geran_sf; + LIBLTE_RRC_CARRIER_FREQS_INFO_LIST_GERAN_STRUCT carrier_freqs_info_list[LIBLTE_RRC_MAX_GNFG]; + uint8 t_resel_geran; + uint8 carrier_freqs_info_list_size; + bool t_resel_geran_sf_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_7_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT *sib7, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_7_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT *sib7); + +/********************************************************************* + IE Name: System Information Block Type 8 + + Description: Contains information relevant only for inter-RAT + cell re-selection i.e. information about CDMA2000 + frequencies and CDMA2000 neighboring cells relevant + for cell re-selection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_CDMA_BAND_CLASS 32 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM band_class; + uint8 cell_resel_prio; + uint8 thresh_x_high; + uint8 thresh_x_low; + bool cell_resel_prio_present; +}LIBLTE_RRC_BAND_CLASS_INFO_CDMA2000_STRUCT; +typedef struct{ + uint16 arfcn; + uint16 phys_cell_id_list[16]; + uint8 phys_cell_id_list_size; +}LIBLTE_RRC_NEIGH_CELLS_PER_BAND_CLASS_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM band_class; + LIBLTE_RRC_NEIGH_CELLS_PER_BAND_CLASS_CDMA2000_STRUCT neigh_cells_per_freq_list[16]; + uint8 neigh_cells_per_freq_list_size; +}LIBLTE_RRC_NEIGH_CELL_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_BAND_CLASS_INFO_CDMA2000_STRUCT band_class_list[LIBLTE_RRC_MAX_CDMA_BAND_CLASS]; + LIBLTE_RRC_NEIGH_CELL_CDMA2000_STRUCT neigh_cell_list[16]; + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT t_resel_cdma2000_sf; + uint8 band_class_list_size; + uint8 neigh_cell_list_size; + uint8 t_resel_cdma2000; + bool t_resel_cdma2000_sf_present; +}LIBLTE_RRC_CELL_RESELECTION_PARAMS_CDMA2000_STRUCT; +typedef struct{ + LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT sys_time_info_cdma2000; + LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT pre_reg_info_hrpd; + LIBLTE_RRC_CELL_RESELECTION_PARAMS_CDMA2000_STRUCT cell_resel_params_hrpd; + LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT csfb_reg_param_1xrtt; + LIBLTE_RRC_CELL_RESELECTION_PARAMS_CDMA2000_STRUCT cell_resel_params_1xrtt; + uint64 long_code_state_1xrtt; + uint8 search_win_size; + bool sys_time_info_present; + bool search_win_size_present; + bool params_hrpd_present; + bool cell_resel_params_hrpd_present; + bool params_1xrtt_present; + bool csfb_reg_param_1xrtt_present; + bool long_code_state_1xrtt_present; + bool cell_resel_params_1xrtt_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_8_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *sib8, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_8_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *sib8); + +/********************************************************************* + IE Name: System Information Block Type 9 + + Description: Contains a home eNB name (HNB name) + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// lb:1, ub:48 +typedef struct{ + uint32 hnb_name_size; + uint8 hnb_name[48]; + bool hnb_name_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT; + +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_9_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_9_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9); + +/********************************************************************* + IE Name: System Information Block Type 10 + + Description: Contains an ETWS primary notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 11 + + Description: Contains an ETWS secondary notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 12 + + Description: Contains a CMAS notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 13 + + Description: Contains the information required to acquire the + MBMS control information associated with one or more + MBSFN areas + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT mbsfn_area_info_list_r9[LIBLTE_RRC_MAX_MBSFN_AREAS]; + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbsfn_notification_config; + uint8 mbsfn_area_info_list_r9_size; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_13_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_13_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + +/******************************************************************************* + MESSAGE DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + Message Name: UL Information Transfer + + Description: Used for the uplink transfer dedicated NAS + information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS = 0, + LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_CDMA2000_1XRTT, + LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_CDMA2000_HRPD, + LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_N_ITEMS, +}LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_ENUM; +static const char liblte_rrc_ul_information_transfer_type_text[LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_N_ITEMS][20] = {"NAS", + "CDMA2000-1XRTT", + "CDMA2000-HRPD"}; +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT dedicated_info; + LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_ENUM dedicated_info_type; +}LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_information_transfer_msg(LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *ul_info_transfer, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_information_transfer_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *ul_info_transfer); + +/********************************************************************* + Message Name: UL Handover Preparation Transfer (CDMA2000) + + Description: Used for the uplink transfer of handover related + CDMA2000 information when requested by the higher + layers + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_handover_preparation_transfer_msg(LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT *ul_handover_prep_transfer, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_handover_preparation_transfer_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT *ul_handover_prep_transfer); + +/********************************************************************* + Message Name: UE Information Response + + Description: Used by the UE to transfer the information requested + by the E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_information_response_msg(LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT *ue_info_resp, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_information_response_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT *ue_info_resp); + +/********************************************************************* + Message Name: UE Information Request + + Description: Used by E-UTRAN to retrieve information from the UE + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; + bool rach_report_req; + bool rlf_report_req; +}LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_information_request_msg(LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *ue_info_req, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_information_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *ue_info_req); + +/********************************************************************* + Message Name: UE Capability Information + + Description: Used to transfer UE radio access capabilities + requested by the E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_RAT_CAPABILITIES 8 +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_RAT_TYPE_ENUM rat_type; + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT eutra_capability; +}LIBLTE_RRC_UE_CAPABILITY_RAT_CONTAINER_STRUCT; + +typedef struct{ + uint8 rrc_transaction_id; + LIBLTE_RRC_UE_CAPABILITY_RAT_CONTAINER_STRUCT ue_capability_rat[LIBLTE_RRC_MAX_RAT_CAPABILITIES]; + uint32 N_ue_caps; +}LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_capability_information_msg(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *ue_capability_info, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_capability_information_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *ue_capability_info); + +/********************************************************************* + Message Name: UE Capability Enquiry + + Description: Used to request the transfer of UE radio access + capabilities for E-UTRA as well as for other RATs + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; + LIBLTE_RRC_RAT_TYPE_ENUM ue_capability_request[LIBLTE_RRC_MAX_RAT_CAPABILITIES]; + uint32 N_ue_cap_reqs; + +}LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_capability_enquiry_msg(LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *ue_cap_enquiry, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_capability_enquiry_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *ue_cap_enquiry); + +/********************************************************************* + Message Name: System Information Block Type 1 + + Description: Contains information relevant when evaluating if a + UE is allowed to access a cell and defines the + scheduling of other system information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_N_PLMN_IDENTITIES 6 +#define LIBLTE_RRC_MAX_SIB 32 +#define LIBLTE_RRC_MAX_SI_MESSAGE 32 +// Enums +typedef enum{ + LIBLTE_RRC_CELL_BARRED = 0, + LIBLTE_RRC_CELL_NOT_BARRED, + LIBLTE_RRC_CELL_BARRED_N_ITEMS, +}LIBLTE_RRC_CELL_BARRED_ENUM; +static const char liblte_rrc_cell_barred_text[LIBLTE_RRC_CELL_BARRED_N_ITEMS][20] = {"Barred", "Not Barred"}; +typedef enum{ + LIBLTE_RRC_INTRA_FREQ_RESELECTION_ALLOWED = 0, + LIBLTE_RRC_INTRA_FREQ_RESELECTION_NOT_ALLOWED, + LIBLTE_RRC_INTRA_FREQ_RESELECTION_N_ITEMS, +}LIBLTE_RRC_INTRA_FREQ_RESELECTION_ENUM; +static const char liblte_rrc_intra_freq_reselection_text[LIBLTE_RRC_INTRA_FREQ_RESELECTION_N_ITEMS][20] = {"Allowed", "Not Allowed"}; +typedef enum{ + LIBLTE_RRC_SI_WINDOW_LENGTH_MS1 = 0, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS2, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS5, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS10, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS15, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS20, + LIBLTE_RRC_SI_WINDOW_LENGTH_MS40, + LIBLTE_RRC_SI_WINDOW_LENGTH_N_ITEMS, +}LIBLTE_RRC_SI_WINDOW_LENGTH_ENUM; +static const char liblte_rrc_si_window_length_text[LIBLTE_RRC_SI_WINDOW_LENGTH_N_ITEMS][20] = { "1", "2", "5", "10", + "15", "20", "40"}; +static const uint8 liblte_rrc_si_window_length_num[LIBLTE_RRC_SI_WINDOW_LENGTH_N_ITEMS] = {1, 2, 5, 10, 15, 20, 40}; +typedef enum{ + LIBLTE_RRC_RESV_FOR_OPER = 0, + LIBLTE_RRC_NOT_RESV_FOR_OPER, + LIBLTE_RRC_RESV_FOR_OPER_N_ITEMS, +}LIBLTE_RRC_RESV_FOR_OPER_ENUM; +static const char liblte_rrc_resv_for_oper_text[LIBLTE_RRC_RESV_FOR_OPER_N_ITEMS][20] = {"Reserved", "Not Reserved"}; +typedef enum{ + LIBLTE_RRC_SI_PERIODICITY_RF8 = 0, + LIBLTE_RRC_SI_PERIODICITY_RF16, + LIBLTE_RRC_SI_PERIODICITY_RF32, + LIBLTE_RRC_SI_PERIODICITY_RF64, + LIBLTE_RRC_SI_PERIODICITY_RF128, + LIBLTE_RRC_SI_PERIODICITY_RF256, + LIBLTE_RRC_SI_PERIODICITY_RF512, + LIBLTE_RRC_SI_PERIODICITY_N_ITEMS, +}LIBLTE_RRC_SI_PERIODICITY_ENUM; +static const char liblte_rrc_si_periodicity_text[LIBLTE_RRC_SI_PERIODICITY_N_ITEMS][20] = { "8", "16", "32", "64", + "128", "256", "512"}; +static const uint16 liblte_rrc_si_periodicity_num[LIBLTE_RRC_SI_PERIODICITY_N_ITEMS] = {8, 16, 32, 64, 128, 256, 512}; +typedef enum{ + LIBLTE_RRC_SIB_TYPE_3 = 0, + LIBLTE_RRC_SIB_TYPE_4, + LIBLTE_RRC_SIB_TYPE_5, + LIBLTE_RRC_SIB_TYPE_6, + LIBLTE_RRC_SIB_TYPE_7, + LIBLTE_RRC_SIB_TYPE_8, + LIBLTE_RRC_SIB_TYPE_9, + LIBLTE_RRC_SIB_TYPE_10, + LIBLTE_RRC_SIB_TYPE_11, + LIBLTE_RRC_SIB_TYPE_12_v920, + LIBLTE_RRC_SIB_TYPE_13_v920, + LIBLTE_RRC_SIB_TYPE_14_v1130, + LIBLTE_RRC_SIB_TYPE_15_v1130, + LIBLTE_RRC_SIB_TYPE_16_v1130, + LIBLTE_RRC_SIB_TYPE_17_v1250, + LIBLTE_RRC_SIB_TYPE_18_v1250, + LIBLTE_RRC_SIB_TYPE_N_ITEMS, +}LIBLTE_RRC_SIB_TYPE_ENUM; +static const char liblte_rrc_sib_type_text[LIBLTE_RRC_SIB_TYPE_N_ITEMS][20] = { "3", "4", "5", "6", + "7", "8", "9", "10", + "11", "12_v920", "13_v920", "14_v1130", + "15_v1130", "16_v1130", "17_v1250", "18_v1250"}; +static const uint8 liblte_rrc_sib_type_num[LIBLTE_RRC_SIB_TYPE_N_ITEMS] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; +// Structs +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT id; + LIBLTE_RRC_RESV_FOR_OPER_ENUM resv_for_oper; +}LIBLTE_RRC_PLMN_IDENTITY_LIST_STRUCT; +typedef struct{ + LIBLTE_RRC_SIB_TYPE_ENUM sib_type; +}LIBLTE_RRC_SIB_MAPPING_INFO_STRUCT; +typedef struct{ + LIBLTE_RRC_SIB_MAPPING_INFO_STRUCT sib_mapping_info[LIBLTE_RRC_MAX_SIB]; + LIBLTE_RRC_SI_PERIODICITY_ENUM si_periodicity; + uint32 N_sib_mapping_info; +}LIBLTE_RRC_SCHEDULING_INFO_STRUCT; +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_LIST_STRUCT plmn_id[LIBLTE_RRC_MAX_N_PLMN_IDENTITIES]; + LIBLTE_RRC_SCHEDULING_INFO_STRUCT sched_info[LIBLTE_RRC_MAX_SI_MESSAGE]; + LIBLTE_RRC_TDD_CONFIG_STRUCT tdd_cnfg; + LIBLTE_RRC_CELL_BARRED_ENUM cell_barred; + LIBLTE_RRC_INTRA_FREQ_RESELECTION_ENUM intra_freq_reselection; + LIBLTE_RRC_SI_WINDOW_LENGTH_ENUM si_window_length; + uint32 cell_id; + uint32 csg_id; + uint32 N_plmn_ids; + uint32 N_sched_info; + uint16 tracking_area_code; + int16 q_rx_lev_min; + uint8 csg_indication; + uint8 q_rx_lev_min_offset; + uint8 freq_band_indicator; + uint8 system_info_value_tag; + int8 p_max; + bool tdd; + bool p_max_present; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_1_msg(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_1_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1, + uint32 *N_bits_used); + +/********************************************************************* + Message Name: System Information + + Description: Conveys one or more System Information Blocks + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2 = 0, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_10, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_11, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_14, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_15, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_16, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_17, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_18, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1, // Intentionally not first + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS, +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_ENUM; +static const char liblte_rrc_sys_info_block_type_text[LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS][20] = { "2", "3", "4", "5", + "6", "7", "8", "9", + "10", "11", "12", "13", + "14", "15", "16", "17", + "18", "1"}; +static const uint8 liblte_rrc_sys_info_block_type_num[LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_N_ITEMS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 1}; +// Structs +typedef union{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT sib3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT sib4; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT sib5; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT sib6; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT sib7; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT sib8; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT sib9; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_UNION; +typedef struct{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_UNION sib; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_ENUM sib_type; +}LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT; +typedef struct{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB]; + uint32 N_sibs; +}LIBLTE_RRC_SYS_INFO_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_msg(LIBLTE_RRC_SYS_INFO_MSG_STRUCT *sibs, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SYS_INFO_MSG_STRUCT *sibs); + +/********************************************************************* + Message Name: Security Mode Failure + + Description: Used to indicate an unsuccessful completion of a + security mode command + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; +}LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_failure_msg(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *security_mode_failure, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_failure_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *security_mode_failure); + +/********************************************************************* + Message Name: Security Mode Complete + + Description: Used to confirm the successful completion of a + security mode command + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; +}LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_complete_msg(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *security_mode_complete, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *security_mode_complete); + +/********************************************************************* + Message Name: Security Mode Command + + Description: Used to command the activation of AS security + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT sec_algs; + uint8 rrc_transaction_id; +}LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_command_msg(LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *security_mode_cmd, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_command_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *security_mode_cmd); + +/********************************************************************* + Message Name: RRC Connection Setup Complete + + Description: Used to confirm the successful completion of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; + uint16 mmegi; + uint8 mmec; + bool plmn_id_present; +}LIBLTE_RRC_REGISTERED_MME_STRUCT; +typedef struct{ + LIBLTE_RRC_REGISTERED_MME_STRUCT registered_mme; + LIBLTE_BYTE_MSG_STRUCT dedicated_info_nas; + uint8 rrc_transaction_id; + uint8 selected_plmn_id; + bool registered_mme_present; +}LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_setup_complete_msg(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *con_setup_complete, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_setup_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *con_setup_complete); + +/********************************************************************* + Message Name: RRC Connection Setup + + Description: Used to establish SRB1 + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT rr_cnfg; + uint8 rrc_transaction_id; +}LIBLTE_RRC_CONNECTION_SETUP_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_setup_msg(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *con_setup, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_setup_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_SETUP_STRUCT *con_setup); + +/********************************************************************* + Message Name: RRC Connection Request + + Description: Used to request the establishment of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI = 0, + LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE, + LIBLTE_RRC_CON_REQ_UE_ID_TYPE_N_ITEMS, +}LIBLTE_RRC_CON_REQ_UE_ID_TYPE_ENUM; +static const char liblte_rrc_con_req_ue_id_type_text[LIBLTE_RRC_CON_REQ_UE_ID_TYPE_N_ITEMS][20] = {"S-TMSI", + "Random Value"}; +typedef enum{ + LIBLTE_RRC_CON_REQ_EST_CAUSE_EMERGENCY = 0, + LIBLTE_RRC_CON_REQ_EST_CAUSE_HIGH_PRIO_ACCESS, + LIBLTE_RRC_CON_REQ_EST_CAUSE_MT_ACCESS, + LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING, + LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_DATA, + LIBLTE_RRC_CON_REQ_EST_CAUSE_SPARE3, + LIBLTE_RRC_CON_REQ_EST_CAUSE_SPARE2, + LIBLTE_RRC_CON_REQ_EST_CAUSE_SPARE1, + LIBLTE_RRC_CON_REQ_EST_CAUSE_N_ITEMS, +}LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM; +static const char liblte_rrc_con_req_est_cause_text[LIBLTE_RRC_CON_REQ_EST_CAUSE_N_ITEMS][100] = {"Emergency", + "High Priority Access", + "MT Access", + "MO Signalling", + "MO Data", + "SPARE", + "SPARE", + "SPARE"}; +// Structs +typedef union{ + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + uint64 random; +}LIBLTE_RRC_CON_REQ_UE_ID_UNION; +typedef struct{ + LIBLTE_RRC_CON_REQ_UE_ID_UNION ue_id; + LIBLTE_RRC_CON_REQ_UE_ID_TYPE_ENUM ue_id_type; + LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause; +}LIBLTE_RRC_CONNECTION_REQUEST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_request_msg(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *con_req, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *con_req); + +/********************************************************************* + Message Name: RRC Connection Release + + Description: Used to command the release of an RRC connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_RELEASE_CAUSE_LOAD_BALANCING_TAU_REQUIRED = 0, + LIBLTE_RRC_RELEASE_CAUSE_OTHER, + LIBLTE_RRC_RELEASE_CAUSE_CS_FALLBACK_HIGH_PRIORITY, + LIBLTE_RRC_RELEASE_CAUSE_SPARE1, + LIBLTE_RRC_RELEASE_CAUSE_N_ITEMS, +}LIBLTE_RRC_RELEASE_CAUSE_ENUM; +static const char liblte_rrc_release_cause_text[LIBLTE_RRC_RELEASE_CAUSE_N_ITEMS][100] = {"Load Balancing TAU Required", + "Other", + "CS Fallback High Priority", + "SPARE"}; +// Structs +typedef struct{ + LIBLTE_RRC_RELEASE_CAUSE_ENUM release_cause; + uint8 rrc_transaction_id; +}LIBLTE_RRC_CONNECTION_RELEASE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_release_msg(LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *con_release, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_release_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *con_release); + +/********************************************************************* + Message Name: RRC Connection Reject + + Description: Used to reject the RRC connection establishment + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 wait_time; +}LIBLTE_RRC_CONNECTION_REJECT_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reject_msg(LIBLTE_RRC_CONNECTION_REJECT_STRUCT *con_rej, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reject_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REJECT_STRUCT *con_rej); + +/********************************************************************* + Message Name: RRC Connection Reestablishment Request + + Description: Used to request the reestablishment of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_CON_REEST_REQ_CAUSE_RECONFIG_FAILURE = 0, + LIBLTE_RRC_CON_REEST_REQ_CAUSE_HANDOVER_FAILURE, + LIBLTE_RRC_CON_REEST_REQ_CAUSE_OTHER_FAILURE, + LIBLTE_RRC_CON_REEST_REQ_CAUSE_SPARE1, + LIBLTE_RRC_CON_REEST_REQ_CAUSE_N_ITEMS, +}LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM; +static const char liblte_rrc_con_reest_req_cause_text[LIBLTE_RRC_CON_REEST_REQ_CAUSE_N_ITEMS][100] = {"Reconfiguration Failure", + "Handover Failure", + "Other Failure", + "SPARE"}; +// Structs +typedef struct{ + uint16 c_rnti; + uint16 phys_cell_id; + uint16 short_mac_i; +}LIBLTE_RRC_CON_REEST_REQ_UE_ID_STRUCT; +typedef struct{ + LIBLTE_RRC_CON_REEST_REQ_UE_ID_STRUCT ue_id; + LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM cause; +}LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_request_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *con_reest_req, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *con_reest_req); + +/********************************************************************* + Message Name: RRC Connection Reestablishment Reject + + Description: Used to indicate the rejection of an RRC connection + reestablishment request + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_reject_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *con_reest_rej, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_reject_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *con_reest_rej); + +/********************************************************************* + Message Name: RRC Connection Reestablishment Complete + + Description: Used to confirm the successful completion of an RRC + connection reestablishment + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; +}LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_complete_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *con_reest_complete, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *con_reest_complete); + +/********************************************************************* + Message Name: RRC Connection Reestablishment + + Description: Used to resolve contention and to re-establish SRB1 + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT rr_cnfg; + uint8 rrc_transaction_id; + uint8 next_hop_chaining_count; +}LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *con_reest, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *con_reest); + +/********************************************************************* + Message Name: RRC Connection Reconfiguration Complete + + Description: Used to confirm the successful completion of an RRC + connection reconfiguration + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; +}LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reconfiguration_complete_msg(LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *con_reconfig_complete, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reconfiguration_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *con_reconfig_complete); + +/********************************************************************* + Message Name: RRC Connection Reconfiguration + + Description: Modifies an RRC connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_HANDOVER_TYPE_INTRA_LTE = 0, + LIBLTE_RRC_HANDOVER_TYPE_INTER_RAT, + LIBLTE_RRC_HANDOVER_TYPE_N_ITEMS, +}LIBLTE_RRC_HANDOVER_TYPE_ENUM; +static const char liblte_rrc_handover_type_text[LIBLTE_RRC_HANDOVER_TYPE_N_ITEMS][20] = {"Intra LTE", + "Inter RAT"}; +// Structs +typedef struct{ + LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT sec_alg_cnfg; + uint8 next_hop_chaining_count; + bool key_change_ind; + bool sec_alg_cnfg_present; +}LIBLTE_RRC_INTRA_LTE_HANDOVER_STRUCT; +typedef struct{ + LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT sec_alg_cnfg; + uint8 nas_sec_param_to_eutra[6]; +}LIBLTE_RRC_INTER_RAT_HANDOVER_STRUCT; +typedef struct{ + LIBLTE_RRC_INTRA_LTE_HANDOVER_STRUCT intra_lte; + LIBLTE_RRC_INTER_RAT_HANDOVER_STRUCT inter_rat; + LIBLTE_RRC_HANDOVER_TYPE_ENUM ho_type; +}LIBLTE_RRC_SECURITY_CONFIG_HO_STRUCT; +typedef struct{ + LIBLTE_RRC_MEAS_CONFIG_STRUCT meas_cnfg; + LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT mob_ctrl_info; + LIBLTE_BYTE_MSG_STRUCT ded_info_nas_list[LIBLTE_RRC_MAX_DRB]; + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT rr_cnfg_ded; + LIBLTE_RRC_SECURITY_CONFIG_HO_STRUCT sec_cnfg_ho; + uint32 N_ded_info_nas; + uint8 rrc_transaction_id; + bool meas_cnfg_present; + bool mob_ctrl_info_present; + bool rr_cnfg_ded_present; + bool sec_cnfg_ho_present; +}LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reconfiguration_msg(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *con_reconfig, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reconfiguration_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *con_reconfig); + +/********************************************************************* + Message Name: RN Reconfiguration Complete + + Description: Used to confirm the successful completion of an RN + reconfiguration + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + uint8 rrc_transaction_id; +}LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rn_reconfiguration_complete_msg(LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *rn_reconfig_complete, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rn_reconfiguration_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *rn_reconfig_complete); + +/********************************************************************* + Message Name: RN Reconfiguration + + Description: Modifies the RRC connection between the RN and the + E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_RN_RECONFIGURATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_rn_reconfiguration_msg(LIBLTE_RRC_RN_RECONFIGURATION_STRUCT *rn_reconfig, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rn_reconfiguration_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_RN_RECONFIGURATION_STRUCT *rn_reconfig); + +/********************************************************************* + Message Name: Proximity Indication + + Description: Used to indicate that the UE is entering or leaving + the proximity of one or more cells whose CSG IDs are + in the UEs CSG whitelist + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_ENTERING = 0, + LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_LEAVING, + LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_N_ITEMS, +}LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_ENUM; +static const char liblte_rrc_proximity_indication_type_text[LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_N_ITEMS][20] = {"Entering", "Leaving"}; +typedef enum{ + LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_EUTRA = 0, + LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_UTRA, + LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_N_ITEMS, +}LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_ENUM; +static const char liblte_rrc_proximity_indication_carrier_freq_type_text[LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_N_ITEMS][20] = {"EUTRA", "UTRA"}; +// Structs +typedef struct{ + LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_ENUM type; + LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_ENUM carrier_freq_type; + uint16 carrier_freq; +}LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_proximity_indication_msg(LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *proximity_ind, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_proximity_indication_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *proximity_ind); + +/********************************************************************* + Message Name: Paging + + Description: Used for the notification of one or more UEs + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_PAGE_REC 16 +// Enums +typedef enum{ + LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_S_TMSI = 0, + LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_IMSI, + LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_N_ITEMS, +}LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_ENUM; +static const char liblte_rrc_paging_ue_identity_type_text[LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_N_ITEMS][20] = {"S-TMSI", "IMSI"}; +typedef enum{ + LIBLTE_RRC_CN_DOMAIN_PS = 0, + LIBLTE_RRC_CN_DOMAIN_CS, + LIBLTE_RRC_CN_DOMAIN_N_ITEMS, +}LIBLTE_RRC_CN_DOMAIN_ENUM; +static const char liblte_rrc_cn_domain_text[LIBLTE_RRC_CN_DOMAIN_N_ITEMS][20] = {"PS", "CS"}; +typedef enum{ + LIBLTE_RRC_CMAS_INDICATION_R9_TRUE = 0, + LIBLTE_RRC_CMAS_INDICATION_R9_N_ITEMS, +}LIBLTE_RRC_CMAS_INDICATION_R9_ENUM; +static const char liblte_rrc_cmas_indication_r9_text[LIBLTE_RRC_CMAS_INDICATION_R9_N_ITEMS][20] = {"TRUE"}; +typedef enum{ + LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_TRUE = 0, + LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_N_ITEMS, +}LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_ENUM; +static const char liblte_rrc_system_info_modification_text[LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_N_ITEMS][20] = {"TRUE"}; +typedef enum{ + LIBLTE_RRC_ETWS_INDICATION_TRUE = 0, + LIBLTE_RRC_ETWS_INDICATION_N_ITEMS, +}LIBLTE_RRC_ETWS_INDICATION_ENUM; +static const char liblte_rrc_etws_indication_text[LIBLTE_RRC_ETWS_INDICATION_N_ITEMS][20] = {"TRUE"}; +// Structs +typedef struct{ + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_ENUM ue_identity_type; + uint32 imsi_size; + uint8 imsi[21]; +}LIBLTE_RRC_PAGING_UE_IDENTITY_STRUCT; +typedef struct{ + LIBLTE_RRC_PAGING_UE_IDENTITY_STRUCT ue_identity; + LIBLTE_RRC_CN_DOMAIN_ENUM cn_domain; +}LIBLTE_RRC_PAGING_RECORD_STRUCT; +typedef struct{ + LIBLTE_RRC_CMAS_INDICATION_R9_ENUM cmas_ind_r9; + bool cmas_ind_present; + bool non_crit_ext_present; +}LIBLTE_RRC_PAGING_V920_IES_STRUCT; +typedef struct{ + LIBLTE_RRC_PAGING_V920_IES_STRUCT non_crit_ext; + uint8 late_non_crit_ext; + bool late_non_crit_ext_present; + bool non_crit_ext_present; +}LIBLTE_RRC_PAGING_V890_IES_STRUCT; +typedef struct{ + LIBLTE_RRC_PAGING_RECORD_STRUCT paging_record_list[LIBLTE_RRC_MAX_PAGE_REC]; + LIBLTE_RRC_PAGING_V890_IES_STRUCT non_crit_ext; + LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_ENUM system_info_modification; + LIBLTE_RRC_ETWS_INDICATION_ENUM etws_indication; + uint32 paging_record_list_size; + bool system_info_modification_present; + bool etws_indication_present; + bool non_crit_ext_present; +}LIBLTE_RRC_PAGING_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_paging_msg(LIBLTE_RRC_PAGING_STRUCT *page, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_paging_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PAGING_STRUCT *page); + +/********************************************************************* + Message Name: Mobility From EUTRA Command + + Description: Used to command handover or a cell change from E-UTRA + to another RAT, or enhanced CS fallback to CDMA2000 + 1xRTT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mobility_from_eutra_command_msg(LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT *mobility_from_eutra_cmd, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mobility_from_eutra_command_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT *mobility_from_eutra_cmd); + +/********************************************************************* + Message Name: Measurement Report + + Description: Used for the indication of measurement results + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +typedef struct{ + LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT cell_global_id; + uint16 tracking_area_code; + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_identity_list[5]; + uint32 n_plmn_identity_list; + bool have_plmn_identity_list; +}LIBLTE_RRC_CGI_INFO_STRUCT; +LIBLTE_ERROR_ENUM liblte_rrc_pack_cgi_info_ie(LIBLTE_RRC_CGI_INFO_STRUCT *cgi_info, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cgi_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_CGI_INFO_STRUCT *cgi_info); + +typedef struct{ + uint8 rsrp_result; + bool have_rsrp; + uint8 rsrq_result; + bool have_rsrq; +}LIBLTE_RRC_MEAS_RESULT_STRUCT; +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_result_ie(LIBLTE_RRC_MEAS_RESULT_STRUCT *meas_result, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_result_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_RESULT_STRUCT *meas_result); + +typedef struct{ + uint16 phys_cell_id; + LIBLTE_RRC_CGI_INFO_STRUCT cgi_info; + bool have_cgi_info; + LIBLTE_RRC_MEAS_RESULT_STRUCT meas_result; +}LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT; +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_result_eutra_ie(LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *meas_result_eutra, + uint8 **ie_ptr); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_result_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *meas_result_eutra); + +typedef struct{ + //FIXME +}LIBLTE_RRC_MEAS_RESULT_UTRA_STRUCT; +typedef struct{ + //FIXME +}LIBLTE_RRC_MEAS_RESULT_GERAN_STRUCT; +typedef struct{ + //FIXME +}LIBLTE_RRC_MEAS_RESULT_CDMA2000_STRUCT; + + + +typedef struct{ + LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT result_eutra_list[8]; + uint8 n_result; +}LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT; + +typedef struct{ + LIBLTE_RRC_MEAS_RESULT_UTRA_STRUCT result_utra_list[8]; + uint8 n_result; +}LIBLTE_RRC_MEAS_RESULT_LIST_UTRA_STRUCT; + +typedef struct{ + LIBLTE_RRC_MEAS_RESULT_GERAN_STRUCT result_geran_list[8]; + uint8 n_result; +}LIBLTE_RRC_MEAS_RESULT_LIST_GERAN_STRUCT; + + +typedef struct{ + bool pre_registration_status_HRPD; + LIBLTE_RRC_MEAS_RESULT_CDMA2000_STRUCT cdma2000[8]; +}LIBLTE_RRC_MEAS_RESULTS_CDMA2000_STRUCT; + +typedef enum{ + LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA = 0, + LIBLTE_RRC_MEAS_RESULT_LIST_UTRA, + LIBLTE_RRC_MEAS_RESULT_LIST_GERAN, + LIBLTE_RRC_MEAS_RESULTS_CDMA2000, + LIBLTE_RRC_MEAS_RESULT_N_ITEMS, +}LIBLTE_RRC_MEAS_RESULT_NEIGH_CELLS_ENUM; +static const char liblte_rrc_meas_reult_neigh_cells_text[LIBLTE_RRC_MEAS_RESULT_N_ITEMS][32] = { "measResultListEUTRA", "measResultListUTRA", "measResultListGERAN", "measResultsCDMA2000"}; + + +typedef union{ + LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT eutra; + LIBLTE_RRC_MEAS_RESULT_LIST_UTRA_STRUCT utra; + LIBLTE_RRC_MEAS_RESULT_LIST_GERAN_STRUCT geran; + LIBLTE_RRC_MEAS_RESULTS_CDMA2000_STRUCT cdma2000; +}LIBLTE_RRC_MEAS_RESULT_NEIGH_CELLS_UNION; + +//TODO: pack/unpack for the result lists + + +// Structs +typedef struct{ + uint8 meas_id; + uint8 pcell_rsrp_result; + uint8 pcell_rsrq_result; + + LIBLTE_RRC_MEAS_RESULT_NEIGH_CELLS_UNION meas_result_neigh_cells; + LIBLTE_RRC_MEAS_RESULT_NEIGH_CELLS_ENUM meas_result_neigh_cells_choice; + bool have_meas_result_neigh_cells; +}LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_measurement_report_msg(LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *meas_report, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_measurement_report_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *meas_report); + +/********************************************************************* + Message Name: MBSFN Area Configuration + + Description: Contains the MBMS control information applicable for + an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +#define LIBLTE_RRC_MAX_PMCH_PER_MBSFN 15 + +// Enums +typedef enum{ + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF4 = 0, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF8, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF16, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF32, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF64, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF128, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256, + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS, +}LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM; +static const char liblte_rrc_mbsfn_common_sf_alloc_period_r9_text[LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS][20] = {"4", "8", "16", "32", + "64", "128", "256"}; +static const uint32 liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_N_ITEMS] = {4, 8, 16, 32, + 64, 128, 256}; +// Structs +typedef struct{ + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT commonsf_allocpatternlist_r9[LIBLTE_RRC_MAX_MBSFN_ALLOCATIONS]; + uint8 commonsf_allocpatternlist_r9_size; + LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM commonsf_allocperiod_r9; + LIBLTE_RRC_PMCH_INFO_R9_STRUCT pmch_infolist_r9[LIBLTE_RRC_MAX_PMCH_PER_MBSFN]; + uint8 pmch_infolist_r9_size; +}LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT; + +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *msg, + LIBLTE_RRC_PAGING_STRUCT *mbsfn_area_cnfg); + +/********************************************************************* + Message Name: Master Information Block + + Description: Includes the system information transmitted on BCH + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Inlined with BCCH BCH Message + +/********************************************************************* + Message Name: Logged Measurements Configuration + + Description: Used by E-UTRAN to configure the UE to perform + logging of measurement results while in RRC_IDLE + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_logged_measurements_configuration_msg(LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT *logged_measurements_config, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_logged_measurements_configuration_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT *logged_measurements_config); + +/********************************************************************* + Message Name: Handover From EUTRA Preparation Request (CDMA2000) + + Description: Used to trigger the handover preparation procedure + with a CDMA2000 RAT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_handover_from_eutra_preparation_request_msg(LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT *handover_from_eutra_prep_req, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_handover_from_eutra_preparation_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT *handover_from_eutra_prep_req); + +/********************************************************************* + Message Name: DL Information Transfer + + Description: Used for the downlink transfer of dedicated NAS + information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_NAS = 0, + LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_CDMA2000_1XRTT, + LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_CDMA2000_HRPD, + LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_N_ITEMS, +}LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_ENUM; +static const char liblte_rrc_dl_information_transfer_type_text[LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_N_ITEMS][20] = {"NAS", + "CDMA2000-1XRTT", + "CDMA2000-HRPD"}; +// Structs +typedef struct{ + LIBLTE_BYTE_MSG_STRUCT dedicated_info; + LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_ENUM dedicated_info_type; + uint8 rrc_transaction_id; +}LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_information_transfer_msg(LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *dl_info_transfer, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_information_transfer_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *dl_info_transfer); + +/********************************************************************* + Message Name: CSFB Parameters Response CDMA2000 + + Description: Used to provide the CDMA2000 1xRTT parameters to the + UE so the UE can register with the CDMA2000 1xRTT + network to support CSFB to CDMA2000 1xRTT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_parameters_response_cdma2000_msg(LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT *csfb_params_resp_cdma2000, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_parameters_response_cdma2000_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT *csfb_params_resp_cdma2000); + +/********************************************************************* + Message Name: CSFB Parameters Request CDMA2000 + + Description: Used by the UE to obtain the CDMA2000 1xRTT + parameters from the network + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ +}LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_parameters_request_cdma2000_msg(LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *csfb_params_req_cdma2000, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_parameters_request_cdma2000_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *csfb_params_req_cdma2000); + +/********************************************************************* + Message Name: Counter Check Response + + Description: Used by the UE to respond to a Counter Check message + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_counter_check_response_msg(LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT *counter_check_resp, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_counter_check_response_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT *counter_check_resp); + +/********************************************************************* + Message Name: Counter Check + + Description: Used by the E-UTRAN to indicate the current COUNT MSB + values associated to each DRB and to request the UE + to compare these to its COUNT MSB values and to + report the comparison results to E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef struct{ + // FIXME +}LIBLTE_RRC_COUNTER_CHECK_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_counter_check_msg(LIBLTE_RRC_COUNTER_CHECK_STRUCT *counter_check, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_counter_check_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_COUNTER_CHECK_STRUCT *counter_check); + +/********************************************************************* + Message Name: BCCH BCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE via BCH on the BCCH + logical channel + + Document Reference: 36.331 v10.0.0 Sections 6.2.1 and 6.2.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_DL_BANDWIDTH_6 = 0, + LIBLTE_RRC_DL_BANDWIDTH_15, + LIBLTE_RRC_DL_BANDWIDTH_25, + LIBLTE_RRC_DL_BANDWIDTH_50, + LIBLTE_RRC_DL_BANDWIDTH_75, + LIBLTE_RRC_DL_BANDWIDTH_100, + LIBLTE_RRC_DL_BANDWIDTH_N_ITEMS, +}LIBLTE_RRC_DL_BANDWIDTH_ENUM; +static const char liblte_rrc_dl_bandwidth_text[LIBLTE_RRC_DL_BANDWIDTH_N_ITEMS][20] = {"1.4", "3", "5", "10", + "15", "20"}; +static const double liblte_rrc_dl_bandwidth_num[LIBLTE_RRC_DL_BANDWIDTH_N_ITEMS] = {1.4, 3, 5, 10, 15, 20}; +// Structs +typedef struct{ + LIBLTE_RRC_PHICH_CONFIG_STRUCT phich_config; + LIBLTE_RRC_DL_BANDWIDTH_ENUM dl_bw; + uint8 sfn_div_4; +}LIBLTE_RRC_MIB_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_bcch_bch_msg(LIBLTE_RRC_MIB_STRUCT *mib, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_bch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MIB_STRUCT *mib); + +/********************************************************************* + Message Name: BCCH DLSCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE via DLSCH on the BCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef LIBLTE_RRC_SYS_INFO_MSG_STRUCT LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_bcch_dlsch_msg(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *bcch_dlsch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_dlsch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *bcch_dlsch_msg); + +/********************************************************************* + Message Name: MCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the MCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT LIBLTE_RRC_MCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_mcch_msg(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg); + +/********************************************************************* + Message Name: PCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the PCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +// Structs +typedef LIBLTE_RRC_PAGING_STRUCT LIBLTE_RRC_PCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_pcch_msg(LIBLTE_RRC_PCCH_MSG_STRUCT *pcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PCCH_MSG_STRUCT *pcch_msg); + +/********************************************************************* + Message Name: DL CCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the CCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST = 0, + LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ, + LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ, + LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP, + LIBLTE_RRC_DL_CCCH_MSG_TYPE_N_ITEMS, +}LIBLTE_RRC_DL_CCCH_MSG_TYPE_ENUM; +static const char liblte_rrc_dl_ccch_msg_type_text[LIBLTE_RRC_DL_CCCH_MSG_TYPE_N_ITEMS][100] = {"RRC Connection Reestablishment", + "RRC Connection Reestablishment Reject", + "RRC Connection Reject", + "RRC Connection Setup"}; +// Structs +typedef union{ + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT rrc_con_reest; + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT rrc_con_reest_rej; + LIBLTE_RRC_CONNECTION_REJECT_STRUCT rrc_con_rej; + LIBLTE_RRC_CONNECTION_SETUP_STRUCT rrc_con_setup; +}LIBLTE_RRC_DL_CCCH_MSG_TYPE_UNION; +typedef struct{ + LIBLTE_RRC_DL_CCCH_MSG_TYPE_UNION msg; + LIBLTE_RRC_DL_CCCH_MSG_TYPE_ENUM msg_type; +}LIBLTE_RRC_DL_CCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_ccch_msg(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_ccch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg); + +/********************************************************************* + Message Name: DL DCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the DCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_DL_DCCH_MSG_TYPE_CSFB_PARAMS_RESP_CDMA2000 = 0, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_HANDOVER_FROM_EUTRA_PREP_REQ, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_MOBILITY_FROM_EUTRA_COMMAND, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_COUNTER_CHECK, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_INFO_REQ, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_LOGGED_MEASUREMENTS_CONFIG, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_RN_RECONFIG, + LIBLTE_RRC_DL_DCCH_MSG_TYPE_N_ITEMS, +}LIBLTE_RRC_DL_DCCH_MSG_TYPE_ENUM; +static const char liblte_rrc_dl_dcch_msg_type_text[LIBLTE_RRC_DL_DCCH_MSG_TYPE_N_ITEMS][100] = {"CSFB Parameters Response CDMA2000", + "DL Information Transfer", + "Handover From EUTRA Preparation Request", + "Mobility From EUTRA Command", + "RRC Connection Reconfiguration", + "RRC Connection Release", + "Security Mode Command", + "UE Capability Enquiry", + "Counter Check", + "UE Information Request", + "Logged Measurements Configuration", + "RN Reconfiguration"}; +// Structs +typedef union{ + LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT csfb_params_resp_cdma2000; + LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT dl_info_transfer; + LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT handover_from_eutra_prep_req; + LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT mobility_from_eutra_cmd; + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT rrc_con_reconfig; + LIBLTE_RRC_CONNECTION_RELEASE_STRUCT rrc_con_release; + LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT security_mode_cmd; + LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT ue_cap_enquiry; + LIBLTE_RRC_COUNTER_CHECK_STRUCT counter_check; + LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT ue_info_req; + LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT logged_measurements_config; + LIBLTE_RRC_RN_RECONFIGURATION_STRUCT rn_reconfig; +}LIBLTE_RRC_DL_DCCH_MSG_TYPE_UNION; +typedef struct{ + LIBLTE_RRC_DL_DCCH_MSG_TYPE_UNION msg; + LIBLTE_RRC_DL_DCCH_MSG_TYPE_ENUM msg_type; +}LIBLTE_RRC_DL_DCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_dcch_msg(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_dcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg); + +/********************************************************************* + Message Name: UL CCCH Message + + Description: Contains the set of RRC messages that may be sent + from the UE to the E-UTRAN on the CCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ = 0, + LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ, + LIBLTE_RRC_UL_CCCH_MSG_TYPE_N_ITEMS, +}LIBLTE_RRC_UL_CCCH_MSG_TYPE_ENUM; +static const char liblte_rrc_ul_ccch_msg_type_text[LIBLTE_RRC_UL_CCCH_MSG_TYPE_N_ITEMS][100] = {"RRC Connection Reestablishment Request", + "RRC Connection Request"}; +// Structs +typedef union{ + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT rrc_con_reest_req; + LIBLTE_RRC_CONNECTION_REQUEST_STRUCT rrc_con_req; +}LIBLTE_RRC_UL_CCCH_MSG_TYPE_UNION; +typedef struct{ + LIBLTE_RRC_UL_CCCH_MSG_TYPE_UNION msg; + LIBLTE_RRC_UL_CCCH_MSG_TYPE_ENUM msg_type; +}LIBLTE_RRC_UL_CCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_ccch_msg(LIBLTE_RRC_UL_CCCH_MSG_STRUCT *ul_ccch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_ccch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_CCCH_MSG_STRUCT *ul_ccch_msg); + +/********************************************************************* + Message Name: UL DCCH Message + + Description: Contains the set of RRC messages that may be sent + from the UE to the E-UTRAN on the DCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_RRC_UL_DCCH_MSG_TYPE_CSFB_PARAMS_REQ_CDMA2000 = 0, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_FAILURE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_HANDOVER_PREP_TRANSFER, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_COUNTER_CHECK_RESP, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_INFO_RESP, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_PROXIMITY_IND, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_RN_RECONFIG_COMPLETE, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_SPARE2, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_SPARE1, + LIBLTE_RRC_UL_DCCH_MSG_TYPE_N_ITEMS, +}LIBLTE_RRC_UL_DCCH_MSG_TYPE_ENUM; +static const char liblte_rrc_ul_dcch_msg_type_text[LIBLTE_RRC_UL_DCCH_MSG_TYPE_N_ITEMS][100] = {"CSFB Parameters Request CDMA2000", + "Measurement Report", + "RRC Connection Reconfiguration Complete", + "RRC Connection Reestablishment Complete", + "RRC Connection Setup Complete", + "Security Mode Complete", + "Security Mode Failure", + "UE Capability Information", + "UL Handover Preparation Transfer", + "UL Information Transfer", + "Counter Check Response", + "UE Information Response", + "Proximity Indication", + "RN Reconfiguration Complete", + "SPARE", + "SPARE"}; +// Structs +typedef union{ + LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT csfb_params_req_cdma2000; + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT measurement_report; + LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT rrc_con_reconfig_complete; + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT rrc_con_reest_complete; + LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT rrc_con_setup_complete; + LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT security_mode_complete; + LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT security_mode_failure; + LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT ue_capability_info; + LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT ul_handover_prep_transfer; + LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT ul_info_transfer; + LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT counter_check_resp; + LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT ue_info_resp; + LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT proximity_ind; + LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT rn_reconfig_complete; +}LIBLTE_RRC_UL_DCCH_MSG_TYPE_UNION; +typedef struct{ + LIBLTE_RRC_UL_DCCH_MSG_TYPE_UNION msg; + LIBLTE_RRC_UL_DCCH_MSG_TYPE_ENUM msg_type; +}LIBLTE_RRC_UL_DCCH_MSG_STRUCT; +// Functions +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_dcch_msg(LIBLTE_RRC_UL_DCCH_MSG_STRUCT *ul_dcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_dcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_DCCH_MSG_STRUCT *ul_dcch_msg); + +#endif // SRSLTE_LIBLTE_RRC_H diff --git a/lib/include/srslte/asn1/liblte_s1ap.h b/lib/include/srslte/asn1/liblte_s1ap.h new file mode 100644 index 0000000..0874ec7 --- /dev/null +++ b/lib/include/srslte/asn1/liblte_s1ap.h @@ -0,0 +1,10355 @@ +/******************************************************************************* +/* +/* Copyright 2016 Software Radio Systems Limited +/* +********************************************************************************/ + +#ifndef SRSLTE_LIBLTE_S1AP_H +#define SRSLTE_LIBLTE_S1AP_H + +/******************************************************************************* +/* Warnings/Todos +********************************************************************************/ +// Extensions are not yet handled correctly +// Dynamic Sequence Of types have max 32 elements to reduce memory footprint +// Container Lists are not yet handled correctly e.g. E-RAB-IE-ContainerList + +/******************************************************************************* +/* INCLUDES +********************************************************************************/ + +#include "liblte_common.h" + +/******************************************************************************* + LOGGING +*******************************************************************************/ + +typedef void (*log_handler_t)(void *ctx, char *str); + +void liblte_log_register_handler(void *ctx, log_handler_t handler); + + +/******************************************************************************* +/* MAX defines +********************************************************************************/ +#define LIBLTE_S1AP_MAXPRIVATEIES 65535 +#define LIBLTE_S1AP_MAXPROTOCOLIES 65535 +#define LIBLTE_S1AP_MAXNOOFE_RABS 256 +#define LIBLTE_S1AP_MAXNOOFTACS 256 +#define LIBLTE_S1AP_MAXNOOFBPLMNS 6 +#define LIBLTE_S1AP_MAXNOOFEPLMNS 15 +#define LIBLTE_S1AP_MAXNOOFFORBLACS 4096 +#define LIBLTE_S1AP_MAXNOOFINDIVIDUALS1CONNECTIONSTORESET 256 +#define LIBLTE_S1AP_MAXNOOFTAIFORWARNING 65535 +#define LIBLTE_S1AP_MAXNOOFEMERGENCYAREAID 65535 +#define LIBLTE_S1AP_MAXNOOFCELLINEAI 65535 +#define LIBLTE_S1AP_MAXNOOFENBX2EXTTLAS 16 +#define LIBLTE_S1AP_MAXNOOFRATS 8 +#define LIBLTE_S1AP_MAXNOOFMMECS 256 +#define LIBLTE_S1AP_MAXNOOFTAFORMDT 8 +#define LIBLTE_S1AP_MAXNOOFCELLSFORRESTART 256 +#define LIBLTE_S1AP_MAXNOOFRESTARTEMERGENCYAREAIDS 256 +#define LIBLTE_S1AP_MAXNOOFCSGS 256 +#define LIBLTE_S1AP_MAXNOOFERRORS 256 +#define LIBLTE_S1AP_MAXNOOFEPLMNSPLUSONE 16 +#define LIBLTE_S1AP_MAXNOOFCELLS 16 +#define LIBLTE_S1AP_MAXNOOFCELLINTAI 65535 +#define LIBLTE_S1AP_MAXNOOFENBX2GTPTLAS 16 +#define LIBLTE_S1AP_MAXNOOFCELLIDFORMDT 32 +#define LIBLTE_S1AP_MAXNOOFRESTARTTAIS 2048 +#define LIBLTE_S1AP_MAXPROTOCOLEXTENSIONS 65535 +#define LIBLTE_S1AP_MAXNOOFPLMNSPERMME 32 +#define LIBLTE_S1AP_MAXNOOFCELLID 65535 +#define LIBLTE_S1AP_MAXNOOFGROUPIDS 65535 +#define LIBLTE_S1AP_MAXNOOFTAIS 256 +#define LIBLTE_S1AP_MAXNOOFENBX2TLAS 2 +#define LIBLTE_S1AP_MAXNOOFMDTPLMNS 16 +#define LIBLTE_S1AP_MAXNOOFFORBTACS 4096 + +/******************************************************************************* +/* Elementary Procedures +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION = 0, + LIBLTE_S1AP_PROC_ID_HANDOVERRESOURCEALLOCATION = 1, + LIBLTE_S1AP_PROC_ID_HANDOVERNOTIFICATION = 2, + LIBLTE_S1AP_PROC_ID_PATHSWITCHREQUEST = 3, + LIBLTE_S1AP_PROC_ID_HANDOVERCANCEL = 4, + LIBLTE_S1AP_PROC_ID_E_RABSETUP = 5, + LIBLTE_S1AP_PROC_ID_E_RABMODIFY = 6, + LIBLTE_S1AP_PROC_ID_E_RABRELEASE = 7, + LIBLTE_S1AP_PROC_ID_E_RABRELEASEINDICATION = 8, + LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP = 9, + LIBLTE_S1AP_PROC_ID_PAGING = 10, + LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT = 11, + LIBLTE_S1AP_PROC_ID_INITIALUEMESSAGE = 12, + LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT = 13, + LIBLTE_S1AP_PROC_ID_RESET = 14, + LIBLTE_S1AP_PROC_ID_ERRORINDICATION = 15, + LIBLTE_S1AP_PROC_ID_NASNONDELIVERYINDICATION = 16, + LIBLTE_S1AP_PROC_ID_S1SETUP = 17, + LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASEREQUEST = 18, + LIBLTE_S1AP_PROC_ID_DOWNLINKS1CDMA2000TUNNELING = 19, + LIBLTE_S1AP_PROC_ID_UPLINKS1CDMA2000TUNNELING = 20, + LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION = 21, + LIBLTE_S1AP_PROC_ID_UECAPABILITYINFOINDICATION = 22, + LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE = 23, + LIBLTE_S1AP_PROC_ID_ENBSTATUSTRANSFER = 24, + LIBLTE_S1AP_PROC_ID_MMESTATUSTRANSFER = 25, + LIBLTE_S1AP_PROC_ID_DEACTIVATETRACE = 26, + LIBLTE_S1AP_PROC_ID_TRACESTART = 27, + LIBLTE_S1AP_PROC_ID_TRACEFAILUREINDICATION = 28, + LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONUPDATE = 29, + LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONUPDATE = 30, + LIBLTE_S1AP_PROC_ID_LOCATIONREPORTINGCONTROL = 31, + LIBLTE_S1AP_PROC_ID_LOCATIONREPORTINGFAILUREINDICATION = 32, + LIBLTE_S1AP_PROC_ID_LOCATIONREPORT = 33, + LIBLTE_S1AP_PROC_ID_OVERLOADSTART = 34, + LIBLTE_S1AP_PROC_ID_OVERLOADSTOP = 35, + LIBLTE_S1AP_PROC_ID_WRITEREPLACEWARNING = 36, + LIBLTE_S1AP_PROC_ID_ENBDIRECTINFORMATIONTRANSFER = 37, + LIBLTE_S1AP_PROC_ID_MMEDIRECTINFORMATIONTRANSFER = 38, + LIBLTE_S1AP_PROC_ID_PRIVATEMESSAGE = 39, + LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONTRANSFER = 40, + LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONTRANSFER = 41, + LIBLTE_S1AP_PROC_ID_CELLTRAFFICTRACE = 42, + LIBLTE_S1AP_PROC_ID_KILL = 43, + LIBLTE_S1AP_PROC_ID_DOWNLINKUEASSOCIATEDLPPATRANSPORT = 44, + LIBLTE_S1AP_PROC_ID_UPLINKUEASSOCIATEDLPPATRANSPORT = 45, + LIBLTE_S1AP_PROC_ID_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT = 46, + LIBLTE_S1AP_PROC_ID_UPLINKNONUEASSOCIATEDLPPATRANSPORT = 47, + LIBLTE_S1AP_PROC_ID_UERADIOCAPABILITYMATCH = 48, + LIBLTE_S1AP_PROC_ID_PWSRESTARTINDICATION = 49, + LIBLTE_S1AP_PROC_N_ITEMS, +}LIBLTE_S1AP_PROC_ENUM; +static const char liblte_s1ap_proc_text[LIBLTE_S1AP_PROC_N_ITEMS][64] = { + "id-HandoverPreparation", + "id-HandoverResourceAllocation", + "id-HandoverNotification", + "id-PathSwitchRequest", + "id-HandoverCancel", + "id-E-RABSetup", + "id-E-RABModify", + "id-E-RABRelease", + "id-E-RABReleaseIndication", + "id-InitialContextSetup", + "id-Paging", + "id-downlinkNASTransport", + "id-initialUEMessage", + "id-uplinkNASTransport", + "id-Reset", + "id-ErrorIndication", + "id-NASNonDeliveryIndication", + "id-S1Setup", + "id-UEContextReleaseRequest", + "id-DownlinkS1cdma2000tunneling", + "id-UplinkS1cdma2000tunneling", + "id-UEContextModification", + "id-UECapabilityInfoIndication", + "id-UEContextRelease", + "id-eNBStatusTransfer", + "id-MMEStatusTransfer", + "id-DeactivateTrace", + "id-TraceStart", + "id-TraceFailureIndication", + "id-ENBConfigurationUpdate", + "id-MMEConfigurationUpdate", + "id-LocationReportingControl", + "id-LocationReportingFailureIndication", + "id-LocationReport", + "id-OverloadStart", + "id-OverloadStop", + "id-WriteReplaceWarning", + "id-eNBDirectInformationTransfer", + "id-MMEDirectInformationTransfer", + "id-PrivateMessage", + "id-eNBConfigurationTransfer", + "id-MMEConfigurationTransfer", + "id-CellTrafficTrace", + "id-Kill", + "id-downlinkUEAssociatedLPPaTransport", + "id-uplinkUEAssociatedLPPaTransport", + "id-downlinkNonUEAssociatedLPPaTransport", + "id-uplinkNonUEAssociatedLPPaTransport", + "id-UERadioCapabilityMatch", + "id-PWSRestartIndication", +}; + + + +/******************************************************************************* +/* ProtocolIE Ids +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID = 0, + LIBLTE_S1AP_IE_ID_HANDOVERTYPE = 1, + LIBLTE_S1AP_IE_ID_CAUSE = 2, + LIBLTE_S1AP_IE_ID_SOURCEID = 3, + LIBLTE_S1AP_IE_ID_TARGETID = 4, + LIBLTE_S1AP_IE_ID_SPARE5 = 5, + LIBLTE_S1AP_IE_ID_SPARE6 = 6, + LIBLTE_S1AP_IE_ID_SPARE7 = 7, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID = 8, + LIBLTE_S1AP_IE_ID_SPARE9 = 9, + LIBLTE_S1AP_IE_ID_SPARE10 = 10, + LIBLTE_S1AP_IE_ID_SPARE11 = 11, + LIBLTE_S1AP_IE_ID_E_RABSUBJECTTODATAFORWARDINGLIST = 12, + LIBLTE_S1AP_IE_ID_E_RABTORELEASELISTHOCMD = 13, + LIBLTE_S1AP_IE_ID_E_RABDATAFORWARDINGITEM = 14, + LIBLTE_S1AP_IE_ID_E_RABRELEASEITEMBEARERRELCOMP = 15, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTBEARERSUREQ = 16, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMBEARERSUREQ = 17, + LIBLTE_S1AP_IE_ID_E_RABADMITTEDLIST = 18, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTHOREQACK = 19, + LIBLTE_S1AP_IE_ID_E_RABADMITTEDITEM = 20, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPITEMHOREQACK = 21, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLLIST = 22, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLITEM = 23, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTCTXTSUREQ = 24, + LIBLTE_S1AP_IE_ID_TRACEACTIVATION = 25, + LIBLTE_S1AP_IE_ID_NAS_PDU = 26, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMHOREQ = 27, + LIBLTE_S1AP_IE_ID_E_RABSETUPLISTBEARERSURES = 28, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTBEARERSURES = 29, + LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDLISTBEARERMODREQ = 30, + LIBLTE_S1AP_IE_ID_E_RABMODIFYLISTBEARERMODRES = 31, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOMODIFYLIST = 32, + LIBLTE_S1AP_IE_ID_E_RABTOBERELEASEDLIST = 33, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTORELEASELIST = 34, + LIBLTE_S1AP_IE_ID_E_RABITEM = 35, + LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDITEMBEARERMODREQ = 36, + LIBLTE_S1AP_IE_ID_E_RABMODIFYITEMBEARERMODRES = 37, + LIBLTE_S1AP_IE_ID_E_RABRELEASEITEM = 38, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMBEARERSURES = 39, + LIBLTE_S1AP_IE_ID_SECURITYCONTEXT = 40, + LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST = 41, + LIBLTE_S1AP_IE_ID_SPARE42 = 42, + LIBLTE_S1AP_IE_ID_UEPAGINGID = 43, + LIBLTE_S1AP_IE_ID_PAGINGDRX = 44, + LIBLTE_S1AP_IE_ID_SPARE45 = 45, + LIBLTE_S1AP_IE_ID_TAILIST = 46, + LIBLTE_S1AP_IE_ID_TAIITEM = 47, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTCTXTSURES = 48, + LIBLTE_S1AP_IE_ID_E_RABRELEASEITEMHOCMD = 49, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMCTXTSURES = 50, + LIBLTE_S1AP_IE_ID_E_RABSETUPLISTCTXTSURES = 51, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMCTXTSUREQ = 52, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTHOREQ = 53, + LIBLTE_S1AP_IE_ID_SPARE54 = 54, + LIBLTE_S1AP_IE_ID_GERANTOLTEHOINFORMATIONRES = 55, + LIBLTE_S1AP_IE_ID_SPARE56 = 56, + LIBLTE_S1AP_IE_ID_UTRANTOLTEHOINFORMATIONRES = 57, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS = 58, + LIBLTE_S1AP_IE_ID_GLOBAL_ENB_ID = 59, + LIBLTE_S1AP_IE_ID_ENBNAME = 60, + LIBLTE_S1AP_IE_ID_MMENAME = 61, + LIBLTE_S1AP_IE_ID_SPARE62 = 62, + LIBLTE_S1AP_IE_ID_SERVEDPLMNS = 63, + LIBLTE_S1AP_IE_ID_SUPPORTEDTAS = 64, + LIBLTE_S1AP_IE_ID_TIMETOWAIT = 65, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE = 66, + LIBLTE_S1AP_IE_ID_TAI = 67, + LIBLTE_S1AP_IE_ID_SPARE68 = 68, + LIBLTE_S1AP_IE_ID_E_RABRELEASELISTBEARERRELCOMP = 69, + LIBLTE_S1AP_IE_ID_CDMA2000PDU = 70, + LIBLTE_S1AP_IE_ID_CDMA2000RATTYPE = 71, + LIBLTE_S1AP_IE_ID_CDMA2000SECTORID = 72, + LIBLTE_S1AP_IE_ID_SECURITYKEY = 73, + LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY = 74, + LIBLTE_S1AP_IE_ID_GUMMEI_ID = 75, + LIBLTE_S1AP_IE_ID_SPARE76 = 76, + LIBLTE_S1AP_IE_ID_SPARE77 = 77, + LIBLTE_S1AP_IE_ID_E_RABINFORMATIONLISTITEM = 78, + LIBLTE_S1AP_IE_ID_DIRECT_FORWARDING_PATH_AVAILABILITY = 79, + LIBLTE_S1AP_IE_ID_UEIDENTITYINDEXVALUE = 80, + LIBLTE_S1AP_IE_ID_SPARE81 = 81, + LIBLTE_S1AP_IE_ID_SPARE82 = 82, + LIBLTE_S1AP_IE_ID_CDMA2000HOSTATUS = 83, + LIBLTE_S1AP_IE_ID_CDMA2000HOREQUIREDINDICATION = 84, + LIBLTE_S1AP_IE_ID_SPARE85 = 85, + LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID = 86, + LIBLTE_S1AP_IE_ID_RELATIVEMMECAPACITY = 87, + LIBLTE_S1AP_IE_ID_SOURCEMME_UE_S1AP_ID = 88, + LIBLTE_S1AP_IE_ID_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM = 89, + LIBLTE_S1AP_IE_ID_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER = 90, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM = 91, + LIBLTE_S1AP_IE_ID_RESETTYPE = 92, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK = 93, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULITEM = 94, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULLIST = 95, + LIBLTE_S1AP_IE_ID_S_TMSI = 96, + LIBLTE_S1AP_IE_ID_CDMA2000ONEXRAND = 97, + LIBLTE_S1AP_IE_ID_REQUESTTYPE = 98, + LIBLTE_S1AP_IE_ID_UE_S1AP_IDS = 99, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI = 100, + LIBLTE_S1AP_IE_ID_OVERLOADRESPONSE = 101, + LIBLTE_S1AP_IE_ID_CDMA2000ONEXSRVCCINFO = 102, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOBERELEASEDLIST = 103, + LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER = 104, + LIBLTE_S1AP_IE_ID_SERVEDGUMMEIS = 105, + LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP = 106, + LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES = 107, + LIBLTE_S1AP_IE_ID_CSFALLBACKINDICATOR = 108, + LIBLTE_S1AP_IE_ID_CNDOMAIN = 109, + LIBLTE_S1AP_IE_ID_E_RABRELEASEDLIST = 110, + LIBLTE_S1AP_IE_ID_MESSAGEIDENTIFIER = 111, + LIBLTE_S1AP_IE_ID_SERIALNUMBER = 112, + LIBLTE_S1AP_IE_ID_WARNINGAREALIST = 113, + LIBLTE_S1AP_IE_ID_REPETITIONPERIOD = 114, + LIBLTE_S1AP_IE_ID_NUMBEROFBROADCASTREQUEST = 115, + LIBLTE_S1AP_IE_ID_WARNINGTYPE = 116, + LIBLTE_S1AP_IE_ID_WARNINGSECURITYINFO = 117, + LIBLTE_S1AP_IE_ID_DATACODINGSCHEME = 118, + LIBLTE_S1AP_IE_ID_WARNINGMESSAGECONTENTS = 119, + LIBLTE_S1AP_IE_ID_BROADCASTCOMPLETEDAREALIST = 120, + LIBLTE_S1AP_IE_ID_INTER_SYSTEMINFORMATIONTRANSFERTYPEEDT = 121, + LIBLTE_S1AP_IE_ID_INTER_SYSTEMINFORMATIONTRANSFERTYPEMDT = 122, + LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER = 123, + LIBLTE_S1AP_IE_ID_SRVCCOPERATIONPOSSIBLE = 124, + LIBLTE_S1AP_IE_ID_SRVCCHOINDICATION = 125, + LIBLTE_S1AP_IE_ID_NAS_DOWNLINKCOUNT = 126, + LIBLTE_S1AP_IE_ID_CSG_ID = 127, + LIBLTE_S1AP_IE_ID_CSG_IDLIST = 128, + LIBLTE_S1AP_IE_ID_SONCONFIGURATIONTRANSFERECT = 129, + LIBLTE_S1AP_IE_ID_SONCONFIGURATIONTRANSFERMCT = 130, + LIBLTE_S1AP_IE_ID_TRACECOLLECTIONENTITYIPADDRESS = 131, + LIBLTE_S1AP_IE_ID_MSCLASSMARK2 = 132, + LIBLTE_S1AP_IE_ID_MSCLASSMARK3 = 133, + LIBLTE_S1AP_IE_ID_RRC_ESTABLISHMENT_CAUSE = 134, + LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSFROME_UTRAN = 135, + LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSTOE_UTRAN = 136, + LIBLTE_S1AP_IE_ID_DEFAULTPAGINGDRX = 137, + LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER_SECONDARY = 138, + LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER_SECONDARY = 139, + LIBLTE_S1AP_IE_ID_EUTRANROUNDTRIPDELAYESTIMATIONINFO = 140, + LIBLTE_S1AP_IE_ID_BROADCASTCANCELLEDAREALIST = 141, + LIBLTE_S1AP_IE_ID_CONCURRENTWARNINGMESSAGEINDICATOR = 142, + LIBLTE_S1AP_IE_ID_DATA_FORWARDING_NOT_POSSIBLE = 143, + LIBLTE_S1AP_IE_ID_EXTENDEDREPETITIONPERIOD = 144, + LIBLTE_S1AP_IE_ID_CELLACCESSMODE = 145, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS = 146, + LIBLTE_S1AP_IE_ID_LPPA_PDU = 147, + LIBLTE_S1AP_IE_ID_ROUTING_ID = 148, + LIBLTE_S1AP_IE_ID_TIME_SYNCHRONIZATION_INFO = 149, + LIBLTE_S1AP_IE_ID_PS_SERVICENOTAVAILABLE = 150, + LIBLTE_S1AP_IE_ID_PAGINGPRIORITY = 151, + LIBLTE_S1AP_IE_ID_X2TNLCONFIGURATIONINFO = 152, + LIBLTE_S1AP_IE_ID_ENBX2EXTENDEDTRANSPORTLAYERADDRESSES = 153, + LIBLTE_S1AP_IE_ID_GUMMEILIST = 154, + LIBLTE_S1AP_IE_ID_GW_TRANSPORTLAYERADDRESS = 155, + LIBLTE_S1AP_IE_ID_CORRELATION_ID = 156, + LIBLTE_S1AP_IE_ID_SOURCEMME_GUMMEI = 157, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2 = 158, + LIBLTE_S1AP_IE_ID_REGISTEREDLAI = 159, + LIBLTE_S1AP_IE_ID_RELAYNODE_INDICATOR = 160, + LIBLTE_S1AP_IE_ID_TRAFFICLOADREDUCTIONINDICATION = 161, + LIBLTE_S1AP_IE_ID_MDTCONFIGURATION = 162, + LIBLTE_S1AP_IE_ID_MMERELAYSUPPORTINDICATOR = 163, + LIBLTE_S1AP_IE_ID_GWCONTEXTRELEASEINDICATION = 164, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTALLOWED = 165, + LIBLTE_S1AP_IE_ID_PRIVACYINDICATOR = 166, + LIBLTE_S1AP_IE_ID_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY = 167, + LIBLTE_S1AP_IE_ID_HO_CAUSE = 168, + LIBLTE_S1AP_IE_ID_VOICESUPPORTMATCHINDICATOR = 169, + LIBLTE_S1AP_IE_ID_GUMMEITYPE = 170, + LIBLTE_S1AP_IE_ID_M3CONFIGURATION = 171, + LIBLTE_S1AP_IE_ID_M4CONFIGURATION = 172, + LIBLTE_S1AP_IE_ID_M5CONFIGURATION = 173, + LIBLTE_S1AP_IE_ID_MDT_LOCATION_INFO = 174, + LIBLTE_S1AP_IE_ID_MOBILITYINFORMATION = 175, + LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF = 176, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTPLMNLIST = 177, + LIBLTE_S1AP_IE_ID_SIGNALLINGBASEDMDTPLMNLIST = 178, + LIBLTE_S1AP_IE_ID_ULCOUNTVALUEEXTENDED = 179, + LIBLTE_S1AP_IE_ID_DLCOUNTVALUEEXTENDED = 180, + LIBLTE_S1AP_IE_ID_RECEIVESTATUSOFULPDCPSDUSEXTENDED = 181, + LIBLTE_S1AP_IE_ID_ECGILISTFORRESTART = 182, + LIBLTE_S1AP_IE_ID_SIPTO_CORRELATION_ID = 183, + LIBLTE_S1AP_IE_ID_SIPTO_L_GW_TRANSPORTLAYERADDRESS = 184, + LIBLTE_S1AP_IE_ID_TRANSPORTINFORMATION = 185, + LIBLTE_S1AP_IE_ID_LHN_ID = 186, + LIBLTE_S1AP_IE_ID_ADDITIONALCSFALLBACKINDICATOR = 187, + LIBLTE_S1AP_IE_ID_TAILISTFORRESTART = 188, + LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION = 189, + LIBLTE_S1AP_IE_ID_EMERGENCYAREAIDLISTFORRESTART = 190, + LIBLTE_S1AP_IE_ID_KILLALLWARNINGMESSAGES = 191, + LIBLTE_S1AP_IE_N_ITEMS, +}LIBLTE_S1AP_IE_ENUM; +static const char liblte_s1ap_ie_text[LIBLTE_S1AP_IE_N_ITEMS][64] = { + "id-MME-UE-S1AP-ID", + "id-HandoverType", + "id-Cause", + "id-SourceID", + "id-TargetID", + "id-spare5", + "id-spare6", + "id-spare7", + "id-eNB-UE-S1AP-ID", + "id-spare9", + "id-spare10", + "id-spare11", + "id-E-RABSubjecttoDataForwardingList", + "id-E-RABtoReleaseListHOCmd", + "id-E-RABDataForwardingItem", + "id-E-RABReleaseItemBearerRelComp", + "id-E-RABToBeSetupListBearerSUReq", + "id-E-RABToBeSetupItemBearerSUReq", + "id-E-RABAdmittedList", + "id-E-RABFailedToSetupListHOReqAck", + "id-E-RABAdmittedItem", + "id-E-RABFailedtoSetupItemHOReqAck", + "id-E-RABToBeSwitchedDLList", + "id-E-RABToBeSwitchedDLItem", + "id-E-RABToBeSetupListCtxtSUReq", + "id-TraceActivation", + "id-NAS-PDU", + "id-E-RABToBeSetupItemHOReq", + "id-E-RABSetupListBearerSURes", + "id-E-RABFailedToSetupListBearerSURes", + "id-E-RABToBeModifiedListBearerModReq", + "id-E-RABModifyListBearerModRes", + "id-E-RABFailedToModifyList", + "id-E-RABToBeReleasedList", + "id-E-RABFailedToReleaseList", + "id-E-RABItem", + "id-E-RABToBeModifiedItemBearerModReq", + "id-E-RABModifyItemBearerModRes", + "id-E-RABReleaseItem", + "id-E-RABSetupItemBearerSURes", + "id-SecurityContext", + "id-HandoverRestrictionList", + "id-spare42", + "id-UEPagingID", + "id-pagingDRX", + "id-spare45", + "id-TAIList", + "id-TAIItem", + "id-E-RABFailedToSetupListCtxtSURes", + "id-E-RABReleaseItemHOCmd", + "id-E-RABSetupItemCtxtSURes", + "id-E-RABSetupListCtxtSURes", + "id-E-RABToBeSetupItemCtxtSUReq", + "id-E-RABToBeSetupListHOReq", + "id-spare54", + "id-GERANtoLTEHOInformationRes", + "id-spare56", + "id-UTRANtoLTEHOInformationRes", + "id-CriticalityDiagnostics", + "id-Global-ENB-ID", + "id-eNBname", + "id-MMEname", + "id-spare62", + "id-ServedPLMNs", + "id-SupportedTAs", + "id-TimeToWait", + "id-uEaggregateMaximumBitrate", + "id-TAI", + "id-spare68", + "id-E-RABReleaseListBearerRelComp", + "id-cdma2000PDU", + "id-cdma2000RATType", + "id-cdma2000SectorID", + "id-SecurityKey", + "id-UERadioCapability", + "id-GUMMEI-ID", + "id-spare76", + "id-spare77", + "id-E-RABInformationListItem", + "id-Direct-Forwarding-Path-Availability", + "id-UEIdentityIndexValue", + "id-spare81", + "id-spare82", + "id-cdma2000HOStatus", + "id-cdma2000HORequiredIndication", + "id-spare85", + "id-E-UTRAN-Trace-ID", + "id-RelativeMMECapacity", + "id-SourceMME-UE-S1AP-ID", + "id-Bearers-SubjectToStatusTransfer-Item", + "id-eNB-StatusTransfer-TransparentContainer", + "id-UE-associatedLogicalS1-ConnectionItem", + "id-ResetType", + "id-UE-associatedLogicalS1-ConnectionListResAck", + "id-E-RABToBeSwitchedULItem", + "id-E-RABToBeSwitchedULList", + "id-S-TMSI", + "id-cdma2000OneXRAND", + "id-RequestType", + "id-UE-S1AP-IDs", + "id-EUTRAN-CGI", + "id-OverloadResponse", + "id-cdma2000OneXSRVCCInfo", + "id-E-RABFailedToBeReleasedList", + "id-Source-ToTarget-TransparentContainer", + "id-ServedGUMMEIs", + "id-SubscriberProfileIDforRFP", + "id-UESecurityCapabilities", + "id-CSFallbackIndicator", + "id-CNDomain", + "id-E-RABReleasedList", + "id-MessageIdentifier", + "id-SerialNumber", + "id-WarningAreaList", + "id-RepetitionPeriod", + "id-NumberofBroadcastRequest", + "id-WarningType", + "id-WarningSecurityInfo", + "id-DataCodingScheme", + "id-WarningMessageContents", + "id-BroadcastCompletedAreaList", + "id-Inter-SystemInformationTransferTypeEDT", + "id-Inter-SystemInformationTransferTypeMDT", + "id-Target-ToSource-TransparentContainer", + "id-SRVCCOperationPossible", + "id-SRVCCHOIndication", + "id-NAS-DownlinkCount", + "id-CSG-Id", + "id-CSG-IdList", + "id-SONConfigurationTransferECT", + "id-SONConfigurationTransferMCT", + "id-TraceCollectionEntityIPAddress", + "id-MSClassmark2", + "id-MSClassmark3", + "id-RRC-Establishment-Cause", + "id-NASSecurityParametersfromE-UTRAN", + "id-NASSecurityParameterstoE-UTRAN", + "id-DefaultPagingDRX", + "id-Source-ToTarget-TransparentContainer-Secondary", + "id-Target-ToSource-TransparentContainer-Secondary", + "id-EUTRANRoundTripDelayEstimationInfo", + "id-BroadcastCancelledAreaList", + "id-ConcurrentWarningMessageIndicator", + "id-Data-Forwarding-Not-Possible", + "id-ExtendedRepetitionPeriod", + "id-CellAccessMode", + "id-CSGMembershipStatus", + "id-LPPa-PDU", + "id-Routing-ID", + "id-Time-Synchronization-Info", + "id-PS-ServiceNotAvailable", + "id-PagingPriority", + "id-x2TNLConfigurationInfo", + "id-eNBX2ExtendedTransportLayerAddresses", + "id-GUMMEIList", + "id-GW-TransportLayerAddress", + "id-Correlation-ID", + "id-SourceMME-GUMMEI", + "id-MME-UE-S1AP-ID-2", + "id-RegisteredLAI", + "id-RelayNode-Indicator", + "id-TrafficLoadReductionIndication", + "id-MDTConfiguration", + "id-MMERelaySupportIndicator", + "id-GWContextReleaseIndication", + "id-ManagementBasedMDTAllowed", + "id-PrivacyIndicator", + "id-Time-UE-StayedInCell-EnhancedGranularity", + "id-HO-Cause", + "id-VoiceSupportMatchIndicator", + "id-GUMMEIType", + "id-M3Configuration", + "id-M4Configuration", + "id-M5Configuration", + "id-MDT-Location-Info", + "id-MobilityInformation", + "id-Tunnel-Information-for-BBF", + "id-ManagementBasedMDTPLMNList", + "id-SignallingBasedMDTPLMNList", + "id-ULCOUNTValueExtended", + "id-DLCOUNTValueExtended", + "id-ReceiveStatusOfULPDCPSDUsExtended", + "id-ECGIListForRestart", + "id-SIPTO-Correlation-ID", + "id-SIPTO-L-GW-TransportLayerAddress", + "id-TransportInformation", + "id-LHN-ID", + "id-AdditionalCSFallbackIndicator", + "id-TAIListForRestart", + "id-UserLocationInformation", + "id-EmergencyAreaIDListForRestart", + "id-KillAllWarningMessages", +}; + + +/******************************************************************************* +/* ProtocolIE Criticality ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_NOTIFY, + LIBLTE_S1AP_CRITICALITY_N_ITEMS, +}LIBLTE_S1AP_CRITICALITY_ENUM; +static const char liblte_s1ap_criticality_text[LIBLTE_S1AP_CRITICALITY_N_ITEMS][80] = { + "reject", + "ignore", + "notify", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticality( + LIBLTE_S1AP_CRITICALITY_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticality( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITY_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE local INTEGER +********************************************************************************/ +typedef struct{ +uint16_t local; +}LIBLTE_S1AP_LOCAL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_local( + LIBLTE_S1AP_LOCAL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_local( + uint8_t **ptr, + LIBLTE_S1AP_LOCAL_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PrivateIE_ID CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_LOCAL, + LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_GLOBAL, + LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_N_ITEMS, +}LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_ENUM; +static const char liblte_s1ap_privateie_id_choice_text[LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_N_ITEMS][50] = { + "local", + "global", +}; + +typedef union{ + LIBLTE_S1AP_LOCAL_STRUCT local; + LIBLTE_ASN1_OID_STRUCT global; +}LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_UNION; + +typedef struct{ + LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_UNION choice; + LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_PRIVATEIE_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_id( + LIBLTE_S1AP_PRIVATEIE_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_id( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionID INTEGER +********************************************************************************/ +typedef struct{ +uint16_t ProtocolExtensionID; +}LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensionid( + LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensionid( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TriggeringMessage ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_TRIGGERINGMESSAGE_INITIATING_MESSAGE, + LIBLTE_S1AP_TRIGGERINGMESSAGE_SUCCESSFUL_OUTCOME, + LIBLTE_S1AP_TRIGGERINGMESSAGE_UNSUCCESSFULL_OUTCOME, + LIBLTE_S1AP_TRIGGERINGMESSAGE_N_ITEMS, +}LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM; +static const char liblte_s1ap_triggeringmessage_text[LIBLTE_S1AP_TRIGGERINGMESSAGE_N_ITEMS][80] = { + "initiating-message", + "successful-outcome", + "unsuccessfull-outcome", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_triggeringmessage( + LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_triggeringmessage( + uint8_t **ptr, + LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE Presence ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PRESENCE_OPTIONAL, + LIBLTE_S1AP_PRESENCE_CONDITIONAL, + LIBLTE_S1AP_PRESENCE_MANDATORY, + LIBLTE_S1AP_PRESENCE_N_ITEMS, +}LIBLTE_S1AP_PRESENCE_ENUM; +static const char liblte_s1ap_presence_text[LIBLTE_S1AP_PRESENCE_N_ITEMS][80] = { + "optional", + "conditional", + "mandatory", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_presence( + LIBLTE_S1AP_PRESENCE_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_presence( + uint8_t **ptr, + LIBLTE_S1AP_PRESENCE_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ID INTEGER +********************************************************************************/ +typedef struct{ +uint16_t ProtocolIE_ID; +}LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_id( + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_id( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProcedureCode INTEGER +********************************************************************************/ +typedef struct{ +uint8_t ProcedureCode; +}LIBLTE_S1AP_PROCEDURECODE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_procedurecode( + LIBLTE_S1AP_PROCEDURECODE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_procedurecode( + uint8_t **ptr, + LIBLTE_S1AP_PROCEDURECODE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_Field SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT id; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT value; +}LIBLTE_S1AP_PROTOCOLIE_FIELD_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_field( + LIBLTE_S1AP_PROTOCOLIE_FIELD_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_field( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_FIELD_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionField SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT id; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT extensionValue; +}LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensionfield( + LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensionfield( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_FieldPair SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT id; + LIBLTE_S1AP_CRITICALITY_ENUM firstCriticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT firstValue; + LIBLTE_S1AP_CRITICALITY_ENUM secondCriticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT secondValue; +}LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_fieldpair( + LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_fieldpair( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionContainer DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensioncontainer( + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensioncontainer( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ContainerPair DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:0, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_containerpair( + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_containerpair( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ContainerPairList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:None, ub:None +typedef struct{ + uint32_t len; + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIRLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_containerpairlist( + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIRLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_containerpairlist( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIRLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PrivateIE_Field SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_PRIVATEIE_ID_STRUCT id; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT value; +}LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_field( + LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_field( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ProtocolIE_SingleContainer SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT id; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_ASN1_OPEN_TYPE_STRUCT value; +}LIBLTE_S1AP_PROTOCOLIE_SINGLECONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_singlecontainer( + LIBLTE_S1AP_PROTOCOLIE_SINGLECONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_singlecontainer( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_SINGLECONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PrivateIE_Container DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_container( + LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_container( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE BitRate INTEGER +********************************************************************************/ +typedef struct{ +uint32_t BitRate; +}LIBLTE_S1AP_BITRATE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bitrate( + LIBLTE_S1AP_BITRATE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bitrate( + uint8_t **ptr, + LIBLTE_S1AP_BITRATE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CauseMisc ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSEMISC_CONTROL_PROCESSING_OVERLOAD, + LIBLTE_S1AP_CAUSEMISC_NOT_ENOUGH_USER_PLANE_PROCESSING_RESOURCES, + LIBLTE_S1AP_CAUSEMISC_HARDWARE_FAILURE, + LIBLTE_S1AP_CAUSEMISC_OM_INTERVENTION, + LIBLTE_S1AP_CAUSEMISC_UNSPECIFIED, + LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN, + LIBLTE_S1AP_CAUSEMISC_N_ITEMS, +}LIBLTE_S1AP_CAUSEMISC_ENUM; +static const char liblte_s1ap_causemisc_text[LIBLTE_S1AP_CAUSEMISC_N_ITEMS][80] = { + "control-processing-overload", + "not-enough-user-plane-processing-resources", + "hardware-failure", + "om-intervention", + "unspecified", + "unknown-PLMN", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSEMISC_ENUM e; +}LIBLTE_S1AP_CAUSEMISC_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causemisc( + LIBLTE_S1AP_CAUSEMISC_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causemisc( + uint8_t **ptr, + LIBLTE_S1AP_CAUSEMISC_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CauseRadioNetwork ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED, + LIBLTE_S1AP_CAUSERADIONETWORK_TX2RELOCOVERALL_EXPIRY, + LIBLTE_S1AP_CAUSERADIONETWORK_SUCCESSFUL_HANDOVER, + LIBLTE_S1AP_CAUSERADIONETWORK_RELEASE_DUE_TO_EUTRAN_GENERATED_REASON, + LIBLTE_S1AP_CAUSERADIONETWORK_HANDOVER_CANCELLED, + LIBLTE_S1AP_CAUSERADIONETWORK_PARTIAL_HANDOVER, + LIBLTE_S1AP_CAUSERADIONETWORK_HO_FAILURE_IN_TARGET_EPC_ENB_OR_TARGET_SYSTEM, + LIBLTE_S1AP_CAUSERADIONETWORK_HO_TARGET_NOT_ALLOWED, + LIBLTE_S1AP_CAUSERADIONETWORK_TS1RELOCOVERALL_EXPIRY, + LIBLTE_S1AP_CAUSERADIONETWORK_TS1RELOCPREP_EXPIRY, + LIBLTE_S1AP_CAUSERADIONETWORK_CELL_NOT_AVAILABLE, + LIBLTE_S1AP_CAUSERADIONETWORK_UNKNOWN_TARGETID, + LIBLTE_S1AP_CAUSERADIONETWORK_NO_RADIO_RESOURCES_AVAILABLE_IN_TARGET_CELL, + LIBLTE_S1AP_CAUSERADIONETWORK_UNKNOWN_MME_UE_S1AP_ID, + LIBLTE_S1AP_CAUSERADIONETWORK_UNKNOWN_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CAUSERADIONETWORK_UNKNOWN_PAIR_UE_S1AP_ID, + LIBLTE_S1AP_CAUSERADIONETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON, + LIBLTE_S1AP_CAUSERADIONETWORK_TIME_CRITICAL_HANDOVER, + LIBLTE_S1AP_CAUSERADIONETWORK_RESOURCE_OPTIMISATION_HANDOVER, + LIBLTE_S1AP_CAUSERADIONETWORK_REDUCE_LOAD_IN_SERVING_CELL, + LIBLTE_S1AP_CAUSERADIONETWORK_USER_INACTIVITY, + LIBLTE_S1AP_CAUSERADIONETWORK_RADIO_CONNECTION_WITH_UE_LOST, + LIBLTE_S1AP_CAUSERADIONETWORK_LOAD_BALANCING_TAU_REQUIRED, + LIBLTE_S1AP_CAUSERADIONETWORK_CS_FALLBACK_TRIGGERED, + LIBLTE_S1AP_CAUSERADIONETWORK_UE_NOT_AVAILABLE_FOR_PS_SERVICE, + LIBLTE_S1AP_CAUSERADIONETWORK_RADIO_RESOURCES_NOT_AVAILABLE, + LIBLTE_S1AP_CAUSERADIONETWORK_FAILURE_IN_RADIO_INTERFACE_PROCEDURE, + LIBLTE_S1AP_CAUSERADIONETWORK_INVALID_QOS_COMBINATION, + LIBLTE_S1AP_CAUSERADIONETWORK_INTERRAT_REDIRECTION, + LIBLTE_S1AP_CAUSERADIONETWORK_INTERACTION_WITH_OTHER_PROCEDURE, + LIBLTE_S1AP_CAUSERADIONETWORK_UNKNOWN_E_RAB_ID, + LIBLTE_S1AP_CAUSERADIONETWORK_MULTIPLE_E_RAB_ID_INSTANCES, + LIBLTE_S1AP_CAUSERADIONETWORK_ENCRYPTION_AND_OR_INTEGRITY_PROTECTION_ALGORITHMS_NOT_SUPPORTED, + LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED, + LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTER_SYSTEM_HANDOVER_TRIGGERED, + LIBLTE_S1AP_CAUSERADIONETWORK_X2_HANDOVER_TRIGGERED, + LIBLTE_S1AP_CAUSERADIONETWORK_REDIRECTION_TOWARDS_1XRTT, + LIBLTE_S1AP_CAUSERADIONETWORK_NOT_SUPPORTED_QCI_VALUE, + LIBLTE_S1AP_CAUSERADIONETWORK_INVALID_CSG_ID, + LIBLTE_S1AP_CAUSERADIONETWORK_N_ITEMS, +}LIBLTE_S1AP_CAUSERADIONETWORK_ENUM; +static const char liblte_s1ap_causeradionetwork_text[LIBLTE_S1AP_CAUSERADIONETWORK_N_ITEMS][80] = { + "unspecified", + "tx2relocoverall-expiry", + "successful-handover", + "release-due-to-eutran-generated-reason", + "handover-cancelled", + "partial-handover", + "ho-failure-in-target-EPC-eNB-or-target-system", + "ho-target-not-allowed", + "tS1relocoverall-expiry", + "tS1relocprep-expiry", + "cell-not-available", + "unknown-targetID", + "no-radio-resources-available-in-target-cell", + "unknown-mme-ue-s1ap-id", + "unknown-enb-ue-s1ap-id", + "unknown-pair-ue-s1ap-id", + "handover-desirable-for-radio-reason", + "time-critical-handover", + "resource-optimisation-handover", + "reduce-load-in-serving-cell", + "user-inactivity", + "radio-connection-with-ue-lost", + "load-balancing-tau-required", + "cs-fallback-triggered", + "ue-not-available-for-ps-service", + "radio-resources-not-available", + "failure-in-radio-interface-procedure", + "invalid-qos-combination", + "interrat-redirection", + "interaction-with-other-procedure", + "unknown-E-RAB-ID", + "multiple-E-RAB-ID-instances", + "encryption-and-or-integrity-protection-algorithms-not-supported", + "s1-intra-system-handover-triggered", + "s1-inter-system-handover-triggered", + "x2-handover-triggered", + "redirection-towards-1xRTT", + "not-supported-QCI-value", + "invalid-CSG-Id", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM e; +}LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causeradionetwork( + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causeradionetwork( + uint8_t **ptr, + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CauseNas ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSENAS_NORMAL_RELEASE, + LIBLTE_S1AP_CAUSENAS_AUTHENTICATION_FAILURE, + LIBLTE_S1AP_CAUSENAS_DETACH, + LIBLTE_S1AP_CAUSENAS_UNSPECIFIED, + LIBLTE_S1AP_CAUSENAS_CSG_SUBSCRIPTION_EXPIRY, + LIBLTE_S1AP_CAUSENAS_N_ITEMS, +}LIBLTE_S1AP_CAUSENAS_ENUM; +static const char liblte_s1ap_causenas_text[LIBLTE_S1AP_CAUSENAS_N_ITEMS][80] = { + "normal-release", + "authentication-failure", + "detach", + "unspecified", + "csg-subscription-expiry", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSENAS_ENUM e; +}LIBLTE_S1AP_CAUSENAS_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causenas( + LIBLTE_S1AP_CAUSENAS_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causenas( + uint8_t **ptr, + LIBLTE_S1AP_CAUSENAS_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CellIdentity STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_CELLIDENTITY_BIT_STRING_LEN 28 +typedef struct{ + uint8_t buffer[28]; +}LIBLTE_S1AP_CELLIDENTITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellidentity( + LIBLTE_S1AP_CELLIDENTITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellidentity( + uint8_t **ptr, + LIBLTE_S1AP_CELLIDENTITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000PDU DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000PDU_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000pdu( + LIBLTE_S1AP_CDMA2000PDU_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000pdu( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000PDU_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000SectorID DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000SECTORID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000sectorid( + LIBLTE_S1AP_CDMA2000SECTORID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000sectorid( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000SECTORID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000HORequiredIndication ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_TRUE, + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_N_ITEMS, +}LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM; +static const char liblte_s1ap_cdma2000horequiredindication_text[LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_N_ITEMS][80] = { + "true", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM e; +}LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000horequiredindication( + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000horequiredindication( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXMSI DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexmsi( + LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexmsi( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXRAND DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexrand( + LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexrand( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CNDomain ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CNDOMAIN_PS, + LIBLTE_S1AP_CNDOMAIN_CS, + LIBLTE_S1AP_CNDOMAIN_N_ITEMS, +}LIBLTE_S1AP_CNDOMAIN_ENUM; +static const char liblte_s1ap_cndomain_text[LIBLTE_S1AP_CNDOMAIN_N_ITEMS][80] = { + "ps", + "cs", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cndomain( + LIBLTE_S1AP_CNDOMAIN_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cndomain( + uint8_t **ptr, + LIBLTE_S1AP_CNDOMAIN_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE Correlation_ID STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_CORRELATION_ID_OCTET_STRING_LEN 4 +typedef struct{ + uint8_t buffer[4]; +}LIBLTE_S1AP_CORRELATION_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_correlation_id( + LIBLTE_S1AP_CORRELATION_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_correlation_id( + uint8_t **ptr, + LIBLTE_S1AP_CORRELATION_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE AdditionalCSFallbackIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_NO_RESTRICTION, + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_RESTRICTION, + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_N_ITEMS, +}LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM; +static const char liblte_s1ap_additionalcsfallbackindicator_text[LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_N_ITEMS][80] = { + "no-restriction", + "restriction", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM e; +}LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_additionalcsfallbackindicator( + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_additionalcsfallbackindicator( + uint8_t **ptr, + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE DL_Forwarding ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_DL_FORWARDING_DL_FORWARDING_PROPOSED, + LIBLTE_S1AP_DL_FORWARDING_N_ITEMS, +}LIBLTE_S1AP_DL_FORWARDING_ENUM; +static const char liblte_s1ap_dl_forwarding_text[LIBLTE_S1AP_DL_FORWARDING_N_ITEMS][80] = { + "dL-Forwarding-proposed", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_DL_FORWARDING_ENUM e; +}LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_dl_forwarding( + LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_dl_forwarding( + uint8_t **ptr, + LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Data_Forwarding_Not_Possible ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_DATA_FORWARDING_NOT_POSSIBLE, + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_N_ITEMS, +}LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM; +static const char liblte_s1ap_data_forwarding_not_possible_text[LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_N_ITEMS][80] = { + "data-Forwarding-not-Possible", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM e; +}LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_data_forwarding_not_possible( + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_data_forwarding_not_possible( + uint8_t **ptr, + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_EMERGENCYAREAID_OCTET_STRING_LEN 3 +typedef struct{ + uint8_t buffer[3]; +}LIBLTE_S1AP_EMERGENCYAREAID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid( + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE macroENB_ID STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN 20 +typedef struct{ + uint8_t buffer[20]; +}LIBLTE_S1AP_MACROENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_macroenb_id( + LIBLTE_S1AP_MACROENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_macroenb_id( + uint8_t **ptr, + LIBLTE_S1AP_MACROENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE homeENB_ID STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_HOMEENB_ID_BIT_STRING_LEN 28 +typedef struct{ + uint8_t buffer[28]; +}LIBLTE_S1AP_HOMEENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_homeenb_id( + LIBLTE_S1AP_HOMEENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_homeenb_id( + uint8_t **ptr, + LIBLTE_S1AP_HOMEENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENB_ID CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID, + LIBLTE_S1AP_ENB_ID_CHOICE_HOMEENB_ID, + LIBLTE_S1AP_ENB_ID_CHOICE_N_ITEMS, +}LIBLTE_S1AP_ENB_ID_CHOICE_ENUM; +static const char liblte_s1ap_enb_id_choice_text[LIBLTE_S1AP_ENB_ID_CHOICE_N_ITEMS][50] = { + "macroENB_ID", + "homeENB_ID", +}; + +typedef union{ + LIBLTE_S1AP_MACROENB_ID_STRUCT macroENB_ID; + LIBLTE_S1AP_HOMEENB_ID_STRUCT homeENB_ID; +}LIBLTE_S1AP_ENB_ID_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_ENB_ID_CHOICE_UNION choice; + LIBLTE_S1AP_ENB_ID_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_ENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_id( + LIBLTE_S1AP_ENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_id( + uint8_t **ptr, + LIBLTE_S1AP_ENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENBname PrintableString +********************************************************************************/ +typedef struct{ + bool ext; + uint32_t n_octets; + uint8_t buffer[150]; +}LIBLTE_S1AP_ENBNAME_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbname( + LIBLTE_S1AP_ENBNAME_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbname( + uint8_t **ptr, + LIBLTE_S1AP_ENBNAME_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EncryptionAlgorithms STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_ENCRYPTIONALGORITHMS_BIT_STRING_LEN 16 +typedef struct{ + bool ext; + uint8_t buffer[16]; +}LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_encryptionalgorithms( + LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_encryptionalgorithms( + uint8_t **ptr, + LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EventType ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_EVENTTYPE_DIRECT, + LIBLTE_S1AP_EVENTTYPE_CHANGE_OF_SERVE_CELL, + LIBLTE_S1AP_EVENTTYPE_STOP_CHANGE_OF_SERVE_CELL, + LIBLTE_S1AP_EVENTTYPE_N_ITEMS, +}LIBLTE_S1AP_EVENTTYPE_ENUM; +static const char liblte_s1ap_eventtype_text[LIBLTE_S1AP_EVENTTYPE_N_ITEMS][80] = { + "direct", + "change-of-serve-cell", + "stop-change-of-serve-cell", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_EVENTTYPE_ENUM e; +}LIBLTE_S1AP_EVENTTYPE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eventtype( + LIBLTE_S1AP_EVENTTYPE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eventtype( + uint8_t **ptr, + LIBLTE_S1AP_EVENTTYPE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE ExtendedRNC_ID INTEGER +********************************************************************************/ +typedef struct{ +uint16_t ExtendedRNC_ID; +}LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_extendedrnc_id( + LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_extendedrnc_id( + uint8_t **ptr, + LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenInterRATs ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_FORBIDDENINTERRATS_ALL, + LIBLTE_S1AP_FORBIDDENINTERRATS_GERAN, + LIBLTE_S1AP_FORBIDDENINTERRATS_UTRAN, + LIBLTE_S1AP_FORBIDDENINTERRATS_CDMA2000, + LIBLTE_S1AP_FORBIDDENINTERRATS_GERANANDUTRAN, + LIBLTE_S1AP_FORBIDDENINTERRATS_CDMA2000ANDUTRAN, + LIBLTE_S1AP_FORBIDDENINTERRATS_N_ITEMS, +}LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM; +static const char liblte_s1ap_forbiddeninterrats_text[LIBLTE_S1AP_FORBIDDENINTERRATS_N_ITEMS][80] = { + "all", + "geran", + "utran", + "cdma2000", + "geranandutran", + "cdma2000andutran", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM e; +}LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddeninterrats( + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddeninterrats( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE GWContextReleaseIndication ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_TRUE, + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_N_ITEMS, +}LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM; +static const char liblte_s1ap_gwcontextreleaseindication_text[LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_N_ITEMS][80] = { + "true", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM e; +}LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gwcontextreleaseindication( + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gwcontextreleaseindication( + uint8_t **ptr, + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE HFN INTEGER +********************************************************************************/ +typedef struct{ +uint32_t HFN; +}LIBLTE_S1AP_HFN_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_hfn( + LIBLTE_S1AP_HFN_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_hfn( + uint8_t **ptr, + LIBLTE_S1AP_HFN_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE IMSI DYNAMIC OCTET STRING +********************************************************************************/ +// lb:3, ub:8 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[8]; +}LIBLTE_S1AP_IMSI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_imsi( + LIBLTE_S1AP_IMSI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_imsi( + uint8_t **ptr, + LIBLTE_S1AP_IMSI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE InterfacesToTrace STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_INTERFACESTOTRACE_BIT_STRING_LEN 8 +typedef struct{ + uint8_t buffer[8]; +}LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_interfacestotrace( + LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_interfacestotrace( + uint8_t **ptr, + LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LAC STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_LAC_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_LAC_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lac( + LIBLTE_S1AP_LAC_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lac( + uint8_t **ptr, + LIBLTE_S1AP_LAC_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LastVisitedUTRANCellInformation DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedutrancellinformation( + LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedutrancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE L3_Information DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_L3_INFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_l3_information( + LIBLTE_S1AP_L3_INFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_l3_information( + uint8_t **ptr, + LIBLTE_S1AP_L3_INFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LHN_ID DYNAMIC OCTET STRING +********************************************************************************/ +// lb:32, ub:256 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[256]; +}LIBLTE_S1AP_LHN_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lhn_id( + LIBLTE_S1AP_LHN_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lhn_id( + uint8_t **ptr, + LIBLTE_S1AP_LHN_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LoggingDuration ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_LOGGINGDURATION_M10, + LIBLTE_S1AP_LOGGINGDURATION_M20, + LIBLTE_S1AP_LOGGINGDURATION_M40, + LIBLTE_S1AP_LOGGINGDURATION_M60, + LIBLTE_S1AP_LOGGINGDURATION_M90, + LIBLTE_S1AP_LOGGINGDURATION_M120, + LIBLTE_S1AP_LOGGINGDURATION_N_ITEMS, +}LIBLTE_S1AP_LOGGINGDURATION_ENUM; +static const char liblte_s1ap_loggingduration_text[LIBLTE_S1AP_LOGGINGDURATION_N_ITEMS][80] = { + "m10", + "m20", + "m40", + "m60", + "m90", + "m120", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggingduration( + LIBLTE_S1AP_LOGGINGDURATION_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggingduration( + uint8_t **ptr, + LIBLTE_S1AP_LOGGINGDURATION_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE MDT_Activation ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_MDT_ACTIVATION_IMMEDIATE_MDT_ONLY, + LIBLTE_S1AP_MDT_ACTIVATION_IMMEDIATE_MDT_AND_TRACE, + LIBLTE_S1AP_MDT_ACTIVATION_LOGGED_MDT_ONLY, + LIBLTE_S1AP_MDT_ACTIVATION_N_ITEMS, +}LIBLTE_S1AP_MDT_ACTIVATION_ENUM; +static const char liblte_s1ap_mdt_activation_text[LIBLTE_S1AP_MDT_ACTIVATION_N_ITEMS][80] = { + "immediate-MDT-only", + "immediate-MDT-and-Trace", + "logged-MDT-only", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_MDT_ACTIVATION_ENUM e; +}LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_activation( + LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_activation( + uint8_t **ptr, + LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE ManagementBasedMDTAllowed ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ALLOWED, + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_N_ITEMS, +}LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM; +static const char liblte_s1ap_managementbasedmdtallowed_text[LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_N_ITEMS][80] = { + "allowed", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM e; +}LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_managementbasedmdtallowed( + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_managementbasedmdtallowed( + uint8_t **ptr, + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE PrivacyIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PRIVACYINDICATOR_IMMEDIATE_MDT, + LIBLTE_S1AP_PRIVACYINDICATOR_LOGGED_MDT, + LIBLTE_S1AP_PRIVACYINDICATOR_N_ITEMS, +}LIBLTE_S1AP_PRIVACYINDICATOR_ENUM; +static const char liblte_s1ap_privacyindicator_text[LIBLTE_S1AP_PRIVACYINDICATOR_N_ITEMS][80] = { + "immediate-MDT", + "logged-MDT", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM e; +}LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privacyindicator( + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privacyindicator( + uint8_t **ptr, + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE MeasurementsToActivate STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_BIT_STRING_LEN 8 +typedef struct{ + uint8_t buffer[8]; +}LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_measurementstoactivate( + LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_measurementstoactivate( + uint8_t **ptr, + LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MessageIdentifier STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_MESSAGEIDENTIFIER_BIT_STRING_LEN 16 +typedef struct{ + uint8_t buffer[16]; +}LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_messageidentifier( + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_messageidentifier( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MMEname PrintableString +********************************************************************************/ +typedef struct{ + bool ext; + uint32_t n_octets; + uint8_t buffer[150]; +}LIBLTE_S1AP_MMENAME_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmename( + LIBLTE_S1AP_MMENAME_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmename( + uint8_t **ptr, + LIBLTE_S1AP_MMENAME_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MME_Group_ID STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_MME_GROUP_ID_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_MME_GROUP_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_group_id( + LIBLTE_S1AP_MME_GROUP_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_group_id( + uint8_t **ptr, + LIBLTE_S1AP_MME_GROUP_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MME_UE_S1AP_ID INTEGER +********************************************************************************/ +typedef struct{ +uint32_t MME_UE_S1AP_ID; +}LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_ue_s1ap_id( + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_ue_s1ap_id( + uint8_t **ptr, + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MSClassmark2 DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_MSCLASSMARK2_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_msclassmark2( + LIBLTE_S1AP_MSCLASSMARK2_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_msclassmark2( + uint8_t **ptr, + LIBLTE_S1AP_MSCLASSMARK2_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE NAS_PDU DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_NAS_PDU_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nas_pdu( + LIBLTE_S1AP_NAS_PDU_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nas_pdu( + uint8_t **ptr, + LIBLTE_S1AP_NAS_PDU_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE NASSecurityParameterstoE_UTRAN DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nassecurityparameterstoe_utran( + LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nassecurityparameterstoe_utran( + uint8_t **ptr, + LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE NumberOfBroadcasts INTEGER +********************************************************************************/ +typedef struct{ +uint16_t NumberOfBroadcasts; +}LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_numberofbroadcasts( + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_numberofbroadcasts( + uint8_t **ptr, + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE OverloadAction ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_OVERLOADACTION_REJECT_NON_EMERGENCY_MO_DT, + LIBLTE_S1AP_OVERLOADACTION_REJECT_RRC_CR_SIGNALLING, + LIBLTE_S1AP_OVERLOADACTION_PERMIT_EMERGENCY_SESSIONS_AND_MOBILE_TERMINATED_SERVICES_ONLY, + LIBLTE_S1AP_OVERLOADACTION_PERMIT_HIGH_PRIORITY_SESSIONS_AND_MOBILE_TERMINATED_SERVICES_ONLY, + LIBLTE_S1AP_OVERLOADACTION_REJECT_DELAY_TOLERANT_ACCESS, + LIBLTE_S1AP_OVERLOADACTION_N_ITEMS, +}LIBLTE_S1AP_OVERLOADACTION_ENUM; +static const char liblte_s1ap_overloadaction_text[LIBLTE_S1AP_OVERLOADACTION_N_ITEMS][80] = { + "reject-non-emergency-mo-dt", + "reject-rrc-cr-signalling", + "permit-emergency-sessions-and-mobile-terminated-services-only", + "permit-high-priority-sessions-and-mobile-terminated-services-only", + "reject-delay-tolerant-access", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_OVERLOADACTION_ENUM e; +}LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadaction( + LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadaction( + uint8_t **ptr, + LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE PagingDRX ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PAGINGDRX_V32, + LIBLTE_S1AP_PAGINGDRX_V64, + LIBLTE_S1AP_PAGINGDRX_V128, + LIBLTE_S1AP_PAGINGDRX_V256, + LIBLTE_S1AP_PAGINGDRX_N_ITEMS, +}LIBLTE_S1AP_PAGINGDRX_ENUM; +static const char liblte_s1ap_pagingdrx_text[LIBLTE_S1AP_PAGINGDRX_N_ITEMS][80] = { + "v32", + "v64", + "v128", + "v256", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_PAGINGDRX_ENUM e; +}LIBLTE_S1AP_PAGINGDRX_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pagingdrx( + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pagingdrx( + uint8_t **ptr, + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE PDCP_SN INTEGER +********************************************************************************/ +typedef struct{ +uint16_t PDCP_SN; +}LIBLTE_S1AP_PDCP_SN_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pdcp_sn( + LIBLTE_S1AP_PDCP_SN_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pdcp_sn( + uint8_t **ptr, + LIBLTE_S1AP_PDCP_SN_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Port_Number STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_PORT_NUMBER_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_PORT_NUMBER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_port_number( + LIBLTE_S1AP_PORT_NUMBER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_port_number( + uint8_t **ptr, + LIBLTE_S1AP_PORT_NUMBER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Pre_emptionVulnerability ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_NOT_PRE_EMPTABLE, + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE, + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_N_ITEMS, +}LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM; +static const char liblte_s1ap_pre_emptionvulnerability_text[LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_N_ITEMS][80] = { + "not-pre-emptable", + "pre-emptable", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pre_emptionvulnerability( + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pre_emptionvulnerability( + uint8_t **ptr, + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE PS_ServiceNotAvailable ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_PS_SERVICE_NOT_AVAILABLE, + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_N_ITEMS, +}LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM; +static const char liblte_s1ap_ps_servicenotavailable_text[LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_N_ITEMS][80] = { + "ps-service-not-available", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM e; +}LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ps_servicenotavailable( + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ps_servicenotavailable( + uint8_t **ptr, + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE ReceiveStatusofULPDCPSDUs STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_BIT_STRING_LEN 4096 +typedef struct{ + uint8_t buffer[4096]; +}LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_receivestatusofulpdcpsdus( + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_receivestatusofulpdcpsdus( + uint8_t **ptr, + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RelativeMMECapacity INTEGER +********************************************************************************/ +typedef struct{ +uint8_t RelativeMMECapacity; +}LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_relativemmecapacity( + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_relativemmecapacity( + uint8_t **ptr, + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RAC STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_RAC_OCTET_STRING_LEN 1 +typedef struct{ + uint8_t buffer[1]; +}LIBLTE_S1AP_RAC_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rac( + LIBLTE_S1AP_RAC_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rac( + uint8_t **ptr, + LIBLTE_S1AP_RAC_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ReportIntervalMDT ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_REPORTINTERVALMDT_MS120, + LIBLTE_S1AP_REPORTINTERVALMDT_MS240, + LIBLTE_S1AP_REPORTINTERVALMDT_MS480, + LIBLTE_S1AP_REPORTINTERVALMDT_MS640, + LIBLTE_S1AP_REPORTINTERVALMDT_MS1024, + LIBLTE_S1AP_REPORTINTERVALMDT_MS2048, + LIBLTE_S1AP_REPORTINTERVALMDT_MS5120, + LIBLTE_S1AP_REPORTINTERVALMDT_MS10240, + LIBLTE_S1AP_REPORTINTERVALMDT_MIN1, + LIBLTE_S1AP_REPORTINTERVALMDT_MIN6, + LIBLTE_S1AP_REPORTINTERVALMDT_MIN12, + LIBLTE_S1AP_REPORTINTERVALMDT_MIN30, + LIBLTE_S1AP_REPORTINTERVALMDT_MIN60, + LIBLTE_S1AP_REPORTINTERVALMDT_N_ITEMS, +}LIBLTE_S1AP_REPORTINTERVALMDT_ENUM; +static const char liblte_s1ap_reportintervalmdt_text[LIBLTE_S1AP_REPORTINTERVALMDT_N_ITEMS][80] = { + "ms120", + "ms240", + "ms480", + "ms640", + "ms1024", + "ms2048", + "ms5120", + "ms10240", + "min1", + "min6", + "min12", + "min30", + "min60", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportintervalmdt( + LIBLTE_S1AP_REPORTINTERVALMDT_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportintervalmdt( + uint8_t **ptr, + LIBLTE_S1AP_REPORTINTERVALMDT_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE ReportArea ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_REPORTAREA_ECGI, + LIBLTE_S1AP_REPORTAREA_N_ITEMS, +}LIBLTE_S1AP_REPORTAREA_ENUM; +static const char liblte_s1ap_reportarea_text[LIBLTE_S1AP_REPORTAREA_N_ITEMS][80] = { + "ecgi", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_REPORTAREA_ENUM e; +}LIBLTE_S1AP_REPORTAREA_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportarea( + LIBLTE_S1AP_REPORTAREA_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportarea( + uint8_t **ptr, + LIBLTE_S1AP_REPORTAREA_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE RNC_ID INTEGER +********************************************************************************/ +typedef struct{ +uint16_t RNC_ID; +}LIBLTE_S1AP_RNC_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rnc_id( + LIBLTE_S1AP_RNC_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rnc_id( + uint8_t **ptr, + LIBLTE_S1AP_RNC_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RRC_Establishment_Cause ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_EMERGENCY, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_HIGHPRIORITYACCESS, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_MT_ACCESS, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_MO_SIGNALLING, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_MO_DATA, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_DELAY_TOLERANTACCESS, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_N_ITEMS, +}LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM; +static const char liblte_s1ap_rrc_establishment_cause_text[LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_N_ITEMS][80] = { + "emergency", + "highPriorityAccess", + "mt-Access", + "mo-Signalling", + "mo-Data", + "delay-TolerantAccess", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM e; +}LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rrc_establishment_cause( + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rrc_establishment_cause( + uint8_t **ptr, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Routing_ID INTEGER +********************************************************************************/ +typedef struct{ +uint8_t Routing_ID; +}LIBLTE_S1AP_ROUTING_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_routing_id( + LIBLTE_S1AP_ROUTING_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_routing_id( + uint8_t **ptr, + LIBLTE_S1AP_ROUTING_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SONInformationRequest ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SONINFORMATIONREQUEST_X2TNL_CONFIGURATION_INFO, + LIBLTE_S1AP_SONINFORMATIONREQUEST_TIME_SYNCHRONIZATION_INFO, + LIBLTE_S1AP_SONINFORMATIONREQUEST_N_ITEMS, +}LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM; +static const char liblte_s1ap_soninformationrequest_text[LIBLTE_S1AP_SONINFORMATIONREQUEST_N_ITEMS][80] = { + "x2TNL-Configuration-Info", + "time-Synchronization-Info", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM e; +}LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationrequest( + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationrequest( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Source_ToTarget_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_source_totarget_transparentcontainer( + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_source_totarget_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SRVCCHOIndication ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SRVCCHOINDICATION_PSANDCS, + LIBLTE_S1AP_SRVCCHOINDICATION_CSONLY, + LIBLTE_S1AP_SRVCCHOINDICATION_N_ITEMS, +}LIBLTE_S1AP_SRVCCHOINDICATION_ENUM; +static const char liblte_s1ap_srvcchoindication_text[LIBLTE_S1AP_SRVCCHOINDICATION_N_ITEMS][80] = { + "pSandCS", + "cSonly", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM e; +}LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_srvcchoindication( + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_srvcchoindication( + uint8_t **ptr, + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE SourceRNC_ToTargetRNC_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_SOURCERNC_TOTARGETRNC_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourcernc_totargetrnc_transparentcontainer( + LIBLTE_S1AP_SOURCERNC_TOTARGETRNC_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourcernc_totargetrnc_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCERNC_TOTARGETRNC_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SubscriberProfileIDforRFP INTEGER +********************************************************************************/ +typedef struct{ +uint8_t SubscriberProfileIDforRFP; +}LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_subscriberprofileidforrfp( + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_subscriberprofileidforrfp( + uint8_t **ptr, + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SynchronizationStatus ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_SYNCHRONOUS, + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ASYNCHRONOUS, + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_N_ITEMS, +}LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM; +static const char liblte_s1ap_synchronizationstatus_text[LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_N_ITEMS][80] = { + "synchronous", + "asynchronous", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM e; +}LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_synchronizationstatus( + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_synchronizationstatus( + uint8_t **ptr, + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE TargetRNC_ToSourceRNC_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_TARGETRNC_TOSOURCERNC_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_tosourcernc_transparentcontainer( + LIBLTE_S1AP_TARGETRNC_TOSOURCERNC_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_tosourcernc_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETRNC_TOSOURCERNC_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Threshold_RSRQ INTEGER +********************************************************************************/ +typedef struct{ +uint8_t Threshold_RSRQ; +}LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_threshold_rsrq( + LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_threshold_rsrq( + uint8_t **ptr, + LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Time_UE_StayedInCell INTEGER +********************************************************************************/ +typedef struct{ +uint16_t Time_UE_StayedInCell; +}LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_time_ue_stayedincell( + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_time_ue_stayedincell( + uint8_t **ptr, + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TransportLayerAddress DYNAMIC BIT STRING +********************************************************************************/ +// lb:1, ub:160 +typedef struct{ + bool ext; + uint32_t n_bits; + uint8_t buffer[160]; +}LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_transportlayeraddress( + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_transportlayeraddress( + uint8_t **ptr, + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TraceDepth ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_TRACEDEPTH_MINIMUM, + LIBLTE_S1AP_TRACEDEPTH_MEDIUM, + LIBLTE_S1AP_TRACEDEPTH_MAXIMUM, + LIBLTE_S1AP_TRACEDEPTH_MINIMUMWITHOUTVENDORSPECIFICEXTENSION, + LIBLTE_S1AP_TRACEDEPTH_MEDIUMWITHOUTVENDORSPECIFICEXTENSION, + LIBLTE_S1AP_TRACEDEPTH_MAXIMUMWITHOUTVENDORSPECIFICEXTENSION, + LIBLTE_S1AP_TRACEDEPTH_N_ITEMS, +}LIBLTE_S1AP_TRACEDEPTH_ENUM; +static const char liblte_s1ap_tracedepth_text[LIBLTE_S1AP_TRACEDEPTH_N_ITEMS][80] = { + "minimum", + "medium", + "maximum", + "minimumWithoutVendorSpecificExtension", + "mediumWithoutVendorSpecificExtension", + "maximumWithoutVendorSpecificExtension", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_TRACEDEPTH_ENUM e; +}LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracedepth( + LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracedepth( + uint8_t **ptr, + LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE TrafficLoadReductionIndication INTEGER +********************************************************************************/ +typedef struct{ +uint8_t TrafficLoadReductionIndication; +}LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_trafficloadreductionindication( + LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_trafficloadreductionindication( + uint8_t **ptr, + LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UERadioCapability DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapability( + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapability( + uint8_t **ptr, + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE WarningType STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_WARNINGTYPE_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_WARNINGTYPE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningtype( + LIBLTE_S1AP_WARNINGTYPE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningtype( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGTYPE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE WarningMessageContents DYNAMIC OCTET STRING +********************************************************************************/ +// lb:1, ub:9600 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[9600]; +}LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningmessagecontents( + LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningmessagecontents( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CauseProtocol ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSEPROTOCOL_TRANSFER_SYNTAX_ERROR, + LIBLTE_S1AP_CAUSEPROTOCOL_ABSTRACT_SYNTAX_ERROR_REJECT, + LIBLTE_S1AP_CAUSEPROTOCOL_ABSTRACT_SYNTAX_ERROR_IGNORE_AND_NOTIFY, + LIBLTE_S1AP_CAUSEPROTOCOL_MESSAGE_NOT_COMPATIBLE_WITH_RECEIVER_STATE, + LIBLTE_S1AP_CAUSEPROTOCOL_SEMANTIC_ERROR, + LIBLTE_S1AP_CAUSEPROTOCOL_ABSTRACT_SYNTAX_ERROR_FALSELY_CONSTRUCTED_MESSAGE, + LIBLTE_S1AP_CAUSEPROTOCOL_UNSPECIFIED, + LIBLTE_S1AP_CAUSEPROTOCOL_N_ITEMS, +}LIBLTE_S1AP_CAUSEPROTOCOL_ENUM; +static const char liblte_s1ap_causeprotocol_text[LIBLTE_S1AP_CAUSEPROTOCOL_N_ITEMS][80] = { + "transfer-syntax-error", + "abstract-syntax-error-reject", + "abstract-syntax-error-ignore-and-notify", + "message-not-compatible-with-receiver-state", + "semantic-error", + "abstract-syntax-error-falsely-constructed-message", + "unspecified", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM e; +}LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causeprotocol( + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causeprotocol( + uint8_t **ptr, + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CellAccessMode ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CELLACCESSMODE_HYBRID, + LIBLTE_S1AP_CELLACCESSMODE_N_ITEMS, +}LIBLTE_S1AP_CELLACCESSMODE_ENUM; +static const char liblte_s1ap_cellaccessmode_text[LIBLTE_S1AP_CELLACCESSMODE_N_ITEMS][80] = { + "hybrid", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CELLACCESSMODE_ENUM e; +}LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellaccessmode( + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellaccessmode( + uint8_t **ptr, + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000RATType ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CDMA2000RATTYPE_HRPD, + LIBLTE_S1AP_CDMA2000RATTYPE_ONEXRTT, + LIBLTE_S1AP_CDMA2000RATTYPE_N_ITEMS, +}LIBLTE_S1AP_CDMA2000RATTYPE_ENUM; +static const char liblte_s1ap_cdma2000rattype_text[LIBLTE_S1AP_CDMA2000RATTYPE_N_ITEMS][80] = { + "hRPD", + "onexRTT", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM e; +}LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000rattype( + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000rattype( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXMEID DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexmeid( + LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexmeid( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cell_Size ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CELL_SIZE_VERYSMALL, + LIBLTE_S1AP_CELL_SIZE_SMALL, + LIBLTE_S1AP_CELL_SIZE_MEDIUM, + LIBLTE_S1AP_CELL_SIZE_LARGE, + LIBLTE_S1AP_CELL_SIZE_N_ITEMS, +}LIBLTE_S1AP_CELL_SIZE_ENUM; +static const char liblte_s1ap_cell_size_text[LIBLTE_S1AP_CELL_SIZE_N_ITEMS][80] = { + "verysmall", + "small", + "medium", + "large", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CELL_SIZE_ENUM e; +}LIBLTE_S1AP_CELL_SIZE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cell_size( + LIBLTE_S1AP_CELL_SIZE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cell_size( + uint8_t **ptr, + LIBLTE_S1AP_CELL_SIZE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CI STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_CI_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_CI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ci( + LIBLTE_S1AP_CI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ci( + uint8_t **ptr, + LIBLTE_S1AP_CI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CSFallbackIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_REQUIRED, + LIBLTE_S1AP_CSFALLBACKINDICATOR_CS_FALLBACK_HIGH_PRIORITY, + LIBLTE_S1AP_CSFALLBACKINDICATOR_N_ITEMS, +}LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM; +static const char liblte_s1ap_csfallbackindicator_text[LIBLTE_S1AP_CSFALLBACKINDICATOR_N_ITEMS][80] = { + "cs-fallback-required", + "cs-fallback-high-priority", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM e; +}LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csfallbackindicator( + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csfallbackindicator( + uint8_t **ptr, + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CSGMembershipStatus ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_MEMBER, + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_NOT_MEMBER, + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_N_ITEMS, +}LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM; +static const char liblte_s1ap_csgmembershipstatus_text[LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_N_ITEMS][80] = { + "member", + "not-member", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csgmembershipstatus( + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csgmembershipstatus( + uint8_t **ptr, + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE DataCodingScheme STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_DATACODINGSCHEME_BIT_STRING_LEN 8 +typedef struct{ + uint8_t buffer[8]; +}LIBLTE_S1AP_DATACODINGSCHEME_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_datacodingscheme( + LIBLTE_S1AP_DATACODINGSCHEME_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_datacodingscheme( + uint8_t **ptr, + LIBLTE_S1AP_DATACODINGSCHEME_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaIDList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaidlist( + LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaidlist( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaIDListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaidlistforrestart( + LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaidlistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENB_UE_S1AP_ID INTEGER +********************************************************************************/ +typedef struct{ +uint32_t ENB_UE_S1AP_ID; +}LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_ue_s1ap_id( + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_ue_s1ap_id( + uint8_t **ptr, + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RAB_ID INTEGER +********************************************************************************/ +typedef struct{ + bool ext; +uint8_t E_RAB_ID; +}LIBLTE_S1AP_E_RAB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rab_id( + LIBLTE_S1AP_E_RAB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rab_id( + uint8_t **ptr, + LIBLTE_S1AP_E_RAB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABInformationListItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT dL_Forwarding; + bool dL_Forwarding_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlistitem( + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlistitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EUTRANRoundTripDelayEstimationInfo INTEGER +********************************************************************************/ +typedef struct{ +uint16_t EUTRANRoundTripDelayEstimationInfo; +}LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutranroundtripdelayestimationinfo( + LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutranroundtripdelayestimationinfo( + uint8_t **ptr, + LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenLACs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:4096 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_LAC_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_FORBIDDENLACS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlacs( + LIBLTE_S1AP_FORBIDDENLACS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlacs( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLACS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GTP_TEID STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_GTP_TEID_OCTET_STRING_LEN 4 +typedef struct{ + uint8_t buffer[4]; +}LIBLTE_S1AP_GTP_TEID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gtp_teid( + LIBLTE_S1AP_GTP_TEID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gtp_teid( + uint8_t **ptr, + LIBLTE_S1AP_GTP_TEID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GUMMEIType ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_GUMMEITYPE_NATIVE, + LIBLTE_S1AP_GUMMEITYPE_MAPPED, + LIBLTE_S1AP_GUMMEITYPE_N_ITEMS, +}LIBLTE_S1AP_GUMMEITYPE_ENUM; +static const char liblte_s1ap_gummeitype_text[LIBLTE_S1AP_GUMMEITYPE_N_ITEMS][80] = { + "native", + "mapped", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_GUMMEITYPE_ENUM e; +}LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummeitype( + LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummeitype( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE HandoverType ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_HANDOVERTYPE_INTRALTE, + LIBLTE_S1AP_HANDOVERTYPE_LTETOUTRAN, + LIBLTE_S1AP_HANDOVERTYPE_LTETOGERAN, + LIBLTE_S1AP_HANDOVERTYPE_UTRANTOLTE, + LIBLTE_S1AP_HANDOVERTYPE_GERANTOLTE, + LIBLTE_S1AP_HANDOVERTYPE_N_ITEMS, +}LIBLTE_S1AP_HANDOVERTYPE_ENUM; +static const char liblte_s1ap_handovertype_text[LIBLTE_S1AP_HANDOVERTYPE_N_ITEMS][80] = { + "intralte", + "ltetoutran", + "ltetogeran", + "utrantolte", + "gerantolte", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_HANDOVERTYPE_ENUM e; +}LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovertype( + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovertype( + uint8_t **ptr, + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE IntegrityProtectionAlgorithms STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_BIT_STRING_LEN 16 +typedef struct{ + bool ext; + uint8_t buffer[16]; +}LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_integrityprotectionalgorithms( + LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_integrityprotectionalgorithms( + uint8_t **ptr, + LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT *ie); + +//TODO: Type undefined NULL + +/******************************************************************************* +/* ProtocolIE LastVisitedGERANCellInformation CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_UNDEFINED, + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_N_ITEMS, +}LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_ENUM; +static const char liblte_s1ap_lastvisitedgerancellinformation_choice_text[LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_N_ITEMS][50] = { + "undefined", +}; + +typedef union{ + //TODO: NULL undefined; +}LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_UNION choice; + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedgerancellinformation( + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedgerancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Links_to_log ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_LINKS_TO_LOG_UPLINK, + LIBLTE_S1AP_LINKS_TO_LOG_DOWNLINK, + LIBLTE_S1AP_LINKS_TO_LOG_BOTH_UPLINK_AND_DOWNLINK, + LIBLTE_S1AP_LINKS_TO_LOG_N_ITEMS, +}LIBLTE_S1AP_LINKS_TO_LOG_ENUM; +static const char liblte_s1ap_links_to_log_text[LIBLTE_S1AP_LINKS_TO_LOG_N_ITEMS][80] = { + "uplink", + "downlink", + "both-uplink-and-downlink", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_LINKS_TO_LOG_ENUM e; +}LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_links_to_log( + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_links_to_log( + uint8_t **ptr, + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE LoggingInterval ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_LOGGINGINTERVAL_MS128, + LIBLTE_S1AP_LOGGINGINTERVAL_MS256, + LIBLTE_S1AP_LOGGINGINTERVAL_MS512, + LIBLTE_S1AP_LOGGINGINTERVAL_MS1024, + LIBLTE_S1AP_LOGGINGINTERVAL_MS2048, + LIBLTE_S1AP_LOGGINGINTERVAL_MS3072, + LIBLTE_S1AP_LOGGINGINTERVAL_MS4096, + LIBLTE_S1AP_LOGGINGINTERVAL_MS6144, + LIBLTE_S1AP_LOGGINGINTERVAL_N_ITEMS, +}LIBLTE_S1AP_LOGGINGINTERVAL_ENUM; +static const char liblte_s1ap_logginginterval_text[LIBLTE_S1AP_LOGGINGINTERVAL_N_ITEMS][80] = { + "ms128", + "ms256", + "ms512", + "ms1024", + "ms2048", + "ms3072", + "ms4096", + "ms6144", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_logginginterval( + LIBLTE_S1AP_LOGGINGINTERVAL_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_logginginterval( + uint8_t **ptr, + LIBLTE_S1AP_LOGGINGINTERVAL_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE M3period ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_M3PERIOD_MS100, + LIBLTE_S1AP_M3PERIOD_MS1000, + LIBLTE_S1AP_M3PERIOD_MS10000, + LIBLTE_S1AP_M3PERIOD_N_ITEMS, +}LIBLTE_S1AP_M3PERIOD_ENUM; +static const char liblte_s1ap_m3period_text[LIBLTE_S1AP_M3PERIOD_N_ITEMS][80] = { + "ms100", + "ms1000", + "ms10000", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_M3PERIOD_ENUM e; +}LIBLTE_S1AP_M3PERIOD_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3period( + LIBLTE_S1AP_M3PERIOD_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3period( + uint8_t **ptr, + LIBLTE_S1AP_M3PERIOD_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE M4period ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_M4PERIOD_MS1024, + LIBLTE_S1AP_M4PERIOD_MS2048, + LIBLTE_S1AP_M4PERIOD_MS5120, + LIBLTE_S1AP_M4PERIOD_MS10240, + LIBLTE_S1AP_M4PERIOD_MIN1, + LIBLTE_S1AP_M4PERIOD_N_ITEMS, +}LIBLTE_S1AP_M4PERIOD_ENUM; +static const char liblte_s1ap_m4period_text[LIBLTE_S1AP_M4PERIOD_N_ITEMS][80] = { + "ms1024", + "ms2048", + "ms5120", + "ms10240", + "min1", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_M4PERIOD_ENUM e; +}LIBLTE_S1AP_M4PERIOD_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4period( + LIBLTE_S1AP_M4PERIOD_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4period( + uint8_t **ptr, + LIBLTE_S1AP_M4PERIOD_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE M5period ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_M5PERIOD_MS1024, + LIBLTE_S1AP_M5PERIOD_MS2048, + LIBLTE_S1AP_M5PERIOD_MS5120, + LIBLTE_S1AP_M5PERIOD_MS10240, + LIBLTE_S1AP_M5PERIOD_MIN1, + LIBLTE_S1AP_M5PERIOD_N_ITEMS, +}LIBLTE_S1AP_M5PERIOD_ENUM; +static const char liblte_s1ap_m5period_text[LIBLTE_S1AP_M5PERIOD_N_ITEMS][80] = { + "ms1024", + "ms2048", + "ms5120", + "ms10240", + "min1", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_M5PERIOD_ENUM e; +}LIBLTE_S1AP_M5PERIOD_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5period( + LIBLTE_S1AP_M5PERIOD_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5period( + uint8_t **ptr, + LIBLTE_S1AP_M5PERIOD_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE MobilityInformation STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_MOBILITYINFORMATION_BIT_STRING_LEN 32 +typedef struct{ + uint8_t buffer[32]; +}LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mobilityinformation( + LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mobilityinformation( + uint8_t **ptr, + LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MME_Code STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_MME_CODE_OCTET_STRING_LEN 1 +typedef struct{ + uint8_t buffer[1]; +}LIBLTE_S1AP_MME_CODE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_code( + LIBLTE_S1AP_MME_CODE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_code( + uint8_t **ptr, + LIBLTE_S1AP_MME_CODE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MSClassmark3 DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_MSCLASSMARK3_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_msclassmark3( + LIBLTE_S1AP_MSCLASSMARK3_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_msclassmark3( + uint8_t **ptr, + LIBLTE_S1AP_MSCLASSMARK3_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE NumberofBroadcastRequest INTEGER +********************************************************************************/ +typedef struct{ +uint16_t NumberofBroadcastRequest; +}LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_numberofbroadcastrequest( + LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_numberofbroadcastrequest( + uint8_t **ptr, + LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE OverloadResponse CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_OVERLOADACTION, + LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_ENUM; +static const char liblte_s1ap_overloadresponse_choice_text[LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_N_ITEMS][50] = { + "overloadAction", +}; + +typedef union{ + LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT overloadAction; +}LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_UNION choice; + LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadresponse( + LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadresponse( + uint8_t **ptr, + LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PDCP_SNExtended INTEGER +********************************************************************************/ +typedef struct{ +uint16_t PDCP_SNExtended; +}LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pdcp_snextended( + LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pdcp_snextended( + uint8_t **ptr, + LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Pre_emptionCapability ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION, + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_MAY_TRIGGER_PRE_EMPTION, + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_N_ITEMS, +}LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM; +static const char liblte_s1ap_pre_emptioncapability_text[LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_N_ITEMS][80] = { + "shall-not-trigger-pre-emption", + "may-trigger-pre-emption", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pre_emptioncapability( + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pre_emptioncapability( + uint8_t **ptr, + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE QCI INTEGER +********************************************************************************/ +typedef struct{ +uint8_t QCI; +}LIBLTE_S1AP_QCI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_qci( + LIBLTE_S1AP_QCI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_qci( + uint8_t **ptr, + LIBLTE_S1AP_QCI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RelayNode_Indicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_RELAYNODE_INDICATOR_TRUE, + LIBLTE_S1AP_RELAYNODE_INDICATOR_N_ITEMS, +}LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM; +static const char liblte_s1ap_relaynode_indicator_text[LIBLTE_S1AP_RELAYNODE_INDICATOR_N_ITEMS][80] = { + "true", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM e; +}LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_relaynode_indicator( + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_relaynode_indicator( + uint8_t **ptr, + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE M1ReportingTrigger ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_M1REPORTINGTRIGGER_PERIODIC, + LIBLTE_S1AP_M1REPORTINGTRIGGER_A2EVENTTRIGGERED, + LIBLTE_S1AP_M1REPORTINGTRIGGER_A2EVENTTRIGGERED_PERIODIC, + LIBLTE_S1AP_M1REPORTINGTRIGGER_N_ITEMS, +}LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM; +static const char liblte_s1ap_m1reportingtrigger_text[LIBLTE_S1AP_M1REPORTINGTRIGGER_N_ITEMS][80] = { + "periodic", + "a2eventtriggered", + "a2eventtriggered-periodic", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM e; +}LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1reportingtrigger( + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1reportingtrigger( + uint8_t **ptr, + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE RIMInformation DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_RIMINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_riminformation( + LIBLTE_S1AP_RIMINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_riminformation( + uint8_t **ptr, + LIBLTE_S1AP_RIMINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RepetitionPeriod INTEGER +********************************************************************************/ +typedef struct{ +uint16_t RepetitionPeriod; +}LIBLTE_S1AP_REPETITIONPERIOD_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_repetitionperiod( + LIBLTE_S1AP_REPETITIONPERIOD_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_repetitionperiod( + uint8_t **ptr, + LIBLTE_S1AP_REPETITIONPERIOD_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SecurityKey STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN 256 +typedef struct{ + uint8_t buffer[256]; +}LIBLTE_S1AP_SECURITYKEY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitykey( + LIBLTE_S1AP_SECURITYKEY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitykey( + uint8_t **ptr, + LIBLTE_S1AP_SECURITYKEY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SerialNumber STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_SERIALNUMBER_BIT_STRING_LEN 16 +typedef struct{ + uint8_t buffer[16]; +}LIBLTE_S1AP_SERIALNUMBER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_serialnumber( + LIBLTE_S1AP_SERIALNUMBER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_serialnumber( + uint8_t **ptr, + LIBLTE_S1AP_SERIALNUMBER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SourceBSS_ToTargetBSS_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_SOURCEBSS_TOTARGETBSS_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourcebss_totargetbss_transparentcontainer( + LIBLTE_S1AP_SOURCEBSS_TOTARGETBSS_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourcebss_totargetbss_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEBSS_TOTARGETBSS_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SRVCCOperationPossible ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_POSSIBLE, + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_N_ITEMS, +}LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM; +static const char liblte_s1ap_srvccoperationpossible_text[LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_N_ITEMS][80] = { + "possible", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM e; +}LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_srvccoperationpossible( + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_srvccoperationpossible( + uint8_t **ptr, + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE ServedGroupIDs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_MME_GROUP_ID_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgroupids( + LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgroupids( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE StratumLevel INTEGER +********************************************************************************/ +typedef struct{ + bool ext; +uint8_t StratumLevel; +}LIBLTE_S1AP_STRATUMLEVEL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_stratumlevel( + LIBLTE_S1AP_STRATUMLEVEL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_stratumlevel( + uint8_t **ptr, + LIBLTE_S1AP_STRATUMLEVEL_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAC STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_TAC_OCTET_STRING_LEN 2 +typedef struct{ + uint8_t buffer[2]; +}LIBLTE_S1AP_TAC_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tac( + LIBLTE_S1AP_TAC_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tac( + uint8_t **ptr, + LIBLTE_S1AP_TAC_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAC_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TALISTFORMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_talistformdt( + LIBLTE_S1AP_TALISTFORMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_talistformdt( + uint8_t **ptr, + LIBLTE_S1AP_TALISTFORMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TBCD_STRING STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_TBCD_STRING_OCTET_STRING_LEN 3 +typedef struct{ + uint8_t buffer[3]; +}LIBLTE_S1AP_TBCD_STRING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tbcd_string( + LIBLTE_S1AP_TBCD_STRING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tbcd_string( + uint8_t **ptr, + LIBLTE_S1AP_TBCD_STRING_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Target_ToSource_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_target_tosource_transparentcontainer( + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_target_tosource_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Threshold_RSRP INTEGER +********************************************************************************/ +typedef struct{ +uint8_t Threshold_RSRP; +}LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_threshold_rsrp( + LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_threshold_rsrp( + uint8_t **ptr, + LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Time_UE_StayedInCell_EnhancedGranularity INTEGER +********************************************************************************/ +typedef struct{ +uint16_t Time_UE_StayedInCell_EnhancedGranularity; +}LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_time_ue_stayedincell_enhancedgranularity( + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_time_ue_stayedincell_enhancedgranularity( + uint8_t **ptr, + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_UTRAN_Trace_ID STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_E_UTRAN_TRACE_ID_OCTET_STRING_LEN 8 +typedef struct{ + uint8_t buffer[8]; +}LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_utran_trace_id( + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_utran_trace_id( + uint8_t **ptr, + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TypeOfError ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_TYPEOFERROR_NOT_UNDERSTOOD, + LIBLTE_S1AP_TYPEOFERROR_MISSING, + LIBLTE_S1AP_TYPEOFERROR_N_ITEMS, +}LIBLTE_S1AP_TYPEOFERROR_ENUM; +static const char liblte_s1ap_typeoferror_text[LIBLTE_S1AP_TYPEOFERROR_N_ITEMS][80] = { + "not-understood", + "missing", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_TYPEOFERROR_ENUM e; +}LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_typeoferror( + LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_typeoferror( + uint8_t **ptr, + LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE UEAggregateMaximumBitrate SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_BITRATE_STRUCT uEaggregateMaximumBitRateDL; + LIBLTE_S1AP_BITRATE_STRUCT uEaggregateMaximumBitRateUL; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueaggregatemaximumbitrate( + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueaggregatemaximumbitrate( + uint8_t **ptr, + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UE_S1AP_ID_pair SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT mME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_id_pair( + LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_id_pair( + uint8_t **ptr, + LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UEIdentityIndexValue STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_UEIDENTITYINDEXVALUE_BIT_STRING_LEN 10 +typedef struct{ + uint8_t buffer[10]; +}LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueidentityindexvalue( + LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueidentityindexvalue( + uint8_t **ptr, + LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UESecurityCapabilities SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT encryptionAlgorithms; + LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT integrityProtectionAlgorithms; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uesecuritycapabilities( + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uesecuritycapabilities( + uint8_t **ptr, + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE VoiceSupportMatchIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_SUPPORTED, + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_NOT_SUPPORTED, + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_N_ITEMS, +}LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM; +static const char liblte_s1ap_voicesupportmatchindicator_text[LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_N_ITEMS][80] = { + "supported", + "not-supported", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM e; +}LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_voicesupportmatchindicator( + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_voicesupportmatchindicator( + uint8_t **ptr, + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE WarningSecurityInfo STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_WARNINGSECURITYINFO_OCTET_STRING_LEN 50 +typedef struct{ + uint8_t buffer[50]; +}LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningsecurityinfo( + LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningsecurityinfo( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENBX2GTPTLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2gtptlas( + LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2gtptlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CauseTransport ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSETRANSPORT_TRANSPORT_RESOURCE_UNAVAILABLE, + LIBLTE_S1AP_CAUSETRANSPORT_UNSPECIFIED, + LIBLTE_S1AP_CAUSETRANSPORT_N_ITEMS, +}LIBLTE_S1AP_CAUSETRANSPORT_ENUM; +static const char liblte_s1ap_causetransport_text[LIBLTE_S1AP_CAUSETRANSPORT_N_ITEMS][80] = { + "transport-resource-unavailable", + "unspecified", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSETRANSPORT_ENUM e; +}LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causetransport( + LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causetransport( + uint8_t **ptr, + LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000HOStatus ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CDMA2000HOSTATUS_HOSUCCESS, + LIBLTE_S1AP_CDMA2000HOSTATUS_HOFAILURE, + LIBLTE_S1AP_CDMA2000HOSTATUS_N_ITEMS, +}LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM; +static const char liblte_s1ap_cdma2000hostatus_text[LIBLTE_S1AP_CDMA2000HOSTATUS_N_ITEMS][80] = { + "hOSuccess", + "hOFailure", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM e; +}LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000hostatus( + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000hostatus( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXPilot DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexpilot( + LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexpilot( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ConcurrentWarningMessageIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_TRUE, + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_N_ITEMS, +}LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM; +static const char liblte_s1ap_concurrentwarningmessageindicator_text[LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_N_ITEMS][80] = { + "true", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_concurrentwarningmessageindicator( + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_concurrentwarningmessageindicator( + uint8_t **ptr, + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE COUNTvalue SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_PDCP_SN_STRUCT pDCP_SN; + LIBLTE_S1AP_HFN_STRUCT hFN; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_COUNTVALUE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalue( + LIBLTE_S1AP_COUNTVALUE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalue( + uint8_t **ptr, + LIBLTE_S1AP_COUNTVALUE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics_IE_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CRITICALITY_ENUM iECriticality; + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT iE_ID; + LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT typeOfError; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_item( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_item( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENBX2TLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:2 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_ENBX2TLAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2tlas( + LIBLTE_S1AP_ENBX2TLAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2tlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2TLAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ExtendedRepetitionPeriod INTEGER +********************************************************************************/ +typedef struct{ +uint32_t ExtendedRepetitionPeriod; +}LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_extendedrepetitionperiod( + LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_extendedrepetitionperiod( + uint8_t **ptr, + LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenTACs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:4096 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAC_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_FORBIDDENTACS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentacs( + LIBLTE_S1AP_FORBIDDENTACS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentacs( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTACS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GBR_QosInformation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_BITRATE_STRUCT e_RAB_MaximumBitrateDL; + LIBLTE_S1AP_BITRATE_STRUCT e_RAB_MaximumBitrateUL; + LIBLTE_S1AP_BITRATE_STRUCT e_RAB_GuaranteedBitrateDL; + LIBLTE_S1AP_BITRATE_STRUCT e_RAB_GuaranteedBitrateUL; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gbr_qosinformation( + LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gbr_qosinformation( + uint8_t **ptr, + LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE HFNModified INTEGER +********************************************************************************/ +typedef struct{ +uint32_t HFNModified; +}LIBLTE_S1AP_HFNMODIFIED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_hfnmodified( + LIBLTE_S1AP_HFNMODIFIED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_hfnmodified( + uint8_t **ptr, + LIBLTE_S1AP_HFNMODIFIED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE KillAllWarningMessages ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_TRUE, + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_N_ITEMS, +}LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM; +static const char liblte_s1ap_killallwarningmessages_text[LIBLTE_S1AP_KILLALLWARNINGMESSAGES_N_ITEMS][80] = { + "true", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killallwarningmessages( + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killallwarningmessages( + uint8_t **ptr, + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE LPPa_PDU DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_LPPA_PDU_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lppa_pdu( + LIBLTE_S1AP_LPPA_PDU_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lppa_pdu( + uint8_t **ptr, + LIBLTE_S1AP_LPPA_PDU_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M3Configuration SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_M3PERIOD_ENUM_EXT m3period; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_M3CONFIGURATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3configuration( + LIBLTE_S1AP_M3CONFIGURATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3configuration( + uint8_t **ptr, + LIBLTE_S1AP_M3CONFIGURATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M5Configuration SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_M5PERIOD_ENUM_EXT m5period; + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT m5_links_to_log; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_M5CONFIGURATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5configuration( + LIBLTE_S1AP_M5CONFIGURATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5configuration( + uint8_t **ptr, + LIBLTE_S1AP_M5CONFIGURATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MeasurementThresholdA2 CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRP, + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRQ, + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_N_ITEMS, +}LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_ENUM; +static const char liblte_s1ap_measurementthresholda2_choice_text[LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_N_ITEMS][50] = { + "threshold_RSRP", + "threshold_RSRQ", +}; + +typedef union{ + LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT threshold_RSRP; + LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT threshold_RSRQ; +}LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_UNION choice; + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_measurementthresholda2( + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_measurementthresholda2( + uint8_t **ptr, + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M_TMSI STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_M_TMSI_OCTET_STRING_LEN 4 +typedef struct{ + uint8_t buffer[4]; +}LIBLTE_S1AP_M_TMSI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m_tmsi( + LIBLTE_S1AP_M_TMSI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m_tmsi( + uint8_t **ptr, + LIBLTE_S1AP_M_TMSI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE OldBSS_ToNewBSS_Information DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_OLDBSS_TONEWBSS_INFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_oldbss_tonewbss_information( + LIBLTE_S1AP_OLDBSS_TONEWBSS_INFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_oldbss_tonewbss_information( + uint8_t **ptr, + LIBLTE_S1AP_OLDBSS_TONEWBSS_INFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PLMNidentity STATIC OCTET STRING +********************************************************************************/ +#define LIBLTE_S1AP_PLMNIDENTITY_OCTET_STRING_LEN 3 +typedef struct{ + uint8_t buffer[3]; +}LIBLTE_S1AP_PLMNIDENTITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_plmnidentity( + LIBLTE_S1AP_PLMNIDENTITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_plmnidentity( + uint8_t **ptr, + LIBLTE_S1AP_PLMNIDENTITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ReceiveStatusOfULPDCPSDUsExtended DYNAMIC BIT STRING +********************************************************************************/ +// lb:1, ub:16384 +typedef struct{ + uint32_t n_bits; + uint8_t buffer[16384]; +}LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_receivestatusofulpdcpsdusextended( + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_receivestatusofulpdcpsdusextended( + uint8_t **ptr, + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RequestType SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EVENTTYPE_ENUM_EXT eventType; + LIBLTE_S1AP_REPORTAREA_ENUM_EXT reportArea; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_REQUESTTYPE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_requesttype( + LIBLTE_S1AP_REQUESTTYPE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_requesttype( + uint8_t **ptr, + LIBLTE_S1AP_REQUESTTYPE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RRC_Container DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_RRC_CONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rrc_container( + LIBLTE_S1AP_RRC_CONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rrc_container( + uint8_t **ptr, + LIBLTE_S1AP_RRC_CONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE nextHopChainingCount INTEGER +********************************************************************************/ +typedef struct{ +uint8_t nextHopChainingCount; +}LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nexthopchainingcount( + LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nexthopchainingcount( + uint8_t **ptr, + LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SecurityContext SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT nextHopChainingCount; + LIBLTE_S1AP_SECURITYKEY_STRUCT nextHopParameter; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SECURITYCONTEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitycontext( + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitycontext( + uint8_t **ptr, + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ServedMMECs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_MME_CODE_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_SERVEDMMECS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedmmecs( + LIBLTE_S1AP_SERVEDMMECS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedmmecs( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDMMECS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TimeSynchronizationInfo SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_STRATUMLEVEL_STRUCT stratumLevel; + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT synchronizationStatus; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timesynchronizationinfo( + LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timesynchronizationinfo( + uint8_t **ptr, + LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMNidentity; + LIBLTE_S1AP_TAC_STRUCT tAC; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai( + LIBLTE_S1AP_TAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai( + uint8_t **ptr, + LIBLTE_S1AP_TAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TABasedMDT SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TALISTFORMDT_STRUCT tAListforMDT; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TABASEDMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tabasedmdt( + LIBLTE_S1AP_TABASEDMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tabasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_TABASEDMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TargeteNB_ToSourceeNB_TransparentContainer SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_RRC_CONTAINER_STRUCT rRC_Container; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_tosourceenb_transparentcontainer( + LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_tosourceenb_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M1ThresholdEventA2 SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT measurementThreshold; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1thresholdeventa2( + LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1thresholdeventa2( + uint8_t **ptr, + LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TransportInformation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT uL_GTP_TEID; +}LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_transportinformation( + LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_transportinformation( + uint8_t **ptr, + LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TunnelInformation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_PORT_NUMBER_STRUCT uDP_Port_Number; + bool uDP_Port_Number_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TUNNELINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tunnelinformation( + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tunnelinformation( + uint8_t **ptr, + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UE_S1AP_IDs CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR, + LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID, + LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_N_ITEMS, +}LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_ENUM; +static const char liblte_s1ap_ue_s1ap_ids_choice_text[LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_N_ITEMS][50] = { + "uE_S1AP_ID_pair", + "mME_UE_S1AP_ID", +}; + +typedef union{ + LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT uE_S1AP_ID_pair; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT mME_UE_S1AP_ID; +}LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UNION choice; + LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_UE_S1AP_IDS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_ids( + LIBLTE_S1AP_UE_S1AP_IDS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_ids( + uint8_t **ptr, + LIBLTE_S1AP_UE_S1AP_IDS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENBX2ExtTLA SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT iPsecTLA; + bool iPsecTLA_present; + LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT gTPTLAa; + bool gTPTLAa_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_ENBX2EXTTLA_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttla( + LIBLTE_S1AP_ENBX2EXTTLA_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttla( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2EXTTLA_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE BPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:6 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TBCD_STRING_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_BPLMNS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bplmns( + LIBLTE_S1AP_BPLMNS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bplmns( + uint8_t **ptr, + LIBLTE_S1AP_BPLMNS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cause CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK, + LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT, + LIBLTE_S1AP_CAUSE_CHOICE_NAS, + LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL, + LIBLTE_S1AP_CAUSE_CHOICE_MISC, + LIBLTE_S1AP_CAUSE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_CAUSE_CHOICE_ENUM; +static const char liblte_s1ap_cause_choice_text[LIBLTE_S1AP_CAUSE_CHOICE_N_ITEMS][50] = { + "radioNetwork", + "transport", + "nas", + "protocol", + "misc", +}; + +typedef union{ + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT radioNetwork; + LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT transport; + LIBLTE_S1AP_CAUSENAS_ENUM_EXT nas; + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT protocol; + LIBLTE_S1AP_CAUSEMISC_ENUM_EXT misc; +}LIBLTE_S1AP_CAUSE_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSE_CHOICE_UNION choice; + LIBLTE_S1AP_CAUSE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_CAUSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cause( + LIBLTE_S1AP_CAUSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cause( + uint8_t **ptr, + LIBLTE_S1AP_CAUSE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXSRVCCInfo SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT cdma2000OneXMEID; + LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT cdma2000OneXMSI; + LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT cdma2000OneXPilot; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexsrvccinfo( + LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexsrvccinfo( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CGI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMNidentity; + LIBLTE_S1AP_LAC_STRUCT lAC; + LIBLTE_S1AP_CI_STRUCT cI; + LIBLTE_S1AP_RAC_STRUCT rAC; + bool rAC_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CGI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cgi( + LIBLTE_S1AP_CGI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cgi( + uint8_t **ptr, + LIBLTE_S1AP_CGI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE COUNTValueExtended SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT pDCP_SNExtended; + LIBLTE_S1AP_HFNMODIFIED_STRUCT hFNModified; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalueextended( + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalueextended( + uint8_t **ptr, + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics_IE_List DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_list( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_list( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Global_ENB_ID SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMNidentity; + LIBLTE_S1AP_ENB_ID_STRUCT eNB_ID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_global_enb_id( + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_global_enb_id( + uint8_t **ptr, + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:15 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TBCD_STRING_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_EPLMNS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eplmns( + LIBLTE_S1AP_EPLMNS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eplmns( + uint8_t **ptr, + LIBLTE_S1AP_EPLMNS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_CAUSE_STRUCT cause; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem( + LIBLTE_S1AP_E_RABITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EUTRAN_CGI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMNidentity; + LIBLTE_S1AP_CELLIDENTITY_STRUCT cell_ID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_EUTRAN_CGI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutran_cgi( + LIBLTE_S1AP_EUTRAN_CGI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutran_cgi( + uint8_t **ptr, + LIBLTE_S1AP_EUTRAN_CGI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenTAs_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMN_Identity; + LIBLTE_S1AP_FORBIDDENTACS_STRUCT forbiddenTACs; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas_item( + LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas_item( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenLAs_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMN_Identity; + LIBLTE_S1AP_FORBIDDENLACS_STRUCT forbiddenLACs; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas_item( + LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas_item( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LAI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMNidentity; + LIBLTE_S1AP_LAC_STRUCT lAC; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_LAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lai( + LIBLTE_S1AP_LAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lai( + uint8_t **ptr, + LIBLTE_S1AP_LAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M4Configuration SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_M4PERIOD_ENUM_EXT m4period; + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT m4_links_to_log; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_M4CONFIGURATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4configuration( + LIBLTE_S1AP_M4CONFIGURATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4configuration( + uint8_t **ptr, + LIBLTE_S1AP_M4CONFIGURATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MDTPLMNList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TBCD_STRING_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_MDTPLMNLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdtplmnlist( + LIBLTE_S1AP_MDTPLMNLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdtplmnlist( + uint8_t **ptr, + LIBLTE_S1AP_MDTPLMNLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MMERelaySupportIndicator ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_TRUE, + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_N_ITEMS, +}LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM; +static const char liblte_s1ap_mmerelaysupportindicator_text[LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_N_ITEMS][80] = { + "true", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM e; +}LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmerelaysupportindicator( + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmerelaysupportindicator( + uint8_t **ptr, + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE PagingPriority ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL1, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL2, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL3, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL4, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL5, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL6, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL7, + LIBLTE_S1AP_PAGINGPRIORITY_PRIOLEVEL8, + LIBLTE_S1AP_PAGINGPRIORITY_N_ITEMS, +}LIBLTE_S1AP_PAGINGPRIORITY_ENUM; +static const char liblte_s1ap_pagingpriority_text[LIBLTE_S1AP_PAGINGPRIORITY_N_ITEMS][80] = { + "priolevel1", + "priolevel2", + "priolevel3", + "priolevel4", + "priolevel5", + "priolevel6", + "priolevel7", + "priolevel8", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_PAGINGPRIORITY_ENUM e; +}LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pagingpriority( + LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pagingpriority( + uint8_t **ptr, + LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE PriorityLevel INTEGER +********************************************************************************/ +typedef struct{ +uint8_t PriorityLevel; +}LIBLTE_S1AP_PRIORITYLEVEL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_prioritylevel( + LIBLTE_S1AP_PRIORITYLEVEL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_prioritylevel( + uint8_t **ptr, + LIBLTE_S1AP_PRIORITYLEVEL_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ECGIListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ecgilistforrestart( + LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ecgilistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SourceeNB_ID SEQUENCE +********************************************************************************/ +typedef struct{ + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT global_ENB_ID; + LIBLTE_S1AP_TAI_STRUCT selected_TAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SOURCEENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_id( + LIBLTE_S1AP_SOURCEENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_id( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ServedPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:32 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TBCD_STRING_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_SERVEDPLMNS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedplmns( + LIBLTE_S1AP_SERVEDPLMNS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedplmns( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDPLMNS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SupportedTAs_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAC_STRUCT tAC; + LIBLTE_S1AP_BPLMNS_STRUCT broadcastPLMNs; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas_item( + LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas_item( + uint8_t **ptr, + LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAIListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAILISTFORMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistformdt( + LIBLTE_S1AP_TAILISTFORMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistformdt( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CompletedCellinTAI_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai_item( + LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai_item( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TargeteNB_ID SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT global_ENB_ID; + LIBLTE_S1AP_TAI_STRUCT selected_TAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TARGETENB_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_id( + LIBLTE_S1AP_TARGETENB_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_id( + uint8_t **ptr, + LIBLTE_S1AP_TARGETENB_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TargetBSS_ToSourceBSS_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_TARGETBSS_TOSOURCEBSS_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetbss_tosourcebss_transparentcontainer( + LIBLTE_S1AP_TARGETBSS_TOSOURCEBSS_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetbss_tosourcebss_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETBSS_TOSOURCEBSS_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAIListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:2048 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAILISTFORRESTART_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistforrestart( + LIBLTE_S1AP_TAILISTFORRESTART_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORRESTART_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UserLocationInformation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eutran_cgi; + LIBLTE_S1AP_TAI_STRUCT tai; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_userlocationinformation( + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_userlocationinformation( + uint8_t **ptr, + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENBX2ExtTLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_ENBX2EXTTLA_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttlas( + LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE AllocationAndRetentionPriority SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_PRIORITYLEVEL_STRUCT priorityLevel; + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM pre_emptionCapability; + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM pre_emptionVulnerability; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_allocationandretentionpriority( + LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_allocationandretentionpriority( + uint8_t **ptr, + LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CancelledCellinEAI_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT numberOfBroadcasts; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai_item( + LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai_item( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CancelledCellinTAI_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT numberOfBroadcasts; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai_item( + LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai_item( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellID_Broadcast_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast_item( + LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellID_Cancelled_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT numberOfBroadcasts; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled_item( + LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellIdListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:32 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellidlistformdt( + LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellidlistformdt( + uint8_t **ptr, + LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CSG_Id STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_CSG_ID_BIT_STRING_LEN 27 +typedef struct{ + uint8_t buffer[27]; +}LIBLTE_S1AP_CSG_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_id( + LIBLTE_S1AP_CSG_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_id( + uint8_t **ptr, + LIBLTE_S1AP_CSG_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CSG_IdList_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CSG_ID_STRUCT cSG_Id; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist_item( + LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist_item( + uint8_t **ptr, + LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Direct_Forwarding_Path_Availability ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_DIRECTPATHAVAILABLE, + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_N_ITEMS, +}LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM; +static const char liblte_s1ap_direct_forwarding_path_availability_text[LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_N_ITEMS][80] = { + "directPathAvailable", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM e; +}LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_direct_forwarding_path_availability( + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_direct_forwarding_path_availability( + uint8_t **ptr, + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE CompletedCellinEAI_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eCGI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai_item( + LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai_item( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABInformationList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlist( + LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenTAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_FORBIDDENTAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas( + LIBLTE_S1AP_FORBIDDENTAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GUMMEI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT pLMN_Identity; + LIBLTE_S1AP_MME_GROUP_ID_STRUCT mME_Group_ID; + LIBLTE_S1AP_MME_CODE_STRUCT mME_Code; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_GUMMEI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummei( + LIBLTE_S1AP_GUMMEI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummei( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LoggedMDT SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_LOGGINGINTERVAL_ENUM loggingInterval; + LIBLTE_S1AP_LOGGINGDURATION_ENUM loggingDuration; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_LOGGEDMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggedmdt( + LIBLTE_S1AP_LOGGEDMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggedmdt( + uint8_t **ptr, + LIBLTE_S1AP_LOGGEDMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE NASSecurityParametersfromE_UTRAN DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nassecurityparametersfrome_utran( + LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nassecurityparametersfrome_utran( + uint8_t **ptr, + LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ReportAmountMDT ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_REPORTAMOUNTMDT_R1, + LIBLTE_S1AP_REPORTAMOUNTMDT_R2, + LIBLTE_S1AP_REPORTAMOUNTMDT_R4, + LIBLTE_S1AP_REPORTAMOUNTMDT_R8, + LIBLTE_S1AP_REPORTAMOUNTMDT_R16, + LIBLTE_S1AP_REPORTAMOUNTMDT_R32, + LIBLTE_S1AP_REPORTAMOUNTMDT_R64, + LIBLTE_S1AP_REPORTAMOUNTMDT_RINFINITY, + LIBLTE_S1AP_REPORTAMOUNTMDT_N_ITEMS, +}LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM; +static const char liblte_s1ap_reportamountmdt_text[LIBLTE_S1AP_REPORTAMOUNTMDT_N_ITEMS][80] = { + "r1", + "r2", + "r4", + "r8", + "r16", + "r32", + "r64", + "rinfinity", +}; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportamountmdt( + LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportamountmdt( + uint8_t **ptr, + LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM *ie); + +/******************************************************************************* +/* ProtocolIE ServedGUMMEIsItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_SERVEDPLMNS_STRUCT servedPLMNs; + LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT servedGroupIDs; + LIBLTE_S1AP_SERVEDMMECS_STRUCT servedMMECs; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeisitem( + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeisitem( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE S_TMSI SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_CODE_STRUCT mMEC; + LIBLTE_S1AP_M_TMSI_STRUCT m_TMSI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_S_TMSI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s_tmsi( + LIBLTE_S1AP_S_TMSI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s_tmsi( + uint8_t **ptr, + LIBLTE_S1AP_S_TMSI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAIListforWarning DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAILISTFORWARNING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistforwarning( + LIBLTE_S1AP_TAILISTFORWARNING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistforwarning( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORWARNING_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CompletedCellinTAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai( + LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TargetRNC_ID SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_LAI_STRUCT lAI; + LIBLTE_S1AP_RAC_STRUCT rAC; + bool rAC_present; + LIBLTE_S1AP_RNC_ID_STRUCT rNC_ID; + LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT extendedRNC_ID; + bool extendedRNC_ID_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TARGETRNC_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_id( + LIBLTE_S1AP_TARGETRNC_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_id( + uint8_t **ptr, + LIBLTE_S1AP_TARGETRNC_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UE_associatedLogicalS1_ConnectionItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT mME_UE_S1AP_ID; + bool mME_UE_S1AP_ID_present; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + bool eNB_UE_S1AP_ID_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitem( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE UEPagingID CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_UEPAGINGID_CHOICE_S_TMSI, + LIBLTE_S1AP_UEPAGINGID_CHOICE_IMSI, + LIBLTE_S1AP_UEPAGINGID_CHOICE_N_ITEMS, +}LIBLTE_S1AP_UEPAGINGID_CHOICE_ENUM; +static const char liblte_s1ap_uepagingid_choice_text[LIBLTE_S1AP_UEPAGINGID_CHOICE_N_ITEMS][50] = { + "s_TMSI", + "iMSI", +}; + +typedef union{ + LIBLTE_S1AP_S_TMSI_STRUCT s_TMSI; + LIBLTE_S1AP_IMSI_STRUCT iMSI; +}LIBLTE_S1AP_UEPAGINGID_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_UEPAGINGID_CHOICE_UNION choice; + LIBLTE_S1AP_UEPAGINGID_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_UEPAGINGID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uepagingid( + LIBLTE_S1AP_UEPAGINGID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uepagingid( + uint8_t **ptr, + LIBLTE_S1AP_UEPAGINGID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE Bearers_SubjectToStatusTransfer_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_COUNTVALUE_STRUCT uL_COUNTvalue; + LIBLTE_S1AP_COUNTVALUE_STRUCT dL_COUNTvalue; + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT receiveStatusofULPDCPSDUs; + bool receiveStatusofULPDCPSDUs_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_item( + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_item( + uint8_t **ptr, + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CancelledCellinEAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai( + LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellID_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CELLID_BROADCAST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast( + LIBLTE_S1AP_CELLID_BROADCAST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_BROADCAST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellBasedMDT SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT cellIdListforMDT; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CELLBASEDMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellbasedmdt( + LIBLTE_S1AP_CELLBASEDMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellbasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_CELLBASEDMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CSG_IdList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CSG_IDLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist( + LIBLTE_S1AP_CSG_IDLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist( + uint8_t **ptr, + LIBLTE_S1AP_CSG_IDLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ECGIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_ECGILIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ecgilist( + LIBLTE_S1AP_ECGILIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ecgilist( + uint8_t **ptr, + LIBLTE_S1AP_ECGILIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Cancelled_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT emergencyAreaID; + LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT cancelledCellinEAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled_item( + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GERAN_Cell_ID SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_LAI_STRUCT lAI; + LIBLTE_S1AP_RAC_STRUCT rAC; + LIBLTE_S1AP_CI_STRUCT cI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_GERAN_CELL_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_geran_cell_id( + LIBLTE_S1AP_GERAN_CELL_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_geran_cell_id( + uint8_t **ptr, + LIBLTE_S1AP_GERAN_CELL_ID_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rablist( + LIBLTE_S1AP_E_RABLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rablist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ForbiddenLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_FORBIDDENLAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas( + LIBLTE_S1AP_FORBIDDENLAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MDT_Location_Info STATIC BIT STRING +********************************************************************************/ +#define LIBLTE_S1AP_MDT_LOCATION_INFO_BIT_STRING_LEN 8 +typedef struct{ + uint8_t buffer[8]; +}LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_location_info( + LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_location_info( + uint8_t **ptr, + LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE M1PeriodicReporting SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_REPORTINTERVALMDT_ENUM reportInterval; + LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM reportAmount; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1periodicreporting( + LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1periodicreporting( + uint8_t **ptr, + LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE eHRPD_Sector_ID DYNAMIC OCTET STRING +********************************************************************************/ +// lb:0, ub:16318 +typedef struct{ + uint32_t n_octets; + uint8_t buffer[16318]; +}LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ehrpd_sector_id( + LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ehrpd_sector_id( + uint8_t **ptr, + LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RIMRoutingAddress CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_GERAN_CELL_ID, + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_TARGETRNC_ID, + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_EHRPD_SECTOR_ID, + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_N_ITEMS, +}LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_ENUM; +static const char liblte_s1ap_rimroutingaddress_choice_text[LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_N_ITEMS][50] = { + "gERAN_Cell_ID", + "targetRNC_ID", + "eHRPD_Sector_ID", +}; + +typedef union{ + LIBLTE_S1AP_GERAN_CELL_ID_STRUCT gERAN_Cell_ID; + LIBLTE_S1AP_TARGETRNC_ID_STRUCT targetRNC_ID; + LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT eHRPD_Sector_ID; +}LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_UNION choice; + LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimroutingaddress( + LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimroutingaddress( + uint8_t **ptr, + LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ServedGUMMEIs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeis( + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeis( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAIBasedMDT SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAILISTFORMDT_STRUCT tAIListforMDT; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TAIBASEDMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taibasedmdt( + LIBLTE_S1AP_TAIBASEDMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taibasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_TAIBASEDMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAI_Broadcast_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAI_STRUCT tAI; + LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT completedCellinTAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast_item( + LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TargetID CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID, + LIBLTE_S1AP_TARGETID_CHOICE_TARGETRNC_ID, + LIBLTE_S1AP_TARGETID_CHOICE_CGI, + LIBLTE_S1AP_TARGETID_CHOICE_N_ITEMS, +}LIBLTE_S1AP_TARGETID_CHOICE_ENUM; +static const char liblte_s1ap_targetid_choice_text[LIBLTE_S1AP_TARGETID_CHOICE_N_ITEMS][50] = { + "targeteNB_ID", + "targetRNC_ID", + "cGI", +}; + +typedef union{ + LIBLTE_S1AP_TARGETENB_ID_STRUCT targeteNB_ID; + LIBLTE_S1AP_TARGETRNC_ID_STRUCT targetRNC_ID; + LIBLTE_S1AP_CGI_STRUCT cGI; +}LIBLTE_S1AP_TARGETID_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_TARGETID_CHOICE_UNION choice; + LIBLTE_S1AP_TARGETID_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_TARGETID_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetid( + LIBLTE_S1AP_TARGETID_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetid( + uint8_t **ptr, + LIBLTE_S1AP_TARGETID_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE WarningAreaList CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_CELLIDLIST, + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_TRACKINGAREALISTFORWARNING, + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_EMERGENCYAREAIDLIST, + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_N_ITEMS, +}LIBLTE_S1AP_WARNINGAREALIST_CHOICE_ENUM; +static const char liblte_s1ap_warningarealist_choice_text[LIBLTE_S1AP_WARNINGAREALIST_CHOICE_N_ITEMS][50] = { + "cellIDList", + "trackingAreaListforWarning", + "emergencyAreaIDList", +}; + +typedef union{ + LIBLTE_S1AP_ECGILIST_STRUCT cellIDList; + LIBLTE_S1AP_TAILISTFORWARNING_STRUCT trackingAreaListforWarning; + LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT emergencyAreaIDList; +}LIBLTE_S1AP_WARNINGAREALIST_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_UNION choice; + LIBLTE_S1AP_WARNINGAREALIST_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_WARNINGAREALIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningarealist( + LIBLTE_S1AP_WARNINGAREALIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningarealist( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGAREALIST_STRUCT *ie); + +//TODO: Type pLMNWide NULL + +/******************************************************************************* +/* ProtocolIE AreaScopeOfMDT CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_CELLBASED, + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TABASED, + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_PLMNWIDE, + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TAIBASED, + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_N_ITEMS, +}LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_ENUM; +static const char liblte_s1ap_areascopeofmdt_choice_text[LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_N_ITEMS][50] = { + "cellBased", + "tABased", + "pLMNWide", + "tAIBased", +}; + +typedef union{ + LIBLTE_S1AP_CELLBASEDMDT_STRUCT cellBased; + LIBLTE_S1AP_TABASEDMDT_STRUCT tABased; + //TODO: NULL pLMNWide; + LIBLTE_S1AP_TAIBASEDMDT_STRUCT tAIBased; +}LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_UNION choice; + LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_areascopeofmdt( + LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_areascopeofmdt( + uint8_t **ptr, + LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CancelledCellinTAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai( + LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellType SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CELL_SIZE_ENUM_EXT cell_Size; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CELLTYPE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltype( + LIBLTE_S1AP_CELLTYPE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltype( + uint8_t **ptr, + LIBLTE_S1AP_CELLTYPE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled( + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE GUMMEIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_GUMMEI_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_GUMMEILIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummeilist( + LIBLTE_S1AP_GUMMEILIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummeilist( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEILIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABLevelQoSParameters SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_QCI_STRUCT qCI; + LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT allocationRetentionPriority; + LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT gbrQosInformation; + bool gbrQosInformation_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rablevelqosparameters( + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rablevelqosparameters( + uint8_t **ptr, + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LastVisitedEUTRANCellInformation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT global_Cell_ID; + LIBLTE_S1AP_CELLTYPE_STRUCT cellType; + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT time_UE_StayedInCell; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedeutrancellinformation( + LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedeutrancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE RIMTransfer SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_RIMINFORMATION_STRUCT rIMInformation; + LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT rIMRoutingAddress; + bool rIMRoutingAddress_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_RIMTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimtransfer( + LIBLTE_S1AP_RIMTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimtransfer( + uint8_t **ptr, + LIBLTE_S1AP_RIMTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SupportedTAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_SUPPORTEDTAS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas( + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas( + uint8_t **ptr, + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAI_Cancelled_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAI_STRUCT tAI; + LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT cancelledCellinTAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled_item( + LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE X2TNLConfigurationInfo SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENBX2TLAS_STRUCT eNBX2TransportLayerAddresses; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_x2tnlconfigurationinfo( + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_x2tnlconfigurationinfo( + uint8_t **ptr, + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List Bearers_SubjectToStatusTransferList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransferlist( + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransferlist( + uint8_t **ptr, + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CellID_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_CELLID_CANCELLED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled( + LIBLTE_S1AP_CELLID_CANCELLED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_CANCELLED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CompletedCellinEAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai( + LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE HandoverRestrictionList SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TBCD_STRING_STRUCT servingPLMN; + LIBLTE_S1AP_EPLMNS_STRUCT equivalentPLMNs; + bool equivalentPLMNs_present; + LIBLTE_S1AP_FORBIDDENTAS_STRUCT forbiddenTAs; + bool forbiddenTAs_present; + LIBLTE_S1AP_FORBIDDENLAS_STRUCT forbiddenLAs; + bool forbiddenLAs_present; + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT forbiddenInterRATs; + bool forbiddenInterRATs_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrestrictionlist( + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrestrictionlist( + uint8_t **ptr, + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE LastVisitedCell_Item CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL, + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_UTRAN_CELL, + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_GERAN_CELL, + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_N_ITEMS, +}LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_ENUM; +static const char liblte_s1ap_lastvisitedcell_item_choice_text[LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_N_ITEMS][50] = { + "e_UTRAN_Cell", + "uTRAN_Cell", + "gERAN_Cell", +}; + +typedef union{ + LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT e_UTRAN_Cell; + LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT uTRAN_Cell; + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT gERAN_Cell; +}LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_UNION choice; + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedcell_item( + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedcell_item( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SONInformationReply SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT x2TNLConfigurationInfo; + bool x2TNLConfigurationInfo_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationreply( + LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationreply( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAI_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAI_BROADCAST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast( + LIBLTE_S1AP_TAI_BROADCAST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_TAI_BROADCAST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TimeToWait ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_TIMETOWAIT_V1S, + LIBLTE_S1AP_TIMETOWAIT_V2S, + LIBLTE_S1AP_TIMETOWAIT_V5S, + LIBLTE_S1AP_TIMETOWAIT_V10S, + LIBLTE_S1AP_TIMETOWAIT_V20S, + LIBLTE_S1AP_TIMETOWAIT_V60S, + LIBLTE_S1AP_TIMETOWAIT_N_ITEMS, +}LIBLTE_S1AP_TIMETOWAIT_ENUM; +static const char liblte_s1ap_timetowait_text[LIBLTE_S1AP_TIMETOWAIT_N_ITEMS][80] = { + "v1s", + "v2s", + "v5s", + "v10s", + "v20s", + "v60s", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_TIMETOWAIT_ENUM e; +}LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timetowait( + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timetowait( + uint8_t **ptr, + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE UE_HistoryInformation DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_historyinformation( + LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_historyinformation( + uint8_t **ptr, + LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_PROCEDURECODE_STRUCT procedureCode; + bool procedureCode_present; + LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM triggeringMessage; + bool triggeringMessage_present; + LIBLTE_S1AP_CRITICALITY_ENUM procedureCriticality; + bool procedureCriticality_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT iEsCriticalityDiagnostics; + bool iEsCriticalityDiagnostics_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Broadcast_Item SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT emergencyAreaID; + LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT completedCellinEAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast_item( + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ImmediateMDT SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT measurementsToActivate; + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT m1reportingTrigger; + LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT m1thresholdeventA2; + bool m1thresholdeventA2_present; + LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT m1periodicReporting; + bool m1periodicReporting_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_IMMEDIATEMDT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_immediatemdt( + LIBLTE_S1AP_IMMEDIATEMDT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_immediatemdt( + uint8_t **ptr, + LIBLTE_S1AP_IMMEDIATEMDT_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MDTMode CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_MDTMODE_CHOICE_IMMEDIATEMDT, + LIBLTE_S1AP_MDTMODE_CHOICE_LOGGEDMDT, + LIBLTE_S1AP_MDTMODE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_MDTMODE_CHOICE_ENUM; +static const char liblte_s1ap_mdtmode_choice_text[LIBLTE_S1AP_MDTMODE_CHOICE_N_ITEMS][50] = { + "immediateMDT", + "loggedMDT", +}; + +typedef union{ + LIBLTE_S1AP_IMMEDIATEMDT_STRUCT immediateMDT; + LIBLTE_S1AP_LOGGEDMDT_STRUCT loggedMDT; +}LIBLTE_S1AP_MDTMODE_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_MDTMODE_CHOICE_UNION choice; + LIBLTE_S1AP_MDTMODE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_MDTMODE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdtmode( + LIBLTE_S1AP_MDTMODE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdtmode( + uint8_t **ptr, + LIBLTE_S1AP_MDTMODE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SourceeNB_ToTargeteNB_TransparentContainer SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_RRC_CONTAINER_STRUCT rRC_Container; + LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT e_RABInformationList; + bool e_RABInformationList_present; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT targetCell_ID; + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT subscriberProfileIDforRFP; + bool subscriberProfileIDforRFP_present; + LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT uE_HistoryInformation; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer( + LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_totargetenb_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast( + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE MDT_Configuration SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT mdt_Activation; + LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT areaScopeOfMDT; + LIBLTE_S1AP_MDTMODE_STRUCT mDTMode; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_configuration( + LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_configuration( + uint8_t **ptr, + LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAI_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAI_CANCELLED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled( + LIBLTE_S1AP_TAI_CANCELLED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_TAI_CANCELLED_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE BroadcastCancelledAreaList CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_CELLID_CANCELLED, + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_TAI_CANCELLED, + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_EMERGENCYAREAID_CANCELLED, + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_N_ITEMS, +}LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_ENUM; +static const char liblte_s1ap_broadcastcancelledarealist_choice_text[LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_N_ITEMS][50] = { + "cellID_Cancelled", + "tAI_Cancelled", + "emergencyAreaID_Cancelled", +}; + +typedef union{ + LIBLTE_S1AP_CELLID_CANCELLED_STRUCT cellID_Cancelled; + LIBLTE_S1AP_TAI_CANCELLED_STRUCT tAI_Cancelled; + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT emergencyAreaID_Cancelled; +}LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_UNION choice; + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_broadcastcancelledarealist( + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_broadcastcancelledarealist( + uint8_t **ptr, + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ENB_StatusTransfer_TransparentContainer SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT bearers_SubjectToStatusTransferList; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_statustransfer_transparentcontainer( + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_statustransfer_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TraceActivation SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT e_UTRAN_Trace_ID; + LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT interfacesToTrace; + LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT traceDepth; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT traceCollectionEntityIPAddress; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TRACEACTIVATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_traceactivation( + LIBLTE_S1AP_TRACEACTIVATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_traceactivation( + uint8_t **ptr, + LIBLTE_S1AP_TRACEACTIVATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE BroadcastCompletedAreaList CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_CELLID_BROADCAST, + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_TAI_BROADCAST, + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_EMERGENCYAREAID_BROADCAST, + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_N_ITEMS, +}LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_ENUM; +static const char liblte_s1ap_broadcastcompletedarealist_choice_text[LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_N_ITEMS][50] = { + "cellID_Broadcast", + "tAI_Broadcast", + "emergencyAreaID_Broadcast", +}; + +typedef union{ + LIBLTE_S1AP_CELLID_BROADCAST_STRUCT cellID_Broadcast; + LIBLTE_S1AP_TAI_BROADCAST_STRUCT tAI_Broadcast; + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT emergencyAreaID_Broadcast; +}LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_UNION choice; + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_broadcastcompletedarealist( + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_broadcastcompletedarealist( + uint8_t **ptr, + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SONInformation CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREQUEST, + LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREPLY, + LIBLTE_S1AP_SONINFORMATION_CHOICE_N_ITEMS, +}LIBLTE_S1AP_SONINFORMATION_CHOICE_ENUM; +static const char liblte_s1ap_soninformation_choice_text[LIBLTE_S1AP_SONINFORMATION_CHOICE_N_ITEMS][50] = { + "sONInformationRequest", + "sONInformationReply", +}; + +typedef union{ + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT sONInformationRequest; + LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT sONInformationReply; +}LIBLTE_S1AP_SONINFORMATION_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_SONINFORMATION_CHOICE_UNION choice; + LIBLTE_S1AP_SONINFORMATION_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_SONINFORMATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformation( + LIBLTE_S1AP_SONINFORMATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformation( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATION_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE SONConfigurationTransfer SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TARGETENB_ID_STRUCT targeteNB_ID; + LIBLTE_S1AP_SOURCEENB_ID_STRUCT sourceeNB_ID; + LIBLTE_S1AP_SONINFORMATION_STRUCT sONInformation; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sonconfigurationtransfer( + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sonconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ResetAll ENUMERATED +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_RESETALL_RESET_ALL, + LIBLTE_S1AP_RESETALL_N_ITEMS, +}LIBLTE_S1AP_RESETALL_ENUM; +static const char liblte_s1ap_resetall_text[LIBLTE_S1AP_RESETALL_N_ITEMS][80] = { + "reset-all", +}; + +typedef struct{ + bool ext; + LIBLTE_S1AP_RESETALL_ENUM e; +}LIBLTE_S1AP_RESETALL_ENUM_EXT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resetall( + LIBLTE_S1AP_RESETALL_ENUM_EXT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resetall( + uint8_t **ptr, + LIBLTE_S1AP_RESETALL_ENUM_EXT *ie); + +/******************************************************************************* +/* ProtocolIE Inter_SystemInformationTransferType CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_RIMTRANSFER, + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_ENUM; +static const char liblte_s1ap_inter_systeminformationtransfertype_choice_text[LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_N_ITEMS][50] = { + "rIMTransfer", +}; + +typedef union{ + LIBLTE_S1AP_RIMTRANSFER_STRUCT rIMTransfer; +}LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_UNION choice; + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_inter_systeminformationtransfertype( + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_inter_systeminformationtransfertype( + uint8_t **ptr, + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RAB_IE_ContainerPairList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RAB_IE_CONTAINERPAIRLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rab_ie_containerpairlist( + LIBLTE_S1AP_E_RAB_IE_CONTAINERPAIRLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rab_ie_containerpairlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RAB_IE_CONTAINERPAIRLIST_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABDataForwardingItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT dL_transportLayerAddress; + bool dL_transportLayerAddress_present; + LIBLTE_S1AP_GTP_TEID_STRUCT dL_gTP_TEID; + bool dL_gTP_TEID_present; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT uL_TransportLayerAddress; + bool uL_TransportLayerAddress_present; + LIBLTE_S1AP_GTP_TEID_STRUCT uL_GTP_TEID; + bool uL_GTP_TEID_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem( + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemHOReq SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT e_RABlevelQosParameters; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq( + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABAdmittedItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT dL_transportLayerAddress; + bool dL_transportLayerAddress_present; + LIBLTE_S1AP_GTP_TEID_STRUCT dL_gTP_TEID; + bool dL_gTP_TEID_present; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT uL_TransportLayerAddress; + bool uL_TransportLayerAddress_present; + LIBLTE_S1AP_GTP_TEID_STRUCT uL_GTP_TEID; + bool uL_GTP_TEID_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem( + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABFailedToSetupItemHOReqAck SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_CAUSE_STRUCT cause; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack( + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqack( + uint8_t **ptr, + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeSwitchedDLItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem( + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeSwitchedULItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem( + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemBearerSUReq SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT e_RABlevelQoSParameters; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_NAS_PDU_STRUCT nAS_PDU; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureq( + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABSetupItemBearerSURes SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersures( + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeModifiedItemBearerModReq SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT e_RABLevelQoSParameters; + LIBLTE_S1AP_NAS_PDU_STRUCT nAS_PDU; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifieditembearermodreq( + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifieditembearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABModifyItemBearerModRes SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodres( + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodres( + uint8_t **ptr, + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABReleaseItemBearerRelComp SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcomp( + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemCtxtSUReq SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT e_RABlevelQoSParameters; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_NAS_PDU_STRUCT nAS_PDU; + bool nAS_PDU_present; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureq( + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE E_RABSetupItemCtxtSURes SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RAB_ID_STRUCT e_RAB_ID; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT transportLayerAddress; + LIBLTE_S1AP_GTP_TEID_STRUCT gTP_TEID; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsures( + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE TAIItem SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAI_STRUCT tAI; + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT iE_Extensions; + bool iE_Extensions_present; +}LIBLTE_S1AP_TAIITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitem( + LIBLTE_S1AP_TAIITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitem( + uint8_t **ptr, + LIBLTE_S1AP_TAIITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List UE_associatedLogicalS1_ConnectionListRes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionlistres( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionlistres( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List UE_associatedLogicalS1_ConnectionListResAck DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionlistresack( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionlistresack( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE PrivateMessage SEQUENCE +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT privateIEs; +}LIBLTE_S1AP_PRIVATEMESSAGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privatemessage( + LIBLTE_S1AP_PRIVATEMESSAGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privatemessage( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEMESSAGE_STRUCT *ie); + +/******************************************************************************* +/* ProtocolIE ResetType CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_RESETTYPE_CHOICE_S1_INTERFACE, + LIBLTE_S1AP_RESETTYPE_CHOICE_PARTOFS1_INTERFACE, + LIBLTE_S1AP_RESETTYPE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_RESETTYPE_CHOICE_ENUM; +static const char liblte_s1ap_resettype_choice_text[LIBLTE_S1AP_RESETTYPE_CHOICE_N_ITEMS][50] = { + "s1_Interface", + "partOfS1_Interface", +}; + +typedef union{ + LIBLTE_S1AP_RESETALL_ENUM_EXT s1_Interface; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT partOfS1_Interface; +}LIBLTE_S1AP_RESETTYPE_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_RESETTYPE_CHOICE_UNION choice; + LIBLTE_S1AP_RESETTYPE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_RESETTYPE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resettype( + LIBLTE_S1AP_RESETTYPE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resettype( + uint8_t **ptr, + LIBLTE_S1AP_RESETTYPE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABSubjecttoDataForwardingList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsubjecttodataforwardinglist( + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsubjecttodataforwardinglist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListHOReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplisthoreq( + LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplisthoreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABAdmittedList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmittedlist( + LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmittedlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeSwitchedDLList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddllist( + LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddllist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeSwitchedULList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedullist( + LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedullist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListBearerSUReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplistbearersureq( + LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplistbearersureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABSetupListBearerSURes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuplistbearersures( + LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuplistbearersures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeModifiedListBearerModReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifiedlistbearermodreq( + LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifiedlistbearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABModifyListBearerModRes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifylistbearermodres( + LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifylistbearermodres( + uint8_t **ptr, + LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABReleaseListBearerRelComp DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaselistbearerrelcomp( + LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaselistbearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListCtxtSUReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplistctxtsureq( + LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplistctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABSetupListCtxtSURes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuplistctxtsures( + LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuplistctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List TAIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_TAIITEM_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_TAILIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailist( + LIBLTE_S1AP_TAILIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailist( + uint8_t **ptr, + LIBLTE_S1AP_TAILIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Container List E_RABFailedtoSetupListHOReqAck DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +typedef struct{ + uint32_t len; + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT buffer[32]; //WARNING: Artificial limit to reduce memory footprint +}LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetuplisthoreqack( + LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetuplisthoreqack( + uint8_t **ptr, + LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message AllocationAndRetentionPriority_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_ALLOCATIONANDRETENTIONPRIORITY_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_allocationandretentionpriority_ext( + LIBLTE_S1AP_MESSAGE_ALLOCATIONANDRETENTIONPRIORITY_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_allocationandretentionpriority_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ALLOCATIONANDRETENTIONPRIORITY_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CancelledCellinEAI_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINEAI_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai_item_ext( + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINEAI_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINEAI_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CancelledCellinTAI_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINTAI_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai_item_ext( + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINTAI_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINTAI_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CellID_Broadcast_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CELLID_BROADCAST_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_CELLID_BROADCAST_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLID_BROADCAST_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CellID_Cancelled_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CELLID_CANCELLED_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_CELLID_CANCELLED_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLID_CANCELLED_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CellBasedMDT_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CELLBASEDMDT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellbasedmdt_ext( + LIBLTE_S1AP_MESSAGE_CELLBASEDMDT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellbasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLBASEDMDT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Cdma2000OneXSRVCCInfo_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CDMA2000ONEXSRVCCINFO_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexsrvccinfo_ext( + LIBLTE_S1AP_MESSAGE_CDMA2000ONEXSRVCCINFO_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexsrvccinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CDMA2000ONEXSRVCCINFO_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CellType_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CELLTYPE_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltype_ext( + LIBLTE_S1AP_MESSAGE_CELLTYPE_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltype_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLTYPE_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CGI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CGI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cgi_ext( + LIBLTE_S1AP_MESSAGE_CGI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cgi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CGI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CSG_IdList_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CSG_IDLIST_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist_item_ext( + LIBLTE_S1AP_MESSAGE_CSG_IDLIST_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CSG_IDLIST_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message COUNTvalue_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_COUNTVALUE_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalue_ext( + LIBLTE_S1AP_MESSAGE_COUNTVALUE_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalue_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COUNTVALUE_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message COUNTValueExtended_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_COUNTVALUEEXTENDED_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalueextended_ext( + LIBLTE_S1AP_MESSAGE_COUNTVALUEEXTENDED_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalueextended_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COUNTVALUEEXTENDED_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CriticalityDiagnostics_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ext( + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CriticalityDiagnostics_IE_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_IE_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_item_ext( + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_IE_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_IE_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message EmergencyAreaID_Broadcast_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_BROADCAST_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_BROADCAST_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_BROADCAST_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message EmergencyAreaID_Cancelled_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_CANCELLED_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_CANCELLED_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_CANCELLED_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CompletedCellinEAI_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINEAI_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai_item_ext( + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINEAI_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINEAI_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message GERAN_Cell_ID_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_GERAN_CELL_ID_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_geran_cell_id_ext( + LIBLTE_S1AP_MESSAGE_GERAN_CELL_ID_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_geran_cell_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GERAN_CELL_ID_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message GlobalENB_ID_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_GLOBALENB_ID_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_globalenb_id_ext( + LIBLTE_S1AP_MESSAGE_GLOBALENB_ID_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_globalenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GLOBALENB_ID_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENB_StatusTransfer_TransparentContainer_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_statustransfer_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_statustransfer_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABInformationListItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLISTITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlistitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLISTITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlistitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLISTITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABQoSParameters_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABQOSPARAMETERS_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabqosparameters_ext( + LIBLTE_S1AP_MESSAGE_E_RABQOSPARAMETERS_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabqosparameters_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABQOSPARAMETERS_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message EUTRAN_CGI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_EUTRAN_CGI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutran_cgi_ext( + LIBLTE_S1AP_MESSAGE_EUTRAN_CGI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutran_cgi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EUTRAN_CGI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ForbiddenTAs_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_FORBIDDENTAS_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas_item_ext( + LIBLTE_S1AP_MESSAGE_FORBIDDENTAS_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_FORBIDDENTAS_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ForbiddenLAs_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_FORBIDDENLAS_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas_item_ext( + LIBLTE_S1AP_MESSAGE_FORBIDDENLAS_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_FORBIDDENLAS_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message GBR_QosInformation_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_GBR_QOSINFORMATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gbr_qosinformation_ext( + LIBLTE_S1AP_MESSAGE_GBR_QOSINFORMATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gbr_qosinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GBR_QOSINFORMATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message GUMMEI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_GUMMEI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummei_ext( + LIBLTE_S1AP_MESSAGE_GUMMEI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummei_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GUMMEI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverRestrictionList_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_HANDOVERRESTRICTIONLIST_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrestrictionlist_ext( + LIBLTE_S1AP_MESSAGE_HANDOVERRESTRICTIONLIST_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrestrictionlist_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERRESTRICTIONLIST_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LAI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_LAI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lai_ext( + LIBLTE_S1AP_MESSAGE_LAI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lai_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LAI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LoggedMDT_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_LOGGEDMDT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggedmdt_ext( + LIBLTE_S1AP_MESSAGE_LOGGEDMDT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOGGEDMDT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message M3Configuration_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_M3CONFIGURATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3configuration_ext( + LIBLTE_S1AP_MESSAGE_M3CONFIGURATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M3CONFIGURATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message M4Configuration_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_M4CONFIGURATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4configuration_ext( + LIBLTE_S1AP_MESSAGE_M4CONFIGURATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M4CONFIGURATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message M5Configuration_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_M5CONFIGURATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5configuration_ext( + LIBLTE_S1AP_MESSAGE_M5CONFIGURATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M5CONFIGURATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message M1PeriodicReporting_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_M1PERIODICREPORTING_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1periodicreporting_ext( + LIBLTE_S1AP_MESSAGE_M1PERIODICREPORTING_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1periodicreporting_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M1PERIODICREPORTING_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message RequestType_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_REQUESTTYPE_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_requesttype_ext( + LIBLTE_S1AP_MESSAGE_REQUESTTYPE_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_requesttype_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_REQUESTTYPE_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message RIMTransfer_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_RIMTRANSFER_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimtransfer_ext( + LIBLTE_S1AP_MESSAGE_RIMTRANSFER_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimtransfer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RIMTRANSFER_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SecurityContext_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_SECURITYCONTEXT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitycontext_ext( + LIBLTE_S1AP_MESSAGE_SECURITYCONTEXT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitycontext_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SECURITYCONTEXT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SourceeNB_ID_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_SOURCEENB_ID_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_id_ext( + LIBLTE_S1AP_MESSAGE_SOURCEENB_ID_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SOURCEENB_ID_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ServedGUMMEIsItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_SERVEDGUMMEISITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeisitem_ext( + LIBLTE_S1AP_MESSAGE_SERVEDGUMMEISITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeisitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SERVEDGUMMEISITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SupportedTAs_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_SUPPORTEDTAS_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas_item_ext( + LIBLTE_S1AP_MESSAGE_SUPPORTEDTAS_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SUPPORTEDTAS_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TimeSynchronizationInfo_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TIMESYNCHRONIZATIONINFO_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timesynchronizationinfo_ext( + LIBLTE_S1AP_MESSAGE_TIMESYNCHRONIZATIONINFO_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timesynchronizationinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TIMESYNCHRONIZATIONINFO_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message S_TMSI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_S_TMSI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s_tmsi_ext( + LIBLTE_S1AP_MESSAGE_S_TMSI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s_tmsi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S_TMSI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAIBasedMDT_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TAIBASEDMDT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taibasedmdt_ext( + LIBLTE_S1AP_MESSAGE_TAIBASEDMDT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taibasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIBASEDMDT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAI_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TAI_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_ext( + LIBLTE_S1AP_MESSAGE_TAI_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAI_Broadcast_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TAI_BROADCAST_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_TAI_BROADCAST_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_BROADCAST_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAI_Cancelled_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TAI_CANCELLED_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_TAI_CANCELLED_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_CANCELLED_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TABasedMDT_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TABASEDMDT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tabasedmdt_ext( + LIBLTE_S1AP_MESSAGE_TABASEDMDT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tabasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TABASEDMDT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CompletedCellinTAI_Item_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINTAI_ITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai_item_ext( + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINTAI_ITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINTAI_ITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TargeteNB_ID_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TARGETENB_ID_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_id_ext( + LIBLTE_S1AP_MESSAGE_TARGETENB_ID_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETENB_ID_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TargetRNC_ID_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TARGETRNC_ID_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_id_ext( + LIBLTE_S1AP_MESSAGE_TARGETRNC_ID_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETRNC_ID_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TargeteNB_ToSourceeNB_TransparentContainer_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_tosourceenb_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_tosourceenb_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message M1ThresholdEventA2_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_M1THRESHOLDEVENTA2_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1thresholdeventa2_ext( + LIBLTE_S1AP_MESSAGE_M1THRESHOLDEVENTA2_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1thresholdeventa2_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M1THRESHOLDEVENTA2_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Tunnel_Information_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TUNNEL_INFORMATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tunnel_information_ext( + LIBLTE_S1AP_MESSAGE_TUNNEL_INFORMATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tunnel_information_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TUNNEL_INFORMATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEAggregate_MaximumBitrates_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_UEAGGREGATE_MAXIMUMBITRATES_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueaggregate_maximumbitrates_ext( + LIBLTE_S1AP_MESSAGE_UEAGGREGATE_MAXIMUMBITRATES_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueaggregate_maximumbitrates_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UEAGGREGATE_MAXIMUMBITRATES_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UE_S1AP_ID_pair_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_UE_S1AP_ID_PAIR_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_id_pair_ext( + LIBLTE_S1AP_MESSAGE_UE_S1AP_ID_PAIR_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_id_pair_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_S1AP_ID_PAIR_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItemExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitemext( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UESecurityCapabilities_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_UESECURITYCAPABILITIES_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uesecuritycapabilities_ext( + LIBLTE_S1AP_MESSAGE_UESECURITYCAPABILITIES_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uesecuritycapabilities_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UESECURITYCAPABILITIES_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UserLocationInformation_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_USERLOCATIONINFORMATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_userlocationinformation_ext( + LIBLTE_S1AP_MESSAGE_USERLOCATIONINFORMATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_userlocationinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_USERLOCATIONINFORMATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBX2ExtTLA_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_ENBX2EXTTLA_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttla_ext( + LIBLTE_S1AP_MESSAGE_ENBX2EXTTLA_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttla_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBX2EXTTLA_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SourceeNB_ToTargeteNB_TransparentContainer_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT MobilityInformation; + bool MobilityInformation_present; +}LIBLTE_S1AP_MESSAGE_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_totargetenb_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABInformationList STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT E_RABInformationListItem; +}LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLIST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlist( + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLIST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlist( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLIST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LastVisitedEUTRANCellInformation_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT Time_UE_StayedInCell_EnhancedGranularity; + bool Time_UE_StayedInCell_EnhancedGranularity_present; + LIBLTE_S1AP_CAUSE_STRUCT HO_Cause; + bool HO_Cause_present; +}LIBLTE_S1AP_MESSAGE_LASTVISITEDEUTRANCELLINFORMATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedeutrancellinformation_ext( + LIBLTE_S1AP_MESSAGE_LASTVISITEDEUTRANCELLINFORMATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedeutrancellinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LASTVISITEDEUTRANCELLINFORMATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SONInformationReply_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT Time_Synchronization_Info; + bool Time_Synchronization_Info_present; +}LIBLTE_S1AP_MESSAGE_SONINFORMATIONREPLY_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationreply_ext( + LIBLTE_S1AP_MESSAGE_SONINFORMATIONREPLY_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationreply_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SONINFORMATIONREPLY_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Bearers_SubjectToStatusTransfer_ItemExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT ULCOUNTValueExtended; + bool ULCOUNTValueExtended_present; + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT DLCOUNTValueExtended; + bool DLCOUNTValueExtended_present; + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT ReceiveStatusOfULPDCPSDUsExtended; + bool ReceiveStatusOfULPDCPSDUsExtended_present; +}LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEMEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_itemext( + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEMEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_itemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEMEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABITEM_STRUCT E_RABItem; +}LIBLTE_S1AP_MESSAGE_E_RABITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem( + LIBLTE_S1AP_MESSAGE_E_RABITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MDT_Configuration_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MDTPLMNLIST_STRUCT SignallingBasedMDTPLMNList; + bool SignallingBasedMDTPLMNList_present; +}LIBLTE_S1AP_MESSAGE_MDT_CONFIGURATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_configuration_ext( + LIBLTE_S1AP_MESSAGE_MDT_CONFIGURATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MDT_CONFIGURATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message X2TNLConfigurationInfo_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT eNBX2ExtendedTransportLayerAddresses; + bool eNBX2ExtendedTransportLayerAddresses_present; +}LIBLTE_S1AP_MESSAGE_X2TNLCONFIGURATIONINFO_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_x2tnlconfigurationinfo_ext( + LIBLTE_S1AP_MESSAGE_X2TNLCONFIGURATIONINFO_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_x2tnlconfigurationinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_X2TNLCONFIGURATIONINFO_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Bearers_SubjectToStatusTransfer_Item STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT Bearers_SubjectToStatusTransfer_Item; +}LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_item( + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_item( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ImmediateMDT_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_M3CONFIGURATION_STRUCT M3Configuration; + bool M3Configuration_present; + LIBLTE_S1AP_M4CONFIGURATION_STRUCT M4Configuration; + bool M4Configuration_present; + LIBLTE_S1AP_M5CONFIGURATION_STRUCT M5Configuration; + bool M5Configuration_present; + LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT MDT_Location_Info; + bool MDT_Location_Info_present; +}LIBLTE_S1AP_MESSAGE_IMMEDIATEMDT_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_immediatemdt_ext( + LIBLTE_S1AP_MESSAGE_IMMEDIATEMDT_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_immediatemdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_IMMEDIATEMDT_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message SONConfigurationTransfer_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT x2TNLConfigurationInfo; + bool x2TNLConfigurationInfo_present; +}LIBLTE_S1AP_MESSAGE_SONCONFIGURATIONTRANSFER_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sonconfigurationtransfer_ext( + LIBLTE_S1AP_MESSAGE_SONCONFIGURATIONTRANSFER_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sonconfigurationtransfer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SONCONFIGURATIONTRANSFER_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TraceActivation_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT MDTConfiguration; + bool MDTConfiguration_present; +}LIBLTE_S1AP_MESSAGE_TRACEACTIVATION_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_traceactivation_ext( + LIBLTE_S1AP_MESSAGE_TRACEACTIVATION_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_traceactivation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACEACTIVATION_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverRequired STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT HandoverType; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_TARGETID_STRUCT TargetID; + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT Direct_Forwarding_Path_Availability; + bool Direct_Forwarding_Path_Availability_present; + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT SRVCCHOIndication; + bool SRVCCHOIndication_present; + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT Source_ToTarget_TransparentContainer; + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT Source_ToTarget_TransparentContainer_Secondary; + bool Source_ToTarget_TransparentContainer_Secondary_present; + LIBLTE_S1AP_MSCLASSMARK2_STRUCT MSClassmark2; + bool MSClassmark2_present; + LIBLTE_S1AP_MSCLASSMARK3_STRUCT MSClassmark3; + bool MSClassmark3_present; + LIBLTE_S1AP_CSG_ID_STRUCT CSG_Id; + bool CSG_Id_present; + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT CellAccessMode; + bool CellAccessMode_present; + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT PS_ServiceNotAvailable; + bool PS_ServiceNotAvailable_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequired( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequired( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABDataForwardingItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverPreparationFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverpreparationfailure( + LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverpreparationfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemHOReq_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT Data_Forwarding_Not_Possible; + bool Data_Forwarding_Not_Possible_present; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABAdmittedItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem_ext( + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABFailedToSetupItemHOReqAckExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACKEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqackext( + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACKEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqackext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACKEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverfailure( + LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverNotify STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_TAI_STRUCT TAI; + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT Tunnel_Information_for_BBF; + bool Tunnel_Information_for_BBF_present; + LIBLTE_S1AP_LHN_ID_STRUCT LHN_ID; + bool LHN_ID_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovernotify( + LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovernotify( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedDLItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedULItem_Ext STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_EXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_EXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_EXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message PathSwitchRequestFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequestfailure( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequestfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverCancel STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; +}LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercancel( + LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercancel( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverCancelAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercancelacknowledge( + LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercancelacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemBearerSUReqExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CORRELATION_ID_STRUCT Correlation_ID; + bool Correlation_ID_present; + LIBLTE_S1AP_CORRELATION_ID_STRUCT SIPTO_Correlation_ID; + bool SIPTO_Correlation_ID_present; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupItemBearerSUResExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURESEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersuresext( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURESEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersuresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURESEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeModifyItemBearerModReqExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT TransportInformation; + bool TransportInformation_present; +}LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFYITEMBEARERMODREQEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifyitembearermodreqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFYITEMBEARERMODREQEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifyitembearermodreqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFYITEMBEARERMODREQEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABModifyItemBearerModResExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRESEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodresext( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRESEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRESEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABReleaseCommand STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + bool uEaggregateMaximumBitrate_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABToBeReleasedList; + LIBLTE_S1AP_NAS_PDU_STRUCT NAS_PDU; + bool NAS_PDU_present; +}LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleasecommand( + LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleasecommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABReleaseItemBearerRelCompExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMPEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcompext( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMPEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcompext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMPEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABReleaseIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABReleasedList; + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT UserLocationInformation; + bool UserLocationInformation_present; +}LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseindication( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemCtxtSUReqExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CORRELATION_ID_STRUCT Correlation_ID; + bool Correlation_ID_present; + LIBLTE_S1AP_CORRELATION_ID_STRUCT SIPTO_Correlation_ID; + bool SIPTO_Correlation_ID_present; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupItemCtxtSUResExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURESEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsuresext( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURESEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsuresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURESEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message InitialContextSetupFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetupfailure( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetupfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAIItemExt STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_TAIITEMEXT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitemext( + LIBLTE_S1AP_MESSAGE_TAIITEMEXT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIITEMEXT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextReleaseRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT GWContextReleaseIndication; + bool GWContextReleaseIndication_present; +}LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleaserequest( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleaserequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextReleaseCommand STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_UE_S1AP_IDS_STRUCT UE_S1AP_IDs; + LIBLTE_S1AP_CAUSE_STRUCT Cause; +}LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleasecommand( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleasecommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextReleaseComplete STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT UserLocationInformation; + bool UserLocationInformation_present; +}LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleasecomplete( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleasecomplete( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextModificationRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_SECURITYKEY_STRUCT SecurityKey; + bool SecurityKey_present; + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT SubscriberProfileIDforRFP; + bool SubscriberProfileIDforRFP_present; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + bool uEaggregateMaximumBitrate_present; + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT CSFallbackIndicator; + bool CSFallbackIndicator_present; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT UESecurityCapabilities; + bool UESecurityCapabilities_present; + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM CSGMembershipStatus; + bool CSGMembershipStatus_present; + LIBLTE_S1AP_LAI_STRUCT RegisteredLAI; + bool RegisteredLAI_present; + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT AdditionalCSFallbackIndicator; + bool AdditionalCSFallbackIndicator_present; +}LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationrequest( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextModificationResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationresponse( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UEContextModificationFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationfailure( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UERadioCapabilityMatchRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT UERadioCapability; + bool UERadioCapability_present; +}LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapabilitymatchrequest( + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapabilitymatchrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UERadioCapabilityMatchResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT VoiceSupportMatchIndicator; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapabilitymatchresponse( + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapabilitymatchresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message DownlinkNASTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_NAS_PDU_STRUCT NAS_PDU; + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT HandoverRestrictionList; + bool HandoverRestrictionList_present; + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT SubscriberProfileIDforRFP; + bool SubscriberProfileIDforRFP_present; +}LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinknastransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinknastransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message InitialUEMessage STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_NAS_PDU_STRUCT NAS_PDU; + LIBLTE_S1AP_TAI_STRUCT TAI; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT RRC_Establishment_Cause; + LIBLTE_S1AP_S_TMSI_STRUCT S_TMSI; + bool S_TMSI_present; + LIBLTE_S1AP_CSG_ID_STRUCT CSG_Id; + bool CSG_Id_present; + LIBLTE_S1AP_GUMMEI_STRUCT GUMMEI_ID; + bool GUMMEI_ID_present; + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT CellAccessMode; + bool CellAccessMode_present; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT GW_TransportLayerAddress; + bool GW_TransportLayerAddress_present; + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT RelayNode_Indicator; + bool RelayNode_Indicator_present; + LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT GUMMEIType; + bool GUMMEIType_present; + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT Tunnel_Information_for_BBF; + bool Tunnel_Information_for_BBF_present; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT SIPTO_L_GW_TransportLayerAddress; + bool SIPTO_L_GW_TransportLayerAddress_present; + LIBLTE_S1AP_LHN_ID_STRUCT LHN_ID; + bool LHN_ID_present; +}LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialuemessage( + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialuemessage( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UplinkNASTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_NAS_PDU_STRUCT NAS_PDU; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_TAI_STRUCT TAI; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT GW_TransportLayerAddress; + bool GW_TransportLayerAddress_present; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT SIPTO_L_GW_TransportLayerAddress; + bool SIPTO_L_GW_TransportLayerAddress_present; + LIBLTE_S1AP_LHN_ID_STRUCT LHN_ID; + bool LHN_ID_present; +}LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinknastransport( + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinknastransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message NASNonDeliveryIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_NAS_PDU_STRUCT NAS_PDU; + LIBLTE_S1AP_CAUSE_STRUCT Cause; +}LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nasnondeliveryindication( + LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nasnondeliveryindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT UE_associatedLogicalS1_ConnectionItem; +}LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitem( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItemRes STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT UE_associatedLogicalS1_ConnectionItem; +}LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMRES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitemres( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMRES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitemres( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMRES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ErrorIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + bool MME_UE_S1AP_ID_present; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + bool eNB_UE_S1AP_ID_present; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + bool Cause_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_errorindication( + LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_errorindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message S1SetupRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT Global_ENB_ID; + LIBLTE_S1AP_ENBNAME_STRUCT eNBname; + bool eNBname_present; + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT SupportedTAs; + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT DefaultPagingDRX; + LIBLTE_S1AP_CSG_IDLIST_STRUCT CSG_IdList; + bool CSG_IdList_present; +}LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setuprequest( + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message S1SetupResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MMENAME_STRUCT MMEname; + bool MMEname_present; + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT ServedGUMMEIs; + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT RelativeMMECapacity; + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT MMERelaySupportIndicator; + bool MMERelaySupportIndicator_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setupresponse( + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message S1SetupFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT TimeToWait; + bool TimeToWait_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setupfailure( + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setupfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdate STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENBNAME_STRUCT eNBname; + bool eNBname_present; + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT SupportedTAs; + bool SupportedTAs_present; + LIBLTE_S1AP_CSG_IDLIST_STRUCT CSG_IdList; + bool CSG_IdList_present; + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT DefaultPagingDRX; + bool DefaultPagingDRX_present; +}LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdate( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdate( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdateAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdateacknowledge( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdateacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdateFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT TimeToWait; + bool TimeToWait_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdatefailure( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdatefailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdate STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MMENAME_STRUCT MMEname; + bool MMEname_present; + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT ServedGUMMEIs; + bool ServedGUMMEIs_present; + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT RelativeMMECapacity; + bool RelativeMMECapacity_present; +}LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdate( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdate( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdateAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdateacknowledge( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdateacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdateFailure STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT TimeToWait; + bool TimeToWait_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdatefailure( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdatefailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UplinkS1cdma2000tunneling STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT cdma2000RATType; + LIBLTE_S1AP_CDMA2000SECTORID_STRUCT cdma2000SectorID; + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT cdma2000HORequiredIndication; + bool cdma2000HORequiredIndication_present; + LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT cdma2000OneXSRVCCInfo; + bool cdma2000OneXSRVCCInfo_present; + LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT cdma2000OneXRAND; + bool cdma2000OneXRAND_present; + LIBLTE_S1AP_CDMA2000PDU_STRUCT cdma2000PDU; + LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT EUTRANRoundTripDelayEstimationInfo; + bool EUTRANRoundTripDelayEstimationInfo_present; +}LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinks1cdma2000tunneling( + LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinks1cdma2000tunneling( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UECapabilityInfoIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT UERadioCapability; +}LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecapabilityinfoindication( + LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecapabilityinfoindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBStatusTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT eNB_StatusTransfer_TransparentContainer; +}LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbstatustransfer( + LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbstatustransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEStatusTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT eNB_StatusTransfer_TransparentContainer; +}LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmestatustransfer( + LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmestatustransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TraceStart STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_TRACEACTIVATION_STRUCT TraceActivation; +}LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracestart( + LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracestart( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TraceFailureIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT E_UTRAN_Trace_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; +}LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracefailureindication( + LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracefailureindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message DeactivateTrace STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT E_UTRAN_Trace_ID; +}LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_deactivatetrace( + LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_deactivatetrace( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message CellTrafficTrace STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT E_UTRAN_Trace_ID; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT TraceCollectionEntityIPAddress; + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT PrivacyIndicator; + bool PrivacyIndicator_present; +}LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltraffictrace( + LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltraffictrace( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LocationReportingControl STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_REQUESTTYPE_STRUCT RequestType; +}LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreportingcontrol( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreportingcontrol( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LocationReportingFailureIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CAUSE_STRUCT Cause; +}LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreportingfailureindication( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreportingfailureindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message LocationReport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_TAI_STRUCT TAI; + LIBLTE_S1AP_REQUESTTYPE_STRUCT RequestType; +}LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreport( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message OverloadStart STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT OverloadResponse; + LIBLTE_S1AP_GUMMEILIST_STRUCT GUMMEIList; + bool GUMMEIList_present; + LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT TrafficLoadReductionIndication; + bool TrafficLoadReductionIndication_present; +}LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadstart( + LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadstart( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message OverloadStop STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_GUMMEILIST_STRUCT GUMMEIList; + bool GUMMEIList_present; +}LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadstop( + LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadstop( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message WriteReplaceWarningRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT MessageIdentifier; + LIBLTE_S1AP_SERIALNUMBER_STRUCT SerialNumber; + LIBLTE_S1AP_WARNINGAREALIST_STRUCT WarningAreaList; + bool WarningAreaList_present; + LIBLTE_S1AP_REPETITIONPERIOD_STRUCT RepetitionPeriod; + LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT ExtendedRepetitionPeriod; + bool ExtendedRepetitionPeriod_present; + LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT NumberofBroadcastRequest; + LIBLTE_S1AP_WARNINGTYPE_STRUCT WarningType; + bool WarningType_present; + LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT WarningSecurityInfo; + bool WarningSecurityInfo_present; + LIBLTE_S1AP_DATACODINGSCHEME_STRUCT DataCodingScheme; + bool DataCodingScheme_present; + LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT WarningMessageContents; + bool WarningMessageContents_present; + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM ConcurrentWarningMessageIndicator; + bool ConcurrentWarningMessageIndicator_present; +}LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_writereplacewarningrequest( + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_writereplacewarningrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message WriteReplaceWarningResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT MessageIdentifier; + LIBLTE_S1AP_SERIALNUMBER_STRUCT SerialNumber; + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT BroadcastCompletedAreaList; + bool BroadcastCompletedAreaList_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_writereplacewarningresponse( + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_writereplacewarningresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEDirectInformationTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT Inter_SystemInformationTransferTypeMDT; +}LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmedirectinformationtransfer( + LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmedirectinformationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBConfigurationTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT SONConfigurationTransferECT; + bool SONConfigurationTransferECT_present; +}LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationtransfer( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message MMEConfigurationTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT SONConfigurationTransferMCT; + bool SONConfigurationTransferMCT_present; +}LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationtransfer( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message PrivateMessage STRUCT +********************************************************************************/ +typedef struct{ + bool ext; +}LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privatemessage( + LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privatemessage( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message KillRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT MessageIdentifier; + LIBLTE_S1AP_SERIALNUMBER_STRUCT SerialNumber; + LIBLTE_S1AP_WARNINGAREALIST_STRUCT WarningAreaList; + bool WarningAreaList_present; + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM KillAllWarningMessages; + bool KillAllWarningMessages_present; +}LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killrequest( + LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message KillResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT MessageIdentifier; + LIBLTE_S1AP_SERIALNUMBER_STRUCT SerialNumber; + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT BroadcastCancelledAreaList; + bool BroadcastCancelledAreaList_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killresponse( + LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message PWSRestartIndication STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT ECGIListForRestart; + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT Global_ENB_ID; + LIBLTE_S1AP_TAILISTFORRESTART_STRUCT TAIListForRestart; + LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT EmergencyAreaIDListForRestart; + bool EmergencyAreaIDListForRestart_present; +}LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pwsrestartindication( + LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pwsrestartindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message DownlinkUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_ROUTING_ID_STRUCT Routing_ID; + LIBLTE_S1AP_LPPA_PDU_STRUCT LPPa_PDU; +}LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinkueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinkueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UplinkUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_ROUTING_ID_STRUCT Routing_ID; + LIBLTE_S1AP_LPPA_PDU_STRUCT LPPa_PDU; +}LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinkueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinkueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message DownlinkNonUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ROUTING_ID_STRUCT Routing_ID; + LIBLTE_S1AP_LPPA_PDU_STRUCT LPPa_PDU; +}LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinknonueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinknonueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message UplinkNonUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ROUTING_ID_STRUCT Routing_ID; + LIBLTE_S1AP_LPPA_PDU_STRUCT LPPa_PDU; +}LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinknonueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinknonueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ENBDirectInformationTransfer STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT Inter_SystemInformationTransferTypeEDT; +}LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbdirectinformationtransfer( + LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbdirectinformationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABDataForwardingItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT E_RABDataForwardingItem; +}LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem( + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemHOReq STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT E_RABToBeSetupItemHOReq; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABAdmittedItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT E_RABAdmittedItem; +}LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem( + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABFailedtoSetupItemHOReqAck STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT E_RABFailedtoSetupItemHOReqAck; +}LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack( + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqack( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedDLItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT E_RABToBeSwitchedDLItem; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedULItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT E_RABToBeSwitchedULItem; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemBearerSUReq STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT E_RABToBeSetupItemBearerSUReq; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupItemBearerSURes STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT E_RABSetupItemBearerSURes; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersures( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersures( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeModifiedItemBearerModReq STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT E_RABToBeModifiedItemBearerModReq; +}LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifieditembearermodreq( + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifieditembearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABModifyItemBearerModRes STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT E_RABModifyItemBearerModRes; +}LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodres( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodres( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABReleaseItemBearerRelComp STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT E_RABReleaseItemBearerRelComp; +}LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMP_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcomp( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemCtxtSUReq STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT E_RABToBeSetupItemCtxtSUReq; +}LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupItemCtxtSURes STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT E_RABSetupItemCtxtSURes; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURES_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsures( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURES_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURES_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message TAIItem STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_TAIITEM_STRUCT TAIItem; +}LIBLTE_S1AP_MESSAGE_TAIITEM_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitem( + LIBLTE_S1AP_MESSAGE_TAIITEM_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIITEM_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message ResetAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT UE_associatedLogicalS1_ConnectionListResAck; + bool UE_associatedLogicalS1_ConnectionListResAck_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resetacknowledge( + LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resetacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Reset STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_RESETTYPE_STRUCT ResetType; +}LIBLTE_S1AP_MESSAGE_RESET_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reset( + LIBLTE_S1AP_MESSAGE_RESET_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reset( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RESET_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message DownlinkS1cdma2000tunneling STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT E_RABSubjecttoDataForwardingList; + bool E_RABSubjecttoDataForwardingList_present; + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT cdma2000HOStatus; + bool cdma2000HOStatus_present; + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT cdma2000RATType; + LIBLTE_S1AP_CDMA2000PDU_STRUCT cdma2000PDU; +}LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinks1cdma2000tunneling( + LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinks1cdma2000tunneling( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverCommand STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT HandoverType; + LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT NASSecurityParametersfromE_UTRAN; + bool NASSecurityParametersfromE_UTRAN_present; + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT E_RABSubjecttoDataForwardingList; + bool E_RABSubjecttoDataForwardingList_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABtoReleaseListHOCmd; + bool E_RABtoReleaseListHOCmd_present; + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT Target_ToSource_TransparentContainer; + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT Target_ToSource_TransparentContainer_Secondary; + bool Target_ToSource_TransparentContainer_Secondary_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercommand( + LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT HandoverType; + LIBLTE_S1AP_CAUSE_STRUCT Cause; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT E_RABToBeSetupListHOReq; + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT Source_ToTarget_TransparentContainer; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT UESecurityCapabilities; + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT HandoverRestrictionList; + bool HandoverRestrictionList_present; + LIBLTE_S1AP_TRACEACTIVATION_STRUCT TraceActivation; + bool TraceActivation_present; + LIBLTE_S1AP_REQUESTTYPE_STRUCT RequestType; + bool RequestType_present; + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT SRVCCOperationPossible; + bool SRVCCOperationPossible_present; + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT SecurityContext; + LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT NASSecurityParameterstoE_UTRAN; + bool NASSecurityParameterstoE_UTRAN_present; + LIBLTE_S1AP_CSG_ID_STRUCT CSG_Id; + bool CSG_Id_present; + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM CSGMembershipStatus; + bool CSGMembershipStatus_present; + LIBLTE_S1AP_GUMMEI_STRUCT GUMMEI_ID; + bool GUMMEI_ID_present; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID_2; + bool MME_UE_S1AP_ID_2_present; + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT ManagementBasedMDTAllowed; + bool ManagementBasedMDTAllowed_present; + LIBLTE_S1AP_MDTPLMNLIST_STRUCT ManagementBasedMDTPLMNList; + bool ManagementBasedMDTPLMNList_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequest( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message PathSwitchRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT E_RABToBeSwitchedDLList; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT SourceMME_UE_S1AP_ID; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT EUTRAN_CGI; + LIBLTE_S1AP_TAI_STRUCT TAI; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT UESecurityCapabilities; + LIBLTE_S1AP_CSG_ID_STRUCT CSG_Id; + bool CSG_Id_present; + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT CellAccessMode; + bool CellAccessMode_present; + LIBLTE_S1AP_GUMMEI_STRUCT SourceMME_GUMMEI; + bool SourceMME_GUMMEI_present; + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM CSGMembershipStatus; + bool CSGMembershipStatus_present; + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT Tunnel_Information_for_BBF; + bool Tunnel_Information_for_BBF_present; + LIBLTE_S1AP_LHN_ID_STRUCT LHN_ID; + bool LHN_ID_present; +}LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequest( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message PathSwitchRequestAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + bool uEaggregateMaximumBitrate_present; + LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT E_RABToBeSwitchedULList; + bool E_RABToBeSwitchedULList_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABToBeReleasedList; + bool E_RABToBeReleasedList_present; + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT SecurityContext; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID_2; + bool MME_UE_S1AP_ID_2_present; + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM CSGMembershipStatus; + bool CSGMembershipStatus_present; +}LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequestacknowledge( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequestacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + bool uEaggregateMaximumBitrate_present; + LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT E_RABToBeSetupListBearerSUReq; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuprequest( + LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABSetupResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT E_RABSetupListBearerSURes; + bool E_RABSetupListBearerSURes_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABFailedToSetupListBearerSURes; + bool E_RABFailedToSetupListBearerSURes_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupresponse( + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABModifyRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + bool uEaggregateMaximumBitrate_present; + LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT E_RABToBeModifiedListBearerModReq; +}LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyrequest( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABModifyResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT E_RABModifyListBearerModRes; + bool E_RABModifyListBearerModRes_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABFailedToModifyList; + bool E_RABFailedToModifyList_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyresponse( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message E_RABReleaseResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT E_RABReleaseListBearerRelComp; + bool E_RABReleaseListBearerRelComp_present; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABFailedToReleaseList; + bool E_RABFailedToReleaseList_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT UserLocationInformation; + bool UserLocationInformation_present; +}LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseresponse( + LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message InitialContextSetupRequest STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT uEaggregateMaximumBitrate; + LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT E_RABToBeSetupListCtxtSUReq; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT UESecurityCapabilities; + LIBLTE_S1AP_SECURITYKEY_STRUCT SecurityKey; + LIBLTE_S1AP_TRACEACTIVATION_STRUCT TraceActivation; + bool TraceActivation_present; + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT HandoverRestrictionList; + bool HandoverRestrictionList_present; + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT UERadioCapability; + bool UERadioCapability_present; + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT SubscriberProfileIDforRFP; + bool SubscriberProfileIDforRFP_present; + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT CSFallbackIndicator; + bool CSFallbackIndicator_present; + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT SRVCCOperationPossible; + bool SRVCCOperationPossible_present; + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM CSGMembershipStatus; + bool CSGMembershipStatus_present; + LIBLTE_S1AP_LAI_STRUCT RegisteredLAI; + bool RegisteredLAI_present; + LIBLTE_S1AP_GUMMEI_STRUCT GUMMEI_ID; + bool GUMMEI_ID_present; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID_2; + bool MME_UE_S1AP_ID_2_present; + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT ManagementBasedMDTAllowed; + bool ManagementBasedMDTAllowed_present; + LIBLTE_S1AP_MDTPLMNLIST_STRUCT ManagementBasedMDTPLMNList; + bool ManagementBasedMDTPLMNList_present; + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT AdditionalCSFallbackIndicator; + bool AdditionalCSFallbackIndicator_present; +}LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetuprequest( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message InitialContextSetupResponse STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT E_RABSetupListCtxtSURes; + LIBLTE_S1AP_E_RABLIST_STRUCT E_RABFailedToSetupListCtxtSURes; + bool E_RABFailedToSetupListCtxtSURes_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; +}LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetupresponse( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message Paging STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT UEIdentityIndexValue; + LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID; + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT pagingDRX; + bool pagingDRX_present; + LIBLTE_S1AP_CNDOMAIN_ENUM CNDomain; + LIBLTE_S1AP_TAILIST_STRUCT TAIList; + LIBLTE_S1AP_CSG_IDLIST_STRUCT CSG_IdList; + bool CSG_IdList_present; + LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT PagingPriority; + bool PagingPriority_present; +}LIBLTE_S1AP_MESSAGE_PAGING_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_paging( + LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_paging( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *ie); + +/******************************************************************************* +/* Protocol Message HandoverRequestAcknowledge STRUCT +********************************************************************************/ +typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT E_RABAdmittedList; + LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT E_RABFailedToSetupListHOReqAck; + bool E_RABFailedToSetupListHOReqAck_present; + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT Target_ToSource_TransparentContainer; + LIBLTE_S1AP_CSG_ID_STRUCT CSG_Id; + bool CSG_Id_present; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT CellAccessMode; + bool CellAccessMode_present; +}LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequestacknowledge( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT *ie, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequestacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT *ie); + +/******************************************************************************* +/* Procedure code criticality lookups +********************************************************************************/ +static const LIBLTE_S1AP_CRITICALITY_ENUM liblte_s1ap_procedure_criticality[50] = { + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + LIBLTE_S1AP_CRITICALITY_REJECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, +}; + +/******************************************************************************* +/* ProtocolIE-Field +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_header( + uint32_t len, + uint32_t ie_id, + LIBLTE_S1AP_CRITICALITY_ENUM crit, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_header( + uint8_t **ptr, + uint32_t *ie_id, + LIBLTE_S1AP_CRITICALITY_ENUM *crit, + uint32_t *len); + +/******************************************************************************* +/* InitiatingMessage CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGCONTROL, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKS1CDMA2000TUNNELING, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNONUEASSOCIATEDLPPATRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKS1CDMA2000TUNNELING, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACESTART, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERCANCEL, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UERADIOCAPABILITYMATCHREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMEDIRECTINFORMATIONTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACEFAILUREINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONUPDATE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_WRITEREPLACEWARNINGREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBDIRECTINFORMATIONTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKUEASSOCIATEDLPPATRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASECOMMAND, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_NASNONDELIVERYINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONUPDATE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKUEASSOCIATEDLPPATRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABMODIFYREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTMODIFICATIONREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_RESET, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTART, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASEINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGFAILUREINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DEACTIVATETRACE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PATHSWITCHREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTOP, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERNOTIFY, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PWSRESTARTINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMESTATUSTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_CELLTRAFFICTRACE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_KILLREQUEST, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PRIVATEMESSAGE, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBSTATUSTRANSFER, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ERRORINDICATION, + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_N_ITEMS, +}LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENUM; +static const char liblte_s1ap_initiatingmessage_choice_text[LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_N_ITEMS][50] = { + "LocationReportingControl", + "DownlinkS1cdma2000tunneling", + "S1SetupRequest", + "UECapabilityInfoIndication", + "LocationReport", + "UplinkNonUEAssociatedLPPaTransport", + "UplinkS1cdma2000tunneling", + "MMEConfigurationTransfer", + "TraceStart", + "HandoverCancel", + "UERadioCapabilityMatchRequest", + "DownlinkNASTransport", + "InitialContextSetupRequest", + "HandoverRequired", + "MMEDirectInformationTransfer", + "TraceFailureIndication", + "MMEConfigurationUpdate", + "WriteReplaceWarningRequest", + "ENBDirectInformationTransfer", + "DownlinkUEAssociatedLPPaTransport", + "E-RABReleaseCommand", + "NASNonDeliveryIndication", + "ENBConfigurationUpdate", + "UplinkUEAssociatedLPPaTransport", + "InitialUEMessage", + "E-RABModifyRequest", + "UEContextModificationRequest", + "E-RABSetupRequest", + "Reset", + "OverloadStart", + "E-RABReleaseIndication", + "LocationReportingFailureIndication", + "DeactivateTrace", + "PathSwitchRequest", + "HandoverRequest", + "DownlinkNonUEAssociatedLPPaTransport", + "OverloadStop", + "Paging", + "HandoverNotify", + "PWSRestartIndication", + "UEContextReleaseRequest", + "UplinkNASTransport", + "ENBConfigurationTransfer", + "MMEStatusTransfer", + "CellTrafficTrace", + "UEContextReleaseCommand", + "KillRequest", + "PrivateMessage", + "ENBStatusTransfer", + "ErrorIndication", +}; + +typedef union{ + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT LocationReportingControl; + LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT DownlinkS1cdma2000tunneling; + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT S1SetupRequest; + LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT UECapabilityInfoIndication; + LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT LocationReport; + LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT UplinkNonUEAssociatedLPPaTransport; + LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT UplinkS1cdma2000tunneling; + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT MMEConfigurationTransfer; + LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT TraceStart; + LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT HandoverCancel; + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT UERadioCapabilityMatchRequest; + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT DownlinkNASTransport; + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT InitialContextSetupRequest; + LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT HandoverRequired; + LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT MMEDirectInformationTransfer; + LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT TraceFailureIndication; + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT MMEConfigurationUpdate; + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT WriteReplaceWarningRequest; + LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT ENBDirectInformationTransfer; + LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT DownlinkUEAssociatedLPPaTransport; + LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT E_RABReleaseCommand; + LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT NASNonDeliveryIndication; + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT ENBConfigurationUpdate; + LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT UplinkUEAssociatedLPPaTransport; + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT InitialUEMessage; + LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT E_RABModifyRequest; + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT UEContextModificationRequest; + LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT E_RABSetupRequest; + LIBLTE_S1AP_MESSAGE_RESET_STRUCT Reset; + LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT OverloadStart; + LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT E_RABReleaseIndication; + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT LocationReportingFailureIndication; + LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT DeactivateTrace; + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT PathSwitchRequest; + LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT HandoverRequest; + LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT DownlinkNonUEAssociatedLPPaTransport; + LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT OverloadStop; + LIBLTE_S1AP_MESSAGE_PAGING_STRUCT Paging; + LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT HandoverNotify; + LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT PWSRestartIndication; + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT UEContextReleaseRequest; + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT UplinkNASTransport; + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT ENBConfigurationTransfer; + LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT MMEStatusTransfer; + LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT CellTrafficTrace; + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT UEContextReleaseCommand; + LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT KillRequest; + LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT PrivateMessage; + LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT ENBStatusTransfer; + LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT ErrorIndication; +}LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UNION; + +typedef struct{ + uint8_t procedureCode; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UNION choice; + LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initiatingmessage( + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initiatingmessage( + uint8_t **ptr, + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg); + +/******************************************************************************* +/* UnsuccessfulOutcome CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERPREPARATIONFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEFAILURE, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_N_ITEMS, +}LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_ENUM; +static const char liblte_s1ap_unsuccessfuloutcome_choice_text[LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_N_ITEMS][50] = { + "S1SetupFailure", + "PathSwitchRequestFailure", + "UEContextModificationFailure", + "InitialContextSetupFailure", + "ENBConfigurationUpdateFailure", + "HandoverPreparationFailure", + "HandoverFailure", + "MMEConfigurationUpdateFailure", +}; + +typedef union{ + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT S1SetupFailure; + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT PathSwitchRequestFailure; + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT UEContextModificationFailure; + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT InitialContextSetupFailure; + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT ENBConfigurationUpdateFailure; + LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT HandoverPreparationFailure; + LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT HandoverFailure; + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT MMEConfigurationUpdateFailure; +}LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UNION; + +typedef struct{ + uint8_t procedureCode; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UNION choice; + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_unsuccessfuloutcome( + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_unsuccessfuloutcome( + uint8_t **ptr, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg); + +/******************************************************************************* +/* SuccessfulOutcome CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERREQUESTACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UERADIOCAPABILITYMATCHRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_RESETACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABMODIFYRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_WRITEREPLACEWARNINGRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_KILLRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONRESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCOMMAND, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCANCELACKNOWLEDGE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABRELEASERESPONSE, + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_N_ITEMS, +}LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_ENUM; +static const char liblte_s1ap_successfuloutcome_choice_text[LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_N_ITEMS][50] = { + "HandoverRequestAcknowledge", + "UEContextReleaseComplete", + "UERadioCapabilityMatchResponse", + "InitialContextSetupResponse", + "E-RABSetupResponse", + "PathSwitchRequestAcknowledge", + "MMEConfigurationUpdateAcknowledge", + "ResetAcknowledge", + "ENBConfigurationUpdateAcknowledge", + "E-RABModifyResponse", + "WriteReplaceWarningResponse", + "S1SetupResponse", + "KillResponse", + "UEContextModificationResponse", + "HandoverCommand", + "HandoverCancelAcknowledge", + "E-RABReleaseResponse", +}; + +typedef union{ + LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT HandoverRequestAcknowledge; + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT UEContextReleaseComplete; + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT UERadioCapabilityMatchResponse; + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT InitialContextSetupResponse; + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT E_RABSetupResponse; + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT PathSwitchRequestAcknowledge; + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT MMEConfigurationUpdateAcknowledge; + LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT ResetAcknowledge; + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT ENBConfigurationUpdateAcknowledge; + LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT E_RABModifyResponse; + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT WriteReplaceWarningResponse; + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT S1SetupResponse; + LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT KillResponse; + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT UEContextModificationResponse; + LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT HandoverCommand; + LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT HandoverCancelAcknowledge; + LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT E_RABReleaseResponse; +}LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UNION; + +typedef struct{ + uint8_t procedureCode; + LIBLTE_S1AP_CRITICALITY_ENUM criticality; + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UNION choice; + LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_successfuloutcome( + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg, + uint8_t **ptr); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_successfuloutcome( + uint8_t **ptr, + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + +/******************************************************************************* +/* S1AP_PDU CHOICE +********************************************************************************/ +typedef enum{ + LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE, + LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME, + LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME, + LIBLTE_S1AP_S1AP_PDU_CHOICE_N_ITEMS, +}LIBLTE_S1AP_S1AP_PDU_CHOICE_ENUM; +static const char liblte_s1ap_s1ap_pdu_choice_text[LIBLTE_S1AP_S1AP_PDU_CHOICE_N_ITEMS][50] = { + "initiatingMessage", + "successfulOutcome", + "unsuccessfulOutcome", +}; + +typedef union{ + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT initiatingMessage; + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT successfulOutcome; + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT unsuccessfulOutcome; +}LIBLTE_S1AP_S1AP_PDU_CHOICE_UNION; + +typedef struct{ + bool ext; + LIBLTE_S1AP_S1AP_PDU_CHOICE_UNION choice; + LIBLTE_S1AP_S1AP_PDU_CHOICE_ENUM choice_type; +}LIBLTE_S1AP_S1AP_PDU_STRUCT; + +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1ap_pdu( + LIBLTE_S1AP_S1AP_PDU_STRUCT *s1ap_pdu, + LIBLTE_BYTE_MSG_STRUCT *msg); +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1ap_pdu( + LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_S1AP_S1AP_PDU_STRUCT *s1ap_pdu); +#endif // SRSLTE_LIBLTE_S1AP_H diff --git a/lib/include/srslte/build_info.h.in b/lib/include/srslte/build_info.h.in new file mode 100644 index 0000000..5409ab0 --- /dev/null +++ b/lib/include/srslte/build_info.h.in @@ -0,0 +1,64 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_BUILD_INFO_H_IN +#define SRSLTE_BUILD_INFO_H_IN + +# ifdef __cplusplus +extern "C" { +# endif + +#ifdef BUILD_TYPE_RELEASE + static char build_mode[] = "Release"; +#else + #ifdef BUILD_TYPE_DEBUG + static char build_mode[] = "Debug"; + #else + #ifdef BUILD_TYPE_RELWITHDEBINFO + static char build_mode[] = "RelWithDebInfo"; + #else + static char build_mode[] = "unknown"; + #endif + #endif +#endif + +// the configured build options for srsLTE +static char build_info[] = "commit @GIT_COMMIT_HASH@ on branch @GIT_BRANCH@"; + +SRSLTE_API char* srslte_get_build_info() { + return build_info; +}; + +SRSLTE_API char* srslte_get_build_mode() { + return build_mode; +} + +# ifdef __cplusplus +} +# endif + +#endif // BUILD_INFO_ diff --git a/lib/include/srslte/common/bcd_helpers.h b/lib/include/srslte/common/bcd_helpers.h new file mode 100644 index 0000000..b145a2d --- /dev/null +++ b/lib/include/srslte/common/bcd_helpers.h @@ -0,0 +1,197 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_BCD_HELPERS_H +#define SRSLTE_BCD_HELPERS_H + +#include +#include +#include +#include + +namespace srslte { + +/****************************************************************************** + * Convert between string and BCD-coded MCC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MCC 001 results in 0xF001 + *****************************************************************************/ +inline bool string_to_mcc(std::string str, uint16_t *mcc) +{ + uint32_t len = str.size(); + if(len != 3) { + return false; + } + if(!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2])) { + return false; + } + *mcc = 0xF000; + *mcc |= ((uint8_t)(str[0]-'0') << 8); + *mcc |= ((uint8_t)(str[1]-'0') << 4); + *mcc |= ((uint8_t)(str[2]-'0')); + return true; +} + +inline bool mcc_to_string(uint16_t mcc, std::string *str) +{ + if((mcc & 0xF000) != 0xF000) { + return false; + } + *str = ""; + *str += ((mcc & 0x0F00) >> 8) + '0'; + *str += ((mcc & 0x00F0) >> 4) + '0'; + *str += (mcc & 0x000F) + '0'; + return true; +} + +/****************************************************************************** + * Convert between string and BCD-coded MNC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 results in 0xF001 + * MNC 01 results in 0xFF01 + *****************************************************************************/ +inline bool string_to_mnc(std::string str, uint16_t *mnc) +{ + uint32_t len = str.size(); + if(len != 3 && len != 2) { + return false; + } + if(len == 3) { + if(!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2])) { + return false; + } + *mnc = 0xF000; + *mnc |= ((uint8_t)(str[0]-'0') << 8); + *mnc |= ((uint8_t)(str[1]-'0') << 4); + *mnc |= ((uint8_t)(str[2]-'0')); + } + if(len == 2) { + if(!isdigit(str[0]) || !isdigit(str[1])) { + return false; + } + *mnc = 0xFF00; + *mnc |= ((uint8_t)(str[0]-'0') << 4); + *mnc |= ((uint8_t)(str[1]-'0')); + } + + return true; +} + +inline bool mnc_to_string(uint16_t mnc, std::string *str) +{ + if((mnc & 0xF000) != 0xF000) { + return false; + } + *str = ""; + if((mnc & 0xFF00) != 0xFF00) { + *str += ((mnc & 0x0F00) >> 8) + '0'; + } + *str += ((mnc & 0x00F0) >> 4) + '0'; + *str += (mnc & 0x000F) + '0'; + return true; +} +inline std::string plmn_id_to_string(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) { + std::string mcc_str, mnc_str; + mnc_to_string(plmn_id.mnc, &mnc_str); + mcc_to_string(plmn_id.mcc, &mcc_str); + return mcc_str + mnc_str; +} + +/****************************************************************************** + * Convert PLMN to BCD-coded MCC and MNC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_plmn_to_mccmnc(uint32_t plmn, uint16_t *mcc, uint16_t *mnc) +{ + uint8_t nibbles[6]; + nibbles[0] = (plmn & 0xF00000) >> 20; + nibbles[1] = (plmn & 0x0F0000) >> 16; + nibbles[2] = (plmn & 0x00F000) >> 12; + nibbles[3] = (plmn & 0x000F00) >> 8; + nibbles[4] = (plmn & 0x0000F0) >> 4; + nibbles[5] = (plmn & 0x00000F); + + *mcc = 0xF000; + *mnc = 0xF000; + *mcc |= nibbles[1] << 8; // MCC digit 1 + *mcc |= nibbles[0] << 4; // MCC digit 2 + *mcc |= nibbles[3]; // MCC digit 3 + + if(nibbles[2] == 0xF) { + // 2-digit MNC + *mnc |= 0x0F00; // MNC digit 1 + *mnc |= nibbles[5] << 4; // MNC digit 2 + *mnc |= nibbles[4]; // MNC digit 3 + } else { + // 3-digit MNC + *mnc |= nibbles[5] << 8; // MNC digit 1 + *mnc |= nibbles[4] << 4; // MNC digit 2 + *mnc |= nibbles[2] ; // MNC digit 3 + } +} + +/****************************************************************************** + * Convert BCD-coded MCC and MNC to PLMN. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn) +{ + uint8_t nibbles[6]; + nibbles[1] = (mcc & 0x0F00) >> 8; // MCC digit 1 + nibbles[0] = (mcc & 0x00F0) >> 4; // MCC digit 2 + nibbles[3] = (mcc & 0x000F); // MCC digit 3 + + if((mnc & 0xFF00) == 0xFF00) { + // 2-digit MNC + nibbles[2] = 0x0F; // MNC digit 1 + nibbles[5] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[4] = (mnc & 0x000F); // MNC digit 3 + } else { + // 3-digit MNC + nibbles[5] = (mnc & 0x0F00) >> 8; // MNC digit 1 + nibbles[4] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[2] = (mnc & 0x000F); // MNC digit 3 + } + + *plmn = 0x000000; + *plmn |= nibbles[0] << 20; + *plmn |= nibbles[1] << 16; + *plmn |= nibbles[2] << 12; + *plmn |= nibbles[3] << 8; + *plmn |= nibbles[4] << 4; + *plmn |= nibbles[5]; +} + + +} // namespace srslte + +#endif // SRSLTE_BCD_HELPERS_H diff --git a/lib/include/srslte/common/block_queue.h b/lib/include/srslte/common/block_queue.h new file mode 100644 index 0000000..63b0e41 --- /dev/null +++ b/lib/include/srslte/common/block_queue.h @@ -0,0 +1,213 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +/****************************************************************************** + * File: block_queue.h + * Description: General-purpose blocking queue. It can behave as a bounded or + * unbounded blocking queue and allows blocking and non-blocking + * operations in both push and pop + *****************************************************************************/ + + +#ifndef SRSLTE_BLOCK_QUEUE_H +#define SRSLTE_BLOCK_QUEUE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace srslte { + +template +class block_queue { + +public: + + // Callback functions for mutexed operations inside pop/push methods + class call_mutexed_itf { + public: + virtual void popping(myobj obj) = 0; + virtual void pushing(myobj obj) = 0; + }; + + block_queue(int capacity = -1) { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cv_empty, NULL); + pthread_cond_init(&cv_full, NULL); + this->capacity = capacity; + mutexed_callback = NULL; + enable = true; + num_threads = 0; + } + ~block_queue() { + // Unlock threads waiting at push or pop + pthread_mutex_lock(&mutex); + enable = false; + pthread_cond_signal(&cv_full); + pthread_cond_signal(&cv_empty); + pthread_mutex_unlock(&mutex); + + // Wait threads blocked in push/pop to exit + while(num_threads>0) { + usleep(100); + } + + // Wait them to exit and destroy cv and mutex + pthread_mutex_lock(&mutex); + pthread_cond_destroy(&cv_full); + pthread_cond_destroy(&cv_empty); + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); + } + void set_mutexed_itf(call_mutexed_itf *itf) { + mutexed_callback = itf; + } + void resize(int new_capacity) { + capacity = new_capacity; + } + + void push(const myobj& value) { + push_(value, true); + } + + bool try_push(const myobj& value) { + return push_(value, false); + } + + bool try_pop(myobj *value) { + return pop_(value, false); + } + + myobj wait_pop() { // blocking pop + myobj value; + bzero(&value, sizeof(myobj)); + pop_(&value, true); + return value; + } + + bool empty() { // queue is empty? + pthread_mutex_lock(&mutex); + bool ret = q.empty(); + pthread_mutex_unlock(&mutex); + return ret; + } + + void clear() { // remove all items + myobj item; + while (try_pop(item)); + } + + myobj front() { + return q.front(); + } + + size_t size() { + return q.size(); + } + +private: + + bool pop_(myobj *value, bool block) { + if (!enable) { + return false; + } + pthread_mutex_lock(&mutex); + num_threads++; + bool ret = false; + if (q.empty() && !block) { + goto exit; + } + while (q.empty() && enable) { + pthread_cond_wait(&cv_empty, &mutex); + } + if (!enable) { + goto exit; + } + if (value) { + *value = q.front(); + q.pop(); + } + ret = true; + if (mutexed_callback) { + mutexed_callback->popping(*value); + } + pthread_cond_signal(&cv_full); + exit: + num_threads--; + pthread_mutex_unlock(&mutex); + return ret; + } + + bool push_(const myobj& value, bool block) { + if (!enable) { + return false; + } + pthread_mutex_lock(&mutex); + num_threads++; + bool ret = false; + if (capacity > 0) { + if (block) { + while(q.size() >= (uint32_t) capacity && enable) { + pthread_cond_wait(&cv_full, &mutex); + } + if (!enable) { + goto exit; + } + } else if (q.size() >= (uint32_t) capacity) { + goto exit; + } + } + q.push(value); + ret = true; + if (mutexed_callback) { + mutexed_callback->pushing(value); + } + pthread_cond_signal(&cv_empty); + exit: + num_threads--; + pthread_mutex_unlock(&mutex); + return ret; + } + + std::queue q; + pthread_mutex_t mutex; + pthread_cond_t cv_empty; + pthread_cond_t cv_full; + call_mutexed_itf *mutexed_callback; + int capacity; + bool enable; + uint32_t num_threads; +}; + +} + +#endif // SRSLTE_BLOCK_QUEUE_H \ No newline at end of file diff --git a/lib/include/srslte/common/buffer_pool.h b/lib/include/srslte/common/buffer_pool.h new file mode 100644 index 0000000..1a72417 --- /dev/null +++ b/lib/include/srslte/common/buffer_pool.h @@ -0,0 +1,210 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_BUFFER_POOL_H +#define SRSLTE_BUFFER_POOL_H + +#include +#include +#include +#include +#include +#include + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/common/log.h" +#include "srslte/common/common.h" + +namespace srslte { + +/****************************************************************************** + * Buffer pool + * + * Preallocates a large number of buffer_t and provides allocate and + * deallocate functions. Provides quick object creation and deletion as well + * as object reuse. + * Singleton class of byte_buffer_t (but other pools of different type can be created) + *****************************************************************************/ + +template +class buffer_pool{ +public: + + // non-static methods + buffer_pool(int capacity_ = -1) + { + uint32_t nof_buffers = POOL_SIZE; + if (capacity_ > 0) { + nof_buffers = (uint32_t) capacity_; + } + pthread_mutex_init(&mutex, NULL); + for(uint32_t i=0;i buffer_cnt; + for (uint32_t i=0;idebug_name)?used[i]->debug_name:"Undefined"]++; + } + std::map::iterator it; + for (it = buffer_cnt.begin(); it != buffer_cnt.end(); it++) { + printf(" - %dx %s\n", it->second, it->first.c_str()); + } +#endif + } + + uint32_t nof_available_pdus() { + return available.size(); + } + + bool is_almost_empty() { + return available.size() < capacity/20; + } + + buffer_t* allocate(const char *debug_name = NULL) + { + pthread_mutex_lock(&mutex); + buffer_t* b = NULL; + + if(available.size() > 0) + { + b = available.top(); + used.push_back(b); + available.pop(); + + if (is_almost_empty()) { + printf("Warning buffer pool capacity is %f %%\n", (float) 100*available.size()/capacity); + } +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED + if (debug_name) { + strncpy(b->debug_name, debug_name, SRSLTE_BUFFER_POOL_LOG_NAME_LEN); + b->debug_name[SRSLTE_BUFFER_POOL_LOG_NAME_LEN-1] = 0; + } +#endif + + } else { + printf("Error - buffer pool is empty\n"); + +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED + print_all_buffers(); +#endif + } + + pthread_mutex_unlock(&mutex); + return b; + } + + bool deallocate(buffer_t *b) + { + bool ret = false; + pthread_mutex_lock(&mutex); + typename std::vector::iterator elem = std::find(used.begin(), used.end(), b); + if (elem != used.end()) { + used.erase(elem); + available.push(b); + ret = true; + } + pthread_mutex_unlock(&mutex); + return ret; + } + + +private: + static const int POOL_SIZE = 2048; + std::stack available; + std::vector used; + pthread_mutex_t mutex; + uint32_t capacity; +}; + + +class byte_buffer_pool { +public: + // Singleton static methods + static byte_buffer_pool *instance; + static byte_buffer_pool* get_instance(int capacity = -1); + static void cleanup(void); + byte_buffer_pool(int capacity = -1) { + log = NULL; + pool = new buffer_pool(capacity); + } + ~byte_buffer_pool() { + delete pool; + } + byte_buffer_t* allocate(const char *debug_name = NULL) { + return pool->allocate(debug_name); + } + void set_log(srslte::log *log) { + this->log = log; + } + void deallocate(byte_buffer_t *b) { + if(!b) { + return; + } + b->reset(); + if (!pool->deallocate(b)) { + if (log) { + log->error("Deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name); + } else { + printf("Error deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name); + } + } + b = NULL; + } + void print_all_buffers() { + pool->print_all_buffers(); + } +private: + srslte::log *log; + buffer_pool *pool; +}; + + +} // namespace srsue + +#endif // SRSLTE_BUFFER_POOL_H diff --git a/lib/include/srslte/common/common.h b/lib/include/srslte/common/common.h new file mode 100644 index 0000000..3313d0e --- /dev/null +++ b/lib/include/srslte/common/common.h @@ -0,0 +1,251 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_COMMON_H +#define SRSLTE_COMMON_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include +#include + +/******************************************************************************* + DEFINES +*******************************************************************************/ + +#define SRSLTE_UE_CATEGORY 4 + +#define SRSLTE_N_SRB 3 +#define SRSLTE_N_DRB 8 +#define SRSLTE_N_RADIO_BEARERS 11 + +#define SRSLTE_N_MCH_LCIDS 32 + +#define HARQ_DELAY_MS 4 +#define MSG3_DELAY_MS 2 // Delay added to HARQ_DELAY_MS +#define TTI_RX(tti) (tti>HARQ_DELAY_MS?((tti-HARQ_DELAY_MS)%10240):(10240+tti-HARQ_DELAY_MS)) +#define TTI_TX(tti) ((tti+HARQ_DELAY_MS)%10240) +#define TTI_RX_ACK(tti) ((tti+(2*HARQ_DELAY_MS))%10240) + +#define UL_PIDOF(tti) (tti%(2*HARQ_DELAY_MS)) + +#define TTIMOD_SZ (((2*HARQ_DELAY_MS) < 10)?10:20) +#define TTIMOD(tti) (tti%TTIMOD_SZ) + +#define ASYNC_DL_SCHED (HARQ_DELAY_MS <= 4) + +// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI +// 3GPP 36.306 Table 4.1.1 +#define SRSLTE_MAX_BUFFER_SIZE_BITS 102048 +#define SRSLTE_MAX_BUFFER_SIZE_BYTES 12756 +#define SRSLTE_BUFFER_HEADER_OFFSET 1020 + +#define SRSLTE_BUFFER_POOL_LOG_ENABLED + +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED +#define pool_allocate (pool->allocate(__PRETTY_FUNCTION__)) +#define SRSLTE_BUFFER_POOL_LOG_NAME_LEN 128 +#else +#define pool_allocate (pool->allocate()) +#endif + +#define ZERO_OBJECT(x) memset(&(x), 0x0, sizeof((x))) + +#include "srslte/srslte.h" + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + +namespace srslte { + +typedef enum{ + ERROR_NONE = 0, + ERROR_INVALID_PARAMS, + ERROR_INVALID_COMMAND, + ERROR_OUT_OF_BOUNDS, + ERROR_CANT_START, + ERROR_ALREADY_STARTED, + ERROR_N_ITEMS, +}error_t; +static const char error_text[ERROR_N_ITEMS][20] = { "None", + "Invalid parameters", + "Invalid command", + "Out of bounds", + "Can't start", + "Already started"}; + +//#define ENABLE_TIMESTAMP + +/****************************************************************************** + * Byte and Bit buffers + * + * Generic buffers with headroom to accommodate packet headers and custom + * copy constructors & assignment operators for quick copying. Byte buffer + * holds a next pointer to support linked lists. + *****************************************************************************/ +class byte_buffer_t{ +public: + uint32_t N_bytes; + uint8_t buffer[SRSLTE_MAX_BUFFER_SIZE_BYTES]; + uint8_t *msg; +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED + char debug_name[SRSLTE_BUFFER_POOL_LOG_NAME_LEN]; +#endif + + byte_buffer_t():N_bytes(0) + { + bzero(buffer, SRSLTE_MAX_BUFFER_SIZE_BYTES); + timestamp_is_set = false; + msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET]; + next = NULL; +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED + bzero(debug_name, SRSLTE_BUFFER_POOL_LOG_NAME_LEN); +#endif + } + byte_buffer_t(const byte_buffer_t& buf) + { + bzero(buffer, SRSLTE_MAX_BUFFER_SIZE_BYTES); + N_bytes = buf.N_bytes; + memcpy(msg, buf.msg, N_bytes); + } + byte_buffer_t & operator= (const byte_buffer_t & buf) + { + // avoid self assignment + if (&buf == this) + return *this; + bzero(buffer, SRSLTE_MAX_BUFFER_SIZE_BYTES); + N_bytes = buf.N_bytes; + memcpy(msg, buf.msg, N_bytes); + return *this; + } + void reset() + { + msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET]; + N_bytes = 0; + timestamp_is_set = false; + } + uint32_t get_headroom() + { + return msg-buffer; + } + // Returns the remaining space from what is reported to be the length of msg + uint32_t get_tailroom() + { + return (sizeof(buffer) - (msg-buffer) - N_bytes); + } + long get_latency_us() + { +#ifdef ENABLE_TIMESTAMP + if(!timestamp_is_set) + return 0; + gettimeofday(×tamp[2], NULL); + get_time_interval(timestamp); + return timestamp[0].tv_usec; +#else + return 0; +#endif + } + + void set_timestamp() + { +#ifdef ENABLE_TIMESTAMP + gettimeofday(×tamp[1], NULL); + timestamp_is_set = true; +#endif + } + +private: + + struct timeval timestamp[3]; + bool timestamp_is_set; + byte_buffer_t *next; +}; + +struct bit_buffer_t{ + uint32_t N_bits; + uint8_t buffer[SRSLTE_MAX_BUFFER_SIZE_BITS]; + uint8_t *msg; +#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED + char debug_name[128]; +#endif + + bit_buffer_t():N_bits(0) + { + msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET]; + } + bit_buffer_t(const bit_buffer_t& buf){ + N_bits = buf.N_bits; + memcpy(msg, buf.msg, N_bits); + } + bit_buffer_t & operator= (const bit_buffer_t & buf){ + // avoid self assignment + if (&buf == this) + return *this; + N_bits = buf.N_bits; + memcpy(msg, buf.msg, N_bits); + return *this; + } + void reset() + { + msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET]; + N_bits = 0; + timestamp_is_set = false; + } + uint32_t get_headroom() + { + return msg-buffer; + } + long get_latency_us() + { +#ifdef ENABLE_TIMESTAMP + if(!timestamp_is_set) + return 0; + gettimeofday(×tamp[2], NULL); + return timestamp[0].tv_usec; +#else + return 0; +#endif + } + void set_timestamp() + { +#ifdef ENABLE_TIMESTAMP + gettimeofday(×tamp[1], NULL); + timestamp_is_set = true; +#endif + } + +private: + struct timeval timestamp[3]; + bool timestamp_is_set; + +}; + +} // namespace srslte + +#endif // SRSLTE_COMMON_H diff --git a/lib/include/srslte/common/config_file.h b/lib/include/srslte/common/config_file.h new file mode 100644 index 0000000..80391f1 --- /dev/null +++ b/lib/include/srslte/common/config_file.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_CONFIG_FILE_H +#define SRSLTE_CONFIG_FILE_H + +#include +#include +#include "common.h" + +bool config_exists(std::string &filename, std::string default_name) +{ + std::ifstream conf(filename.c_str(), std::ios::in); + if(conf.fail()) { + const char *homedir = NULL; + char full_path[256]; + ZERO_OBJECT(full_path); + if ((homedir = getenv("HOME")) == NULL) { + homedir = getpwuid(getuid())->pw_dir; + } + if (!homedir) { + homedir = "."; + } + snprintf(full_path, sizeof(full_path), "%s/.srs/%s", homedir, default_name.c_str()); + filename = std::string(full_path); + + // try to open again + conf.open(filename.c_str()); + if (conf.fail()) { + return false; + } + } + return true; +} + +#endif // SRSLTE_CONFIG_FILE_H diff --git a/lib/include/srslte/common/gen_mch_tables.h b/lib/include/srslte/common/gen_mch_tables.h new file mode 100644 index 0000000..2e5ea04 --- /dev/null +++ b/lib/include/srslte/common/gen_mch_tables.h @@ -0,0 +1,52 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef GEN_MCH_TALBES_H +#define GEN_MCH_TALBES_H + +/****************************************************************************** + * Common mch table generation - used in phch_common of UE and ENB for MBMS + *****************************************************************************/ +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +void generate_frame_mch_table(uint8_t *table, uint8_t alloc); +void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames); +void generate_mcch_table(uint8_t *table, uint32_t sf_alloc); + +#ifdef __cplusplus +} +#endif // __cplusplus + + +#endif // SECURITY_H diff --git a/lib/include/srslte/common/interfaces_common.h b/lib/include/srslte/common/interfaces_common.h new file mode 100644 index 0000000..b0d55db --- /dev/null +++ b/lib/include/srslte/common/interfaces_common.h @@ -0,0 +1,105 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_INTERFACES_COMMON_H +#define SRSLTE_INTERFACES_COMMON_H + +#include "srslte/common/timers.h" +#include "srslte/common/security.h" +#include "srslte/asn1/liblte_rrc.h" +#include + + +namespace srslte { + +class srslte_nas_config_t +{ +public: + srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string user_ = "", std::string pass_ = "", bool force_imsi_attach_ = false) + :lcid(lcid_), + apn(apn_), + user(user_), + pass(pass_), + force_imsi_attach(force_imsi_attach_) + {} + + uint32_t lcid; + std::string apn; + std::string user; + std::string pass; + bool force_imsi_attach; +}; + + +class srslte_gw_config_t +{ +public: + srslte_gw_config_t(uint32_t lcid_ = 0) + :lcid(lcid_) + {} + + uint32_t lcid; +}; + + +class srslte_pdcp_config_t +{ +public: + srslte_pdcp_config_t(bool is_control_ = false, bool is_data_ = false, uint8_t direction_ = SECURITY_DIRECTION_UPLINK) + :direction(direction_) + ,is_control(is_control_) + ,is_data(is_data_) + ,sn_len(12) {} + + uint8_t direction; + bool is_control; + bool is_data; + uint8_t sn_len; + + // TODO: Support the following configurations + // bool do_rohc; +}; + +class mac_interface_timers +{ +public: + /* Timer services with ms resolution. + * timer_id must be lower than MAC_NOF_UPPER_TIMERS + */ + virtual timers::timer* timer_get(uint32_t timer_id) = 0; + virtual void timer_release_id(uint32_t timer_id) = 0; + virtual uint32_t timer_get_unique_id() = 0; +}; + +class read_pdu_interface +{ +public: + virtual int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes) = 0; +}; + +} + +#endif // SRSLTE_INTERFACES_COMMON_H diff --git a/lib/include/srslte/common/liblte_security.h b/lib/include/srslte/common/liblte_security.h new file mode 100644 index 0000000..b821686 --- /dev/null +++ b/lib/include/srslte/common/liblte_security.h @@ -0,0 +1,359 @@ +/******************************************************************************* + + Copyright 2014 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_security.h + + Description: Contains all the definitions for the LTE security algorithm + library. + + Revision History + ---------- ------------- -------------------------------------------- + 08/03/2014 Ben Wojtowicz Created file. + 09/03/2014 Ben Wojtowicz Added key generation and EIA2. + +*******************************************************************************/ + +#ifndef SRSLTE_LIBLTE_SECURITY_H +#define SRSLTE_LIBLTE_SECURITY_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/asn1/liblte_common.h" + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + + +/******************************************************************************* + DECLARATIONS +*******************************************************************************/ + +/********************************************************************* + Name: compute_OPc + + Description: Computes OPc from OP and K. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_compute_opc(uint8 *k, + uint8 *op, + uint8 *op_c); + +/********************************************************************* + Name: liblte_security_generate_k_asme + + Description: Generate the security key Kasme. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_generate_k_asme(uint8 *ck, + uint8 *ik, + uint8 *ak, + uint8 *sqn, + uint16 mcc, + uint16 mnc, + uint8 *k_asme); + +/********************************************************************* + Name: liblte_security_generate_k_enb + + Description: Generate the security key Kenb. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_generate_k_enb(uint8 *k_asme, + uint32 nas_count, + uint8 *k_enb); + +LIBLTE_ERROR_ENUM liblte_security_generate_k_enb_star(uint8 *k_enb, + uint32 pci, + uint32_t earfcn, + uint8 *k_enb_star); + +LIBLTE_ERROR_ENUM liblte_security_generate_nh( uint8_t *k_asme, + uint8_t *sync, + uint8_t *nh); + +/********************************************************************* + Name: liblte_security_generate_k_nas + + Description: Generate the NAS security keys KNASenc and KNASint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +// Defines +// Enums +typedef enum{ + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_EEA0 = 0, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_128_EEA1, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_128_EEA2, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_N_ITEMS, +}LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM; +static const char liblte_security_ciphering_algorithm_id_text[LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_N_ITEMS][20] = {"EEA0", + "128-EEA1", + "128-EEA2"}; +typedef enum{ + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_EIA0 = 0, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_128_EIA1, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_128_EIA2, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_N_ITEMS, +}LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM; +static const char liblte_security_integrity_algorithm_id_text[LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_N_ITEMS][20] = {"EIA0", + "128-EIA1", + "128-EIA2"}; +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_generate_k_nas(uint8 *k_asme, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_nas_enc, + uint8 *k_nas_int); + + + +/********************************************************************* + Name: liblte_security_generate_k_rrc + + Description: Generate the RRC security keys KRRCenc and KRRCint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_generate_k_rrc(uint8 *k_enb, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_rrc_enc, + uint8 *k_rrc_int); + +/********************************************************************* + Name: liblte_security_generate_k_up + + Description: Generate the user plane security keys KUPenc and + KUPint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_generate_k_up(uint8 *k_enb, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_up_enc, + uint8 *k_up_int); + +/********************************************************************* + Name: liblte_security_128_eia2 + + Description: 128-bit integrity algorithm EIA2. + + Document Reference: 33.401 v10.0.0 Annex B.2.3 + 33.102 v10.0.0 Section 6.5.4 + RFC4493 +*********************************************************************/ +// Defines +#define LIBLTE_SECURITY_DIRECTION_UPLINK 0 +#define LIBLTE_SECURITY_DIRECTION_DOWNLINK 1 +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_128_eia2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *mac); +LIBLTE_ERROR_ENUM liblte_security_128_eia2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + LIBLTE_BIT_MSG_STRUCT *msg, + uint8 *mac); + +/********************************************************************* + Name: liblte_security_encryption_eea1 + + Description: 128-bit encryption algorithm EEA1. + + Document Reference: 33.401 v13.1.0 Annex B.1.2 + 35.215 v13.0.0 References + Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D1 v2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_encryption_eea1(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *out); + +/********************************************************************* + Name: liblte_security_decryption_eea1 + + Description: 128-bit decryption algorithm EEA1. + + Document Reference: 33.401 v13.1.0 Annex B.1.2 + 35.215 v13.0.0 References + Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D1 v2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_decryption_eea1(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *ct, + uint32 ct_len, + uint8 *out); + +/********************************************************************* + Name: liblte_security_encryption_eea2 + + Description: 128-bit encryption algorithm EEA2. + + Document Reference: 33.401 v13.1.0 Annex B.1.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_encryption_eea2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *out); + +/********************************************************************* + Name: liblte_security_decryption_eea2 + + Description: 128-bit decryption algorithm EEA2. + + Document Reference: 33.401 v13.1.0 Annex B.1.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_decryption_eea2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *ct, + uint32 ct_len, + uint8 *out); + + +/********************************************************************* + Name: liblte_security_milenage_f1 + + Description: Milenage security function F1. Computes network + authentication code MAC-A from key K, random + challenge RAND, sequence number SQN, and + authentication management field AMF. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_milenage_f1(uint8 *k, + uint8 *op, + uint8 *rand, + uint8 *sqn, + uint8 *amf, + uint8 *mac_a); + +/********************************************************************* + Name: liblte_security_milenage_f1_star + + Description: Milenage security function F1*. Computes resynch + authentication code MAC-S from key K, random + challenge RAND, sequence number SQN, and + authentication management field AMF. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_milenage_f1_star(uint8 *k, + uint8 *op, + uint8 *rand, + uint8 *sqn, + uint8 *amf, + uint8 *mac_s); + +/********************************************************************* + Name: liblte_security_milenage_f2345 + + Description: Milenage security functions F2, F3, F4, and F5. + Computes response RES, confidentiality key CK, + integrity key IK, and anonymity key AK from random + challenge RAND. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_milenage_f2345(uint8 *k, + uint8 *op, + uint8 *rand, + uint8 *res, + uint8 *ck, + uint8 *ik, + uint8 *ak); + +/********************************************************************* + Name: liblte_security_milenage_f5_star + + Description: Milenage security function F5*. Computes resynch + anonymity key AK from key K and random challenge + RAND. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +LIBLTE_ERROR_ENUM liblte_security_milenage_f5_star(uint8 *k, + uint8 *op, + uint8 *rand, + uint8 *ak); + +#endif // SRSLTE_LIBLTE_SECURITY_H diff --git a/lib/include/srslte/common/liblte_ssl.h b/lib/include/srslte/common/liblte_ssl.h new file mode 100644 index 0000000..2ac4e79 --- /dev/null +++ b/lib/include/srslte/common/liblte_ssl.h @@ -0,0 +1,65 @@ +#ifndef SRSLTE_LIBLTE_SSL_H +#define SRSLTE_LIBLTE_SSL_H + +#ifdef HAVE_POLARSSL + +#include "polarssl/sha256.h" +#include "polarssl/aes.h" + +void sha256(const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) +{ + sha256_hmac(key, keylen, input, ilen, output, is224); +} + +#endif // HAVE_POLARSSL + +#ifdef HAVE_MBEDTLS + +#include "mbedtls/md.h" +#include "mbedtls/aes.h" + +typedef mbedtls_aes_context aes_context; + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ) +{ + return mbedtls_aes_setkey_enc(ctx, key, keysize); +} + +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + return mbedtls_aes_crypt_ecb(ctx, mode, input, output); +} + +int aes_crypt_ctr(aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + return mbedtls_aes_crypt_ctr(ctx, length, nc_off, nonce_counter, + stream_block, input, output); +} + +void sha256(const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) +{ + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + key, keylen, + input, ilen, + output ); +} + +#endif // HAVE_MBEDTLS + +#endif // SRSLTE_LIBLTE_SSL_H diff --git a/lib/include/srslte/common/log.h b/lib/include/srslte/common/log.h new file mode 100644 index 0000000..79dbdc0 --- /dev/null +++ b/lib/include/srslte/common/log.h @@ -0,0 +1,157 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: log.h + * + * Description: Abstract logging service + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_LOG_H +#define SRSLTE_LOG_H + +#include +#include + +namespace srslte { + +typedef enum { + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_N_ITEMS +} LOG_LEVEL_ENUM; +static const char log_level_text[LOG_LEVEL_N_ITEMS][16] = {"None ", + "Error ", + "Warning", + "Info ", + "Debug "}; + +static const char log_level_text_short[LOG_LEVEL_N_ITEMS][16] = {"[-]", + "[E]", + "[W]", + "[I]", + "[D]"}; + +class log +{ +public: + + log() { + service_name = ""; + tti = 0; + level = LOG_LEVEL_NONE; + hex_limit = 0; + show_layer_en = true; + add_string_en = false; + level_text_short = true; + } + + log(std::string service_name_) { + service_name = service_name_; + tti = 0; + level = LOG_LEVEL_NONE; + hex_limit = 0; + show_layer_en = true; + add_string_en = false; + level_text_short = true; + } + + virtual ~log() {}; + + // This function shall be called at the start of every tti for printing tti + void step(uint32_t tti_) { + tti = tti_; + add_string_en = false; + } + + void prepend_string(std::string s) { + add_string_en = true; + add_string_val = s; + } + + uint32_t get_tti() { + return tti; + } + + void set_level(LOG_LEVEL_ENUM l) { + level = l; + } + LOG_LEVEL_ENUM get_level() { + return level; + } + + void set_hex_limit(int limit) { + hex_limit = limit; + } + int get_hex_limit() { + return hex_limit; + } + void set_log_level_short(bool enable) { + level_text_short = enable; + } + void show_layer(bool enable) { + show_layer_en = enable; + } + + // Pure virtual methods for logging + virtual void console(const char * message, ...) __attribute__ ((format (printf, 2, 3))) = 0; + virtual void error(const char * message, ...) __attribute__ ((format (printf, 2, 3))) = 0; + virtual void warning(const char * message, ...) __attribute__ ((format (printf, 2, 3))) = 0; + virtual void info(const char * message, ...) __attribute__ ((format (printf, 2, 3))) = 0; + virtual void debug(const char * message, ...) __attribute__ ((format (printf, 2, 3))) = 0; + + // Same with hex dump + virtual void error_hex(const uint8_t *, int, const char *, ...) __attribute__((format (printf, 4, 5))) + {error("error_hex not implemented.\n");} + virtual void warning_hex(const uint8_t *, int, const char *, ...) __attribute__((format (printf, 4, 5))) + {error("warning_hex not implemented.\n");} + virtual void info_hex(const uint8_t *, int, const char *, ...) __attribute__((format (printf, 4, 5))) + {error("info_hex not implemented.\n");} + virtual void debug_hex(const uint8_t *, int, const char *, ...) __attribute__((format (printf, 4, 5))) + {error("debug_hex not implemented.\n");} + +protected: + std::string get_service_name() { return service_name; } + uint32_t tti; + LOG_LEVEL_ENUM level; + int hex_limit; + std::string service_name; + + bool show_layer_en; + bool level_text_short; + bool add_string_en; + std::string add_string_val; +}; + +} // namespace srslte + +#endif // SRSLTE_LOG_H + diff --git a/lib/include/srslte/common/log_filter.h b/lib/include/srslte/common/log_filter.h new file mode 100644 index 0000000..1296fbb --- /dev/null +++ b/lib/include/srslte/common/log_filter.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: log_filter.h + * Description: Log filter for a specific layer or element. + * Performs filtering based on log level, generates + * timestamped log strings and passes them to the + * common logger object. + *****************************************************************************/ + +#ifndef SRSLTE_LOG_FILTER_H +#define SRSLTE_LOG_FILTER_H + +#include +#include + +#include "srslte/phy/common/timestamp.h" +#include "srslte/common/log.h" +#include "srslte/common/logger.h" +#include "srslte/common/logger_stdout.h" + +namespace srslte { + +typedef std::string* str_ptr; + +class log_filter : public srslte::log +{ +public: + + log_filter(); + log_filter(std::string layer); + log_filter(std::string layer, logger *logger_, bool tti=false); + + void init(std::string layer, logger *logger_, bool tti=false); + + void console(const char * message, ...) __attribute__ ((format (printf, 2, 3))); + void error(const char * message, ...) __attribute__ ((format (printf, 2, 3))); + void warning(const char * message, ...) __attribute__ ((format (printf, 2, 3))); + void info(const char * message, ...) __attribute__ ((format (printf, 2, 3))); + void debug(const char * message, ...) __attribute__ ((format (printf, 2, 3))); + + void error_hex(const uint8_t *hex, int size, const char * message, ...) __attribute__((format (printf, 4, 5))); + void warning_hex(const uint8_t *hex, int size, const char * message, ...) __attribute__((format (printf, 4, 5))); + void info_hex(const uint8_t *hex, int size, const char * message, ...) __attribute__((format (printf, 4, 5))); + void debug_hex(const uint8_t *hex, int size, const char * message, ...) __attribute__((format (printf, 4, 5))); + + class time_itf { + public: + virtual srslte_timestamp_t get_time() = 0; + }; + + typedef enum { + TIME, + EPOCH + } time_format_t; + + void set_time_src(time_itf *source, time_format_t format); + +private: + logger *logger_h; + bool do_tti; + + time_itf *time_src; + time_format_t time_format; + + logger_stdout def_logger_stdout; + + void all_log(srslte::LOG_LEVEL_ENUM level, uint32_t tti, const char *msg); + void all_log(srslte::LOG_LEVEL_ENUM level, uint32_t tti, const char *msg, const uint8_t *hex, int size); + void all_log_line(srslte::LOG_LEVEL_ENUM level, uint32_t tti, std::string file, int line, char *msg); + std::string now_time(); + std::string hex_string(const uint8_t *hex, int size); +}; + +} // namespace srsue + +#endif // SRSLTE_LOG_FILTER_H diff --git a/lib/include/srslte/common/logger.h b/lib/include/srslte/common/logger.h new file mode 100644 index 0000000..ed352cd --- /dev/null +++ b/lib/include/srslte/common/logger.h @@ -0,0 +1,48 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: logger.h + * Description: Interface for logging output + *****************************************************************************/ + +#ifndef SRSLTE_LOGGER_H +#define SRSLTE_LOGGER_H + +#include +#include + +namespace srslte { + +class logger +{ +public: + virtual void log(std::string *msg) = 0; +}; + +} // namespace srslte + +#endif // SRSLTE_LOGGER_H diff --git a/lib/include/srslte/common/logger_file.h b/lib/include/srslte/common/logger_file.h new file mode 100644 index 0000000..956753f --- /dev/null +++ b/lib/include/srslte/common/logger_file.h @@ -0,0 +1,77 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: logger_file.h + * Description: Common log object. Maintains a queue of log messages + * and runs a thread to read messages and write to file. + * Multiple producers, single consumer. If full, producers + * increase queue size. If empty, consumer blocks. + *****************************************************************************/ + +#ifndef SRSLTE_LOGGER_FILE_H +#define SRSLTE_LOGGER_FILE_H + +#include +#include +#include +#include "srslte/common/logger.h" +#include "srslte/common/threads.h" + +namespace srslte { + +typedef std::string* str_ptr; + +class logger_file : public thread, public logger +{ +public: + logger_file(); + logger_file(std::string file); + ~logger_file(); + void init(std::string file, int max_length = -1); + // Implementation of log_out + void log(str_ptr msg); + void log(const char *msg); + +private: + void run_thread(); + void flush(); + + uint32_t name_idx; + int64_t max_length; + int64_t cur_length; + FILE* logfile; + bool is_running; + std::string filename; + pthread_cond_t not_empty; + pthread_mutex_t mutex; + pthread_t thread; + std::deque buffer; +}; + +} // namespace srslte + +#endif // SRSLTE_LOGGER_FILE_H diff --git a/lib/include/srslte/common/logger_stdout.h b/lib/include/srslte/common/logger_stdout.h new file mode 100644 index 0000000..4aab21d --- /dev/null +++ b/lib/include/srslte/common/logger_stdout.h @@ -0,0 +1,54 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: logger_stdout.h + * Description: Interface for logging output + *****************************************************************************/ + +#ifndef SRSLTE_LOGGER_STDOUT_H +#define SRSLTE_LOGGER_STDOUT_H + +#include +#include +#include "srslte/common/logger.h" + +namespace srslte { + + class logger_stdout : public logger + { + public: + void log(std::string *msg) { + if (msg) { + fprintf(stdout, "%s", msg->c_str()); + delete msg; + } + } + }; + +} // namespace srslte + +#endif // SRSLTE_LOGGER_STDOUT_H diff --git a/lib/include/srslte/common/mac_pcap.h b/lib/include/srslte/common/mac_pcap.h new file mode 100644 index 0000000..35c9532 --- /dev/null +++ b/lib/include/srslte/common/mac_pcap.h @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_MAC_PCAP_H +#define SRSLTE_MAC_PCAP_H + +#include +#include "srslte/common/pcap.h" + +namespace srslte { + +class mac_pcap +{ +public: + mac_pcap() {enable_write=false; ue_id=0; pcap_file = NULL; }; + void enable(bool en); + void open(const char *filename, uint32_t ue_id = 0); + void close(); + + void set_ue_id(uint16_t ue_id); + + void write_ul_crnti(uint8_t *pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti); + void write_dl_crnti(uint8_t *pdu, uint32_t pdu_len_bytes, uint16_t crnti, bool crc_ok, uint32_t tti); + void write_dl_ranti(uint8_t *pdu, uint32_t pdu_len_bytes, uint16_t ranti, bool crc_ok, uint32_t tti); + + // SI and BCH only for DL + void write_dl_sirnti(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); + void write_dl_bch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); + void write_dl_pch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); + void write_dl_mch(uint8_t *pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti); + +private: + bool enable_write; + FILE *pcap_file; + uint32_t ue_id; + void pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes, uint32_t reTX, bool crc_ok, uint32_t tti, + uint16_t crnti_, uint8_t direction, uint8_t rnti_type); +}; + +} // namespace srslte + +#endif // SRSLTE_MAC_PCAP_H diff --git a/lib/include/srslte/common/metrics_hub.h b/lib/include/srslte/common/metrics_hub.h new file mode 100644 index 0000000..4c27b2b --- /dev/null +++ b/lib/include/srslte/common/metrics_hub.h @@ -0,0 +1,82 @@ + +/****************************************************************************** + * File: metrics_hub.h + * Description: Centralizes metrics interfaces to allow different metrics clients + * to get metrics + *****************************************************************************/ + +#ifndef SRSLTE_METRICS_HUB_H +#define SRSLTE_METRICS_HUB_H + +#include +#include "srslte/common/threads.h" +#include "srslte/srslte.h" + +namespace srslte { + +template +class metrics_interface +{ +public: + virtual bool get_metrics(metrics_t &m) = 0; +}; + +template +class metrics_listener +{ +public: + virtual void set_metrics(metrics_t &m, const uint32_t period_usec) = 0; + virtual void stop() = 0; +}; + +template +class metrics_hub : public periodic_thread +{ +public: + metrics_hub() + :m(NULL) + ,sleep_period_start() + {} + bool init(metrics_interface *m_, float report_period_secs_=1.0) { + m = m_; + // Start with user-default priority + start_periodic(report_period_secs_*1e6, -2); + return true; + } + void stop() { + // stop all listeners + for (uint32_t i=0;istop(); + } + thread_cancel(); + wait_thread_finish(); + } + + void add_listener(metrics_listener *listener) { + listeners.push_back(listener); + } + +private: + void run_period(){ + // get current time and check how long we slept + gettimeofday(&sleep_period_start[2], NULL); + get_time_interval(sleep_period_start); + uint32_t period = sleep_period_start[0].tv_sec*1e6 + sleep_period_start[0].tv_usec; + if (m) { + metrics_t metric; + m->get_metrics(metric); + for (uint32_t i=0;iset_metrics(metric, period); + } + } + // store start of sleep period + gettimeofday(&sleep_period_start[1], NULL); + } + metrics_interface *m; + std::vector*> listeners; + struct timeval sleep_period_start[3]; +}; + +} // namespace srslte + +#endif // SRSLTE_METRICS_HUB_H diff --git a/lib/include/srslte/common/nas_pcap.h b/lib/include/srslte/common/nas_pcap.h new file mode 100644 index 0000000..061ba50 --- /dev/null +++ b/lib/include/srslte/common/nas_pcap.h @@ -0,0 +1,25 @@ +#ifndef SRSLTE_NAS_PCAP_H +#define SRSLTE_NAS_PCAP_H + +#include "srslte/common/pcap.h" + +namespace srslte { + +class nas_pcap +{ +public: + nas_pcap() {enable_write=false; ue_id=0; pcap_file = NULL; } + void enable(); + void open(const char *filename, uint32_t ue_id=0); + void close(); + void write_nas(uint8_t *pdu, uint32_t pdu_len_bytes); +private: + bool enable_write; + FILE *pcap_file; + uint32_t ue_id; + void pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes); +}; + +} //namespace srslte + +#endif // SRSLTE_NAS_PCAP_H diff --git a/lib/include/srslte/common/pcap.h b/lib/include/srslte/common/pcap.h new file mode 100644 index 0000000..18b66a1 --- /dev/null +++ b/lib/include/srslte/common/pcap.h @@ -0,0 +1,401 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_PCAP_H +#define SRSLTE_PCAP_H + +#include +#include +#include +#include + +#define MAC_LTE_DLT 147 +#define NAS_LTE_DLT 148 +#define RLC_LTE_DLT 149 // UDP needs to be selected as protocol + + +/* This structure gets written to the start of the file */ +typedef struct pcap_hdr_s { + unsigned int magic_number; /* magic number */ + unsigned short version_major; /* major version number */ + unsigned short version_minor; /* minor version number */ + unsigned int thiszone; /* GMT to local correction */ + unsigned int sigfigs; /* accuracy of timestamps */ + unsigned int snaplen; /* max length of captured packets, in octets */ + unsigned int network; /* data link type */ +} pcap_hdr_t; + +/* This structure precedes each packet */ +typedef struct pcaprec_hdr_s { + unsigned int ts_sec; /* timestamp seconds */ + unsigned int ts_usec; /* timestamp microseconds */ + unsigned int incl_len; /* number of octets of packet saved in file */ + unsigned int orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + + +/* radioType */ +#define FDD_RADIO 1 +#define TDD_RADIO 2 + +/* Direction */ +#define DIRECTION_UPLINK 0 +#define DIRECTION_DOWNLINK 1 + +/* rntiType */ +#define NO_RNTI 0 /* Used for BCH-BCH */ +#define P_RNTI 1 +#define RA_RNTI 2 +#define C_RNTI 3 +#define SI_RNTI 4 +#define SPS_RNTI 5 +#define M_RNTI 6 + +#define MAC_LTE_START_STRING "mac-lte" +#define MAC_LTE_PAYLOAD_TAG 0x01 +#define MAC_LTE_RNTI_TAG 0x02 +#define MAC_LTE_UEID_TAG 0x03 +#define MAC_LTE_FRAME_SUBFRAME_TAG 0x04 +#define MAC_LTE_PREDFINED_DATA_TAG 0x05 +#define MAC_LTE_RETX_TAG 0x06 +#define MAC_LTE_CRC_STATUS_TAG 0x07 + + + +/* Context information for every MAC PDU that will be logged */ +typedef struct MAC_Context_Info_t { + unsigned short radioType; + unsigned char direction; + unsigned char rntiType; + unsigned short rnti; + unsigned short ueid; + unsigned char isRetx; + unsigned char crcStatusOK; + + unsigned short sysFrameNumber; + unsigned short subFrameNumber; +} MAC_Context_Info_t; + +/* Context information for every NAS PDU that will be logged */ +typedef struct NAS_Context_Info_s { + // No Context yet +} NAS_Context_Info_t; + + +/* RLC-LTE disector */ + +/* rlcMode */ +#define RLC_TM_MODE 1 +#define RLC_UM_MODE 2 +#define RLC_AM_MODE 4 +#define RLC_PREDEF 8 + +/* priority ? */ + +/* channelType */ +#define CHANNEL_TYPE_CCCH 1 +#define CHANNEL_TYPE_BCCH_BCH 2 +#define CHANNEL_TYPE_PCCH 3 +#define CHANNEL_TYPE_SRB 4 +#define CHANNEL_TYPE_DRB 5 +#define CHANNEL_TYPE_BCCH_DL_SCH 6 +#define CHANNEL_TYPE_MCCH 7 +#define CHANNEL_TYPE_MTCH 8 + +/* sequenceNumberLength */ +#define UM_SN_LENGTH_5_BITS 5 +#define UM_SN_LENGTH_10_BITS 10 +#define AM_SN_LENGTH_10_BITS 10 +#define AM_SN_LENGTH_16_BITS 16 + +/* Narrow band mode */ +typedef enum { + rlc_no_nb_mode = 0, + rlc_nb_mode = 1 +} rlc_lte_nb_mode; + +/* Context information for every RLC PDU that will be logged */ +typedef struct { + unsigned char rlcMode; + unsigned char direction; + unsigned char priority; + unsigned char sequenceNumberLength; + unsigned short ueid; + unsigned short channelType; + unsigned short channelId; /* for SRB: 1=SRB1, 2=SRB2, 3=SRB1bis; for DRB: DRB ID */ + unsigned short pduLength; + bool extendedLiField; + rlc_lte_nb_mode nbMode; +} RLC_Context_Info_t; + + +// See Wireshark's packet-rlc-lte.h for details +#define RLC_LTE_START_STRING "rlc-lte" +#define RLC_LTE_SN_LENGTH_TAG 0x02 +#define RLC_LTE_DIRECTION_TAG 0x03 +#define RLC_LTE_PRIORITY_TAG 0x04 +#define RLC_LTE_UEID_TAG 0x05 +#define RLC_LTE_CHANNEL_TYPE_TAG 0x06 +#define RLC_LTE_CHANNEL_ID_TAG 0x07 +#define RLC_LTE_EXT_LI_FIELD_TAG 0x08 +#define RLC_LTE_NB_MODE_TAG 0x09 +#define RLC_LTE_PAYLOAD_TAG 0x01 + + + +/************************************************************************** + * API functions for opening/closing LTE PCAP files * + **************************************************************************/ + +/* Open the file and write file header */ +inline FILE *LTE_PCAP_Open(uint32_t DLT, const char *fileName) +{ + pcap_hdr_t file_header = + { + 0xa1b2c3d4, /* magic number */ + 2, 4, /* version number is 2.4 */ + 0, /* timezone */ + 0, /* sigfigs - apparently all tools do this */ + 65535, /* snaplen - this should be long enough */ + DLT /* Data Link Type (DLT). Set as unused value 147 for now */ + }; + + FILE *fd = fopen(fileName, "w"); + if (fd == NULL) { + printf("Failed to open file \"%s\" for writing\n", fileName); + return NULL; + } + + /* Write the file header */ + fwrite(&file_header, sizeof(pcap_hdr_t), 1, fd); + + return fd; +} + +/* Close the PCAP file */ +inline void LTE_PCAP_Close(FILE *fd) +{ + if(fd) + fclose(fd); +} + + +/************************************************************************** + * API functions for writing MAC-LTE PCAP files * + **************************************************************************/ + +/* Write an individual PDU (PCAP packet header + mac-context + mac-pdu) */ +inline int LTE_PCAP_MAC_WritePDU(FILE *fd, MAC_Context_Info_t *context, + const unsigned char *PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + char context_header[256]; + int offset = 0; + uint16_t tmp16; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /*****************************************************************/ + /* Context information (same as written by UDP heuristic clients */ + context_header[offset++] = context->radioType; + context_header[offset++] = context->direction; + context_header[offset++] = context->rntiType; + + /* RNTI */ + context_header[offset++] = MAC_LTE_RNTI_TAG; + tmp16 = htons(context->rnti); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + /* UEId */ + context_header[offset++] = MAC_LTE_UEID_TAG; + tmp16 = htons(context->ueid); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + /* Subframe Number and System Frame Number */ + /* SFN is stored in 12 MSB and SF in 4 LSB */ + context_header[offset++] = MAC_LTE_FRAME_SUBFRAME_TAG; + tmp16 = (context->sysFrameNumber << 4) | context->subFrameNumber; + tmp16 = htons(tmp16); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + /* CRC Status */ + context_header[offset++] = MAC_LTE_CRC_STATUS_TAG; + context_header[offset++] = context->crcStatusOK; + + /* Data tag immediately preceding PDU */ + context_header[offset++] = MAC_LTE_PAYLOAD_TAG; + + + /****************************************************************/ + /* PCAP Header */ + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = offset + length; + packet_header.orig_len = offset + length; + + /***************************************************************/ + /* Now write everything to the file */ + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(context_header, 1, offset, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + + + +/************************************************************************** + * API functions for writing NAS-EPS PCAP files * + **************************************************************************/ + +/* Write an individual PDU (PCAP packet header + nas-context + nas-pdu) */ +inline int LTE_PCAP_NAS_WritePDU(FILE *fd, NAS_Context_Info_t *context, + const unsigned char *PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /****************************************************************/ + /* PCAP Header */ + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = length; + packet_header.orig_len = length; + + /***************************************************************/ + /* Now write everything to the file */ + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + + +/************************************************************************** + * API functions for writing RLC-LTE PCAP files * + **************************************************************************/ + +/* Write an individual RLC PDU (PCAP packet header + UDP header + rlc-context + rlc-pdu) */ +inline int LTE_PCAP_RLC_WritePDU(FILE *fd, RLC_Context_Info_t *context, + const unsigned char *PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + char context_header[256]; + int offset = 0; + uint16_t tmp16; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /*****************************************************************/ + + // Add dummy UDP header, start with src and dest port + context_header[offset++] = 0xde; + context_header[offset++] = 0xad; + context_header[offset++] = 0xbe; + context_header[offset++] = 0xef; + // length + tmp16 = length + 12; + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + // dummy CRC + context_header[offset++] = 0xde; + context_header[offset++] = 0xad; + + // Start magic string + memcpy(&context_header[offset], RLC_LTE_START_STRING, strlen(RLC_LTE_START_STRING)); + offset += strlen(RLC_LTE_START_STRING); + + // Fixed field RLC mode + context_header[offset++] = context->rlcMode; + + // Conditional fields + if (context->rlcMode == RLC_UM_MODE) { + context_header[offset++] = RLC_LTE_SN_LENGTH_TAG; + context_header[offset++] = context->sequenceNumberLength; + } + + // Optional fields + context_header[offset++] = RLC_LTE_DIRECTION_TAG; + context_header[offset++] = context->direction; + + context_header[offset++] = RLC_LTE_PRIORITY_TAG; + context_header[offset++] = context->priority; + + context_header[offset++] = RLC_LTE_UEID_TAG; + tmp16 = htons(context->ueid); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + context_header[offset++] = RLC_LTE_CHANNEL_TYPE_TAG; + tmp16 = htons(context->channelType); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + context_header[offset++] = RLC_LTE_CHANNEL_ID_TAG; + tmp16 = htons(context->channelId); + memcpy(context_header+offset, &tmp16, 2); + offset += 2; + + // Now the actual PDU + context_header[offset++] = RLC_LTE_PAYLOAD_TAG; + + // PCAP header + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = offset + length; + packet_header.orig_len = offset + length; + + // Write everything to file + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(context_header, 1, offset, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + +#endif // SRSLTE_PCAP_H diff --git a/lib/include/srslte/common/pdu.h b/lib/include/srslte/common/pdu.h new file mode 100644 index 0000000..848db29 --- /dev/null +++ b/lib/include/srslte/common/pdu.h @@ -0,0 +1,426 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_PDU_H +#define SRSLTE_PDU_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/interfaces_common.h" +#include +#include + +/* MAC PDU Packing/Unpacking functions. Section 6 of 36.321 */ +class subh; + +namespace srslte { + +template +class pdu +{ +public: + + pdu(uint32_t max_subheaders_) : subheaders(max_subheaders_) { + max_subheaders = max_subheaders_; + nof_subheaders = 0; + cur_idx = -1; + pdu_len = 0; + rem_len = 0; + last_sdu_idx = -1; + pdu_is_ul = false; + buffer_tx = NULL; + total_sdu_len = 0; + } + + void fprint(FILE *stream) { + fprintf(stream, "Number of Subheaders: %d\n", nof_subheaders); + for (int i=0;i 0) { + nof_subheaders++; + next(); + return true; + } else { + return false; + } + } + + bool next() { + if (cur_idx < nof_subheaders - 1) { + cur_idx++; + return true; + } else { + return false; + } + } + + void del_subh() { + if (nof_subheaders > 0) { + nof_subheaders--; + } + if (cur_idx > 0) { + cur_idx--; + } + } + + SubH* get() { + if (cur_idx >= 0) { + return &subheaders[cur_idx]; + } else { + return NULL; + } + } + + bool is_ul() { + return pdu_is_ul; + } + + uint8_t* get_current_sdu_ptr() { + return &buffer_tx[total_sdu_len+sdu_offset_start]; + } + + void add_sdu(uint32_t sdu_sz) { + total_sdu_len += sdu_sz; + } + + // Section 6.1.2 + void parse_packet(uint8_t *ptr) { + uint8_t *init_ptr = ptr; + nof_subheaders = 0; + bool ret = false; + do { + if (nof_subheaders < (int)max_subheaders) { + ret = subheaders[nof_subheaders].read_subheader(&ptr); + nof_subheaders++; + } + } while (ret && (nof_subheaders + 1) < (int)max_subheaders); + + for (int i=0;i subheaders; + uint32_t pdu_len; + uint32_t rem_len; + int cur_idx; + int nof_subheaders; + uint32_t max_subheaders; + bool pdu_is_ul; + uint8_t* buffer_tx; + uint32_t total_sdu_len; + uint32_t sdu_offset_start; + int last_sdu_idx; + + + /* Prepares the PDU for parsing or writing by setting the number of subheaders to 0 and the pdu length */ + virtual void init_(uint8_t *buffer_tx_ptr, uint32_t pdu_len_bytes, bool is_ulsch) { + nof_subheaders = 0; + pdu_len = pdu_len_bytes; + rem_len = pdu_len; + pdu_is_ul = is_ulsch; + buffer_tx = buffer_tx_ptr; + sdu_offset_start = max_subheaders*2 + 13; // Assuming worst-case 2 bytes per sdu subheader + all possible CE + total_sdu_len = 0; + last_sdu_idx = -1; + reset(); + for (uint32_t i=0;i +class subh +{ +public: + subh(){} + virtual ~subh(){} + + virtual bool read_subheader(uint8_t** ptr) = 0; + virtual void read_payload(uint8_t **ptr) = 0; + virtual void write_subheader(uint8_t** ptr, bool is_last) = 0; + virtual void write_payload(uint8_t **ptr) = 0; + virtual void fprint(FILE *stream) = 0; + + pdu* parent; +private: + virtual void init() = 0; +}; + + +class sch_subh : public subh +{ +public: + sch_subh(subh_type type_ = SCH_SUBH_TYPE) { + lcid = 0; + nof_bytes = 0; + payload = NULL; + nof_mch_sched_ce = 0; + cur_mch_sched_ce = 0; + F_bit = false; + type = type_; + parent = NULL; + bzero(&w_payload_ce, sizeof(w_payload_ce)); + } + + virtual ~sch_subh(){} + + typedef enum { + PHR_REPORT = 26, + CRNTI = 27, + CON_RES_ID = 28, + MTCH_MAX_LCID = 28, + TRUNC_BSR = 28, + TA_CMD = 29, + SHORT_BSR = 29, + DRX_CMD = 30, + LONG_BSR = 30, + MCH_SCHED_INFO = 30, + PADDING = 31, + SDU = 0 + } cetype; + + // Size of MAC CEs + const static int MAC_CE_CONTRES_LEN = 6; + + // Reading functions + bool is_sdu(); + bool is_var_len_ce(); + cetype ce_type(); + uint32_t size_plus_header(); + void set_payload_size(uint32_t size); + + bool read_subheader(uint8_t** ptr); + void read_payload(uint8_t **ptr); + + uint32_t get_sdu_lcid(); + uint32_t get_payload_size(); + uint32_t get_header_size(bool is_last); + uint8_t* get_sdu_ptr(); + + uint16_t get_c_rnti(); + uint64_t get_con_res_id(); + uint8_t get_ta_cmd(); + float get_phr(); + int get_bsr(uint32_t buff_size[4]); + + bool get_next_mch_sched_info(uint8_t *lcid, uint16_t *mtch_stop); + + // Writing functions + void write_subheader(uint8_t** ptr, bool is_last); + void write_payload(uint8_t **ptr); + + int set_sdu(uint32_t lcid, uint32_t nof_bytes, uint8_t *payload); + int set_sdu(uint32_t lcid, uint32_t requested_bytes, read_pdu_interface *sdu_itf); + bool set_c_rnti(uint16_t crnti); + bool set_bsr(uint32_t buff_size[4], sch_subh::cetype format); + bool set_con_res_id(uint64_t con_res_id); + bool set_ta_cmd(uint8_t ta_cmd); + bool set_phr(float phr); + void set_padding(); + void set_padding(uint32_t padding_len); + + void init(); + void fprint(FILE *stream); + + bool set_next_mch_sched_info(uint8_t lcid, uint16_t mtch_stop); + + +protected: + + static const int MAX_CE_PAYLOAD_LEN = 8; + uint32_t lcid; + int nof_bytes; + uint8_t* payload; + uint8_t w_payload_ce[64]; + uint8_t nof_mch_sched_ce; + uint8_t cur_mch_sched_ce; + bool F_bit; + subh_type type; + +private: + uint32_t sizeof_ce(uint32_t lcid, bool is_ul); + static uint8_t buff_size_table(uint32_t buffer_size); + static uint8_t phr_report_table(float phr_value); +}; + + +class sch_pdu : public pdu +{ +public: + sch_pdu(uint32_t max_subh): pdu(max_subh) {} + + void parse_packet(uint8_t *ptr); + uint8_t* write_packet(); + uint8_t* write_packet(srslte::log *log_h); + bool has_space_ce(uint32_t nbytes, bool var_len=false); + bool has_space_sdu(uint32_t nbytes); + int get_pdu_len(); + int rem_size(); + int get_sdu_space(); + + static uint32_t size_header_sdu(uint32_t nbytes); + bool update_space_ce(uint32_t nbytes, bool var_len=false); + bool update_space_sdu(uint32_t nbytes); + void fprint(FILE *stream); + +}; + +class rar_subh : public subh +{ +public: + rar_subh() { + bzero(&grant, sizeof(grant)); + ta = 0; + temp_rnti = 0; + preamble = 0; + parent = NULL; + } + + static const uint32_t RAR_GRANT_LEN = 20; + + // Reading functions + bool read_subheader(uint8_t** ptr); + void read_payload(uint8_t** ptr); + uint32_t get_rapid(); + uint32_t get_ta_cmd(); + uint16_t get_temp_crnti(); + void get_sched_grant(uint8_t grant[RAR_GRANT_LEN]); + + // Writing functoins + void write_subheader(uint8_t** ptr, bool is_last); + void write_payload(uint8_t** ptr); + void set_rapid(uint32_t rapid); + void set_ta_cmd(uint32_t ta); + void set_temp_crnti(uint16_t temp_rnti); + void set_sched_grant(uint8_t grant[RAR_GRANT_LEN]); + + void init(); + void fprint(FILE *stream); + +private: + uint8_t grant[RAR_GRANT_LEN]; + uint32_t ta; + uint16_t temp_rnti; + uint32_t preamble; +}; + +class rar_pdu : public pdu +{ +public: + + rar_pdu(uint32_t max_rars = 16); + + void set_backoff(uint8_t bi); + bool has_backoff(); + uint8_t get_backoff(); + + bool write_packet(uint8_t* ptr); + void fprint(FILE *stream); + +private: + bool has_backoff_indicator; + uint8_t backoff_indicator; + +}; + +class mch_subh : public sch_subh +{ +public: + mch_subh():sch_subh(MCH_SUBH_TYPE){} + + // Size of MAC CEs + const static int MAC_CE_CONTRES_LEN = 6; +}; + +class mch_pdu : public sch_pdu +{ +public: + mch_pdu(uint32_t max_subh) : sch_pdu(max_subh) {} + +private: + + /* Prepares the PDU for parsing or writing by setting the number of subheaders to 0 and the pdu length */ + virtual void init_(uint8_t *buffer_tx_ptr, uint32_t pdu_len_bytes, bool is_ulsch) { + nof_subheaders = 0; + pdu_len = pdu_len_bytes; + rem_len = pdu_len; + pdu_is_ul = is_ulsch; + buffer_tx = buffer_tx_ptr; + sdu_offset_start = max_subheaders*2 + 13; // Assuming worst-case 2 bytes per sdu subheader + all possible CE + total_sdu_len = 0; + last_sdu_idx = -1; + reset(); + for (uint32_t i=0;i pdu_q; + buffer_pool pool; + + process_callback *callback; + log *log_h; +}; + +} // namespace srslte + +#endif // SRSLTE_PDU_QUEUE_H + + + diff --git a/lib/include/srslte/common/rlc_pcap.h b/lib/include/srslte/common/rlc_pcap.h new file mode 100644 index 0000000..3a506f6 --- /dev/null +++ b/lib/include/srslte/common/rlc_pcap.h @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef RLCPCAP_H +#define RLCPCAP_H + +#include +#include "srslte/common/pcap.h" + +namespace srslte { + +class rlc_pcap +{ +public: + rlc_pcap() {enable_write=false; ue_id=0; pcap_file = NULL; }; + void enable(bool en); + void open(const char *filename, uint32_t ue_id = 0); + void close(); + + void set_ue_id(uint16_t ue_id); + + void write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes); + void write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes); + +private: + bool enable_write; + FILE *pcap_file; + uint32_t ue_id; + void pack_and_write(uint8_t* pdu, + uint32_t pdu_len_bytes, + uint8_t mode, + uint8_t direction, + uint8_t priority, + uint8_t seqnumberlength, + uint16_t ueid, + uint16_t channel_type, + uint16_t channel_id); +}; + +} // namespace srsue + +#endif // RLCPCAP_H diff --git a/lib/include/srslte/common/security.h b/lib/include/srslte/common/security.h new file mode 100644 index 0000000..29bd600 --- /dev/null +++ b/lib/include/srslte/common/security.h @@ -0,0 +1,188 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_SECURITY_H +#define SRSLTE_SECURITY_H + +/****************************************************************************** + * Common security header - wraps ciphering/integrity check algorithms. + *****************************************************************************/ + + +#include "srslte/common/common.h" + + +#define SECURITY_DIRECTION_UPLINK 0 +#define SECURITY_DIRECTION_DOWNLINK 1 + +namespace srslte { + +typedef enum{ + CIPHERING_ALGORITHM_ID_EEA0 = 0, + CIPHERING_ALGORITHM_ID_128_EEA1, + CIPHERING_ALGORITHM_ID_128_EEA2, + CIPHERING_ALGORITHM_ID_N_ITEMS, +}CIPHERING_ALGORITHM_ID_ENUM; +static const char ciphering_algorithm_id_text[CIPHERING_ALGORITHM_ID_N_ITEMS][20] = {"EEA0", + "128-EEA1", + "128-EEA2"}; +typedef enum{ + INTEGRITY_ALGORITHM_ID_EIA0 = 0, + INTEGRITY_ALGORITHM_ID_128_EIA1, + INTEGRITY_ALGORITHM_ID_128_EIA2, + INTEGRITY_ALGORITHM_ID_N_ITEMS, +}INTEGRITY_ALGORITHM_ID_ENUM; +static const char integrity_algorithm_id_text[INTEGRITY_ALGORITHM_ID_N_ITEMS][20] = {"EIA0", + "128-EIA1", + "128-EIA2"}; + + +/****************************************************************************** + * Key Generation + *****************************************************************************/ + +uint8_t security_generate_k_asme( uint8_t *ck, + uint8_t *ik, + uint8_t *ak, + uint8_t *sqn, + uint16_t mcc, + uint16_t mnc, + uint8_t *k_asme); + +uint8_t security_generate_k_enb( uint8_t *k_asme, + uint32_t nas_count, + uint8_t *k_enb); + +uint8_t security_generate_k_enb_star( uint8_t *k_enb, + uint32_t pci, + uint32_t earfcn, + uint8_t *k_enb_star); + +uint8_t security_generate_nh( uint8_t *k_asme, + uint8_t *sync, + uint8_t *nh); + +uint8_t security_generate_k_nas( uint8_t *k_asme, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_nas_enc, + uint8_t *k_nas_int); + +uint8_t security_generate_k_rrc( uint8_t *k_enb, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int); + +uint8_t security_generate_k_up( uint8_t *k_enb, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_up_enc, + uint8_t *k_up_int); + +/****************************************************************************** + * Integrity Protection + *****************************************************************************/ + +uint8_t security_128_eia1( uint8_t *key, + uint32_t count, + uint32_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac); + +uint8_t security_128_eia2( uint8_t *key, + uint32_t count, + uint32_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac); + +uint8_t security_md5(const uint8_t *input, + size_t len, + uint8_t *output); + + +/****************************************************************************** + * Encryption / Decryption + *****************************************************************************/ + +uint8_t security_128_eea1( uint8_t *key, + uint32_t count, + uint8_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *msg_out); + +uint8_t security_128_eea2(uint8_t *key, + uint32_t count, + uint8_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *msg_out); + +/****************************************************************************** + * Authentication + *****************************************************************************/ +uint8_t compute_opc( uint8_t *k, + uint8_t *op, + uint8_t *opc); + +uint8_t security_milenage_f1( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *sqn, + uint8_t *amf, + uint8_t *mac_a); + +uint8_t security_milenage_f1_star( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *sqn, + uint8_t *amf, + uint8_t *mac_s); + +uint8_t security_milenage_f2345( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *res, + uint8_t *ck, + uint8_t *ik, + uint8_t *ak); + +uint8_t security_milenage_f5_star( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *ak); + + +} // namespace srslte + +#endif // SRSLTE_SECURITY_H diff --git a/lib/include/srslte/common/snow_3g.h b/lib/include/srslte/common/snow_3g.h new file mode 100644 index 0000000..d4c742b --- /dev/null +++ b/lib/include/srslte/common/snow_3g.h @@ -0,0 +1,74 @@ +/*--------------------------------------------------------- +* snow_3g.h +* +* Adapted from ETSI/SAGE specifications: +* "Specification of the 3GPP Confidentiality and +* Integrity Algorithms UEA2 & UIA2. +* Document 1: UEA2 and UIA2 Specification" +* "Specification of the 3GPP Confidentiality +* and Integrity Algorithms UEA2 & UIA2. +* Document 2: SNOW 3G Specification" +*---------------------------------------------------------*/ +#ifndef SRSLTE_SNOW_3G_H +#define SRSLTE_SNOW_3G_H + +#include +#include +#include +#include + +typedef unsigned char u8; +typedef unsigned int u32; +typedef unsigned long long u64; + +/* Initialization. +* Input k[4]: Four 32-bit words making up 128-bit key. +* Input IV[4]: Four 32-bit words making 128-bit initialization variable. +* Output: All the LFSRs and FSM are initialized for key generation. +* See Section 4.1. +*/ + +void snow3g_initialize(u32 k[4], u32 IV[4]); + +/* Generation of Keystream. +* input n: number of 32-bit words of keystream. +* input z: space for the generated keystream, assumes +* memory is allocated already. +* output: generated keystream which is filled in z +* See section 4.2. +*/ + +void snow3g_generate_keystream(u32 n, u32 *z); + +/* f8. +* Input key: 128 bit Confidentiality Key. +* Input count:32-bit Count, Frame dependent input. +* Input bearer: 5-bit Bearer identity (in the LSB side). +* Input dir:1 bit, direction of transmission. +* Input data: length number of bits, input bit stream. +* Input length: 32 bit Length, i.e., the number of bits to be encrypted or +* decrypted. +* Output data: Output bit stream. Assumes data is suitably memory +* allocated. +* Encrypts/decrypts blocks of data between 1 and 2^32 bits in length as +* defined in Section 3. +*/ + +void snow3g_f8( u8 *key, u32 count, u32 bearer, u32 dir, \ + u8 *data, u32 length ); + +/* f9. +* Input key: 128 bit Integrity Key. +* Input count:32-bit Count, Frame dependent input. +* Input fresh: 32-bit Random number. +* Input dir:1 bit, direction of transmission (in the LSB). +* Input data: length number of bits, input bit stream. +* Input length: 64 bit Length, i.e., the number of bits to be MAC'd. +* Output : 32 bit block used as MAC +* Generates 32-bit MAC using UIA2 algorithm as defined in Section 4. +*/ + +u8* snow3g_f9( u8* key, u32 count, u32 fresh, u32 dir, \ + u8 *data, u64 length); + +#endif // SRSLTE_SNOW_3G_H \ No newline at end of file diff --git a/lib/include/srslte/common/task_dispatcher.h b/lib/include/srslte/common/task_dispatcher.h new file mode 100644 index 0000000..c1d223a --- /dev/null +++ b/lib/include/srslte/common/task_dispatcher.h @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: task_dispatcher.h + * Description: + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TASK_DISPATCHER_H +#define SRSLTE_TASK_DISPATCHER_H + +#include +#include +#include +#include +#include "srslte/common/threads.h" + +namespace srslte { + +class task_dispatcher : public thread +{ +public: + task_dispatcher(uint32_t max_pending_tasks); + ~task_dispatcher(); + void push_task(uint32_t task_code); + virtual void run_task(uint32_t task_code) = 0; +private: + std::queue pending_tasks; + void run_thread(); + pthread_mutex_t mutex; + pthread_cond_t cvar; + bool running; +}; + +} // namespace srslte + +#endif // SRSLTE_TASK_DISPATCHER_H diff --git a/lib/include/srslte/common/thread_pool.h b/lib/include/srslte/common/thread_pool.h new file mode 100644 index 0000000..865fc10 --- /dev/null +++ b/lib/include/srslte/common/thread_pool.h @@ -0,0 +1,105 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: thread_pool.h + * Description: Implements a pool of threads. Pending tasks to execute are + * identified by a pointer. + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_THREAD_POOL_H +#define SRSLTE_THREAD_POOL_H + +#include +#include +#include +#include + +#include "srslte/common/threads.h" + +namespace srslte { + +class thread_pool +{ +public: + + class worker : public thread + { + public: + void setup(uint32_t id, thread_pool *parent, uint32_t prio=0, uint32_t mask = 255); + void stop(); + uint32_t get_id(); + void release(); + protected: + virtual void work_imp() = 0; + private: + uint32_t my_id; + thread_pool *my_parent; + bool running; + void run_thread(); + void wait_to_start(); + void finished(); + }; + + + thread_pool(uint32_t nof_workers); + void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255); + void stop(); + worker* wait_worker(); + worker* wait_worker(uint32_t tti); + worker* wait_worker_nb(uint32_t tti); + void start_worker(worker*); + void start_worker(uint32_t id); + worker* get_worker(uint32_t id); + uint32_t get_nof_workers(); + + +private: + + bool find_finished_worker(uint32_t tti, uint32_t *id); + + typedef enum { + IDLE, + START_WORK, + WORKER_READY, + WORKING + }worker_status; + + std::vector workers; + uint32_t nof_workers; + uint32_t max_workers; + bool running; + pthread_cond_t cvar_queue; + pthread_mutex_t mutex_queue; + std::vector status; + std::vector cvar; + std::vector mutex; + std::stack available_workers; +}; +} + +#endif // SRSLTE_THREAD_POOL_H diff --git a/lib/include/srslte/common/threads.h b/lib/include/srslte/common/threads.h new file mode 100644 index 0000000..c6791ae --- /dev/null +++ b/lib/include/srslte/common/threads.h @@ -0,0 +1,166 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include + +// Default priority for all threads below UHD threads +#define DEFAULT_PRIORITY 60 + +#ifdef __cplusplus + extern "C" { +#endif // __cplusplus + + bool threads_new_rt(pthread_t *thread, void *(*start_routine) (void*), void *arg); + bool threads_new_rt_prio(pthread_t *thread, void *(*start_routine) (void*), void *arg, int prio_offset); + bool threads_new_rt_cpu(pthread_t *thread, void *(*start_routine) (void*), void *arg, int cpu, int prio_offset); + bool threads_new_rt_mask(pthread_t *thread, void *(*start_routine) (void*), void *arg, int mask, int prio_offset); + void threads_print_self(); + +#ifdef __cplusplus +} + +#ifndef SRSLTE_THREADS_H +#define SRSLTE_THREADS_H + +class thread +{ +public: + thread() { + _thread = 0; + } + bool start(int prio = -1) { + return threads_new_rt_prio(&_thread, thread_function_entry, this, prio); + } + bool start_cpu(int prio, int cpu) { + return threads_new_rt_cpu(&_thread, thread_function_entry, this, cpu, prio); + } + bool start_cpu_mask(int prio, int mask){ + return threads_new_rt_mask(&_thread, thread_function_entry, this, mask, prio); +} + void print_priority() { + threads_print_self(); + } + void wait_thread_finish() { + pthread_join(_thread, NULL); + } + void thread_cancel() { + pthread_cancel(_thread); + } +protected: + virtual void run_thread() = 0; +private: + static void *thread_function_entry(void *_this) { ((thread*) _this)->run_thread(); return NULL; } + pthread_t _thread; +}; + +class periodic_thread : public thread +{ +public: + void start_periodic(int period_us_, int priority = -1) { + run_enable = true; + period_us = period_us_; + start(priority); + } + void stop_thread() { + run_enable = false; + wait_thread_finish(); + } +protected: + virtual void run_period() = 0; +private: + int wakeups_missed; + int timer_fd; + int period_us; + bool run_enable; + void run_thread() { + if (make_periodic()) { + return; + } + while(run_enable) { + run_period(); + if (run_enable) { + wait_period(); + } + } + } + int make_periodic() { + int ret = -1; + unsigned int ns; + unsigned int sec; + struct itimerspec itval; + + /* Create the timer */ + ret = timerfd_create (CLOCK_MONOTONIC, 0); + wakeups_missed = 0; + timer_fd = ret; + if (ret > 0) { + /* Make the timer periodic */ + sec = period_us/1e6; + ns = (period_us - (sec * 1000000)) * 1000; + itval.it_interval.tv_sec = sec; + itval.it_interval.tv_nsec = ns; + itval.it_value.tv_sec = sec; + itval.it_value.tv_nsec = ns; + ret = timerfd_settime (timer_fd, 0, &itval, NULL); + if (ret < 0) { + perror("timerfd_settime"); + } + } else { + perror("timerfd_create"); + } + return ret; + } + void wait_period() { + unsigned long long missed; + int ret; + + /* Wait for the next timer event. If we have missed any the + number is written to "missed" */ + ret = read (timer_fd, &missed, sizeof (missed)); + if (ret == -1) + { + perror ("read timer"); + return; + } + + /* "missed" should always be >= 1, but just to be sure, check it is not 0 anyway */ + if (missed > 0) { + wakeups_missed += (missed - 1); + } + } +}; + + + +#endif // SRSLTE_THREADS_H + +#endif // __cplusplus + diff --git a/lib/include/srslte/common/timeout.h b/lib/include/srslte/common/timeout.h new file mode 100644 index 0000000..bca09da --- /dev/null +++ b/lib/include/srslte/common/timeout.h @@ -0,0 +1,131 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: timeout.h + * Description: Millisecond resolution timeouts. Uses a dedicated thread to + * call an optional callback function upon timeout expiry. + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TIMEOUT_H +#define SRSLTE_TIMEOUT_H + +#include +#include +#include +#include +#include "srslte/srslte.h" + +namespace srslte { + +class timeout_callback +{ + public: + virtual void timeout_expired(uint32_t timeout_id) = 0; +}; + +class timeout +{ +public: + timeout():running(false),callback(NULL), thread(0), timeout_id(0) {} + ~timeout() + { + if(running && callback) + pthread_join(thread, NULL); + } + void start(int duration_msec_, uint32_t timeout_id_=0,timeout_callback *callback_=NULL) + { + if(duration_msec_ < 0) + return; + reset(); + gettimeofday(&start_time[1], NULL); + duration_msec = duration_msec_; + running = true; + timeout_id = timeout_id_; + callback = callback_; + if(callback) + pthread_create(&thread, NULL, &thread_start, this); + } + void reset() + { + if(callback) + pthread_cancel(thread); + running = false; + } + static void* thread_start(void *t_) + { + timeout *t = (timeout*)t_; + t->thread_func(); + return NULL; + } + void thread_func() + { + // substract time elapsed until now from timer duration + gettimeofday(&start_time[2], NULL); + get_time_interval(start_time); + + int32_t usec = duration_msec*1000-start_time[0].tv_usec; + if(usec > 0) + usleep(usec); + if(callback && running) + callback->timeout_expired(timeout_id); + } + bool expired() + { + if(running) { + gettimeofday(&start_time[2], NULL); + get_time_interval(start_time); + return start_time[0].tv_usec > duration_msec*1000; + } else { + return false; + } + } + int32_t get_msec_to_expire() { + if (running) { + gettimeofday(&start_time[2], NULL); + get_time_interval(start_time); + return (duration_msec*1000 - start_time[0].tv_usec)/1000; + } + return 0; + } + bool is_running() + { + return running; + } + +private: + struct timeval start_time[3]; + pthread_t thread; + uint32_t timeout_id; + timeout_callback *callback; + bool running; + int duration_msec; +}; + +} // namespace srslte + +#endif // SRSLTE_TIMEOUT_H diff --git a/lib/include/srslte/common/timers.h b/lib/include/srslte/common/timers.h new file mode 100644 index 0000000..046dae3 --- /dev/null +++ b/lib/include/srslte/common/timers.h @@ -0,0 +1,176 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +/****************************************************************************** + * File: timers.h + * Description: Manually incremented timers. Call a callback function upon + * expiry. + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TIMERS_H +#define SRSLTE_TIMERS_H + +#include +#include +#include +#include + +namespace srslte { + +class timer_callback +{ + public: + virtual void timer_expired(uint32_t timer_id) = 0; +}; + +class timers +{ +public: + class timer + { + public: + timer(uint32_t id_=0) {id = id_; counter = 0; timeout = 0; running = false; callback = NULL; } + void set(timer_callback *callback_, uint32_t timeout_) { + callback = callback_; + timeout = timeout_; + reset(); + } + bool is_running() { + return (counter < timeout) && running; + } + bool is_expired() { + return (timeout > 0) && (counter >= timeout); + } + uint32_t get_timeout() { + return timeout; + } + void reset() { + counter = 0; + } + uint32_t value() { + return counter; + } + void step() { + if (running) { + counter++; + if (is_expired()) { + running = false; + if (callback) { + callback->timer_expired(id); + } + } + } + } + void stop() { + running = false; + } + void run() { + running = true; + } + uint32_t id; + private: + timer_callback *callback; + uint32_t timeout; + uint32_t counter; + bool running; + }; + + timers(uint32_t nof_timers_) : timer_list(nof_timers_),used_timers(nof_timers_) { + nof_timers = nof_timers_; + next_timer = 0; + nof_used_timers = 0; + for (uint32_t i=0;istep(); + } + } + void stop_all() { + for (uint32_t i=0;istop(); + } + } + void run_all() { + for (uint32_t i=0;irun(); + } + } + void reset_all() { + for (uint32_t i=0;ireset(); + } + } + timer *get(uint32_t i) { + if (i < nof_timers) { + return &timer_list[i]; + } else { + printf("Error accessing invalid timer %d (Only %d timers available)\n", i, nof_timers); + return NULL; + } + } + void release_id(uint32_t i) { + if (nof_used_timers > 0 && i < nof_timers) { + used_timers[i] = false; + nof_used_timers--; + } else { + fprintf(stderr, "Error releasing timer id=%d: nof_used_timers=%d, nof_timers=%d\n", i, nof_used_timers, nof_timers); + } + } + uint32_t get_unique_id() { + if (nof_used_timers >= nof_timers) { + fprintf(stderr, "Error getting unique timer id: no more timers available\n"); + return 0; + } else { + for (uint32_t i=0;i timer_list; + std::vector used_timers; +}; + +} // namespace srslte + +#endif // SRSLTE_TIMERS_H diff --git a/lib/include/srslte/common/trace.h b/lib/include/srslte/common/trace.h new file mode 100644 index 0000000..00c7739 --- /dev/null +++ b/lib/include/srslte/common/trace.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: trace.h + * Description: + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TRACE_H +#define SRSLTE_TRACE_H + +#include +#include +#include + +namespace srslte { + +template +class trace +{ +public: + + trace(uint32_t nof_elems_) : tti(nof_elems_), data(nof_elems_) { + rpm=0; + nof_elems=nof_elems_; + wrapped = false; + }; + void push_cur_time_us(uint32_t cur_tti) { + struct timeval t; + gettimeofday(&t, NULL); + elemType us = t.tv_sec*1e6+t.tv_usec; + push(cur_tti, us); + } + void push(uint32_t value_tti, elemType value) { + tti[rpm] = value_tti; + data[rpm] = value; + rpm++; + if (rpm >= nof_elems) { + rpm = 0; + wrapped = true; + } + } + bool writeToBinary(std::string filename) { + FILE *f = fopen(filename.c_str(), "w"); + if (f != NULL) { + uint32_t st=wrapped?(rpm+1):0; + do { + writeToBinaryValue(f, st++); + if (st >= nof_elems) { + st=0; + } + } while(st!=rpm); + fclose(f); + return true; + } else { + perror("fopen"); + return false; + } + } + +private: + std::vector tti; + std::vector data; + uint32_t rpm; + uint32_t nof_elems; + bool wrapped; + + void writeToBinaryValue(FILE *f, uint32_t idx) { + fwrite(&tti[idx], 1, sizeof(uint32_t), f); + fwrite(&data[idx], 1, sizeof(elemType), f); + } + +}; + +} // namespace srslte + +#endif // SRSLTE_TRACE_H diff --git a/lib/include/srslte/common/tti_sync.h b/lib/include/srslte/common/tti_sync.h new file mode 100644 index 0000000..dd018f8 --- /dev/null +++ b/lib/include/srslte/common/tti_sync.h @@ -0,0 +1,80 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: tti_synch.h + * Description: Interface used for PHY-MAC synchronization + * (producer-consumer model). The consumer waits while its + * counter is lower than the producer counter. + * The PHY is the consumer. The MAC is the producer. + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TTI_SYNC_H +#define SRSLTE_TTI_SYNC_H + +#include + +namespace srslte { + +class tti_sync +{ + public: + tti_sync(uint32_t modulus_) + { + modulus = modulus_; + increment = 1; + init_counters(0); + } + virtual void increase() = 0; + virtual void increase(uint32_t cnt) = 0; + virtual void resync() = 0; + virtual uint32_t wait() = 0; + virtual void set_producer_cntr(uint32_t) = 0; + uint32_t get_producer_cntr() { return producer_cntr; } + uint32_t get_consumer_cntr() { return consumer_cntr; } + void set_increment(uint32_t increment_) { + increment = increment_; + } + protected: + void increase_producer() { producer_cntr = (producer_cntr + increment)%modulus; } + void increase_producer(uint32_t cnt) { producer_cntr = cnt%modulus; } + void increase_consumer() { consumer_cntr = (consumer_cntr + increment)%modulus; } + bool wait_condition() { return producer_cntr == consumer_cntr; } + void init_counters(uint32_t val) + { + consumer_cntr = val; + producer_cntr = val; + } + uint32_t increment; + uint32_t modulus; + uint32_t producer_cntr; + uint32_t consumer_cntr; +}; + +} // namespace srsue + +#endif // SRSLTE_TTI_SYNC_H diff --git a/lib/include/srslte/common/tti_sync_cv.h b/lib/include/srslte/common/tti_sync_cv.h new file mode 100644 index 0000000..2ba975f --- /dev/null +++ b/lib/include/srslte/common/tti_sync_cv.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: tti_synch_cv.h + * Description: Implements tti_sync interface with condition variables. + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_TTI_SYNC_CV_H +#define SRSLTE_TTI_SYNC_CV_H + +#include +#include "srslte/common/tti_sync.h" + +namespace srslte { + +class tti_sync_cv : public tti_sync +{ + public: + tti_sync_cv(uint32_t modulus = 10240); + ~tti_sync_cv(); + void increase(); + void increase(uint32_t cnt); + uint32_t wait(); + void resync(); + void set_producer_cntr(uint32_t producer_cntr); + + private: + pthread_cond_t cond; + pthread_mutex_t mutex; +}; + +} // namespace srslte + +#endif // SRSLTE_TTI_SYNC_CV_H diff --git a/lib/include/srslte/config.h b/lib/include/srslte/config.h new file mode 100644 index 0000000..02c4377 --- /dev/null +++ b/lib/include/srslte/config.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_CONFIG_H +#define SRSLTE_CONFIG_H + +// Generic helper definitions for shared library support +#if defined _WIN32 || defined __CYGWIN__ + #define SRSLTE_IMPORT __declspec(dllimport) + #define SRSLTE_EXPORT __declspec(dllexport) + #define SRSLTE_LOCAL +#else + #if __GNUC__ >= 4 + #define SRSLTE_IMPORT __attribute__ ((visibility ("default"))) + #define SRSLTE_EXPORT __attribute__ ((visibility ("default"))) + #else + #define SRSLTE_IMPORT + #define SRSLTE_EXPORT + #define SRSLTE_LOCAL + #endif +#endif + +// Define SRSLTE_API +// SRSLTE_API is used for the public API symbols. +#ifdef SRSLTE_DLL_EXPORTS // defined if we are building the SRSLTE DLL (instead of using it) + #define SRSLTE_API SRSLTE_EXPORT +#else + #define SRSLTE_API SRSLTE_IMPORT +#endif + + +// Common error codes +#define SRSLTE_SUCCESS 0 +#define SRSLTE_ERROR -1 +#define SRSLTE_ERROR_INVALID_INPUTS -2 + +// cf_t definition +typedef _Complex float cf_t; + +#ifdef ENABLE_C16 +typedef _Complex short int c16_t; +#endif /* ENABLE_C16 */ + +#endif // SRSLTE_CONFIG_H diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h new file mode 100644 index 0000000..643336b --- /dev/null +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -0,0 +1,288 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/srslte.h" + +#include "srslte/common/common.h" +#include "srslte/common/security.h" +#include "srslte/common/interfaces_common.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/upper/rlc_interface.h" +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/asn1/liblte_s1ap.h" + +#include + +#ifndef SRSLTE_ENB_INTERFACES_H +#define SRSLTE_ENB_INTERFACES_H + +namespace srsenb { + +/* Interface PHY -> MAC */ +class mac_interface_phy +{ +public: + const static int MAX_GRANTS = 64; + + typedef struct { + srslte_enb_dl_pdsch_t sched_grants[MAX_GRANTS]; + uint32_t nof_grants; + uint32_t cfi; + } dl_sched_t; + + typedef struct { + srslte_enb_ul_pusch_t sched_grants[MAX_GRANTS]; + srslte_enb_dl_phich_t phich[MAX_GRANTS]; + uint32_t nof_grants; + uint32_t nof_phich; + } ul_sched_t; + + + virtual int sr_detected(uint32_t tti, uint16_t rnti) = 0; + virtual int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv) = 0; + + virtual int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) = 0; + virtual int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) = 0; + virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) = 0; + virtual int snr_info(uint32_t tti, uint16_t rnti, float snr_db) = 0; + virtual int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) = 0; + virtual int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res) = 0; + + virtual int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) = 0; + virtual int get_mch_sched(bool is_mcch, dl_sched_t *dl_sched_res) = 0; + virtual int get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res) = 0; + + // Radio-Link status + virtual void rl_failure(uint16_t rnti) = 0; + virtual void rl_ok(uint16_t rnti) = 0; + + virtual void tti_clock() = 0; +}; + +/* Interface MAC -> PHY */ +class phy_interface_mac +{ +public: + + /* MAC adds/removes an RNTI to the list of active RNTIs */ + virtual int add_rnti(uint16_t rnti) = 0; + virtual void rem_rnti(uint16_t rnti) = 0; +}; + +/* Interface RRC -> PHY */ +class phy_interface_rrc +{ +public: + + typedef struct { + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbsfn_notification_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT mbsfn_area_info; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + } phy_cfg_mbsfn_t; + + typedef struct { + phy_cfg_mbsfn_t mbsfn; + } phy_rrc_cfg_t; + + + virtual void configure_mbsfn(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT mcch) = 0; + virtual void set_conf_dedicated_ack(uint16_t rnti, bool rrc_completed) = 0; + virtual void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) = 0; + +}; + +class mac_interface_rrc +{ +public: + /* Provides cell configuration including SIB periodicity, etc. */ + virtual int cell_cfg(sched_interface::cell_cfg_t *cell_cfg) = 0; + virtual void reset() = 0; + + /* Manages UE configuration context */ + virtual int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *cfg) = 0; + virtual int ue_rem(uint16_t rnti) = 0; + + /* Manages UE bearers and associated configuration */ + virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg) = 0; + virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; + virtual int set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) = 0; + virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0; + virtual void write_mcch(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) = 0; +}; + +class mac_interface_rlc +{ +public: + virtual int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0; +}; + +//RLC interface for MAC +class rlc_interface_mac +{ +public: + + /* MAC calls RLC to get RLC segment of nof_bytes length. + * Segmentation happens in this function. RLC PDU is stored in payload. */ + virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) = 0; + + virtual void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload) = 0; + virtual void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) = 0; + + /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. + * PDU gets placed into the buffer and higher layer thread gets notified. */ + virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) = 0; + +}; + + +// RLC interface for PDCP +class rlc_interface_pdcp +{ +public: + /* PDCP calls RLC to push an RLC SDU. SDU gets placed into the RLC buffer and MAC pulls + * RLC PDUs according to TB size. */ + virtual void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual bool rb_is_um(uint16_t rnti, uint32_t lcid) = 0; +}; + +// RLC interface for RRC +class rlc_interface_rrc +{ +public: + virtual void clear_buffer(uint16_t rnti) = 0; + virtual void add_user(uint16_t rnti) = 0; + virtual void rem_user(uint16_t rnti) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg) = 0; + virtual void add_bearer_mrb(uint16_t rnti, uint32_t lcid) = 0; +}; + +// PDCP interface for GTPU +class pdcp_interface_gtpu +{ +public: + virtual void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; +}; + +// PDCP interface for RRC +class pdcp_interface_rrc +{ +public: + virtual void reset(uint16_t rnti) = 0; + virtual void add_user(uint16_t rnti) = 0; + virtual void rem_user(uint16_t rnti) = 0; + virtual void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cnfg) = 0; + virtual void config_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc_, + uint8_t *k_rrc_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) = 0; +}; + +// PDCP interface for RLC +class pdcp_interface_rlc +{ +public: + /* RLC calls PDCP to push a PDCP PDU. */ + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; +}; + +// RRC interface for RLC +class rrc_interface_rlc +{ +public: + virtual void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload) = 0; + virtual void read_pdu_pcch(uint8_t *payload, uint32_t payload_size) = 0; + virtual void max_retx_attempted(uint16_t rnti) = 0; +}; + +// RRC interface for MAC +class rrc_interface_mac +{ +public: + /* Radio Link failure */ + virtual void rl_failure(uint16_t rnti) = 0; + virtual void add_user(uint16_t rnti) = 0; + virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; + virtual bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len) = 0; +}; + +// RRC interface for PDCP +class rrc_interface_pdcp +{ +public: + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; +}; + +// RRC interface for S1AP +class rrc_interface_s1ap +{ +public: + virtual void write_dl_info(uint16_t rnti, srslte::byte_buffer_t *sdu) = 0; + virtual void release_complete(uint16_t rnti) = 0; + virtual bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) = 0; + virtual bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) = 0; + virtual bool release_erabs(uint32_t rnti) = 0; + virtual void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) = 0; +}; + +// GTPU interface for PDCP +class gtpu_interface_pdcp +{ +public: + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; +}; + +// GTPU interface for RRC +class gtpu_interface_rrc +{ +public: + virtual void add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, uint32_t *teid_in) = 0; + virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0; + virtual void rem_user(uint16_t rnti) = 0; +}; + +// S1AP interface for RRC +class s1ap_interface_rrc +{ +public: + virtual void initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu) = 0; + virtual void initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec) = 0; + virtual void write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu) = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) = 0; + virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) = 0; + virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) = 0; + // virtual void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) = 0; +}; + +} + +#endif // SRSLTE_ENB_INTERFACES_H diff --git a/lib/include/srslte/interfaces/enb_metrics_interface.h b/lib/include/srslte/interfaces/enb_metrics_interface.h new file mode 100644 index 0000000..4e540ab --- /dev/null +++ b/lib/include/srslte/interfaces/enb_metrics_interface.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_ENB_METRICS_INTERFACE_H +#define SRSLTE_ENB_METRICS_INTERFACE_H + +#include + +#include "srsenb/hdr/upper/common_enb.h" +#include "srsenb/hdr/upper/s1ap_metrics.h" +#include "srsenb/hdr/upper/rrc_metrics.h" +#include "srsue/hdr/upper/gw_metrics.h" +#include "srslte/upper/rlc_metrics.h" +#include "srsenb/hdr/mac/mac_metrics.h" +#include "srsenb/hdr/phy/phy_metrics.h" + +namespace srsenb { + +typedef struct { + uint32_t rf_o; + uint32_t rf_u; + uint32_t rf_l; + bool rf_error; +}rf_metrics_t; + +typedef struct { + rf_metrics_t rf; + phy_metrics_t phy[ENB_METRICS_MAX_USERS]; + mac_metrics_t mac[ENB_METRICS_MAX_USERS]; + rrc_metrics_t rrc; + s1ap_metrics_t s1ap; + bool running; +}enb_metrics_t; + +// ENB interface +class enb_metrics_interface +{ +public: + virtual bool get_metrics(enb_metrics_t &m) = 0; +}; + +} // namespace srsenb + +#endif // SRSLTE_ENB_METRICS_INTERFACE_H diff --git a/lib/include/srslte/interfaces/epc_interfaces.h b/lib/include/srslte/interfaces/epc_interfaces.h new file mode 100644 index 0000000..703a599 --- /dev/null +++ b/lib/include/srslte/interfaces/epc_interfaces.h @@ -0,0 +1,19 @@ +#ifndef SRSLTE_EPC_INTERFACES_H +#define SRSLTE_EPC_INTERFACES_H + +#include "srslte/srslte.h" + +#include "srslte/common/common.h" + +namespace srsepc { + +class hss_interface_s1ap +{ +public: + virtual bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) = 0; + virtual bool gen_update_loc_answer(uint64_t imsi, uint8_t *qci) = 0; + virtual bool resync_sqn(uint64_t imsi, uint8_t *auts) = 0; +}; + +} +#endif // SRSLTE_EPC_INTERFACES_H diff --git a/lib/include/srslte/interfaces/sched_interface.h b/lib/include/srslte/interfaces/sched_interface.h new file mode 100644 index 0000000..5cd8f5f --- /dev/null +++ b/lib/include/srslte/interfaces/sched_interface.h @@ -0,0 +1,270 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/srslte.h" + +#ifndef SRSLTE_SCHED_INTERFACE_H +#define SRSLTE_SCHED_INTERFACE_H + +namespace srsenb { + +class sched_interface +{ +public: + + const static int MAX_SIB_PAYLOAD_LEN = 2048; + const static int MAX_SIBS = 16; + const static int MAX_LC = 6; + const static int MAX_DATA_LIST = 32; + const static int MAX_RAR_LIST = 8; + const static int MAX_BC_LIST = 8; + const static int MAX_RLC_PDU_LIST = 8; + const static int MAX_PHICH_LIST = 8; + + typedef struct { + uint32_t len; + uint32_t period_rf; + } cell_cfg_sib_t; + + + typedef struct { + int pdsch_mcs; + int pdsch_max_mcs; + int pusch_mcs; + int pusch_max_mcs; + int nof_ctrl_symbols; + } sched_args_t; + + + typedef struct { + + // Main cell configuration (used to calculate DCI locations in scheduler) + srslte_cell_t cell; + + /* SIB configuration */ + cell_cfg_sib_t sibs[MAX_SIBS]; + uint32_t si_window_ms; + + /* pusch configuration */ + srslte_pusch_hopping_cfg_t pusch_hopping_cfg; + + /* prach configuration */ + uint32_t prach_config; + uint32_t prach_freq_offset; + uint32_t prach_rar_window; + uint32_t prach_contention_resolution_timer; + + uint32_t maxharq_msg3tx; + uint32_t n1pucch_an; + uint32_t delta_pucch_shift; + + // If non-negative, statically allocate N prbs at the edges of the uplink for PUCCH + int nrb_pucch; + + uint32_t nrb_cqi; + uint32_t ncs_an; + + uint32_t srs_subframe_config; + uint32_t srs_subframe_offset; + uint32_t srs_bw_config; + + } cell_cfg_t; + + + typedef struct { + int priority; + int bsd; + int pbr; + int group; + enum {IDLE = 0, UL, DL, BOTH} direction; + } ue_bearer_cfg_t; + + typedef struct { + + bool continuous_pusch; + + /* ue capabilities, etc */ + + uint32_t maxharq_tx; + uint32_t aperiodic_cqi_period; // if 0 is periodic CQI + uint32_t beta_ack_index; + uint32_t beta_ri_index; + uint32_t beta_cqi_index; + + srslte_pucch_cfg_t pucch_cfg; + uint32_t n_pucch_cqi; + uint32_t sr_I; + uint32_t sr_N_pucch; + bool sr_enabled; + uint32_t cqi_pucch; + uint32_t cqi_idx; + bool cqi_enabled; + + ue_bearer_cfg_t ue_bearers[MAX_LC]; + + } ue_cfg_t; + + typedef struct { + uint32_t lcid; + uint32_t nbytes; + } dl_sched_pdu_t; + + + typedef struct { + uint32_t lcid; + uint32_t lcid_buffer_size; + uint32_t stop; + uint8_t *mtch_payload; + } dl_mtch_sched_t; + + typedef struct { + dl_sched_pdu_t pdu[20]; + dl_mtch_sched_t mtch_sched[8]; + uint32_t num_mtch_sched; + uint8_t *mcch_payload; + uint32_t current_sf_allocation_num; + } dl_pdu_mch_t; + + typedef struct { + uint32_t rnti; + srslte_dci_format_t dci_format; + srslte_ra_dl_dci_t dci; + srslte_dci_location_t dci_location; + uint32_t tbs[SRSLTE_MAX_TB]; + bool mac_ce_ta; + bool mac_ce_rnti; + uint32_t nof_pdu_elems[SRSLTE_MAX_TB]; + dl_sched_pdu_t pdu[SRSLTE_MAX_TB][MAX_RLC_PDU_LIST]; + } dl_sched_data_t; + + typedef struct { + uint32_t rnti; + bool needs_pdcch; + uint32_t current_tx_nb; + uint32_t tbs; + srslte_ra_ul_dci_t dci; + srslte_dci_location_t dci_location; + } ul_sched_data_t; + + typedef struct { + uint32_t ra_id; + srslte_dci_rar_grant_t grant; + } dl_sched_rar_grant_t; + + typedef struct { + uint32_t rarnti; + uint32_t tbs; + srslte_ra_dl_dci_t dci; + srslte_dci_location_t dci_location; + uint32_t nof_grants; + dl_sched_rar_grant_t grants[MAX_RAR_LIST]; + } dl_sched_rar_t; + + typedef struct { + srslte_ra_dl_dci_t dci; + srslte_dci_location_t dci_location; + + enum bc_type { + BCCH, PCCH + } type; + + uint32_t index; + + uint32_t tbs; + + } dl_sched_bc_t; + + typedef struct { + uint32_t cfi; + uint32_t nof_data_elems; + uint32_t nof_rar_elems; + uint32_t nof_bc_elems; + dl_sched_data_t data[MAX_DATA_LIST]; + dl_sched_rar_t rar[MAX_RAR_LIST]; + dl_sched_bc_t bc[MAX_BC_LIST]; + } dl_sched_res_t; + + typedef struct { + uint16_t rnti; + enum phich_elem { + ACK, NACK + } phich; + } ul_sched_phich_t; + + typedef struct { + uint32_t nof_dci_elems; + uint32_t nof_phich_elems; + ul_sched_data_t pusch[MAX_DATA_LIST]; + ul_sched_phich_t phich[MAX_PHICH_LIST]; + } ul_sched_res_t; + + /******************* Scheduler Control ****************************/ + + /* Provides cell configuration including SIB periodicity, etc. */ + virtual int cell_cfg(cell_cfg_t *cell_cfg) = 0; + virtual int reset() = 0; + + /* Manages UE scheduling context */ + virtual int ue_cfg(uint16_t rnti, ue_cfg_t *cfg) = 0; + virtual int ue_rem(uint16_t rnti) = 0; + virtual bool ue_exists(uint16_t rnti) = 0; + + /* Manages UE bearers and associated configuration */ + virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, ue_bearer_cfg_t *cfg) = 0; + virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; + + virtual uint32_t get_ul_buffer(uint16_t rnti) = 0; + virtual uint32_t get_dl_buffer(uint16_t rnti) = 0; + + /******************* Scheduling Interface ***********************/ + /* DL buffer status report */ + virtual int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0; + virtual int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code) = 0; + + /* DL information */ + virtual int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) = 0; + virtual int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size) = 0; + virtual int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) = 0; + virtual int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) = 0; + virtual int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) = 0; + + /* UL information */ + virtual int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc) = 0; + virtual int ul_sr_info(uint32_t tti, uint16_t rnti) = 0; + virtual int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr, bool set_value = true) = 0; + virtual int ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len) = 0; + virtual int ul_phr(uint16_t rnti, int phr) = 0; + virtual int ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code) = 0; + + /* Run Scheduler for this tti */ + virtual int dl_sched(uint32_t tti, dl_sched_res_t *sched_result) = 0; + virtual int ul_sched(uint32_t tti, ul_sched_res_t *sched_result) = 0; + +}; + +} + +#endif // SRSLTE_SCHED_INTERFACE_H diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h new file mode 100644 index 0000000..a4fb2b2 --- /dev/null +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -0,0 +1,670 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: interfaces.h + * Description: Abstract base class interfaces provided by layers + * to other layers. + *****************************************************************************/ + +#ifndef SRSLTE_UE_INTERFACES_H +#define SRSLTE_UE_INTERFACES_H + +#include + +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/common/interfaces_common.h" +#include "srslte/common/common.h" +#include "srslte/common/security.h" +#include "srslte/upper/rlc_interface.h" + +namespace srsue { + +typedef enum { + AUTH_OK, + AUTH_FAILED, + AUTH_SYNCH_FAILURE +} auth_result_t; + +// UE interface +class ue_interface +{ +}; + +// USIM interface for NAS +class usim_interface_nas +{ +public: + virtual std::string get_imsi_str() = 0; + virtual std::string get_imei_str() = 0; + virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; + virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; + virtual bool get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) = 0; + virtual auth_result_t generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) = 0; + virtual void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; +}; + +// USIM interface for RRC +class usim_interface_rrc +{ +public: + virtual void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + virtual void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; +}; + +// GW interface for NAS +class gw_interface_nas +{ +public: + virtual srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str) = 0; +}; + +// GW interface for RRC +class gw_interface_rrc +{ +public: + virtual void add_mch_port(uint32_t lcid, uint32_t port) = 0; +}; + +// GW interface for PDCP +class gw_interface_pdcp +{ +public: + virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; + virtual void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; +}; + +// NAS interface for RRC +class nas_interface_rrc +{ +public: + typedef enum { + BARRING_NONE = 0, + BARRING_MO_DATA, + BARRING_MO_SIGNALLING, + BARRING_MT, + BARRING_ALL + } barring_t; + virtual void set_barring(barring_t barring) = 0; + virtual void paging(LIBLTE_RRC_S_TMSI_STRUCT *ue_identiy) = 0; + virtual bool is_attached() = 0; + virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; + virtual uint32_t get_ul_count() = 0; + virtual bool get_k_asme(uint8_t *k_asme_, uint32_t n) = 0; +}; + +// NAS interface for UE +class nas_interface_ue +{ +public: + virtual bool attach_request() = 0; + virtual bool deattach_request() = 0; +}; + +// NAS interface for UE +class nas_interface_gw +{ +public: + virtual bool attach_request() = 0; +}; + +// RRC interface for MAC +class rrc_interface_mac_common +{ +public: + virtual void ra_problem() = 0; +}; + +class rrc_interface_mac : public rrc_interface_mac_common +{ +public: + virtual void ho_ra_completed(bool ra_successful) = 0; + virtual void release_pucch_srs() = 0; + virtual void run_tti(uint32_t tti) = 0; +}; + +// RRC interface for PHY +class rrc_interface_phy +{ +public: + virtual void in_sync() = 0; + virtual void out_of_sync() = 0; + virtual void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn = -1, int pci = -1) = 0; +}; + +// RRC interface for NAS +class rrc_interface_nas +{ +public: + typedef struct { + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id; + uint16_t tac; + } found_plmn_t; + + const static int MAX_FOUND_PLMNS = 16; + + virtual void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual uint16_t get_mcc() = 0; + virtual uint16_t get_mnc() = 0; + virtual void enable_capabilities() = 0; + virtual int plmn_search(found_plmn_t found_plmns[MAX_FOUND_PLMNS]) = 0; + virtual void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) = 0; + virtual bool connection_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause, + srslte::byte_buffer_t *dedicatedInfoNAS) = 0; + virtual void set_ue_idenity(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi) = 0; + virtual bool is_connected() = 0; + virtual std::string get_rb_name(uint32_t lcid) = 0; +}; + +// RRC interface for PDCP +class rrc_interface_pdcp +{ +public: + virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; + virtual void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu) = 0; + virtual void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu) = 0; + virtual void write_pdu_pcch(srslte::byte_buffer_t *pdu) = 0; + virtual void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; + virtual std::string get_rb_name(uint32_t lcid) = 0; +}; + +// RRC interface for RLC +class rrc_interface_rlc +{ +public: + virtual void max_retx_attempted() = 0; + virtual std::string get_rb_name(uint32_t lcid) = 0; +}; + +// PDCP interface for GW +class pdcp_interface_gw +{ +public: + virtual void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual bool is_drb_enabled(uint32_t lcid) = 0; +}; + +// PDCP interface for RRC +class pdcp_interface_rrc +{ +public: + virtual void reestablish() = 0; + virtual void reset() = 0; + virtual void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual void add_bearer(uint32_t lcid, srslte::srslte_pdcp_config_t cnfg = srslte::srslte_pdcp_config_t()) = 0; + virtual void config_security(uint32_t lcid, + uint8_t *k_enc_, + uint8_t *k_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) = 0; + virtual void config_security_all(uint8_t *k_enc_, + uint8_t *k_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) = 0; + virtual void enable_integrity(uint32_t lcid) = 0; + virtual void enable_encryption(uint32_t lcid) = 0; +}; + +// PDCP interface for RLC +class pdcp_interface_rlc +{ +public: + /* RLC calls PDCP to push a PDCP PDU. */ + virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu) = 0; + virtual void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu) = 0; + virtual void write_pdu_pcch(srslte::byte_buffer_t *sdu) = 0; + virtual void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; +}; + +// RLC interface for RRC +class rlc_interface_rrc +{ +public: + virtual void reset() = 0; + virtual void reestablish() = 0; + virtual void add_bearer(uint32_t lcid) = 0; + virtual void add_bearer(uint32_t lcid, srslte::srslte_rlc_config_t cnfg) = 0; + virtual void add_bearer_mrb(uint32_t lcid) = 0; +}; + +// RLC interface for PDCP +class rlc_interface_pdcp +{ +public: + /* PDCP calls RLC to push an RLC SDU. SDU gets placed into the RLC buffer and MAC pulls + * RLC PDUs according to TB size. */ + virtual void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu) = 0; + virtual bool rb_is_um(uint32_t lcid) = 0; +}; + +//RLC interface for MAC +class rlc_interface_mac : public srslte::read_pdu_interface +{ +public: + /* MAC calls RLC to get buffer state for a logical channel. + * This function should return quickly. */ + virtual uint32_t get_buffer_state(uint32_t lcid) = 0; + virtual uint32_t get_total_buffer_state(uint32_t lcid) = 0; + + + const static int MAX_PDU_SEGMENTS = 20; + + /* MAC calls RLC to get RLC segment of nof_bytes length. + * Segmentation happens in this function. RLC PDU is stored in payload. */ + virtual int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) = 0; + + /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. + * PDU gets placed into the buffer and higher layer thread gets notified. */ + virtual void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) = 0; + virtual void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes) = 0; + virtual void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes) = 0; + virtual void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) = 0; + virtual void write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) = 0; +}; + + +//BSR interface for MUX +class bsr_interface_mux +{ +public: + typedef enum { + LONG_BSR, + SHORT_BSR, + TRUNC_BSR + } bsr_format_t; + + typedef struct { + bsr_format_t format; + uint32_t buff_size[4]; + } bsr_t; + + /* MUX calls BSR to check if it can fit a BSR into PDU */ + virtual bool need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) = 0; + + /* MUX calls BSR to let it generate a padding BSR if there is space in PDU */ + virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr) = 0; + + /* MAX calls BSR to set the Tx TTI */ + virtual void set_tx_tti(uint32_t tti) = 0; +}; + + +/** MAC interface + * + */ +/* Interface PHY -> MAC */ +class mac_interface_phy +{ +public: + typedef struct { + uint32_t nof_mbsfn_services; + } mac_phy_cfg_mbsfn_t; + + + + typedef struct { + uint32_t pid; + uint32_t tti; + uint32_t last_tti; + bool ndi[SRSLTE_MAX_CODEWORDS]; + bool last_ndi[SRSLTE_MAX_CODEWORDS]; + uint32_t n_bytes[SRSLTE_MAX_CODEWORDS]; + int rv[SRSLTE_MAX_CODEWORDS]; + bool tb_en[SRSLTE_MAX_CODEWORDS]; + bool tb_cw_swap; + uint16_t rnti; + bool is_from_rar; + bool is_sps_release; + bool has_cqi_request; + srslte_rnti_type_t rnti_type; + srslte_phy_grant_t phy_grant; + } mac_grant_t; + + typedef struct { + bool decode_enabled[SRSLTE_MAX_TB]; + int rv[SRSLTE_MAX_TB]; + uint16_t rnti; + bool generate_ack; + bool default_ack[SRSLTE_MAX_TB]; + // If non-null, called after tb_decoded_ok to determine if ack needs to be sent + bool (*generate_ack_callback)(void*); + void *generate_ack_callback_arg; + uint8_t *payload_ptr[SRSLTE_MAX_TB]; + srslte_softbuffer_rx_t *softbuffers[SRSLTE_MAX_TB]; + srslte_phy_grant_t phy_grant; + } tb_action_dl_t; + + typedef struct { + bool tx_enabled; + bool expect_ack; + uint32_t rv[SRSLTE_MAX_TB]; + uint16_t rnti; + uint32_t current_tx_nb; + int32_t tti_offset; // relative offset between grant and UL tx/HARQ rx + srslte_softbuffer_tx_t *softbuffers; + srslte_phy_grant_t phy_grant; + uint8_t *payload_ptr[SRSLTE_MAX_TB]; + } tb_action_ul_t; + + /* Indicate reception of UL grant. + * payload_ptr points to memory where MAC PDU must be written by MAC layer */ + virtual void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action) = 0; + + /* Indicate reception of UL grant + HARQ information throught PHICH in the same TTI. */ + virtual void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action) = 0; + + /* Obtain action for a new MCH subframe. */ + virtual void new_mch_dl(srslte_ra_dl_grant_t phy_grant, tb_action_dl_t *action) = 0; + + /* Indicate reception of HARQ information only through PHICH. */ + virtual void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action) = 0; + + /* Indicate reception of DL grant. */ + virtual void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action) = 0; + + /* Indicate successful decoding of PDSCH TB. */ + virtual void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) = 0; + + /* Indicate successful decoding of BCH TB through PBCH */ + virtual void bch_decoded_ok(uint8_t *payload, uint32_t len) = 0; + + /* Indicate successful decoding of PCH TB through PDSCH */ + virtual void pch_decoded_ok(uint32_t len) = 0; + + /* Indicate successful decoding of MCH TB through PMCH */ + virtual void mch_decoded_ok(uint32_t len) = 0; + + /* Communicate the number of mbsfn services available */ + virtual void set_mbsfn_config(uint32_t nof_mbsfn_services) = 0; + + /* Function called every start of a subframe (TTI). Warning, this function is called + * from a high priority thread and should terminate asap + */ + + +}; + +/* Interface RRC -> MAC shared between different RATs */ +class mac_interface_rrc_common +{ +public: + // Class to handle UE specific RNTIs between RRC and MAC + typedef struct { + uint16_t crnti; + uint16_t temp_rnti; + uint16_t tpc_rnti; + uint16_t sps_rnti; + uint64_t contention_id; + } ue_rnti_t; + + typedef struct { + uint32_t max_harq_msg3_tx; + uint32_t max_harq_tx; + } ul_harq_params_t; +}; + +/* Interface RRC -> MAC */ +class mac_interface_rrc : public mac_interface_rrc_common +{ +public: + + typedef struct { + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT main; + LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT rach; + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT sr; + ul_harq_params_t ul_harq_params; + uint32_t prach_config_index; + } mac_cfg_t; + + virtual void clear_rntis() = 0; + + /* Instructs the MAC to start receiving BCCH */ + virtual void bcch_start_rx(int si_window_start, int si_window_length) = 0; + + /* Instructs the MAC to start receiving PCCH */ + virtual void pcch_start_rx() = 0; + + /* RRC configures a logical channel */ + virtual void setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD) = 0; + + /* Instructs the MAC to start receiving an MCH */ + virtual void mch_start_rx(uint32_t lcid) = 0; + + virtual uint32_t get_current_tti() = 0; + + virtual void set_config(mac_cfg_t *mac_cfg) = 0; + virtual void set_config_main(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *main_cfg) = 0; + virtual void set_config_rach(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cfg, uint32_t prach_config_index) = 0; + virtual void set_config_sr(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sr_cfg) = 0; + virtual void get_config(mac_cfg_t *mac_cfg) = 0; + + virtual void get_rntis(ue_rnti_t *rntis) = 0; + virtual void set_contention_id(uint64_t uecri) = 0; + virtual void set_ho_rnti(uint16_t crnti, uint16_t target_pci) = 0; + + virtual void start_noncont_ho(uint32_t preamble_index, uint32_t prach_mask) = 0; + virtual void start_cont_ho() = 0; + + virtual void reconfiguration() = 0; + virtual void reset() = 0; + virtual void wait_uplink() = 0; +}; + + +/** PHY interface + * + */ + +typedef struct { + bool ul_pwr_ctrl_en; + float prach_gain; + int pdsch_max_its; + bool attach_enable_64qam; + int nof_phy_threads; + + int worker_cpu_mask; + int sync_cpu_affinity; + + uint32_t nof_rx_ant; + std::string equalizer_mode; + int cqi_max; + int cqi_fixed; + float snr_ema_coeff; + std::string snr_estim_alg; + bool cfo_is_doppler; + bool cfo_integer_enabled; + float cfo_correct_tol_hz; + float cfo_pss_ema; + float cfo_ref_ema; + float cfo_loop_bw_pss; + float cfo_loop_bw_ref; + float cfo_loop_ref_min; + float cfo_loop_pss_tol; + float sfo_ema; + uint32_t sfo_correct_period; + uint32_t cfo_loop_pss_conv; + uint32_t cfo_ref_mask; + bool average_subframe_enabled; + bool estimator_fil_auto; + float estimator_fil_stddev; + uint32_t estimator_fil_order; + std::string sss_algorithm; + bool rssi_sensor_enabled; + bool sic_pss_enabled; + float rx_gain_offset; + bool pdsch_csi_enabled; + uint32_t intra_freq_meas_len_ms; + uint32_t intra_freq_meas_period_ms; +} phy_args_t; + + +/* RAT agnostic Interface MAC -> PHY */ +class phy_interface_mac_common +{ +public: + + /* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */ + virtual void set_crnti(uint16_t rnti) = 0; + + /* Time advance commands */ + virtual void set_timeadv_rar(uint32_t ta_cmd) = 0; + virtual void set_timeadv(uint32_t ta_cmd) = 0; + + /* Sets RAR grant payload */ + virtual void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) = 0; + + virtual uint32_t get_current_tti() = 0; + + virtual float get_phr() = 0; + virtual float get_pathloss_db() = 0; +}; + +/* Interface MAC -> PHY */ +class phy_interface_mac : public phy_interface_mac_common +{ +public: + + /* Configure PRACH using parameters written by RRC */ + virtual void configure_prach_params() = 0; + + virtual void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) = 0; + virtual int prach_tx_tti() = 0; + /* Indicates the transmission of a SR signal in the next opportunity */ + virtual void sr_send() = 0; + virtual int sr_last_tx_tti() = 0; + + /* Instruct the PHY to decode PDCCH with the CRC scrambled with given RNTI */ + virtual void pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1) = 0; + virtual void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1) = 0; + virtual void pdcch_ul_search_reset() = 0; + virtual void pdcch_dl_search_reset() = 0; + + virtual void set_mch_period_stop(uint32_t stop) = 0; + +}; + +class phy_interface_rrc +{ +public: + + typedef struct { + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT prach_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg; + LIBLTE_RRC_PHICH_CONFIG_STRUCT phich_cnfg; + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; + LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT ul_pwr_ctrl; + LIBLTE_RRC_TDD_CONFIG_STRUCT tdd_cnfg; + LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM ant_info; + } phy_cfg_common_t; + + typedef struct { + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT mbsfn_notification_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT mbsfn_area_info; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + } phy_cfg_mbsfn_t; + + + typedef struct { + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + phy_cfg_common_t common; + phy_cfg_mbsfn_t mbsfn; + bool enable_64qam; + } phy_cfg_t; + + virtual void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL) = 0; + virtual uint32_t get_current_earfcn() = 0; + virtual uint32_t get_current_pci() = 0; + + virtual void get_config(phy_cfg_t *phy_cfg) = 0; + virtual void set_config(phy_cfg_t *phy_cfg) = 0; + virtual void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated) = 0; + virtual void set_config_common(phy_cfg_common_t *common) = 0; + virtual void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd) = 0; + virtual void set_config_64qam_en(bool enable) = 0; + virtual void set_config_mbsfn_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) = 0; + virtual void set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) = 0; + virtual void set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) = 0; + + /* Measurements interface */ + virtual void meas_reset() = 0; + virtual int meas_start(uint32_t earfcn, int pci = -1) = 0; + virtual int meas_stop(uint32_t earfcn, int pci = -1) = 0; + + typedef struct { + enum {CELL_FOUND = 0, CELL_NOT_FOUND, ERROR} found; + enum {MORE_FREQS = 0, NO_MORE_FREQS} last_freq; + } cell_search_ret_t; + + typedef struct { + srslte_cell_t cell; + uint32_t earfcn; + } phy_cell_t; + + /* Cell search and selection procedures */ + virtual cell_search_ret_t cell_search(phy_cell_t *cell) = 0; + virtual bool cell_select(phy_cell_t *cell = NULL) = 0; + virtual bool cell_is_camping() = 0; + + /* Configure UL using parameters written with set_param() */ + virtual void configure_ul_params(bool pregen_disabled = false) = 0; + + virtual void reset() = 0; + +}; + + +} // namespace srsue + +#endif // SRSLTE_UE_INTERFACES_H diff --git a/lib/include/srslte/phy/agc/agc.h b/lib/include/srslte/phy/agc/agc.h new file mode 100644 index 0000000..7d49451 --- /dev/null +++ b/lib/include/srslte/phy/agc/agc.h @@ -0,0 +1,108 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: agc.h + * + * Description: Automatic gain control + * This module is not currently used + * + * Reference: + *********************************************************************************************/ + +#ifndef SRSLTE_AGC_H +#define SRSLTE_AGC_H + +#include +#include +#include + +#include "srslte/config.h" + +#define SRSLTE_AGC_DEFAULT_TARGET 0.3 +#define SRSLTE_AGC_DEFAULT_BW 0.7 + +typedef enum SRSLTE_API { + SRSLTE_AGC_MODE_ENERGY = 0, + SRSLTE_AGC_MODE_PEAK_AMPLITUDE +} srslte_agc_mode_t; + +typedef struct SRSLTE_API{ + float bandwidth; + double gain; + double min_gain; + double max_gain; + float y_out; + bool lock; + bool isfirst; + void *uhd_handler; + double (*set_gain_callback) (void*,double); + srslte_agc_mode_t mode; + float target; + uint32_t nof_frames; + uint32_t frame_cnt; + float *y_tmp; +} srslte_agc_t; + +SRSLTE_API int srslte_agc_init(srslte_agc_t *q, srslte_agc_mode_t mode); + +SRSLTE_API int srslte_agc_init_acc(srslte_agc_t *q, srslte_agc_mode_t mode, uint32_t nof_frames); + +SRSLTE_API int srslte_agc_init_uhd(srslte_agc_t *q, + srslte_agc_mode_t mode, + uint32_t nof_frames, + double (set_gain_callback)(void*, double), + void *uhd_handler); + +SRSLTE_API void srslte_agc_free(srslte_agc_t *q); + +SRSLTE_API void srslte_agc_reset(srslte_agc_t *q); + +SRSLTE_API void srslte_agc_set_gain_range(srslte_agc_t *q, double min_gain, double max_gain); + +SRSLTE_API void srslte_agc_set_bandwidth(srslte_agc_t *q, + float bandwidth); + +SRSLTE_API void srslte_agc_set_target(srslte_agc_t *q, + float target); + +SRSLTE_API float srslte_agc_get_rssi(srslte_agc_t *q); + +SRSLTE_API float srslte_agc_get_output_level(srslte_agc_t *q); + +SRSLTE_API float srslte_agc_get_gain(srslte_agc_t *q); + +SRSLTE_API void srslte_agc_set_gain(srslte_agc_t *q, + float init_gain_value); + +SRSLTE_API void srslte_agc_lock(srslte_agc_t *q, + bool enable); + +SRSLTE_API void srslte_agc_process(srslte_agc_t *q, + cf_t *signal, + uint32_t len); + +#endif // SRSLTE_AGC_H diff --git a/lib/include/srslte/phy/ch_estimation/chest_common.h b/lib/include/srslte/phy/ch_estimation/chest_common.h new file mode 100644 index 0000000..276d9c5 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/chest_common.h @@ -0,0 +1,55 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_CHEST_COMMON_H +#define SRSLTE_CHEST_COMMON_H + +#include +#include "srslte/config.h" + +#define SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN 65 + + +SRSLTE_API void srslte_chest_average_pilots(cf_t *input, + cf_t *output, + float *filter, + uint32_t nof_ref, + uint32_t nof_symbols, + uint32_t filter_len); + +SRSLTE_API void srslte_chest_set_smooth_filter3_coeff(float *smooth_filter, + float w); + +SRSLTE_API float srslte_chest_estimate_noise_pilots(cf_t *noisy, + cf_t *noiseless, + cf_t *noise_vec, + uint32_t nof_pilots); + +SRSLTE_API void srslte_chest_set_triangle_filter(float *fil, + int filter_len); + +#endif // SRSLTE_CHEST_COMMON_H + diff --git a/lib/include/srslte/phy/ch_estimation/chest_dl.h b/lib/include/srslte/phy/ch_estimation/chest_dl.h new file mode 100644 index 0000000..653ee00 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/chest_dl.h @@ -0,0 +1,204 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: chest_dl.h + * + * Description: 3GPP LTE Downlink channel estimator and equalizer. + * Estimates the channel in the resource elements transmitting references and + * interpolates for the rest of the resource grid. + * The equalizer uses the channel estimates to produce an estimation of the + * transmitted symbol. + * This object depends on the srslte_refsignal_t object for creating the LTE + * CSR signal. + * + * Reference: + *********************************************************************************************/ + +#ifndef SRSLTE_CHEST_DL_H +#define SRSLTE_CHEST_DL_H + +#include + +#include "srslte/config.h" + +#include "srslte/phy/ch_estimation/chest_common.h" +#include "srslte/phy/resampling/interp.h" +#include "srslte/phy/ch_estimation/refsignal_dl.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/sync/pss.h" + + +typedef enum { + SRSLTE_NOISE_ALG_REFS, + SRSLTE_NOISE_ALG_PSS, + SRSLTE_NOISE_ALG_EMPTY, +} srslte_chest_dl_noise_alg_t; + +typedef struct { + srslte_cell_t cell; + srslte_refsignal_t csr_refs; + srslte_refsignal_t **mbsfn_refs; + + + cf_t *pilot_estimates; + cf_t *pilot_estimates_average; + cf_t *pilot_recv_signal; + cf_t *tmp_noise; + cf_t *tmp_cfo_estimate; + +#ifdef FREQ_SEL_SNR + float snr_vector[12000]; + float pilot_power[12000]; +#endif + bool smooth_filter_auto; + uint32_t smooth_filter_len; + float smooth_filter[SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN]; + + srslte_interp_linsrslte_vec_t srslte_interp_linvec; + srslte_interp_lin_t srslte_interp_lin; + srslte_interp_lin_t srslte_interp_lin_3; + srslte_interp_lin_t srslte_interp_lin_mbsfn; + float rssi[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + float rsrp[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + float rsrp_corr[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + float noise_estimate[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + float cfo; + + bool rsrp_neighbour; + + bool cfo_estimate_enable; + uint32_t cfo_estimate_sf_mask; + + /* Use PSS for noise estimation in LS linear interpolation mode */ + cf_t pss_signal[SRSLTE_PSS_LEN]; + cf_t tmp_pss[SRSLTE_PSS_LEN]; + cf_t tmp_pss_noisy[SRSLTE_PSS_LEN]; + + srslte_chest_dl_noise_alg_t noise_alg; + int last_nof_antennas; + + bool average_subframe; +} srslte_chest_dl_t; + + +SRSLTE_API int srslte_chest_dl_init(srslte_chest_dl_t *q, + uint32_t max_prb); + +SRSLTE_API void srslte_chest_dl_free(srslte_chest_dl_t *q); + + +SRSLTE_API int srslte_chest_dl_set_mbsfn_area_id(srslte_chest_dl_t *q, + uint16_t mbsfn_area_id); + +SRSLTE_API int srslte_chest_dl_set_cell(srslte_chest_dl_t *q, + srslte_cell_t cell); + +SRSLTE_API void srslte_chest_dl_set_smooth_filter(srslte_chest_dl_t *q, + float *filter, + uint32_t filter_len); + +SRSLTE_API void srslte_chest_dl_set_smooth_filter3_coeff(srslte_chest_dl_t* q, + float w); + +SRSLTE_API void srslte_chest_dl_set_smooth_filter_gauss(srslte_chest_dl_t* q, + uint32_t order, + float std_dev); + +SRSLTE_API void srslte_chest_dl_set_smooth_filter_auto(srslte_chest_dl_t* q, + bool enable); + +SRSLTE_API void srslte_chest_dl_set_noise_alg(srslte_chest_dl_t *q, + srslte_chest_dl_noise_alg_t noise_estimation_alg); + + + +SRSLTE_API int srslte_chest_dl_estimate_multi(srslte_chest_dl_t *q, + cf_t *input[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t sf_idx, + uint32_t nof_rx_antennas); + +SRSLTE_API int srslte_chest_dl_estimate(srslte_chest_dl_t *q, + cf_t *input, + cf_t *ce[SRSLTE_MAX_PORTS], + uint32_t sf_idx); + +SRSLTE_API int srslte_chest_dl_estimate_multi_mbsfn(srslte_chest_dl_t *q, + cf_t *input[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t sf_idx, + uint32_t nof_rx_antennas, + uint16_t mbsfn_area_id); + + +SRSLTE_API int srslte_chest_dl_estimate_port(srslte_chest_dl_t *q, + cf_t *input, + cf_t *ce, + uint32_t sf_idx, + uint32_t port_id, + uint32_t rxant_id); + +SRSLTE_API void srslte_chest_dl_cfo_estimate_enable(srslte_chest_dl_t *q, + bool enable, + uint32_t mask); + +SRSLTE_API void srslte_chest_dl_average_subframe(srslte_chest_dl_t *q, + bool enable); + +SRSLTE_API void srslte_chest_dl_set_rsrp_neighbour(srslte_chest_dl_t *q, + bool rsrp_for_neighbour); + +SRSLTE_API float srslte_chest_dl_get_noise_estimate(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_cfo(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_snr(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_snr_ant_port(srslte_chest_dl_t *q, + uint32_t ant_idx, + uint32_t port_idx); + +SRSLTE_API float srslte_chest_dl_get_rssi(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_rsrq(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_rsrq_ant_port(srslte_chest_dl_t *q, + uint32_t ant_idx, + uint32_t port); + +SRSLTE_API float srslte_chest_dl_get_rsrp_ant_port(srslte_chest_dl_t *q, + uint32_t ant_idx, + uint32_t port); + +SRSLTE_API float srslte_chest_dl_get_rsrp_port(srslte_chest_dl_t *q, + uint32_t port); + +SRSLTE_API float srslte_chest_dl_get_rsrp(srslte_chest_dl_t *q); + +SRSLTE_API float srslte_chest_dl_get_rsrp_neighbour(srslte_chest_dl_t *q); + +#endif // SRSLTE_CHEST_DL_H diff --git a/lib/include/srslte/phy/ch_estimation/chest_ul.h b/lib/include/srslte/phy/ch_estimation/chest_ul.h new file mode 100644 index 0000000..df5c11d --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/chest_ul.h @@ -0,0 +1,121 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: chest_ul.h + * + * Description: 3GPP LTE Uplink channel estimator and equalizer. + * Estimates the channel in the resource elements transmitting references and + * interpolates for the rest of the resource grid. + * The equalizer uses the channel estimates to produce an estimation of the + * transmitted symbol. + * + * Reference: + *********************************************************************************************/ + +#ifndef SRSLTE_CHEST_UL_H +#define SRSLTE_CHEST_UL_H + +#include + +#include "srslte/config.h" + +#include "srslte/phy/ch_estimation/chest_common.h" +#include "srslte/phy/resampling/interp.h" +#include "srslte/phy/ch_estimation/refsignal_ul.h" +#include "srslte/phy/common/phy_common.h" + +typedef struct { + srslte_cell_t cell; + + srslte_refsignal_ul_t dmrs_signal; + srslte_refsignal_ul_dmrs_pregen_t dmrs_pregen; + bool dmrs_signal_configured; + + cf_t *pilot_estimates; + cf_t *pilot_estimates_tmp[4]; + cf_t *pilot_recv_signal; + cf_t *pilot_known_signal; + cf_t *tmp_noise; + +#ifdef FREQ_SEL_SNR + float snr_vector[12000]; + float pilot_power[12000]; +#endif + uint32_t smooth_filter_len; + float smooth_filter[SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN]; + + srslte_interp_linsrslte_vec_t srslte_interp_linvec; + + float pilot_power; + float noise_estimate; + +} srslte_chest_ul_t; + + +SRSLTE_API int srslte_chest_ul_init(srslte_chest_ul_t *q, + uint32_t max_prb); + +SRSLTE_API void srslte_chest_ul_free(srslte_chest_ul_t *q); + +SRSLTE_API int srslte_chest_ul_set_cell(srslte_chest_ul_t *q, + srslte_cell_t cell); + + +SRSLTE_API void srslte_chest_ul_set_cfg(srslte_chest_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg); + +SRSLTE_API void srslte_chest_ul_set_smooth_filter(srslte_chest_ul_t *q, + float *filter, + uint32_t filter_len); + +SRSLTE_API void srslte_chest_ul_set_smooth_filter3_coeff(srslte_chest_ul_t* q, + float w); + +SRSLTE_API int srslte_chest_ul_estimate(srslte_chest_ul_t *q, + cf_t *input, + cf_t *ce, + uint32_t nof_prb, + uint32_t sf_idx, + uint32_t cyclic_shift_for_dmrs, + uint32_t n_prb[2]); + +SRSLTE_API int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t *q, + cf_t *input, + cf_t *ce, + srslte_pucch_format_t format, + uint32_t n_pucch, + uint32_t sf_idx, + uint8_t *pucch2_ack_bits); + +SRSLTE_API float srslte_chest_ul_get_noise_estimate(srslte_chest_ul_t *q); + +SRSLTE_API float srslte_chest_ul_get_snr(srslte_chest_ul_t *q); + + +#endif // SRSLTE_CHEST_UL_H diff --git a/lib/include/srslte/phy/ch_estimation/refsignal_dl.h b/lib/include/srslte/phy/ch_estimation/refsignal_dl.h new file mode 100644 index 0000000..0680918 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/refsignal_dl.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: refsignal_dl.h + * + * Description: Object to manage downlink reference signals for channel estimation. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.10 + *********************************************************************************************/ + +#ifndef SRSLTE_REFSIGNAL_DL_H +#define SRSLTE_REFSIGNAL_DL_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +// Number of references in a subframe: there are 2 symbols for port_id=0,1 x 2 slots x 2 refs per prb +#define SRSLTE_REFSIGNAL_NUM_SF(nof_prb, port_id) (((port_id)<2?8:4)*(nof_prb)) +#define SRSLTE_REFSIGNAL_NUM_SF_MBSFN(nof_prb, port_id) ((2 + 18)*(nof_prb)) + +#define SRSLTE_REFSIGNAL_MAX_NUM_SF(nof_prb) SRSLTE_REFSIGNAL_NUM_SF(nof_prb, 0) +#define SRSLTE_REFSIGNAL_MAX_NUM_SF_MBSFN(nof_prb) SRSLTE_REFSIGNAL_NUM_SF_MBSFN(nof_prb,0) + +#define SRSLTE_REFSIGNAL_PILOT_IDX(i,l,cell) (2*cell.nof_prb*(l)+(i)) + +#define SRSLTE_REFSIGNAL_PILOT_IDX_MBSFN(i,l,cell) ((6*cell.nof_prb*(l)+(i))) + + + +/** Cell-Specific Reference Signal */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + cf_t *pilots[2][SRSLTE_NSUBFRAMES_X_FRAME]; // Saves the reference signal per subframe for ports 0,1 and ports 2,3 + srslte_sf_t type; + uint16_t mbsfn_area_id; +} srslte_refsignal_t; + + + +SRSLTE_API int srslte_refsignal_cs_init(srslte_refsignal_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_refsignal_cs_set_cell(srslte_refsignal_t * q, + srslte_cell_t cell); + +SRSLTE_API void srslte_refsignal_free(srslte_refsignal_t *q); + +SRSLTE_API int srslte_refsignal_cs_put_sf(srslte_cell_t cell, + uint32_t port_id, + cf_t *pilots, + cf_t *sf_symbols); + +SRSLTE_API int srslte_refsignal_cs_get_sf(srslte_cell_t cell, + uint32_t port_id, + cf_t *sf_symbols, + cf_t *pilots); + +SRSLTE_API uint32_t srslte_refsignal_cs_fidx(srslte_cell_t cell, + uint32_t l, + uint32_t port_id, + uint32_t m); + +SRSLTE_API uint32_t srslte_refsignal_cs_nsymbol(uint32_t l, + srslte_cp_t cp, + uint32_t port_id); + +SRSLTE_API uint32_t srslte_refsignal_cs_v(uint32_t port_id, + uint32_t ref_symbol_idx); + +SRSLTE_API uint32_t srslte_refsignal_cs_nof_symbols(uint32_t port_id); + +SRSLTE_API int srslte_refsignal_mbsfn_init(srslte_refsignal_t *q, uint32_t max_prb); + +SRSLTE_API int srslte_refsignal_mbsfn_set_cell(srslte_refsignal_t * q, + srslte_cell_t cell, uint16_t mbsfn_area_id); + +SRSLTE_API int srslte_refsignal_mbsfn_get_sf(srslte_cell_t cell, + uint32_t port_id, + cf_t *sf_symbols, + cf_t *pilots); + +SRSLTE_API uint32_t srslte_refsignal_mbsfn_nsymbol(uint32_t l); + +SRSLTE_API uint32_t srslte_refsignal_mbsfn_fidx(uint32_t l); + +SRSLTE_API uint32_t srslte_refsignal_mbsfn_nof_symbols(); + +SRSLTE_API int srslte_refsignal_mbsfn_put_sf(srslte_cell_t cell, + uint32_t port_id, + cf_t *cs_pilots, + cf_t *mbsfn_pilots, + cf_t *sf_symbols); + +SRSLTE_API int srslte_refsignal_mbsfn_gen_seq(srslte_refsignal_t * q, + srslte_cell_t cell, + uint32_t N_mbsfn_id); + + +#endif // SRSLTE_REFSIGNAL_DL_H diff --git a/lib/include/srslte/phy/ch_estimation/refsignal_ul.h b/lib/include/srslte/phy/ch_estimation/refsignal_ul.h new file mode 100644 index 0000000..7b393e8 --- /dev/null +++ b/lib/include/srslte/phy/ch_estimation/refsignal_ul.h @@ -0,0 +1,212 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: refsignal_ul.h + * + * Description: Object to manage uplink reference signals for channel estimation. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.5 + *********************************************************************************************/ + +#ifndef SRSLTE_REFSIGNAL_UL_H +#define SRSLTE_REFSIGNAL_UL_H + +#include "srslte/config.h" +#include "srslte/phy/phch/pucch.h" +#include "srslte/phy/common/phy_common.h" + +#define SRSLTE_NOF_GROUPS_U 30 +#define SRSLTE_NOF_SEQUENCES_U 2 +#define SRSLTE_NOF_DELTA_SS 30 +#define SRSLTE_NOF_CSHIFT 8 + +#define SRSLTE_REFSIGNAL_UL_L(ns_idx, cp) ((ns_idx+1)*SRSLTE_CP_NSYMB(cp)-4) + +/* PUSCH DMRS common configuration (received in SIB2) */ +typedef struct SRSLTE_API { + uint32_t cyclic_shift; + uint32_t delta_ss; + bool group_hopping_en; + bool sequence_hopping_en; +}srslte_refsignal_dmrs_pusch_cfg_t; + + +typedef struct SRSLTE_API { + // Common Configuration + uint32_t subframe_config; + uint32_t bw_cfg; + + // Dedicated configuration + uint32_t B; + uint32_t b_hop; + uint32_t n_srs; + uint32_t I_srs; + uint32_t k_tc; + uint32_t n_rrc; + bool configured; +}srslte_refsignal_srs_cfg_t; + +/** Uplink DeModulation Reference Signal (DMRS) */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + srslte_pucch_cfg_t pucch_cfg; + srslte_refsignal_srs_cfg_t srs_cfg; + + uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB]; + float *tmp_arg; + uint32_t n_prs_pusch[SRSLTE_NOF_DELTA_SS][SRSLTE_NSLOTS_X_FRAME]; // We precompute n_prs needed for cyclic shift alpha at srslte_refsignal_dl_init() + uint32_t f_gh[SRSLTE_NSLOTS_X_FRAME]; + uint32_t u_pucch[SRSLTE_NSLOTS_X_FRAME]; + uint32_t v_pusch[SRSLTE_NSLOTS_X_FRAME][SRSLTE_NOF_DELTA_SS]; +} srslte_refsignal_ul_t; + +typedef struct { + cf_t **r[SRSLTE_NOF_CSHIFT][SRSLTE_NSUBFRAMES_X_FRAME]; +} srslte_refsignal_ul_dmrs_pregen_t; + +typedef struct { + cf_t *r[SRSLTE_NSUBFRAMES_X_FRAME]; +} srslte_refsignal_srs_pregen_t; + +SRSLTE_API int srslte_refsignal_ul_init(srslte_refsignal_ul_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_refsignal_ul_set_cell(srslte_refsignal_ul_t *q, + srslte_cell_t cell); + +SRSLTE_API void srslte_refsignal_ul_free(srslte_refsignal_ul_t *q); + +SRSLTE_API void srslte_refsignal_ul_set_cfg(srslte_refsignal_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg); + +SRSLTE_API void srslte_refsignal_r_uv_arg_1prb(float *arg, + uint32_t u); + +SRSLTE_API uint32_t srslte_refsignal_dmrs_N_rs(srslte_pucch_format_t format, + srslte_cp_t cp); + +SRSLTE_API uint32_t srslte_refsignal_dmrs_pucch_symbol(uint32_t m, + srslte_pucch_format_t format, + srslte_cp_t cp); + +SRSLTE_API bool srslte_refsignal_dmrs_pusch_cfg_isvalid(srslte_refsignal_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *cfg, + uint32_t nof_prb); + +SRSLTE_API int srslte_refsignal_dmrs_pusch_pregen_init(srslte_refsignal_ul_t *q, + srslte_refsignal_ul_dmrs_pregen_t *pregen, + uint32_t max_prb); + +SRSLTE_API int srslte_refsignal_dmrs_pusch_pregen(srslte_refsignal_ul_t *q, + srslte_refsignal_ul_dmrs_pregen_t *pregen); + +SRSLTE_API void srslte_refsignal_dmrs_pusch_pregen_free(srslte_refsignal_ul_t *q, + srslte_refsignal_ul_dmrs_pregen_t *pregen); + +SRSLTE_API int srslte_refsignal_dmrs_pusch_pregen_put(srslte_refsignal_ul_t *q, + srslte_refsignal_ul_dmrs_pregen_t *pregen, + uint32_t nof_prb, + uint32_t sf_idx, + uint32_t cyclic_shift_for_dmrs, + uint32_t n_prb[2], + cf_t *sf_symbols); + +SRSLTE_API int srslte_refsignal_dmrs_pusch_gen(srslte_refsignal_ul_t *q, + uint32_t nof_prb, + uint32_t sf_idx, + uint32_t cyclic_shift_for_dmrs, + cf_t *r_pusch); + +SRSLTE_API void srslte_refsignal_dmrs_pusch_put(srslte_refsignal_ul_t *q, + cf_t *r_pusch, + uint32_t nof_prb, + uint32_t n_prb[2], + cf_t *sf_symbols); + +SRSLTE_API void srslte_refsignal_dmrs_pusch_get(srslte_refsignal_ul_t *q, + cf_t *sf_symbols, + uint32_t nof_prb, + uint32_t n_prb[2], + cf_t *r_pusch); + +SRSLTE_API int srslte_refsignal_dmrs_pucch_gen(srslte_refsignal_ul_t *q, + srslte_pucch_format_t format, + uint32_t n_pucch, // n_pucch_1 or n_pucch_2 depending on format + uint32_t sf_idx, + uint8_t pucch2_bits[2], + cf_t *r_pucch); + +SRSLTE_API int srslte_refsignal_dmrs_pucch_put(srslte_refsignal_ul_t* q, + srslte_pucch_format_t format, + uint32_t n_pucch, + cf_t *r_pucch, + cf_t *output); + +SRSLTE_API int srslte_refsignal_dmrs_pucch_get(srslte_refsignal_ul_t* q, + srslte_pucch_format_t format, + uint32_t n_pucch, + cf_t *input, + cf_t *r_pucch); + +SRSLTE_API int srslte_refsignal_srs_pregen(srslte_refsignal_ul_t *q, + srslte_refsignal_srs_pregen_t *pregen); + +SRSLTE_API int srslte_refsignal_srs_pregen_put(srslte_refsignal_ul_t *q, + srslte_refsignal_srs_pregen_t *pregen, + uint32_t tti, + cf_t *sf_symbols); + +SRSLTE_API void srslte_refsignal_srs_pregen_free(srslte_refsignal_ul_t *q, + srslte_refsignal_srs_pregen_t *pregen); + +SRSLTE_API int srslte_refsignal_srs_gen(srslte_refsignal_ul_t *q, + uint32_t sf_idx, + cf_t *r_srs); + +SRSLTE_API int srslte_refsignal_srs_put(srslte_refsignal_ul_t *q, + uint32_t tti, + cf_t *r_srs, + cf_t *sf_symbols); + +SRSLTE_API int srslte_refsignal_srs_send_cs(uint32_t subframe_config, + uint32_t sf_idx); + +SRSLTE_API int srslte_refsignal_srs_send_ue(uint32_t I_srs, + uint32_t tti); + +SRSLTE_API uint32_t srslte_refsignal_srs_rb_start_cs(uint32_t bw_cfg, + uint32_t nof_prb); + +SRSLTE_API uint32_t srslte_refsignal_srs_rb_L_cs(uint32_t bw_cfg, + uint32_t nof_prb); + +SRSLTE_API uint32_t srslte_refsignal_srs_M_sc(srslte_refsignal_ul_t *q); + +#endif // SRSLTE_REFSIGNAL_UL_H diff --git a/lib/include/srslte/phy/channel/ch_awgn.h b/lib/include/srslte/phy/channel/ch_awgn.h new file mode 100644 index 0000000..3dbabcd --- /dev/null +++ b/lib/include/srslte/phy/channel/ch_awgn.h @@ -0,0 +1,57 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: ch_awgn.h + * + * Description: Additive white gaussian noise channel object + * + * Reference: + *********************************************************************************************/ + +#include +#include + +#include "srslte/config.h" + +#ifndef SRSLTE_CH_AWGN_H +#define SRSLTE_CH_AWGN_H + +SRSLTE_API void srslte_ch_awgn_c(const cf_t* input, + cf_t* output, + float variance, + uint32_t len); + +SRSLTE_API void srslte_ch_awgn_f(const float* x, + float* y, + float variance, + uint32_t len); + +SRSLTE_API float srslte_ch_awgn_get_variance(float ebno_db, + float rate); + + +#endif // SRSLTE_CH_AWGN_H diff --git a/lib/include/srslte/phy/common/phy_common.h b/lib/include/srslte/phy/common/phy_common.h new file mode 100644 index 0000000..82c2dfa --- /dev/null +++ b/lib/include/srslte/phy/common/phy_common.h @@ -0,0 +1,308 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: phy_common.h + * + * Description: Common parameters and lookup functions for PHY + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 + *********************************************************************************************/ + +#ifndef SRSLTE_PHY_COMMON_H +#define SRSLTE_PHY_COMMON_H + +#include +#include +#include +#include + +#include "srslte/config.h" + +#define SRSLTE_NSUBFRAMES_X_FRAME 10 +#define SRSLTE_NSLOTS_X_FRAME (2*SRSLTE_NSUBFRAMES_X_FRAME) + +#define SRSLTE_NSOFT_BITS 250368 // Soft buffer size for Category 1 UE + +#define SRSLTE_PC_MAX 23 // Maximum TX power for Category 1 UE (in dBm) + +#define SRSLTE_MAX_PORTS 4 +#define SRSLTE_MAX_LAYERS 4 +#define SRSLTE_MAX_CODEWORDS 2 +#define SRSLTE_MAX_TB SRSLTE_MAX_CODEWORDS + +#define SRSLTE_MAX_CODEBLOCKS 32 + +#define SRSLTE_MAX_CODEBOOKS 4 + +#define SRSLTE_LTE_CRC24A 0x1864CFB +#define SRSLTE_LTE_CRC24B 0X1800063 +#define SRSLTE_LTE_CRC16 0x11021 +#define SRSLTE_LTE_CRC8 0x19B + +#define SRSLTE_MAX_MBSFN_AREA_IDS 256 +#define SRSLTE_PMCH_RV 0 + +typedef enum {SRSLTE_CP_NORM, SRSLTE_CP_EXT} srslte_cp_t; +typedef enum {SRSLTE_SF_NORM, SRSLTE_SF_MBSFN} srslte_sf_t; + + +#define SRSLTE_CRNTI_START 0x000B +#define SRSLTE_CRNTI_END 0xFFF3 +#define SRSLTE_RARNTI_START 0x0001 +#define SRSLTE_RARNTI_END 0x000A +#define SRSLTE_SIRNTI 0xFFFF +#define SRSLTE_PRNTI 0xFFFE +#define SRSLTE_MRNTI 0xFFFD + +#define SRSLTE_CELL_ID_UNKNOWN 1000 + +#define SRSLTE_MAX_NSYMB 7 + +#define SRSLTE_MAX_PRB 110 +#define SRSLTE_NRE 12 + +#define SRSLTE_SYMBOL_SZ_MAX 2048 + +#define SRSLTE_CP_NORM_NSYMB 7 +#define SRSLTE_CP_NORM_SF_NSYMB (2*SRSLTE_CP_NORM_NSYMB) +#define SRSLTE_CP_NORM_0_LEN 160 +#define SRSLTE_CP_NORM_LEN 144 + +#define SRSLTE_CP_EXT_NSYMB 6 +#define SRSLTE_CP_EXT_SF_NSYMB (2*SRSLTE_CP_EXT_NSYMB) +#define SRSLTE_CP_EXT_LEN 512 +#define SRSLTE_CP_EXT_7_5_LEN 1024 + +#define SRSLTE_CP_ISNORM(cp) (cp==SRSLTE_CP_NORM) +#define SRSLTE_CP_ISEXT(cp) (cp==SRSLTE_CP_EXT) +#define SRSLTE_CP_NSYMB(cp) (SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_NORM_NSYMB:SRSLTE_CP_EXT_NSYMB) + +#define SRSLTE_CP_LEN(symbol_sz, c) ((int) ceilf((((float) (c)*(symbol_sz))/2048.0f))) +#define SRSLTE_CP_LEN_NORM(symbol, symbol_sz) (((symbol)==0)?SRSLTE_CP_LEN((symbol_sz),SRSLTE_CP_NORM_0_LEN):SRSLTE_CP_LEN((symbol_sz),SRSLTE_CP_NORM_LEN)) +#define SRSLTE_CP_LEN_EXT(symbol_sz) (SRSLTE_CP_LEN((symbol_sz),SRSLTE_CP_EXT_LEN)) + +#define SRSLTE_SLOT_LEN(symbol_sz) (symbol_sz*15/2) +#define SRSLTE_SF_LEN(symbol_sz) (symbol_sz*15) +#define SRSLTE_SF_LEN_MAX (SRSLTE_SF_LEN(SRSLTE_SYMBOL_SZ_MAX)) + +#define SRSLTE_SLOT_LEN_PRB(nof_prb) (SRSLTE_SLOT_LEN(srslte_symbol_sz(nof_prb))) +#define SRSLTE_SF_LEN_PRB(nof_prb) (SRSLTE_SF_LEN(srslte_symbol_sz(nof_prb))) + +#define SRSLTE_SLOT_LEN_RE(nof_prb, cp) (nof_prb*SRSLTE_NRE*SRSLTE_CP_NSYMB(cp)) +#define SRSLTE_SF_LEN_RE(nof_prb, cp) (2*SRSLTE_SLOT_LEN_RE(nof_prb, cp)) + +#define SRSLTE_TA_OFFSET (10e-6) + +#define SRSLTE_LTE_TS 1.0/(15000.0*2048) + +#define SRSLTE_SLOT_IDX_CPNORM(symbol_idx, symbol_sz) (symbol_idx==0?0:(symbol_sz + SRSLTE_CP_LEN(symbol_sz, SRSLTE_CP_NORM_0_LEN) + \ + (symbol_idx-1)*(symbol_sz+SRSLTE_CP_LEN(symbol_sz, SRSLTE_CP_NORM_LEN)))) +#define SRSLTE_SLOT_IDX_CPEXT(idx, symbol_sz) (idx*(symbol_sz+SRSLTE_CP(symbol_sz, SRSLTE_CP_EXT_LEN))) + +#define SRSLTE_RE_IDX(nof_prb, symbol_idx, sample_idx) ((symbol_idx)*(nof_prb)*(SRSLTE_NRE) + sample_idx) + +#define SRSLTE_RS_VSHIFT(cell_id) (cell_id%6) + +#define SRSLTE_GUARD_RE(nof_prb) ((srslte_symbol_sz(nof_prb)-nof_prb*SRSLTE_NRE)/2) + +#define SRSLTE_SYMBOL_HAS_REF(l, cp, nof_ports) ((l == 1 && nof_ports == 4) \ + || l == 0 \ + || l == SRSLTE_CP_NSYMB(cp) - 3) + + + +#define SRSLTE_SYMBOL_HAS_REF_MBSFN(l, s) ((l == 2 && s == 0) || (l == 0 && s == 1) || (l == 4 && s == 1)) + +#define SRSLTE_NON_MBSFN_REGION_GUARD_LENGTH(non_mbsfn_region,symbol_sz) ((non_mbsfn_region == 1)?(SRSLTE_CP_LEN_EXT(symbol_sz) - SRSLTE_CP_LEN_NORM(0, symbol_sz)):(2*SRSLTE_CP_LEN_EXT(symbol_sz) - SRSLTE_CP_LEN_NORM(0, symbol_sz)- SRSLTE_CP_LEN_NORM(1, symbol_sz))) + + + +#define SRSLTE_NOF_LTE_BANDS 38 + +#define SRSLTE_DEFAULT_MAX_FRAMES_PBCH 500 +#define SRSLTE_DEFAULT_MAX_FRAMES_PSS 10 +#define SRSLTE_DEFAULT_NOF_VALID_PSS_FRAMES 10 + + +typedef enum SRSLTE_API { + SRSLTE_PHICH_NORM = 0, + SRSLTE_PHICH_EXT +} srslte_phich_length_t; + +typedef enum SRSLTE_API { + SRSLTE_PHICH_R_1_6 = 0, + SRSLTE_PHICH_R_1_2, + SRSLTE_PHICH_R_1, + SRSLTE_PHICH_R_2 + +} srslte_phich_resources_t; + +typedef enum { + SRSLTE_RNTI_USER = 0, /* Cell RNTI */ + SRSLTE_RNTI_SI, /* System Information RNTI */ + SRSLTE_RNTI_RAR, /* Random Access RNTI */ + SRSLTE_RNTI_TEMP, /* Temporary C-RNTI */ + SRSLTE_RNTI_SPS, /* Semi-Persistent Scheduling C-RNTI */ + SRSLTE_RNTI_PCH, /* Paging RNTI */ + SRSLTE_RNTI_MBSFN, + SRSLTE_RNTI_NOF_TYPES +} srslte_rnti_type_t; + +typedef struct SRSLTE_API { + uint32_t nof_prb; + uint32_t nof_ports; + uint32_t id; + srslte_cp_t cp; + srslte_phich_length_t phich_length; + srslte_phich_resources_t phich_resources; +}srslte_cell_t; + +typedef enum SRSLTE_API { + SRSLTE_MIMO_TYPE_SINGLE_ANTENNA, + SRSLTE_MIMO_TYPE_TX_DIVERSITY, + SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX, + SRSLTE_MIMO_TYPE_CDD +} srslte_mimo_type_t; + +typedef enum SRSLTE_API { + SRSLTE_MIMO_DECODER_ZF, + SRSLTE_MIMO_DECODER_MMSE +} srslte_mimo_decoder_t; + +typedef enum SRSLTE_API { + SRSLTE_MOD_BPSK = 0, + SRSLTE_MOD_QPSK, + SRSLTE_MOD_16QAM, + SRSLTE_MOD_64QAM, + SRSLTE_MOD_LAST +} srslte_mod_t; + +typedef struct SRSLTE_API { + int id; + float fd; +} srslte_earfcn_t; + +enum band_geographical_area { + SRSLTE_BAND_GEO_AREA_ALL, + SRSLTE_BAND_GEO_AREA_NAR, + SRSLTE_BAND_GEO_AREA_APAC, + SRSLTE_BAND_GEO_AREA_EMEA, + SRSLTE_BAND_GEO_AREA_JAPAN, + SRSLTE_BAND_GEO_AREA_CALA, + SRSLTE_BAND_GEO_AREA_NA +}; + +SRSLTE_API bool srslte_cell_isvalid(srslte_cell_t *cell); + +SRSLTE_API void srslte_cell_fprint(FILE *stream, + srslte_cell_t *cell, + uint32_t sfn); + +SRSLTE_API bool srslte_cellid_isvalid(uint32_t cell_id); + +SRSLTE_API bool srslte_nofprb_isvalid(uint32_t nof_prb); + +SRSLTE_API bool srslte_sfidx_isvalid(uint32_t sf_idx); + +SRSLTE_API bool srslte_portid_isvalid(uint32_t port_id); + +SRSLTE_API bool srslte_N_id_2_isvalid(uint32_t N_id_2); + +SRSLTE_API bool srslte_N_id_1_isvalid(uint32_t N_id_1); + +SRSLTE_API bool srslte_symbol_sz_isvalid(uint32_t symbol_sz); + +SRSLTE_API int srslte_symbol_sz(uint32_t nof_prb); + +SRSLTE_API int srslte_symbol_sz_power2(uint32_t nof_prb); + +SRSLTE_API int srslte_nof_prb(uint32_t symbol_sz); + +SRSLTE_API int srslte_sampling_freq_hz(uint32_t nof_prb); + +SRSLTE_API void srslte_use_standard_symbol_size(bool enabled); + +SRSLTE_API uint32_t srslte_re_x_prb(uint32_t ns, + uint32_t symbol, + uint32_t nof_ports, + uint32_t nof_symbols); + +SRSLTE_API uint32_t srslte_voffset(uint32_t symbol_id, + uint32_t cell_id, + uint32_t nof_ports); + +SRSLTE_API int srslte_group_hopping_f_gh(uint32_t f_gh[SRSLTE_NSLOTS_X_FRAME], + uint32_t cell_id); + +SRSLTE_API uint32_t srslte_N_ta_new_rar(uint32_t ta); + +SRSLTE_API uint32_t srslte_N_ta_new(uint32_t N_ta_old, + uint32_t ta); + +SRSLTE_API float srslte_coderate(uint32_t tbs, + uint32_t nof_re); + +SRSLTE_API char *srslte_cp_string(srslte_cp_t cp); + +SRSLTE_API srslte_mod_t srslte_str2mod (char * mod_str); + +SRSLTE_API char *srslte_mod_string(srslte_mod_t mod); + +SRSLTE_API uint32_t srslte_mod_bits_x_symbol(srslte_mod_t mod); + +SRSLTE_API int srslte_band_get_band(uint32_t dl_earfcn); + +SRSLTE_API float srslte_band_fd(uint32_t dl_earfcn); + +SRSLTE_API float srslte_band_fu(uint32_t ul_earfcn); + +SRSLTE_API uint32_t srslte_band_ul_earfcn(uint32_t dl_earfcn); + +SRSLTE_API int srslte_band_get_fd_band(uint32_t band, + srslte_earfcn_t *earfcn, + int earfcn_start, + int earfcn_end, + uint32_t max_elems); + +SRSLTE_API int srslte_band_get_fd_band_all(uint32_t band, + srslte_earfcn_t *earfcn, + uint32_t max_nelems); + +SRSLTE_API int srslte_band_get_fd_region(enum band_geographical_area region, + srslte_earfcn_t *earfcn, + uint32_t max_elems); + +SRSLTE_API int srslte_str2mimotype(char *mimo_type_str, + srslte_mimo_type_t *type); + +SRSLTE_API char *srslte_mimotype2str(srslte_mimo_type_t mimo_type); + +/* Returns the interval tti1-tti2 mod 10240 */ +SRSLTE_API uint32_t srslte_tti_interval(uint32_t tti1, + uint32_t tti2); + +#endif // SRSLTE_PHY_COMMON_H diff --git a/lib/include/srslte/phy/common/phy_logger.h b/lib/include/srslte/phy/common/phy_logger.h new file mode 100644 index 0000000..2f0805d --- /dev/null +++ b/lib/include/srslte/phy/common/phy_logger.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: phy_logger.h + * Description: Interface for logging output + *****************************************************************************/ + +#ifndef SRSLTE_PHY_LOGGER_H +#define SRSLTE_PHY_LOGGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif // __cplusplus +typedef enum {LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_ERROR} phy_logger_level_t; + +typedef void (*phy_log_handler_t)(phy_logger_level_t log_level, void *ctx, char *str); + +void srslte_phy_log_register_handler(void *ctx, phy_log_handler_t handler); + + void srslte_phy_log_print(phy_logger_level_t log_level, const char *format, ...); + +#ifdef __cplusplus +} +#endif // C++ + +#endif // SRSLTE_PHY_LOGGER_H diff --git a/lib/include/srslte/phy/common/sequence.h b/lib/include/srslte/phy/common/sequence.h new file mode 100644 index 0000000..346be1c --- /dev/null +++ b/lib/include/srslte/phy/common/sequence.h @@ -0,0 +1,103 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: sequence.h + * + * Description: Pseudo Random Sequence generation. Sequences are defined by a length-31 Gold + * sequence. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 7.2 + *********************************************************************************************/ + +#ifndef SRSLTE_SEQUENCE_H +#define SRSLTE_SEQUENCE_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +typedef struct SRSLTE_API { + uint8_t *c; + uint8_t *c_bytes; + float *c_float; + short *c_short; + uint32_t cur_len; + uint32_t max_len; +} srslte_sequence_t; + +SRSLTE_API int srslte_sequence_init(srslte_sequence_t *q, uint32_t len); + +SRSLTE_API void srslte_sequence_free(srslte_sequence_t *q); + +SRSLTE_API int srslte_sequence_LTE_pr(srslte_sequence_t *q, + uint32_t len, + uint32_t seed); + +SRSLTE_API int srslte_sequence_set_LTE_pr(srslte_sequence_t *q, + uint32_t len, + uint32_t seed); + +SRSLTE_API int srslte_sequence_pbch(srslte_sequence_t *seq, + srslte_cp_t cp, + uint32_t cell_id); + +SRSLTE_API int srslte_sequence_pcfich(srslte_sequence_t *seq, + uint32_t nslot, + uint32_t cell_id); + +SRSLTE_API int srslte_sequence_phich(srslte_sequence_t *seq, + uint32_t nslot, + uint32_t cell_id); + +SRSLTE_API int srslte_sequence_pdcch(srslte_sequence_t *seq, + uint32_t nslot, + uint32_t cell_id, + uint32_t len); + +SRSLTE_API int srslte_sequence_pdsch(srslte_sequence_t *seq, + uint16_t rnti, + int q, + uint32_t nslot, + uint32_t cell_id, + uint32_t len); + +SRSLTE_API int srslte_sequence_pusch(srslte_sequence_t *seq, + uint16_t rnti, + uint32_t nslot, + uint32_t cell_id, + uint32_t len); + +SRSLTE_API int srslte_sequence_pucch(srslte_sequence_t *seq, + uint16_t rnti, + uint32_t nslot, + uint32_t cell_id); + +SRSLTE_API int srslte_sequence_pmch(srslte_sequence_t *seq, + uint32_t nslot, + uint32_t mbsfn_id, + uint32_t len); + +#endif // SRSLTE_SEQUENCE_H diff --git a/lib/include/srslte/phy/common/timestamp.h b/lib/include/srslte/phy/common/timestamp.h new file mode 100644 index 0000000..f4c505b --- /dev/null +++ b/lib/include/srslte/phy/common/timestamp.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: timestamp.h + * + * Description: A simple timestamp struct with separate variables for full and frac seconds. + * Separate variables are used to avoid loss of precision in our frac seconds. + * Only positive timestamps are supported. + * + * Reference: + *********************************************************************************************/ + +#ifndef SRSLTE_TIMESTAMP_H +#define SRSLTE_TIMESTAMP_H + +#include +#include +#include "srslte/config.h" + +typedef struct SRSLTE_API{ + time_t full_secs; + double frac_secs; +}srslte_timestamp_t; + +SRSLTE_API int srslte_timestamp_init(srslte_timestamp_t *t, + time_t full_secs, + double frac_secs); + +SRSLTE_API int srslte_timestamp_copy(srslte_timestamp_t *dest, + srslte_timestamp_t *src); + +SRSLTE_API int srslte_timestamp_add(srslte_timestamp_t *t, + time_t full_secs, + double frac_secs); + +SRSLTE_API int srslte_timestamp_sub(srslte_timestamp_t *t, + time_t full_secs, + double frac_secs); + +SRSLTE_API double srslte_timestamp_real(srslte_timestamp_t *t); + +SRSLTE_API uint32_t srslte_timestamp_uint32(srslte_timestamp_t *t); + +#endif // SRSLTE_TIMESTAMP_H diff --git a/lib/include/srslte/phy/dft/dft.h b/lib/include/srslte/phy/dft/dft.h new file mode 100644 index 0000000..e62362d --- /dev/null +++ b/lib/include/srslte/phy/dft/dft.h @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +#ifndef SRSLTE_DFT_H +#define SRSLTE_DFT_H + +#include +#include "srslte/config.h" + +/********************************************************************************************** + * File: dft.h + * + * Description: Generic DFT module. + * Supports one-dimensional complex and real transforms. Options are set + * using the dft_plan_set_x functions. + * + * Options (default is false): + * + * mirror - Rearranges negative and positive frequency bins. Swaps after + * transform for FORWARD, swaps before transform for BACKWARD. + * db - Provides output in dB (10*log10(x)). + * norm - Normalizes output (by sqrt(len) for complex, len for real). + * dc - Handles insertion and removal of null DC carrier internally. + * + * Reference: + *********************************************************************************************/ + +typedef enum { + SRSLTE_DFT_COMPLEX, SRSLTE_REAL +}srslte_dft_mode_t; + +typedef enum { + SRSLTE_DFT_FORWARD, SRSLTE_DFT_BACKWARD +}srslte_dft_dir_t; + +typedef struct SRSLTE_API { + int init_size; // DFT length used in the first initialization + int size; // DFT length + void *in; // Input buffer + void *out; // Output buffer + void *p; // DFT plan + bool is_guru; + bool forward; // Forward transform? + bool mirror; // Shift negative and positive frequencies? + bool db; // Provide output in dB? + bool norm; // Normalize output? + bool dc; // Handle insertion/removal of null DC carrier internally? + srslte_dft_dir_t dir; // Forward/Backward + srslte_dft_mode_t mode; // Complex/Real +}srslte_dft_plan_t; + +SRSLTE_API void srslte_dft_load(); + +SRSLTE_API void srslte_dft_exit(); + +SRSLTE_API int srslte_dft_plan(srslte_dft_plan_t *plan, + int dft_points, + srslte_dft_dir_t dir, + srslte_dft_mode_t type); + +SRSLTE_API int srslte_dft_plan_c(srslte_dft_plan_t *plan, + int dft_points, + srslte_dft_dir_t dir); + +SRSLTE_API int srslte_dft_plan_guru_c(srslte_dft_plan_t *plan, + int dft_points, + srslte_dft_dir_t dir, + cf_t *in_buffer, + cf_t *out_buffer, + int istride, + int ostride, + int how_many, + int idist, + int odist); + +SRSLTE_API int srslte_dft_plan_r(srslte_dft_plan_t *plan, + int dft_points, + srslte_dft_dir_t dir); + +SRSLTE_API int srslte_dft_replan(srslte_dft_plan_t *plan, + const int new_dft_points); + +SRSLTE_API int srslte_dft_replan_guru_c(srslte_dft_plan_t *plan, + const int new_dft_points, + cf_t *in_buffer, + cf_t *out_buffer, + int istride, + int ostride, + int how_many, + int idist, + int odist); + +SRSLTE_API int srslte_dft_replan_c(srslte_dft_plan_t *plan, + int new_dft_points); + +SRSLTE_API int srslte_dft_replan_r(srslte_dft_plan_t *plan, + int new_dft_points); + + +SRSLTE_API void srslte_dft_plan_free(srslte_dft_plan_t *plan); + +/* Set options */ + +SRSLTE_API void srslte_dft_plan_set_mirror(srslte_dft_plan_t *plan, + bool val); + +SRSLTE_API void srslte_dft_plan_set_db(srslte_dft_plan_t *plan, + bool val); + +SRSLTE_API void srslte_dft_plan_set_norm(srslte_dft_plan_t *plan, + bool val); + +SRSLTE_API void srslte_dft_plan_set_dc(srslte_dft_plan_t *plan, + bool val); + +/* Compute DFT */ + +SRSLTE_API void srslte_dft_run(srslte_dft_plan_t *plan, + const void *in, + void *out); + +SRSLTE_API void srslte_dft_run_c_zerocopy(srslte_dft_plan_t *plan, + const cf_t *in, + cf_t *out); + +SRSLTE_API void srslte_dft_run_c(srslte_dft_plan_t *plan, + const cf_t *in, + cf_t *out); + +SRSLTE_API void srslte_dft_run_guru_c(srslte_dft_plan_t *plan); + +SRSLTE_API void srslte_dft_run_r(srslte_dft_plan_t *plan, + const float *in, + float *out); + +#endif // SRSLTE_DFT_H + diff --git a/lib/include/srslte/phy/dft/dft_precoding.h b/lib/include/srslte/phy/dft/dft_precoding.h new file mode 100644 index 0000000..c1eb20e --- /dev/null +++ b/lib/include/srslte/phy/dft/dft_precoding.h @@ -0,0 +1,71 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: dft_precoding.h + * + * Description: DFT-based transform precoding object. + * Used in generation of uplink SCFDMA signals. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.3.3 + *********************************************************************************************/ + +#ifndef SRSLTE_DFT_PRECODING_H +#define SRSLTE_DFT_PRECODING_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" + +/* DFT-based Transform Precoding object */ +typedef struct SRSLTE_API { + + uint32_t max_prb; + srslte_dft_plan_t dft_plan[SRSLTE_MAX_PRB+1]; + +}srslte_dft_precoding_t; + +SRSLTE_API int srslte_dft_precoding_init(srslte_dft_precoding_t *q, + uint32_t max_prb, + bool is_tx); + +SRSLTE_API int srslte_dft_precoding_init_tx(srslte_dft_precoding_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_dft_precoding_init_rx(srslte_dft_precoding_t *q, + uint32_t max_prb); + +SRSLTE_API void srslte_dft_precoding_free(srslte_dft_precoding_t *q); + +SRSLTE_API bool srslte_dft_precoding_valid_prb(uint32_t nof_prb); + +SRSLTE_API int srslte_dft_precoding(srslte_dft_precoding_t *q, + cf_t *input, + cf_t *output, + uint32_t nof_prb, + uint32_t nof_symbols); + +#endif // SRSLTE_DFT_PRECODING_H diff --git a/lib/include/srslte/phy/dft/ofdm.h b/lib/include/srslte/phy/dft/ofdm.h new file mode 100644 index 0000000..c28d41b --- /dev/null +++ b/lib/include/srslte/phy/dft/ofdm.h @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +#ifndef SRSLTE_OFDM_H +#define SRSLTE_OFDM_H + +/********************************************************************************************** + * File: ofdm.h + * + * Description: OFDM modulation object. + * Used in generation of downlink OFDM signals. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6 + *********************************************************************************************/ + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" + +/* This is common for both directions */ +typedef struct SRSLTE_API{ + srslte_dft_plan_t fft_plan; + srslte_dft_plan_t fft_plan_sf[2]; + uint32_t max_prb; + uint32_t nof_symbols; + uint32_t symbol_sz; + uint32_t nof_guards; + uint32_t nof_re; + uint32_t slot_sz; + uint32_t sf_sz; + srslte_cp_t cp; + cf_t *tmp; // for removing zero padding + cf_t *in_buffer; + cf_t *out_buffer; + + bool mbsfn_subframe; + uint32_t mbsfn_guard_len; + uint32_t nof_symbols_mbsfn; + uint8_t non_mbsfn_region; + + + bool freq_shift; + float freq_shift_f; + cf_t *shift_buffer; +}srslte_ofdm_t; + +SRSLTE_API int srslte_ofdm_init_(srslte_ofdm_t *q, + srslte_cp_t cp, + cf_t *in_buffer, + cf_t *out_buffer, + int symbol_sz, + int nof_prb, + srslte_dft_dir_t dir); + +SRSLTE_API int srslte_ofdm_init_mbsfn_(srslte_ofdm_t *q, + srslte_cp_t cp, + cf_t *in_buffer, + cf_t *out_buffer, + int symbol_sz, + int nof_prb, + srslte_dft_dir_t dir, + srslte_sf_t sf_type); + +SRSLTE_API int srslte_ofdm_rx_init_mbsfn(srslte_ofdm_t *q, + srslte_cp_t cp_type, + cf_t *in_buffer, + cf_t *out_buffer, + uint32_t max_prb); + +SRSLTE_API int srslte_ofdm_rx_init(srslte_ofdm_t *q, + srslte_cp_t cp_type, + cf_t *in_buffer, + cf_t *out_buffer, + uint32_t max_prb); + +SRSLTE_API int srslte_ofdm_tx_set_prb(srslte_ofdm_t *q, + srslte_cp_t cp, + uint32_t nof_prb); + +SRSLTE_API int srslte_ofdm_rx_set_prb(srslte_ofdm_t *q, + srslte_cp_t cp, + uint32_t nof_prb); + +SRSLTE_API void srslte_ofdm_rx_free(srslte_ofdm_t *q); + +SRSLTE_API void srslte_ofdm_rx_slot(srslte_ofdm_t *q, + int slot_in_sf); + +SRSLTE_API void srslte_ofdm_rx_slot_ng(srslte_ofdm_t *q, + cf_t *input, + cf_t *output); + +SRSLTE_API void srslte_ofdm_rx_sf(srslte_ofdm_t *q); + +SRSLTE_API void srslte_ofdm_rx_sf_ng(srslte_ofdm_t *q, + cf_t *input, + cf_t *output); + +SRSLTE_API int srslte_ofdm_tx_init(srslte_ofdm_t *q, + srslte_cp_t cp_type, + cf_t *in_buffer, + cf_t *out_buffer, + uint32_t nof_prb); + +SRSLTE_API int srslte_ofdm_tx_init_mbsfn(srslte_ofdm_t *q, + srslte_cp_t cp, + cf_t *in_buffer, + cf_t *out_buffer, + uint32_t nof_prb); + + +SRSLTE_API void srslte_ofdm_tx_free(srslte_ofdm_t *q); + +SRSLTE_API void srslte_ofdm_tx_slot(srslte_ofdm_t *q, + int slot_in_sf); + +SRSLTE_API void srslte_ofdm_tx_slot_mbsfn(srslte_ofdm_t *q, + cf_t *input, + cf_t *output); + + +SRSLTE_API void srslte_ofdm_tx_sf(srslte_ofdm_t *q); + +SRSLTE_API int srslte_ofdm_set_freq_shift(srslte_ofdm_t *q, + float freq_shift); + +SRSLTE_API void srslte_ofdm_set_normalize(srslte_ofdm_t *q, + bool normalize_enable); + +SRSLTE_API void srslte_ofdm_set_non_mbsfn_region(srslte_ofdm_t *q, + uint8_t non_mbsfn_region); + + +#endif // SRSLTE_OFDM_H diff --git a/lib/include/srslte/phy/enb/enb_dl.h b/lib/include/srslte/phy/enb/enb_dl.h new file mode 100644 index 0000000..5e0d2bf --- /dev/null +++ b/lib/include/srslte/phy/enb/enb_dl.h @@ -0,0 +1,216 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: enb_dl.h + * + * Description: ENB downlink object. + * + * This module is a frontend to all the downlink data and control + * channel processing modules for the ENB transmitter side. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_ENB_DL_H +#define SRSLTE_ENB_DL_H + +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/sync/pss.h" +#include "srslte/phy/sync/sss.h" +#include "srslte/phy/ch_estimation/refsignal_dl.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/phch/pcfich.h" +#include "srslte/phy/phch/pdcch.h" +#include "srslte/phy/phch/pdsch.h" +#include "srslte/phy/phch/pdsch_cfg.h" +#include "srslte/phy/phch/phich.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/phch/regs.h" + +#include "srslte/phy/enb/enb_ul.h" + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#include "srslte/config.h" + +typedef struct SRSLTE_API { + srslte_cell_t cell; + + cf_t *sf_symbols[SRSLTE_MAX_PORTS]; + cf_t *slot1_symbols[SRSLTE_MAX_PORTS]; + + srslte_ofdm_t ifft[SRSLTE_MAX_PORTS]; + + srslte_ofdm_t ifft_mbsfn; + srslte_pbch_t pbch; + srslte_pcfich_t pcfich; + srslte_regs_t regs; + srslte_pdcch_t pdcch; + srslte_pdsch_t pdsch; + srslte_pmch_t pmch; + srslte_phich_t phich; + + srslte_refsignal_t csr_signal; + srslte_refsignal_t mbsfnr_signal; + srslte_pdsch_cfg_t pdsch_cfg; + srslte_pdsch_cfg_t pmch_cfg; + srslte_ra_dl_dci_t dl_dci; + + srslte_dci_format_t dci_format; + uint32_t cfi; + + cf_t pss_signal[SRSLTE_PSS_LEN]; + float sss_signal0[SRSLTE_SSS_LEN]; + float sss_signal5[SRSLTE_SSS_LEN]; + + float tx_amp; + float rho_b; + + uint8_t tmp[1024*128]; + +} srslte_enb_dl_t; + +typedef struct { + uint16_t rnti; + srslte_dci_format_t dci_format; + srslte_ra_dl_dci_t grant; + srslte_dci_location_t location; + srslte_softbuffer_tx_t *softbuffers[SRSLTE_MAX_TB]; + uint8_t *data[SRSLTE_MAX_TB]; +} srslte_enb_dl_pdsch_t; + +typedef struct { + uint16_t rnti; + uint8_t ack; + uint32_t n_prb_lowest; + uint32_t n_dmrs; +} srslte_enb_dl_phich_t; + +/* This function shall be called just after the initial synchronization */ +SRSLTE_API int srslte_enb_dl_init(srslte_enb_dl_t *q, + cf_t *out_buffer[SRSLTE_MAX_PORTS], + uint32_t max_prb); + +SRSLTE_API void srslte_enb_dl_free(srslte_enb_dl_t *q); + +SRSLTE_API int srslte_enb_dl_set_cell(srslte_enb_dl_t *q, + srslte_cell_t cell); + +SRSLTE_API void srslte_enb_dl_set_cfi(srslte_enb_dl_t *q, + uint32_t cfi); + +SRSLTE_API void srslte_enb_dl_set_power_allocation(srslte_enb_dl_t *q, + float rho_a, + float rho_b); + +SRSLTE_API void srslte_enb_dl_apply_power_allocation(srslte_enb_dl_t *q); + +SRSLTE_API void srslte_enb_dl_prepare_power_allocation(srslte_enb_dl_t *q); + +SRSLTE_API void srslte_enb_dl_set_amp(srslte_enb_dl_t *q, + float amp); + +SRSLTE_API void srslte_enb_dl_set_non_mbsfn_region(srslte_enb_dl_t *q, uint8_t non_mbsfn_region); + +SRSLTE_API void srslte_enb_dl_clear_sf(srslte_enb_dl_t *q); + +SRSLTE_API void srslte_enb_dl_put_sync(srslte_enb_dl_t *q, + uint32_t sf_idx); + +SRSLTE_API void srslte_enb_dl_put_refs(srslte_enb_dl_t *q, + uint32_t sf_idx); + +SRSLTE_API void srslte_enb_dl_put_mib(srslte_enb_dl_t *q, + uint32_t tti); + +SRSLTE_API void srslte_enb_dl_put_pcfich(srslte_enb_dl_t *q, + uint32_t sf_idx); + +SRSLTE_API void srslte_enb_dl_put_phich(srslte_enb_dl_t *q, + uint8_t ack, + uint32_t n_prb_lowest, + uint32_t n_dmrs, + uint32_t sf_idx); + +SRSLTE_API void srslte_enb_dl_put_base(srslte_enb_dl_t *q, + uint32_t tti); + +SRSLTE_API void srslte_enb_dl_put_mbsfn_base(srslte_enb_dl_t *q, + uint32_t tti); + +SRSLTE_API void srslte_enb_dl_gen_signal(srslte_enb_dl_t *q); + +SRSLTE_API void srslte_enb_dl_gen_signal_mbsfn(srslte_enb_dl_t *q); + +SRSLTE_API int srslte_enb_dl_add_rnti(srslte_enb_dl_t *q, + uint16_t rnti); + +SRSLTE_API void srslte_enb_dl_rem_rnti(srslte_enb_dl_t *q, + uint16_t rnti); + +SRSLTE_API int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, + srslte_ra_dl_grant_t *grant, + srslte_softbuffer_tx_t *softbuffer[SRSLTE_MAX_CODEWORDS], + uint16_t rnti, + int rv_idx[SRSLTE_MAX_CODEWORDS], + uint32_t sf_idx, + uint8_t *data[SRSLTE_MAX_CODEWORDS], + srslte_mimo_type_t mimo_type); + +SRSLTE_API int srslte_enb_dl_put_pmch(srslte_enb_dl_t *q, + srslte_ra_dl_grant_t *grant, + srslte_softbuffer_tx_t *softbuffer, + uint32_t sf_idx, + uint8_t *data_mbms); + +SRSLTE_API int srslte_enb_dl_put_pdcch_dl(srslte_enb_dl_t *q, + srslte_ra_dl_dci_t *grant, + srslte_dci_format_t format, + srslte_dci_location_t location, + uint16_t rnti, + uint32_t sf_idx); + +SRSLTE_API int srslte_enb_dl_put_pdcch_ul(srslte_enb_dl_t *q, + srslte_ra_ul_dci_t *grant, + srslte_dci_location_t location, + uint16_t rnti, + uint32_t sf_idx); + +SRSLTE_API void srslte_enb_dl_save_signal(srslte_enb_dl_t *q, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + uint32_t tti, + uint32_t rv_idx, + uint16_t rnti, + uint32_t cfi); + +#endif // SRSLTE_ENB_DL_H diff --git a/lib/include/srslte/phy/enb/enb_ul.h b/lib/include/srslte/phy/enb/enb_ul.h new file mode 100644 index 0000000..49d1748 --- /dev/null +++ b/lib/include/srslte/phy/enb/enb_ul.h @@ -0,0 +1,156 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: enb_ul.h + * + * Description: ENB uplink object. + * + * This module is a frontend to all the uplink data and control + * channel processing modules for the ENB receiver side. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_ENB_UL_H +#define SRSLTE_ENB_UL_H + +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/ch_estimation/chest_ul.h" +#include "srslte/phy/phch/prach.h" +#include "srslte/phy/phch/pusch.h" +#include "srslte/phy/phch/pusch_cfg.h" +#include "srslte/phy/phch/ra.h" + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#include "srslte/config.h" + +typedef struct { + uint32_t n_prb_lowest; + uint32_t n_dmrs; +} srslte_enb_ul_phich_info_t; + +typedef struct { + bool uci_cfg_en; + bool srs_cfg_en; + srslte_uci_cfg_t uci_cfg; + srslte_refsignal_srs_cfg_t srs_cfg; + srslte_pucch_sched_t pucch_sched; +} srslte_enb_ul_user_t; + +typedef struct SRSLTE_API { + srslte_cell_t cell; + + cf_t *sf_symbols; + cf_t *ce; + + srslte_ofdm_t fft; + srslte_chest_ul_t chest; + + srslte_pusch_t pusch; + srslte_pucch_t pucch; + srslte_prach_t prach; + + srslte_pusch_cfg_t pusch_cfg; + + srslte_pusch_hopping_cfg_t hopping_cfg; + + // Configuration for each user + srslte_enb_ul_user_t **users; + +} srslte_enb_ul_t; + +typedef struct { + uint16_t rnti; + srslte_ra_ul_dci_t grant; + srslte_dci_location_t location; + uint32_t rv_idx; + uint32_t current_tx_nb; + uint8_t *data; + srslte_softbuffer_rx_t *softbuffer; + bool needs_pdcch; +} srslte_enb_ul_pusch_t; + +/* This function shall be called just after the initial synchronization */ +SRSLTE_API int srslte_enb_ul_init(srslte_enb_ul_t *q, + cf_t *in_buffer, + uint32_t max_prb); + +SRSLTE_API void srslte_enb_ul_free(srslte_enb_ul_t *q); + +SRSLTE_API int srslte_enb_ul_set_cell(srslte_enb_ul_t *q, + srslte_cell_t cell, + srslte_prach_cfg_t* prach_cfg, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_pucch_cfg_t *pucch_cfg); + +SRSLTE_API int srslte_enb_ul_add_rnti(srslte_enb_ul_t *q, + uint16_t rnti); + +SRSLTE_API void srslte_enb_ul_rem_rnti(srslte_enb_ul_t *q, + uint16_t rnti); + +SRSLTE_API int srslte_enb_ul_cfg_ue(srslte_enb_ul_t *q, uint16_t rnti, + srslte_uci_cfg_t *uci_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_refsignal_srs_cfg_t *srs_cfg); + + +SRSLTE_API void srslte_enb_ul_fft(srslte_enb_ul_t *q); + +SRSLTE_API int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, + uint16_t rnti, + uint32_t pdcch_n_cce, + uint32_t sf_rx, + srslte_uci_data_t *uci_data); + +SRSLTE_API int srslte_enb_ul_get_pusch(srslte_enb_ul_t *q, + srslte_ra_ul_grant_t *grant, + srslte_softbuffer_rx_t *softbuffer, + uint16_t rnti, + uint32_t rv_idx, + uint32_t current_tx_nb, + uint8_t *data, + srslte_cqi_value_t *cqi_value, + srslte_uci_data_t *uci_data, + uint32_t tti); + +SRSLTE_API int srslte_enb_ul_detect_prach(srslte_enb_ul_t *q, + uint32_t tti, + uint32_t freq_offset, + cf_t *signal, + uint32_t *indices, + float *offsets, + float *peak2avg); + + +#endif // SRSLTE_ENB_UL_H diff --git a/lib/include/srslte/phy/fec/cbsegm.h b/lib/include/srslte/phy/fec/cbsegm.h new file mode 100644 index 0000000..7eee832 --- /dev/null +++ b/lib/include/srslte/phy/fec/cbsegm.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "srslte/config.h" + +#ifndef SRSLTE_CBSEGM_H +#define SRSLTE_CBSEGM_H + +#define SRSLTE_NOF_TC_CB_SIZES 188 + + + typedef struct SRSLTE_API { + uint32_t F; + uint32_t C; + uint32_t K1; + uint32_t K2; + uint32_t K1_idx; + uint32_t K2_idx; + uint32_t C1; + uint32_t C2; + uint32_t tbs; +} srslte_cbsegm_t; + +SRSLTE_API int srslte_cbsegm(srslte_cbsegm_t *s, + uint32_t tbs); + +SRSLTE_API int srslte_cbsegm_cbsize(uint32_t index); + +SRSLTE_API bool srslte_cbsegm_cbsize_isvalid(uint32_t size); + +SRSLTE_API int srslte_cbsegm_cbindex(uint32_t long_cb); + +#endif // SRSLTE_CBSEGM_H diff --git a/lib/include/srslte/phy/fec/convcoder.h b/lib/include/srslte/phy/fec/convcoder.h new file mode 100644 index 0000000..99dfd33 --- /dev/null +++ b/lib/include/srslte/phy/fec/convcoder.h @@ -0,0 +1,56 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: convcoder.h + * + * Description: Convolutional encoder. + * LTE uses a tail biting convolutional code with constraint length 7 + * and coding rate 1/3. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.1 + *********************************************************************************************/ + +#ifndef SRSLTE_CONVCODER_H +#define SRSLTE_CONVCODER_H + +#include +#include "srslte/config.h" + +typedef struct SRSLTE_API { + uint32_t R; + uint32_t K; + int poly[3]; + bool tail_biting; +}srslte_convcoder_t; + +SRSLTE_API int srslte_convcoder_encode(srslte_convcoder_t *q, + uint8_t *input, + uint8_t *output, + uint32_t frame_length); + + +#endif // SRSLTE_CONVCODER_H diff --git a/lib/include/srslte/phy/fec/crc.h b/lib/include/srslte/phy/fec/crc.h new file mode 100644 index 0000000..030e7c6 --- /dev/null +++ b/lib/include/srslte/phy/fec/crc.h @@ -0,0 +1,76 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: convcoder.h + * + * Description: Cyclic Redundancy Check + * LTE requires CRC lengths 8, 16, 24A and 24B, each with it's own generator + * polynomial. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.1 + *********************************************************************************************/ + +#ifndef SRSLTE_CRC_H +#define SRSLTE_CRC_H + +#include "srslte/config.h" +#include + +typedef struct SRSLTE_API { + uint64_t table[256]; + int polynom; + int order; + uint64_t crcinit; + uint64_t crcmask; + uint64_t crchighbit; + uint32_t srslte_crc_out; +} srslte_crc_t; + +SRSLTE_API int srslte_crc_init(srslte_crc_t *h, + uint32_t srslte_crc_poly, + int srslte_crc_order); + +SRSLTE_API int srslte_crc_set_init(srslte_crc_t *h, + uint64_t init_value); + +SRSLTE_API uint32_t srslte_crc_attach(srslte_crc_t *h, + uint8_t *data, + int len); + +SRSLTE_API uint32_t srslte_crc_attach_byte(srslte_crc_t *h, + uint8_t *data, + int len); + +SRSLTE_API uint32_t srslte_crc_checksum_byte(srslte_crc_t *h, + uint8_t *data, + int len); + +SRSLTE_API uint32_t srslte_crc_checksum(srslte_crc_t *h, + uint8_t *data, + int len); + +#endif // SRSLTE_CRC_H diff --git a/lib/include/srslte/phy/fec/rm_conv.h b/lib/include/srslte/phy/fec/rm_conv.h new file mode 100644 index 0000000..9681ce8 --- /dev/null +++ b/lib/include/srslte/phy/fec/rm_conv.h @@ -0,0 +1,66 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: rm_conv.h + * + * Description: Rate matching for convolutionally coded transport channels and control + * information. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.4.2 + *********************************************************************************************/ + +#ifndef SRSLTE_RM_CONV_H +#define SRSLTE_RM_CONV_H + +#include "srslte/config.h" + +#ifndef SRSLTE_RX_NULL +#define SRSLTE_RX_NULL 10000 +#endif + +#ifndef SRSLTE_TX_NULL +#define SRSLTE_TX_NULL 100 +#endif +SRSLTE_API int srslte_rm_conv_tx(uint8_t *input, + uint32_t in_len, + uint8_t *output, + uint32_t out_len); + +SRSLTE_API int srslte_rm_conv_rx(float *input, + uint32_t in_len, + float *output, + uint32_t out_len); + + +/************* FIX THIS. MOVE ALL PROCESSING TO INT16 AND HAVE ONLY 1 IMPLEMENTATION ******/ + +SRSLTE_API int srslte_rm_conv_rx_s(int16_t *input, + uint32_t in_len, + int16_t *output, + uint32_t out_len); + +#endif // SRSLTE_RM_CONV_H diff --git a/lib/include/srslte/phy/fec/rm_turbo.h b/lib/include/srslte/phy/fec/rm_turbo.h new file mode 100644 index 0000000..26f9894 --- /dev/null +++ b/lib/include/srslte/phy/fec/rm_turbo.h @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: rm_turbo.h + * + * Description: Rate matching for turbo coded transport channels. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.4.1 + *********************************************************************************************/ + +#ifndef SRSLTE_RM_TURBO_H +#define SRSLTE_RM_TURBO_H + +#include "srslte/config.h" + +#ifndef SRSLTE_RX_NULL +#define SRSLTE_RX_NULL 10000 +#endif + +#ifndef SRSLTE_TX_NULL +#define SRSLTE_TX_NULL 100 +#endif + +#include "srslte/config.h" + + +SRSLTE_API int srslte_rm_turbo_tx(uint8_t *w_buff, + uint32_t buff_len, + uint8_t *input, + uint32_t in_len, + uint8_t *output, + uint32_t out_len, + uint32_t rv_idx); + +SRSLTE_API void srslte_rm_turbo_gentables(); + +SRSLTE_API void srslte_rm_turbo_free_tables(); + +SRSLTE_API int srslte_rm_turbo_tx_lut(uint8_t *w_buff, + uint8_t *systematic, + uint8_t *parity, + uint8_t *output, + uint32_t cb_idx, + uint32_t out_len, + uint32_t w_offset, + uint32_t rv_idx); + +SRSLTE_API int srslte_rm_turbo_rx(float *w_buff, + uint32_t buff_len, + float *input, + uint32_t in_len, + float *output, + uint32_t out_len, + uint32_t rv_idx, + uint32_t nof_filler_bits); + +SRSLTE_API int srslte_rm_turbo_rx_lut(int16_t *input, + int16_t *output, + uint32_t in_len, + uint32_t cb_idx, + uint32_t rv_idx); + + +#endif // SRSLTE_RM_TURBO_H diff --git a/lib/include/srslte/phy/fec/softbuffer.h b/lib/include/srslte/phy/fec/softbuffer.h new file mode 100644 index 0000000..864fc14 --- /dev/null +++ b/lib/include/srslte/phy/fec/softbuffer.h @@ -0,0 +1,85 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: softbuffer.h + * + * Description: Buffer for RX and TX soft bits. This should be provided by MAC. + * Provided here basically for the examples. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_SOFTBUFFER_H +#define SRSLTE_SOFTBUFFER_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +typedef struct SRSLTE_API { + uint32_t max_cb; + int16_t **buffer_f; + uint8_t **data; + bool *cb_crc; + bool tb_crc; +} srslte_softbuffer_rx_t; + +typedef struct SRSLTE_API { + uint32_t max_cb; + uint8_t **buffer_b; +} srslte_softbuffer_tx_t; + +#define SOFTBUFFER_SIZE 18600 + +SRSLTE_API int srslte_softbuffer_rx_init(srslte_softbuffer_rx_t * q, + uint32_t nof_prb); + +SRSLTE_API void srslte_softbuffer_rx_reset(srslte_softbuffer_rx_t *p); + +SRSLTE_API void srslte_softbuffer_rx_reset_tbs(srslte_softbuffer_rx_t *q, + uint32_t tbs); + +SRSLTE_API void srslte_softbuffer_rx_reset_cb(srslte_softbuffer_rx_t *q, + uint32_t nof_cb); + +SRSLTE_API void srslte_softbuffer_rx_free(srslte_softbuffer_rx_t *p); + +SRSLTE_API int srslte_softbuffer_tx_init(srslte_softbuffer_tx_t * q, + uint32_t nof_prb); + +SRSLTE_API void srslte_softbuffer_tx_reset(srslte_softbuffer_tx_t *p); + +SRSLTE_API void srslte_softbuffer_tx_reset_tbs(srslte_softbuffer_tx_t *q, + uint32_t tbs); + +SRSLTE_API void srslte_softbuffer_tx_reset_cb(srslte_softbuffer_tx_t *q, + uint32_t nof_cb); + +SRSLTE_API void srslte_softbuffer_tx_free(srslte_softbuffer_tx_t *p); + + + +#endif // SRSLTE_SOFTBUFFER_H diff --git a/lib/include/srslte/phy/fec/tc_interl.h b/lib/include/srslte/phy/fec/tc_interl.h new file mode 100644 index 0000000..c0ffaae --- /dev/null +++ b/lib/include/srslte/phy/fec/tc_interl.h @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: tc_interl.h + * + * Description: Turbo code interleaver. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2.3 + *********************************************************************************************/ + +#ifndef SRSLTE_TC_INTERL_H +#define SRSLTE_TC_INTERL_H + +#include "srslte/config.h" +#include + +typedef struct SRSLTE_API { + uint16_t *forward; + uint16_t *reverse; + uint32_t max_long_cb; +} srslte_tc_interl_t; + +SRSLTE_API int srslte_tc_interl_LTE_gen(srslte_tc_interl_t *h, + uint32_t long_cb); + +SRSLTE_API int srslte_tc_interl_UMTS_gen(srslte_tc_interl_t *h, + uint32_t long_cb); + +SRSLTE_API int srslte_tc_interl_init(srslte_tc_interl_t *h, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tc_interl_free(srslte_tc_interl_t *h); + +#endif // SRSLTE_TC_INTERL_H diff --git a/lib/include/srslte/phy/fec/turbocoder.h b/lib/include/srslte/phy/fec/turbocoder.h new file mode 100644 index 0000000..485e83f --- /dev/null +++ b/lib/include/srslte/phy/fec/turbocoder.h @@ -0,0 +1,79 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbocoder.h + * + * Description: Turbo coder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBOCODER_H +#define SRSLTE_TURBOCODER_H + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" + +#define SRSLTE_TCOD_MAX_LEN_CB_BYTES (6144/8) + +#ifndef SRSLTE_TX_NULL +#define SRSLTE_TX_NULL 100 +#endif + +typedef struct SRSLTE_API { + uint32_t max_long_cb; + uint8_t *temp; +} srslte_tcod_t; + + +/* This structure is used as an output for the LUT version of the encoder. + * The encoder produces parity bits only and rate matching will interleave them + * with the systematic bits + */ + +SRSLTE_API int srslte_tcod_init(srslte_tcod_t *h, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tcod_free(srslte_tcod_t *h); + +SRSLTE_API int srslte_tcod_encode(srslte_tcod_t *h, + uint8_t *input, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API int srslte_tcod_encode_lut(srslte_tcod_t *h, + uint8_t *input, + uint8_t *parity, + uint32_t cblen_idx); + +SRSLTE_API void srslte_tcod_gentable(); + +#endif // SRSLTE_TURBOCODER_H + diff --git a/lib/include/srslte/phy/fec/turbodecoder.h b/lib/include/srslte/phy/fec/turbodecoder.h new file mode 100644 index 0000000..d882118 --- /dev/null +++ b/lib/include/srslte/phy/fec/turbodecoder.h @@ -0,0 +1,120 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbodecoder.h + * + * Description: Turbo Decoder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBODECODER_H +#define SRSLTE_TURBODECODER_H + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/cbsegm.h" + +#define SRSLTE_TCOD_RATE 3 +#define SRSLTE_TCOD_TOTALTAIL 12 + +#define SRSLTE_TCOD_MAX_LEN_CB 6144 +#define SRSLTE_TCOD_MAX_LEN_CODED (SRSLTE_TCOD_RATE*SRSLTE_TCOD_MAX_LEN_CB+SRSLTE_TCOD_TOTALTAIL) + +#include "srslte/phy/fec/turbodecoder_gen.h" +#include "srslte/phy/fec/turbodecoder_simd.h" + +typedef struct SRSLTE_API { + float *input_conv; + union { + srslte_tdec_simd_t tdec_simd; + srslte_tdec_gen_t tdec_gen; + }; +} srslte_tdec_t; + +SRSLTE_API int srslte_tdec_init(srslte_tdec_t * h, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tdec_free(srslte_tdec_t * h); + +SRSLTE_API int srslte_tdec_reset(srslte_tdec_t * h, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_reset_cb(srslte_tdec_t * h, + uint32_t cb_idx); + +SRSLTE_API int srslte_tdec_get_nof_iterations_cb(srslte_tdec_t * h, + uint32_t cb_idx); + +SRSLTE_API uint32_t srslte_tdec_get_nof_parallel(srslte_tdec_t * h); + +SRSLTE_API void srslte_tdec_iteration(srslte_tdec_t * h, + int16_t* input, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_decision(srslte_tdec_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_decision_byte(srslte_tdec_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_run_all(srslte_tdec_t * h, + int16_t * input, + uint8_t *output, + uint32_t nof_iterations, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_iteration_par(srslte_tdec_t * h, + int16_t* input[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_decision_par(srslte_tdec_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_decision_byte_par(srslte_tdec_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_decision_byte_par_cb(srslte_tdec_t * h, + uint8_t *output, + uint32_t cb_idx, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_run_all_par(srslte_tdec_t * h, + int16_t * input[SRSLTE_TDEC_MAX_NPAR], + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_iterations, + uint32_t long_cb); + +#endif // SRSLTE_TURBODECODER_H diff --git a/lib/include/srslte/phy/fec/turbodecoder_gen.h b/lib/include/srslte/phy/fec/turbodecoder_gen.h new file mode 100644 index 0000000..2fefc22 --- /dev/null +++ b/lib/include/srslte/phy/fec/turbodecoder_gen.h @@ -0,0 +1,99 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbodecoder.h + * + * Description: Turbo Decoder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBODECODER_GEN_H +#define SRSLTE_TURBODECODER_GEN_H + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/cbsegm.h" + +#define SRSLTE_TCOD_RATE 3 +#define SRSLTE_TCOD_TOTALTAIL 12 + +#define SRSLTE_TCOD_MAX_LEN_CB 6144 +#define SRSLTE_TCOD_MAX_LEN_CODED (SRSLTE_TCOD_RATE*SRSLTE_TCOD_MAX_LEN_CB+SRSLTE_TCOD_TOTALTAIL) + +typedef struct SRSLTE_API { + int max_long_cb; + float *beta; +} srslte_map_gen_vl_t; + +typedef struct SRSLTE_API { + int max_long_cb; + + srslte_map_gen_vl_t dec; + + float *llr1; + float *llr2; + float *w; + float *syst; + float *parity; + + int current_cbidx; + uint32_t current_cb_len; + uint32_t n_iter; + srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES]; +} srslte_tdec_gen_t; + +SRSLTE_API int srslte_tdec_gen_init(srslte_tdec_gen_t * h, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tdec_gen_free(srslte_tdec_gen_t * h); + +SRSLTE_API int srslte_tdec_gen_reset(srslte_tdec_gen_t * h, uint32_t long_cb); + +SRSLTE_API void srslte_tdec_gen_iteration(srslte_tdec_gen_t * h, + float * input, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_gen_decision(srslte_tdec_gen_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_gen_decision_byte(srslte_tdec_gen_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_gen_run_all(srslte_tdec_gen_t * h, + float * input, + uint8_t *output, + uint32_t nof_iterations, + uint32_t long_cb); + +#endif // SRSLTE_TURBODECODER_GEN_H diff --git a/lib/include/srslte/phy/fec/turbodecoder_simd.h b/lib/include/srslte/phy/fec/turbodecoder_simd.h new file mode 100644 index 0000000..35c8bee --- /dev/null +++ b/lib/include/srslte/phy/fec/turbodecoder_simd.h @@ -0,0 +1,122 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbodecoder.h + * + * Description: Turbo Decoder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBODECODER_SIMD_H +#define SRSLTE_TURBODECODER_SIMD_H + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/cbsegm.h" + +// Define maximum number of CB decoded in parallel (2 for AVX2) +#define SRSLTE_TDEC_MAX_NPAR 2 + +#define SRSLTE_TCOD_RATE 3 +#define SRSLTE_TCOD_TOTALTAIL 12 + +#define SRSLTE_TCOD_MAX_LEN_CB 6144 +#define SRSLTE_TCOD_MAX_LEN_CODED (SRSLTE_TCOD_RATE*SRSLTE_TCOD_MAX_LEN_CB+SRSLTE_TCOD_TOTALTAIL) + +typedef struct SRSLTE_API { + uint32_t max_long_cb; + uint32_t max_par_cb; + int16_t *alpha; + int16_t *branch; +} map_gen_t; + +typedef struct SRSLTE_API { + uint32_t max_long_cb; + uint32_t max_par_cb; + + map_gen_t dec; + + int16_t *app1[SRSLTE_TDEC_MAX_NPAR]; + int16_t *app2[SRSLTE_TDEC_MAX_NPAR]; + int16_t *ext1[SRSLTE_TDEC_MAX_NPAR]; + int16_t *ext2[SRSLTE_TDEC_MAX_NPAR]; + int16_t *syst[SRSLTE_TDEC_MAX_NPAR]; + int16_t *parity0[SRSLTE_TDEC_MAX_NPAR]; + int16_t *parity1[SRSLTE_TDEC_MAX_NPAR]; + + int cb_mask; + int current_cbidx; + srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES]; + int n_iter[SRSLTE_TDEC_MAX_NPAR]; +} srslte_tdec_simd_t; + +SRSLTE_API int srslte_tdec_simd_init(srslte_tdec_simd_t * h, + uint32_t max_par_cb, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tdec_simd_free(srslte_tdec_simd_t * h); + +SRSLTE_API int srslte_tdec_simd_reset(srslte_tdec_simd_t * h, + uint32_t long_cb); + +SRSLTE_API + +SRSLTE_API int srslte_tdec_simd_get_nof_iterations_cb(srslte_tdec_simd_t * h, + uint32_t cb_idx); + +SRSLTE_API int srslte_tdec_simd_reset_cb(srslte_tdec_simd_t * h, + uint32_t cb_idx); + +SRSLTE_API void srslte_tdec_simd_iteration(srslte_tdec_simd_t * h, + int16_t * input[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_decision(srslte_tdec_simd_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_decision_byte(srslte_tdec_simd_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_decision_byte_cb(srslte_tdec_simd_t * h, + uint8_t *output, + uint32_t cbidx, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_simd_run_all(srslte_tdec_simd_t * h, + int16_t * input[SRSLTE_TDEC_MAX_NPAR], + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_iterations, + uint32_t long_cb); + +#endif // SRSLTE_TURBODECODER_SIMD_H diff --git a/lib/include/srslte/phy/fec/turbodecoder_simd_inter.h b/lib/include/srslte/phy/fec/turbodecoder_simd_inter.h new file mode 100644 index 0000000..054fbc3 --- /dev/null +++ b/lib/include/srslte/phy/fec/turbodecoder_simd_inter.h @@ -0,0 +1,119 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbodecoder.h + * + * Description: Turbo Decoder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBODECODER_SIMD_INTER_H +#define SRSLTE_TURBODECODER_SIMD_INTER_H + + +/** This is an simd inter-frame parallel turbo decoder. Parallizes 8 code-blocks using SSE + * This implementation is currently not functional and not used by the rest of the code + */ + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/cbsegm.h" + +#if LV_HAVE_AVX2 + #define SRSLTE_TDEC_MAX_NPAR 16 +#else + #define SRSLTE_TDEC_MAX_NPAR 8 +#endif + + +typedef struct SRSLTE_API { + int max_long_cb; + + int16_t *syst0; + int16_t *parity0; + int16_t *syst1; + int16_t *parity1; + int16_t *llr1; + int16_t *llr2; + int16_t *w; + int16_t *alpha; + + uint32_t max_par_cb; + int current_cbidx; + uint32_t current_long_cb; + srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES]; + int n_iter[SRSLTE_TDEC_MAX_NPAR]; +} srslte_tdec_simd_inter_t; + +SRSLTE_API int srslte_tdec_simd_inter_init(srslte_tdec_simd_inter_t * h, + uint32_t max_par_cb, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tdec_simd_inter_free(srslte_tdec_simd_inter_t * h); + +SRSLTE_API int srslte_tdec_simd_inter_reset(srslte_tdec_simd_inter_t * h, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_simd_inter_get_nof_iterations_cb(srslte_tdec_simd_inter_t * h, + uint32_t cb_idx); + +SRSLTE_API int srslte_tdec_simd_inter_reset_cb(srslte_tdec_simd_inter_t * h, + uint32_t cb_idx); + +SRSLTE_API void srslte_tdec_simd_inter_iteration(srslte_tdec_simd_inter_t * h, + int16_t * input[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_cb, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_inter_decision(srslte_tdec_simd_inter_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_cb, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_inter_decision_byte(srslte_tdec_simd_inter_t * h, + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_cb, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_simd_inter_decision_byte_cb(srslte_tdec_simd_inter_t * h, + uint8_t *output, + uint32_t cbidx, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_simd_inter_run_all(srslte_tdec_simd_inter_t * h, + int16_t *input[SRSLTE_TDEC_MAX_NPAR], + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_iterations, + uint32_t nof_cb, + uint32_t long_cb); + +#endif // SRSLTE_TURBODECODER_SIMD_INTER_H diff --git a/lib/include/srslte/phy/fec/turbodecoder_sse.h b/lib/include/srslte/phy/fec/turbodecoder_sse.h new file mode 100644 index 0000000..9678fba --- /dev/null +++ b/lib/include/srslte/phy/fec/turbodecoder_sse.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/********************************************************************************************** + * File: turbodecoder.h + * + * Description: Turbo Decoder. + * Parallel Concatenated Convolutional Code (PCCC) with two 8-state constituent + * encoders and one turbo code internal interleaver. The coding rate of turbo + * encoder is 1/3. + * MAP_GEN is the MAX-LOG-MAP generic implementation of the decoder. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.1.3.2 + *********************************************************************************************/ + +#ifndef SRSLTE_TURBODECODER_SSE_ +#define SRSLTE_TURBODECODER_SSE_ + +#include "srslte/config.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/cbsegm.h" + +#define SRSLTE_TCOD_RATE 3 +#define SRSLTE_TCOD_TOTALTAIL 12 + +#define SRSLTE_TCOD_MAX_LEN_CB 6144 +#define SRSLTE_TCOD_MAX_LEN_CODED (SRSLTE_TCOD_RATE*SRSLTE_TCOD_MAX_LEN_CB+SRSLTE_TCOD_TOTALTAIL) + +typedef struct SRSLTE_API { + int max_long_cb; + int16_t *alpha; + int16_t *branch; +} map_gen_t; + +typedef struct SRSLTE_API { + int max_long_cb; + + map_gen_t dec; + + int16_t *app1; + int16_t *app2; + int16_t *ext1; + int16_t *ext2; + int16_t *syst; + int16_t *parity0; + int16_t *parity1; + + int current_cbidx; + srslte_tc_interl_t interleaver[SRSLTE_NOF_TC_CB_SIZES]; + int n_iter; +} srslte_tdec_sse_t; + +SRSLTE_API int srslte_tdec_sse_init(srslte_tdec_sse_t * h, + uint32_t max_long_cb); + +SRSLTE_API void srslte_tdec_sse_free(srslte_tdec_sse_t * h); + +SRSLTE_API int srslte_tdec_sse_reset(srslte_tdec_sse_t * h, uint32_t long_cb); + +SRSLTE_API void srslte_tdec_sse_iteration(srslte_tdec_sse_t * h, + int16_t * input, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_sse_decision(srslte_tdec_sse_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API void srslte_tdec_sse_decision_byte(srslte_tdec_sse_t * h, + uint8_t *output, + uint32_t long_cb); + +SRSLTE_API int srslte_tdec_sse_run_all(srslte_tdec_sse_t * h, + int16_t * input, + uint8_t *output, + uint32_t nof_iterations, + uint32_t long_cb); + +#endif // SRSLTE_TURBODECODER_SSE_ diff --git a/lib/include/srslte/phy/fec/viterbi.h b/lib/include/srslte/phy/fec/viterbi.h new file mode 100644 index 0000000..0f6f0cd --- /dev/null +++ b/lib/include/srslte/phy/fec/viterbi.h @@ -0,0 +1,122 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: viterbi.h + * + * Description: Viterbi decoder for convolutionally encoded data. + * Used for decoding of PBCH and PDCCH (type 37 decoder). + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_VITERBI_H +#define SRSLTE_VITERBI_H + +#include +#include "srslte/config.h" + + + +typedef enum { + SRSLTE_VITERBI_27 = 0, + SRSLTE_VITERBI_29, + SRSLTE_VITERBI_37, + SRSLTE_VITERBI_39 +}srslte_viterbi_type_t; + +typedef struct SRSLTE_API{ + void *ptr; + uint32_t R; + uint32_t K; + uint32_t framebits; + bool tail_biting; + float gain_quant; + int16_t gain_quant_s; + int (*decode) (void*, uint8_t*, uint8_t*, uint32_t); + int (*decode_s) (void*, uint16_t*, uint8_t*, uint32_t); + int (*decode_f) (void*, float*, uint8_t*, uint32_t); + void (*free) (void*); + uint8_t *tmp; + uint16_t *tmp_s; + uint8_t *symbols_uc; + uint16_t *symbols_us; +}srslte_viterbi_t; + +SRSLTE_API int srslte_viterbi_init(srslte_viterbi_t *q, + srslte_viterbi_type_t type, + int poly[3], + uint32_t max_frame_length, + bool tail_bitting); + +SRSLTE_API void srslte_viterbi_set_gain_quant(srslte_viterbi_t *q, + float gain_quant); + +SRSLTE_API void srslte_viterbi_set_gain_quant_s(srslte_viterbi_t *q, + int16_t gain_quant); + +SRSLTE_API void srslte_viterbi_free(srslte_viterbi_t *q); + +SRSLTE_API int srslte_viterbi_decode_f(srslte_viterbi_t *q, + float *symbols, + uint8_t *data, + uint32_t frame_length); + +SRSLTE_API int srslte_viterbi_decode_s(srslte_viterbi_t *q, + int16_t *symbols, + uint8_t *data, + uint32_t frame_length); + +SRSLTE_API int srslte_viterbi_decode_us(srslte_viterbi_t *q, uint16_t *symbols, uint8_t *data, uint32_t frame_length); + +SRSLTE_API int srslte_viterbi_decode_uc(srslte_viterbi_t *q, + uint8_t *symbols, + uint8_t *data, + uint32_t frame_length); + + + +SRSLTE_API int srslte_viterbi_init_sse(srslte_viterbi_t *q, + srslte_viterbi_type_t type, + int poly[3], + uint32_t max_frame_length, + bool tail_bitting); + +SRSLTE_API int srslte_viterbi_init_neon(srslte_viterbi_t *q, + srslte_viterbi_type_t type, + int poly[3], + uint32_t max_frame_length, + bool tail_bitting); + +SRSLTE_API int srslte_viterbi_init_avx2(srslte_viterbi_t *q, + srslte_viterbi_type_t type, + int poly[3], + uint32_t max_frame_length, + bool tail_bitting); + + + +#endif // SRSLTE_VITERBI_H diff --git a/lib/include/srslte/phy/io/binsource.h b/lib/include/srslte/phy/io/binsource.h new file mode 100644 index 0000000..733449b --- /dev/null +++ b/lib/include/srslte/phy/io/binsource.h @@ -0,0 +1,71 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: binsource.h + * + * Description: Pseudo-random binary source. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_BINSOURCE_H +#define SRSLTE_BINSOURCE_H + + +#include +#include "srslte/config.h" + +/* Low-level API */ +typedef struct SRSLTE_API{ + uint32_t seed; + uint32_t *seq_buff; + int seq_buff_nwords; + int seq_cache_nbits; + int seq_cache_rp; +}srslte_binsource_t; + +SRSLTE_API void srslte_binsource_init(srslte_binsource_t* q); + +SRSLTE_API void srslte_binsource_free(srslte_binsource_t* q); + +SRSLTE_API void srslte_binsource_seed_set(srslte_binsource_t* q, + uint32_t seed); + +SRSLTE_API void srslte_binsource_seed_time(srslte_binsource_t *q); + +SRSLTE_API int srslte_binsource_cache_gen(srslte_binsource_t* q, + int nbits); + +SRSLTE_API void srslte_binsource_cache_cpy(srslte_binsource_t* q, + uint8_t *bits, + int nbits); + +SRSLTE_API int srslte_binsource_generate(srslte_binsource_t* q, + uint8_t *bits, + int nbits); + +#endif // SRSLTE_BINSOURCE_H diff --git a/lib/include/srslte/phy/io/filesink.h b/lib/include/srslte/phy/io/filesink.h new file mode 100644 index 0000000..b6fee3a --- /dev/null +++ b/lib/include/srslte/phy/io/filesink.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: filesink.h + * + * Description: File sink. + * Supports writing floats, complex floats and complex shorts + * to file in text or binary formats. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_FILESINK_H +#define SRSLTE_FILESINK_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/io/format.h" + +/* Low-level API */ +typedef struct SRSLTE_API { + FILE *f; + srslte_datatype_t type; +} srslte_filesink_t; + +SRSLTE_API int srslte_filesink_init(srslte_filesink_t *q, + char *filename, + srslte_datatype_t type); + +SRSLTE_API void srslte_filesink_free(srslte_filesink_t *q); + +SRSLTE_API int srslte_filesink_write(srslte_filesink_t *q, + void *buffer, + int nsamples); + +SRSLTE_API int srslte_filesink_write_multi(srslte_filesink_t *q, + void **buffer, + int nsamples, + int nchannels); + +#endif // SRSLTE_FILESINK_H diff --git a/lib/include/srslte/phy/io/filesource.h b/lib/include/srslte/phy/io/filesource.h new file mode 100644 index 0000000..d4ad81f --- /dev/null +++ b/lib/include/srslte/phy/io/filesource.h @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: filesource.h + * + * Description: File source. + * Supports reading floats, complex floats and complex shorts + * from file in text or binary formats. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_FILESOURCE_H +#define SRSLTE_FILESOURCE_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/io/format.h" + +/* Low-level API */ +typedef struct SRSLTE_API { + FILE *f; + srslte_datatype_t type; +} srslte_filesource_t; + +SRSLTE_API int srslte_filesource_init(srslte_filesource_t *q, + char *filename, + srslte_datatype_t type); + +SRSLTE_API void srslte_filesource_free(srslte_filesource_t *q); + +SRSLTE_API void srslte_filesource_seek(srslte_filesource_t *q, + int pos); + +SRSLTE_API int srslte_filesource_read(srslte_filesource_t *q, + void *buffer, + int nsamples); + +SRSLTE_API int srslte_filesource_read_multi(srslte_filesource_t *q, + void **buffer, + int nsamples, + int nof_channels); + +#endif // SRSLTE_FILESOURCE_H diff --git a/lib/include/srslte/phy/io/format.h b/lib/include/srslte/phy/io/format.h new file mode 100644 index 0000000..22c0e39 --- /dev/null +++ b/lib/include/srslte/phy/io/format.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +#ifndef SRSLTE_FORMAT_H +#define SRSLTE_FORMAT_H + +typedef enum { + SRSLTE_FLOAT, + SRSLTE_COMPLEX_FLOAT, + SRSLTE_COMPLEX_SHORT, + SRSLTE_FLOAT_BIN, + SRSLTE_COMPLEX_FLOAT_BIN, + SRSLTE_COMPLEX_SHORT_BIN +} srslte_datatype_t; + +#endif // SRSLTE_FORMAT_H diff --git a/lib/include/srslte/phy/io/netsink.h b/lib/include/srslte/phy/io/netsink.h new file mode 100644 index 0000000..246c520 --- /dev/null +++ b/lib/include/srslte/phy/io/netsink.h @@ -0,0 +1,74 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: netsink.h + * + * Description: Network socket sink. + * Supports writing binary data to a TCP or UDP socket. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_NETSINK_H +#define SRSLTE_NETSINK_H + +#include +#include +#include +#include +#include +#include + +#include "srslte/config.h" + +typedef enum { + SRSLTE_NETSINK_UDP, + SRSLTE_NETSINK_TCP +} srslte_netsink_type_t; + +/* Low-level API */ +typedef struct SRSLTE_API { + int sockfd; + bool connected; + srslte_netsink_type_t type; + struct sockaddr_in servaddr; +}srslte_netsink_t; + +SRSLTE_API int srslte_netsink_init(srslte_netsink_t *q, + const char *address, + uint16_t port, + srslte_netsink_type_t type); + +SRSLTE_API void srslte_netsink_free(srslte_netsink_t *q); + +SRSLTE_API int srslte_netsink_write(srslte_netsink_t *q, + void *buffer, + int nof_bytes); + +SRSLTE_API int srslte_netsink_set_nonblocking(srslte_netsink_t *q); + +#endif // SRSLTE_NETSINK_H diff --git a/lib/include/srslte/phy/io/netsource.h b/lib/include/srslte/phy/io/netsource.h new file mode 100644 index 0000000..ee0ca69 --- /dev/null +++ b/lib/include/srslte/phy/io/netsource.h @@ -0,0 +1,78 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: netsource.h + * + * Description: Network socket source. + * Supports reading binary data from a TCP or UDP socket. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_NETSOURCE_H +#define SRSLTE_NETSOURCE_H + +#include +#include +#include +#include +#include +#include + +#include "srslte/config.h" + +typedef enum { + SRSLTE_NETSOURCE_UDP, + SRSLTE_NETSOURCE_TCP +} srslte_netsource_type_t; + +/* Low-level API */ +typedef struct SRSLTE_API { + int sockfd; + int connfd; + struct sockaddr_in servaddr; + srslte_netsource_type_t type; + struct sockaddr_in cliaddr; +}srslte_netsource_t; + +SRSLTE_API int srslte_netsource_init(srslte_netsource_t *q, + const char *address, + uint16_t port, + srslte_netsource_type_t type); + +SRSLTE_API void srslte_netsource_free(srslte_netsource_t *q); + +SRSLTE_API int srslte_netsource_set_nonblocking(srslte_netsource_t *q); + +SRSLTE_API int srslte_netsource_read(srslte_netsource_t *q, + void *buffer, + int nof_bytes); + +SRSLTE_API int srslte_netsource_set_timeout(srslte_netsource_t *q, + uint32_t microseconds); + +#endif // SRSLTE_NETSOURCE_H diff --git a/lib/include/srslte/phy/mimo/layermap.h b/lib/include/srslte/phy/mimo/layermap.h new file mode 100644 index 0000000..d6328a7 --- /dev/null +++ b/lib/include/srslte/phy/mimo/layermap.h @@ -0,0 +1,94 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: layermap.h + * + * Description: MIMO layer mapping and demapping. + * Single antenna, tx diversity and spatial multiplexing are + * supported. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.3.3 + *****************************************************************************/ + +#ifndef SRSLTE_LAYERMAP_H +#define SRSLTE_LAYERMAP_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +/* Generates the vector of layer-mapped symbols "x" based on the vector of data symbols "d" + */ +SRSLTE_API int srslte_layermap_single(cf_t *d, + cf_t *x, + int nof_symbols); + +SRSLTE_API int srslte_layermap_diversity(cf_t *d, + cf_t *x[SRSLTE_MAX_LAYERS], + int nof_layers, + int nof_symbols); + +SRSLTE_API int srslte_layermap_multiplex(cf_t *d[SRSLTE_MAX_CODEWORDS], + cf_t *x[SRSLTE_MAX_LAYERS], + int nof_cw, + int nof_layers, + int nof_symbols[SRSLTE_MAX_CODEWORDS]); + +SRSLTE_API int srslte_layermap_type(cf_t *d[SRSLTE_MAX_CODEWORDS], + cf_t *x[SRSLTE_MAX_LAYERS], + int nof_cw, + int nof_layers, + int nof_symbols[SRSLTE_MAX_CODEWORDS], + srslte_mimo_type_t type); + + +/* Generates the vector of data symbols "d" based on the vector of layer-mapped symbols "x" + */ +SRSLTE_API int srslte_layerdemap_single(cf_t *x, + cf_t *d, + int nof_symbols); + +SRSLTE_API int srslte_layerdemap_diversity(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *d, + int nof_layers, + int nof_layer_symbols); + +SRSLTE_API int srslte_layerdemap_multiplex(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *d[SRSLTE_MAX_CODEWORDS], + int nof_layers, + int nof_cw, + int nof_layer_symbols, + int nof_symbols[SRSLTE_MAX_CODEWORDS]); + +SRSLTE_API int srslte_layerdemap_type(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *d[SRSLTE_MAX_CODEWORDS], + int nof_layers, + int nof_cw, + int nof_layer_symbols, + int nof_symbols[SRSLTE_MAX_CODEWORDS], + srslte_mimo_type_t type); + +#endif // SRSLTE_LAYERMAP_H diff --git a/lib/include/srslte/phy/mimo/precoding.h b/lib/include/srslte/phy/mimo/precoding.h new file mode 100644 index 0000000..5caf4f0 --- /dev/null +++ b/lib/include/srslte/phy/mimo/precoding.h @@ -0,0 +1,141 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: precoding.h + * + * Description: MIMO precoding and deprecoding. + * Single antenna and tx diversity supported. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.3.4 + *****************************************************************************/ + +#ifndef SRSLTE_PRECODING_H +#define SRSLTE_PRECODING_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +/** The precoder takes as input nlayers vectors "x" from the + * layer mapping and generates nports vectors "y" to be mapped onto + * resources on each of the antenna ports. + */ + + +/* Generates the vector "y" from the input vector "x" + */ +SRSLTE_API int srslte_precoding_single(cf_t *x, + cf_t *y, + int nof_symbols, + float scaling); + +SRSLTE_API int srslte_precoding_diversity(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *y[SRSLTE_MAX_PORTS], + int nof_ports, + int nof_symbols, + float scaling); + +SRSLTE_API int srslte_precoding_cdd(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *y[SRSLTE_MAX_PORTS], + int nof_layers, + int nof_ports, + int nof_symbols, + float scaling); + +SRSLTE_API int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], + cf_t *y[SRSLTE_MAX_PORTS], + int nof_layers, + int nof_ports, + int codebook_idx, + int nof_symbols, + float scaling, + srslte_mimo_type_t type); + +/* Estimates the vector "x" based on the received signal "y" and the channel estimates "h" + */ +SRSLTE_API int srslte_predecoding_single(cf_t *y, + cf_t *h, + cf_t *x, + float *csi, + int nof_symbols, + float scaling, + float noise_estimate); + +SRSLTE_API int srslte_predecoding_single_multi(cf_t *y[SRSLTE_MAX_PORTS], + cf_t *h[SRSLTE_MAX_PORTS], + cf_t *x, + float *csi[SRSLTE_MAX_CODEWORDS], + int nof_rxant, + int nof_symbols, + float scaling, + float noise_estimate); + +SRSLTE_API int srslte_predecoding_diversity(cf_t *y, + cf_t *h[SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], + int nof_ports, + int nof_symbols, + float scaling); + +SRSLTE_API int srslte_predecoding_diversity_multi(cf_t *y[SRSLTE_MAX_PORTS], + cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], + float *csi[SRSLTE_MAX_LAYERS], + int nof_rxant, + int nof_ports, + int nof_symbols, + float scaling); + +SRSLTE_API void srslte_predecoding_set_mimo_decoder (srslte_mimo_decoder_t _mimo_decoder); + +SRSLTE_API int srslte_predecoding_type(cf_t *y[SRSLTE_MAX_PORTS], + cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], + float *csi[SRSLTE_MAX_CODEWORDS], + int nof_rxant, + int nof_ports, + int nof_layers, + int codebook_idx, + int nof_symbols, + srslte_mimo_type_t type, + float scaling, + float noise_estimate); + +SRSLTE_API int srslte_precoding_pmi_select(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t nof_symbols, + float noise_estimate, + int nof_layers, + uint32_t *pmi, + float sinr[SRSLTE_MAX_CODEBOOKS]); + +SRSLTE_API int srslte_precoding_cn(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t nof_tx_antennas, + uint32_t nof_rx_antennas, + uint32_t nof_symbols, + float *cn); + + +#endif // SRSLTE_PRECODING_H diff --git a/lib/include/srslte/phy/modem/demod_hard.h b/lib/include/srslte/phy/modem/demod_hard.h new file mode 100644 index 0000000..689bcca --- /dev/null +++ b/lib/include/srslte/phy/modem/demod_hard.h @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: demod_hard.h + * + * Description: Hard demodulator. + * Supports BPSK, QPSK, 16QAM and 64QAM. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 7.1 + *****************************************************************************/ + +#ifndef SRSLTE_DEMOD_HARD_H +#define SRSLTE_DEMOD_HARD_H + +#include +#include + +#include "srslte/config.h" +#include "modem_table.h" + +typedef struct SRSLTE_API { + srslte_mod_t mod; /* In this implementation, mapping table is hard-coded */ +}srslte_demod_hard_t; + + +SRSLTE_API void srslte_demod_hard_init(srslte_demod_hard_t* q); + +SRSLTE_API void srslte_demod_hard_table_set(srslte_demod_hard_t* q, + srslte_mod_t mod); + +SRSLTE_API int srslte_demod_hard_demodulate(srslte_demod_hard_t* q, + cf_t* symbols, + uint8_t *bits, + uint32_t nsymbols); + +#endif // SRSLTE_DEMOD_HARD_H diff --git a/lib/include/srslte/phy/modem/demod_soft.h b/lib/include/srslte/phy/modem/demod_soft.h new file mode 100644 index 0000000..9ad065e --- /dev/null +++ b/lib/include/srslte/phy/modem/demod_soft.h @@ -0,0 +1,56 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: demod_soft.h + * + * Description: Soft demodulator. + * Supports BPSK, QPSK, 16QAM and 64QAM. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 7.1 + *****************************************************************************/ + +#ifndef SRSLTE_DEMOD_SOFT_H +#define SRSLTE_DEMOD_SOFT_H + +#include +#include + +#include "srslte/config.h" +#include "modem_table.h" + + +SRSLTE_API int srslte_demod_soft_demodulate(srslte_mod_t modulation, + const cf_t* symbols, + float* llr, + int nsymbols); + +SRSLTE_API int srslte_demod_soft_demodulate_s(srslte_mod_t modulation, + const cf_t* symbols, + short* llr, + int nsymbols); + +#endif // SRSLTE_DEMOD_SOFT_H diff --git a/lib/include/srslte/phy/modem/mod.h b/lib/include/srslte/phy/modem/mod.h new file mode 100644 index 0000000..a2f8299 --- /dev/null +++ b/lib/include/srslte/phy/modem/mod.h @@ -0,0 +1,55 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: mod.h + * + * Description: Modulation. + * Supports BPSK, QPSK, 16QAM and 64QAM. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 7.1 + *****************************************************************************/ + +#ifndef SRSLTE_MOD_H +#define SRSLTE_MOD_H + +#include +#include + +#include "srslte/config.h" +#include "modem_table.h" + +SRSLTE_API int srslte_mod_modulate(srslte_modem_table_t* table, + uint8_t *bits, + cf_t* symbols, + uint32_t nbits); + +SRSLTE_API int srslte_mod_modulate_bytes(srslte_modem_table_t* q, + uint8_t *bits, + cf_t* symbols, + uint32_t nbits); + +#endif // SRSLTE_MOD_H diff --git a/lib/include/srslte/phy/modem/modem_table.h b/lib/include/srslte/phy/modem/modem_table.h new file mode 100644 index 0000000..09cea5e --- /dev/null +++ b/lib/include/srslte/phy/modem/modem_table.h @@ -0,0 +1,86 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: modem_table.h + * + * Description: Modem tables used for modulation/demodulation. + * Supports BPSK, QPSK, 16QAM and 64QAM. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 7.1 + *****************************************************************************/ + +#ifndef SRSLTE_MODEM_TABLE_H +#define SRSLTE_MODEM_TABLE_H + +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/config.h" + +typedef struct { + cf_t symbol[8]; +} bpsk_packed_t; + +typedef struct { + cf_t symbol[4]; +} qpsk_packed_t; + +typedef struct { + cf_t symbol[2]; +} qam16_packed_t; + +typedef struct SRSLTE_API { + cf_t* symbol_table; // bit-to-symbol mapping + uint32_t nsymbols; // number of modulation symbols + uint32_t nbits_x_symbol; // number of bits per symbol + + bool byte_tables_init; + bpsk_packed_t *symbol_table_bpsk; + qpsk_packed_t *symbol_table_qpsk; + qam16_packed_t *symbol_table_16qam; +}srslte_modem_table_t; + + +SRSLTE_API void srslte_modem_table_init(srslte_modem_table_t* q); + +SRSLTE_API void srslte_modem_table_free(srslte_modem_table_t* q); + +SRSLTE_API void srslte_modem_table_reset(srslte_modem_table_t* q); + +SRSLTE_API int srslte_modem_table_set(srslte_modem_table_t* q, + cf_t* table, + uint32_t nsymbols, + uint32_t nbits_x_symbol); + +SRSLTE_API int srslte_modem_table_lte(srslte_modem_table_t* q, + srslte_mod_t modulation); + +SRSLTE_API void srslte_modem_table_bytes(srslte_modem_table_t* q); + +#endif // SRSLTE_MODEM_TABLE_H diff --git a/lib/include/srslte/phy/phch/cqi.h b/lib/include/srslte/phy/phch/cqi.h new file mode 100644 index 0000000..481fce4 --- /dev/null +++ b/lib/include/srslte/phy/phch/cqi.h @@ -0,0 +1,185 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: cqi.h + * + * Description: Channel quality indicator message packing. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.2.2.6, 5.2.3.3 + *****************************************************************************/ + +#ifndef SRSLTE_CQI_H +#define SRSLTE_CQI_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +#define SRSLTE_CQI_MAX_BITS 64 +#define SRSLTE_DIF_CQI_MAX_BITS 3 +#define SRSLTE_PMI_MAX_BITS 4 +#define SRSLTE_CQI_STR_MAX_CHAR 64 + +typedef struct { + bool configured; + uint32_t pmi_idx; + uint32_t ri_idx; + bool ri_idx_present; + bool simul_cqi_ack; + bool format_is_subband; + uint32_t subband_size; +} srslte_cqi_periodic_cfg_t; + +/* Table 5.2.2.6.2-1: Fields for channel quality information feedback for higher layer configured subband + CQI reports (transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and + transmission mode 8 configured without PMI/RI reporting). */ + +/* Table 5.2.2.6.2-2: Fields for channel quality information (CQI) feedback for higher layer configured subband CQI + reports (transmission mode 4, transmission mode 5 and transmission mode 6). */ + +typedef struct SRSLTE_API { + uint8_t wideband_cqi_cw0; // 4-bit width + uint32_t subband_diff_cqi_cw0; // 2N-bit width + uint8_t wideband_cqi_cw1; // if RI > 1 then 4-bit width otherwise 0-bit width + uint32_t subband_diff_cqi_cw1; // if RI > 1 then 2N-bit width otherwise 0-bit width + uint32_t pmi; // if RI > 1 then 2-bit width otherwise 1-bit width + uint32_t N; + bool ri_present; + bool pmi_present; + bool four_antenna_ports; // If cell has 4 antenna ports then true otherwise false + bool rank_is_not_one; // If rank > 1 then true otherwise false +} srslte_cqi_hl_subband_t; + +/* Table 5.2.2.6.3-1: Fields for channel quality information feedback for UE selected subband CQI +reports +(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and +transmission mode 8 configured without PMI/RI reporting). */ +typedef struct SRSLTE_API { + uint8_t wideband_cqi; // 4-bit width + uint8_t subband_diff_cqi; // 2-bit width + uint32_t position_subband; // L-bit width + uint32_t L; +} srslte_cqi_ue_subband_t; + +/* Table 5.2.3.3.1-1: Fields for channel quality information feedback for wideband CQI reports +(transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and +transmission mode 8 configured without PMI/RI reporting). +This is for PUCCH Format 2 reports +*/ + +/* Table 5.2.3.3.1-2: UCI fields for channel quality and precoding information (CQI/PMI) feedback for +wideband reports (transmission mode 4, transmission mode 5 and transmission mode 6) +This is for PUCCH Format 2 reports +*/ + +typedef struct SRSLTE_API { + uint8_t wideband_cqi; // 4-bit width + uint8_t spatial_diff_cqi; // If Rank==1 then it is 0-bit width otherwise it is 3-bit width + uint8_t pmi; + bool pmi_present; + bool four_antenna_ports; // If cell has 4 antenna ports then true otherwise false + bool rank_is_not_one; // If rank > 1 then true otherwise false +} srslte_cqi_format2_wideband_t; + +typedef struct SRSLTE_API { + uint8_t subband_cqi; // 4-bit width + uint8_t subband_label; // 1- or 2-bit width + bool subband_label_2_bits; // false, label=1-bit, true label=2-bits +} srslte_cqi_format2_subband_t; + +typedef enum { + SRSLTE_CQI_TYPE_WIDEBAND = 0, + SRSLTE_CQI_TYPE_SUBBAND, + SRSLTE_CQI_TYPE_SUBBAND_UE, + SRSLTE_CQI_TYPE_SUBBAND_HL +} srslte_cqi_type_t; + +typedef struct { + union { + srslte_cqi_format2_wideband_t wideband; + srslte_cqi_format2_subband_t subband; + srslte_cqi_ue_subband_t subband_ue; + srslte_cqi_hl_subband_t subband_hl; + }; + srslte_cqi_type_t type; +} srslte_cqi_value_t; + + +SRSLTE_API int srslte_cqi_size(srslte_cqi_value_t *value); + +SRSLTE_API int srslte_cqi_value_pack(srslte_cqi_value_t *value, + uint8_t buff[SRSLTE_CQI_MAX_BITS]); + +SRSLTE_API int srslte_cqi_hl_subband_pack(srslte_cqi_hl_subband_t *msg, + uint8_t buff[SRSLTE_CQI_MAX_BITS]); + +SRSLTE_API int srslte_cqi_ue_subband_pack(srslte_cqi_ue_subband_t *msg, + uint8_t buff[SRSLTE_CQI_MAX_BITS]); + +SRSLTE_API int srslte_cqi_format2_wideband_pack(srslte_cqi_format2_wideband_t *msg, + uint8_t buff[SRSLTE_CQI_MAX_BITS]); + +SRSLTE_API int srslte_cqi_format2_subband_pack(srslte_cqi_format2_subband_t *msg, + uint8_t buff[SRSLTE_CQI_MAX_BITS]); + +SRSLTE_API int srslte_cqi_value_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], + srslte_cqi_value_t *value); + +SRSLTE_API int srslte_cqi_value_tostring(srslte_cqi_value_t *value, char *buff, uint32_t buff_len); + + +SRSLTE_API int srslte_cqi_hl_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], + srslte_cqi_hl_subband_t *msg); + +SRSLTE_API int srslte_cqi_ue_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], + srslte_cqi_ue_subband_t *msg); + +SRSLTE_API int srslte_cqi_format2_wideband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], + srslte_cqi_format2_wideband_t *msg); + +SRSLTE_API int srslte_cqi_format2_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], + srslte_cqi_format2_subband_t *msg); + +SRSLTE_API bool srslte_cqi_send(uint32_t I_cqi_pmi, + uint32_t tti); + +SRSLTE_API bool srslte_ri_send(uint32_t I_cqi_pmi, + uint32_t I_ri, + uint32_t tti); + +SRSLTE_API uint8_t srslte_cqi_from_snr(float snr); + +SRSLTE_API float srslte_cqi_to_coderate(uint32_t cqi); + +SRSLTE_API int srslte_cqi_hl_get_subband_size(int num_prbs); + +SRSLTE_API int srslte_cqi_hl_get_no_subbands(int num_prbs); + +SRSLTE_API void srslte_cqi_to_str(const uint8_t *cqi_value, int cqi_len, char *str, int str_len); + +#endif // SRSLTE_CQI_H diff --git a/lib/include/srslte/phy/phch/dci.h b/lib/include/srslte/phy/phch/dci.h new file mode 100644 index 0000000..1a7bfbb --- /dev/null +++ b/lib/include/srslte/phy/phch/dci.h @@ -0,0 +1,196 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: dci.h + * + * Description: Downlink control information (DCI). + * Packing/Unpacking functions to convert between bit streams + * and packed DCI UL/DL grants defined in ra.h + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.3.3 + *****************************************************************************/ + +#ifndef SRSLTE_DCI_H +#define SRSLTE_DCI_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/ra.h" + +#define SRSLTE_DCI_MAX_BITS 128 + +#define SRSLTE_RAR_GRANT_LEN 20 + +SRSLTE_API extern int harq_pid_len; + +typedef enum { + SRSLTE_DCI_FORMAT0 = 0, + SRSLTE_DCI_FORMAT1, + SRSLTE_DCI_FORMAT1A, + SRSLTE_DCI_FORMAT1C, + SRSLTE_DCI_FORMAT1B, + SRSLTE_DCI_FORMAT1D, + SRSLTE_DCI_FORMAT2, + SRSLTE_DCI_FORMAT2A, + SRSLTE_DCI_FORMAT2B, + //SRSLTE_DCI_FORMAT3, + //SRSLTE_DCI_FORMAT3A, + SRSLTE_DCI_NOF_FORMATS +} srslte_dci_format_t; + +// Each type is for a different interface to packing/unpacking functions +typedef struct SRSLTE_API { + enum { + SRSLTE_DCI_MSG_TYPE_PUSCH_SCHED, + SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED, + SRSLTE_DCI_MSG_TYPE_MCCH_CHANGE, + SRSLTE_DCI_MSG_TYPE_TPC_COMMAND, + SRSLTE_DCI_MSG_TYPE_RA_PROC_PDCCH + } type; + srslte_dci_format_t format; +}srslte_dci_msg_type_t; + +typedef enum { + SRSLTE_DCI_SPEC_COMMON_ = 0, + SRSLTE_DCI_SPEC_UE = 1 +} dci_spec_t; + +typedef struct SRSLTE_API { + uint32_t L; // Aggregation level + uint32_t ncce; // Position of first CCE of the dci +} srslte_dci_location_t; + +typedef struct SRSLTE_API { + uint8_t data[SRSLTE_DCI_MAX_BITS]; + uint32_t nof_bits; + srslte_dci_format_t format; +} srslte_dci_msg_t; + +typedef struct SRSLTE_API { + uint32_t rba; + uint32_t trunc_mcs; + uint32_t tpc_pusch; + bool ul_delay; + bool cqi_request; + bool hopping_flag; +} srslte_dci_rar_grant_t; + +/* Converts a received PDSCH DL scheduling DCI message + * to ra structures ready to be passed to the harq setup function + */ +SRSLTE_API int srslte_dci_msg_to_dl_grant(srslte_dci_msg_t *msg, + uint16_t msg_rnti, + uint32_t nof_prb, + uint32_t nof_ports, + srslte_ra_dl_dci_t *dl_dci, + srslte_ra_dl_grant_t *grant); + +SRSLTE_API int srslte_dci_msg_to_ul_grant(srslte_dci_msg_t *msg, + uint32_t nof_prb, + uint32_t n_rb_ho, + srslte_ra_ul_dci_t *ul_dci, + srslte_ra_ul_grant_t *grant, + uint32_t harq_pid); + +SRSLTE_API int srslte_dci_rar_to_ul_grant(srslte_dci_rar_grant_t *rar, + uint32_t nof_prb, + uint32_t n_rb_ho, + srslte_ra_ul_dci_t *ul_dci, + srslte_ra_ul_grant_t *grant); + +SRSLTE_API void srslte_dci_rar_grant_unpack(srslte_dci_rar_grant_t *rar, + uint8_t grant[SRSLTE_RAR_GRANT_LEN]); + +SRSLTE_API void srslte_dci_rar_grant_pack(srslte_dci_rar_grant_t *rar, + uint8_t grant[SRSLTE_RAR_GRANT_LEN]); + +SRSLTE_API void srslte_dci_rar_grant_fprint(FILE *stream, + srslte_dci_rar_grant_t *rar); + +SRSLTE_API srslte_dci_format_t srslte_dci_format_from_string(char *str); + +SRSLTE_API char* srslte_dci_format_string(srslte_dci_format_t format); + +SRSLTE_API char* srslte_dci_format_string_short(srslte_dci_format_t format); + +SRSLTE_API int srslte_dci_location_set(srslte_dci_location_t *c, + uint32_t L, + uint32_t nCCE); + +SRSLTE_API bool srslte_dci_location_isvalid(srslte_dci_location_t *c); + +SRSLTE_API int srslte_dci_msg_get_type(srslte_dci_msg_t *msg, + srslte_dci_msg_type_t *type, + uint32_t nof_prb, + uint16_t msg_rnti); + +SRSLTE_API void srslte_dci_msg_type_fprint(FILE *f, + srslte_dci_msg_type_t type); + +// For srslte_dci_msg_type_t = SRSLTE_DCI_MSG_TYPE_PUSCH_SCHED +SRSLTE_API int srslte_dci_msg_pack_pusch(srslte_ra_ul_dci_t *data, + srslte_dci_msg_t *msg, + uint32_t nof_prb); + +SRSLTE_API int srslte_dci_msg_unpack_pusch(srslte_dci_msg_t *msg, + srslte_ra_ul_dci_t *data, + uint32_t nof_prb); + +// For srslte_dci_msg_type_t = SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED +SRSLTE_API int srslte_dci_msg_pack_pdsch(srslte_ra_dl_dci_t *data, + srslte_dci_format_t format, + srslte_dci_msg_t *msg, + uint32_t nof_prb, + uint32_t nof_ports, + bool crc_is_crnti); + +SRSLTE_API int srslte_dci_msg_unpack_pdsch(srslte_dci_msg_t *msg, + srslte_ra_dl_dci_t *data, + uint32_t nof_prb, + uint32_t nof_ports, + bool crc_is_crnti); + +SRSLTE_API uint32_t srslte_dci_format_sizeof(srslte_dci_format_t format, + uint32_t nof_prb, + uint32_t nof_ports); + +SRSLTE_API uint32_t srslte_dci_dl_info(char *info_str, + uint32_t str_len, + srslte_ra_dl_dci_t *dci_msg, + srslte_dci_format_t format); + +SRSLTE_API uint32_t srslte_dci_ul_info(char *info_str, + uint32_t len, + srslte_ra_ul_dci_t *dci_msg); + +// This is for backwards compatibility only for tm1 formats +SRSLTE_API uint32_t srslte_dci_format_sizeof_lut(srslte_dci_format_t format, + uint32_t nof_prb); + +#endif // DCI_ diff --git a/lib/include/srslte/phy/phch/pbch.h b/lib/include/srslte/phy/phch/pbch.h new file mode 100644 index 0000000..1e0af67 --- /dev/null +++ b/lib/include/srslte/phy/phch/pbch.h @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pbch.h + * + * Description: Physical broadcast channel. If cell.nof_ports = 0, the number + * of ports is blindly determined using the CRC of the received + * codeword for 1, 2 and 4 ports + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.6 + *****************************************************************************/ + +#ifndef SRSLTE_PBCH_H +#define SRSLTE_PBCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/fec/rm_conv.h" +#include "srslte/phy/fec/convcoder.h" +#include "srslte/phy/fec/viterbi.h" +#include "srslte/phy/fec/crc.h" + +#define SRSLTE_BCH_PAYLOAD_LEN 24 +#define SRSLTE_BCH_PAYLOADCRC_LEN (SRSLTE_BCH_PAYLOAD_LEN+16) +#define SRSLTE_BCH_ENCODED_LEN 3*(SRSLTE_BCH_PAYLOADCRC_LEN) + +#define SRSLTE_PBCH_MAX_RE 256 // make it avx2-aligned + +/* PBCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + + uint32_t nof_symbols; + + /* buffers */ + cf_t *ce[SRSLTE_MAX_PORTS]; + cf_t *symbols[SRSLTE_MAX_PORTS]; + cf_t *x[SRSLTE_MAX_PORTS]; + cf_t *d; + float *llr; + float *temp; + float rm_f[SRSLTE_BCH_ENCODED_LEN]; + uint8_t *rm_b; + uint8_t data[SRSLTE_BCH_PAYLOADCRC_LEN]; + uint8_t data_enc[SRSLTE_BCH_ENCODED_LEN]; + + uint32_t frame_idx; + + /* tx & rx objects */ + srslte_modem_table_t mod; + srslte_sequence_t seq; + srslte_viterbi_t decoder; + srslte_crc_t crc; + srslte_convcoder_t encoder; + bool search_all_ports; + +} srslte_pbch_t; + +SRSLTE_API int srslte_pbch_init(srslte_pbch_t *q); + +SRSLTE_API void srslte_pbch_free(srslte_pbch_t *q); + +SRSLTE_API int srslte_pbch_set_cell(srslte_pbch_t *q, + srslte_cell_t cell); + +SRSLTE_API int srslte_pbch_decode(srslte_pbch_t *q, + cf_t *slot1_symbols, + cf_t *ce_slot1[SRSLTE_MAX_PORTS], + float noise_estimate, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], + uint32_t *nof_tx_ports, + int *sfn_offset); + +SRSLTE_API int srslte_pbch_encode(srslte_pbch_t *q, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], + cf_t *slot1_symbols[SRSLTE_MAX_PORTS], + uint32_t frame_idx); + +SRSLTE_API void srslte_pbch_decode_reset(srslte_pbch_t *q); + +SRSLTE_API void srslte_pbch_mib_unpack(uint8_t *msg, + srslte_cell_t *cell, + uint32_t *sfn); + +SRSLTE_API void srslte_pbch_mib_pack(srslte_cell_t *cell, + uint32_t sfn, + uint8_t *msg); + +#endif // SRSLTE_PBCH_H diff --git a/lib/include/srslte/phy/phch/pcfich.h b/lib/include/srslte/phy/phch/pcfich.h new file mode 100644 index 0000000..554a150 --- /dev/null +++ b/lib/include/srslte/phy/phch/pcfich.h @@ -0,0 +1,111 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pcfich.h + * + * Description: Physical control format indicator channel + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.7 + *****************************************************************************/ + +#ifndef SRSLTE_PCFICH_H +#define SRSLTE_PCFICH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/phch/regs.h" + +#define PCFICH_CFI_LEN 32 +#define PCFICH_RE PCFICH_CFI_LEN/2 + +/* PCFICH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + int nof_symbols; + + uint32_t nof_rx_antennas; + + /* handler to REGs resource mapper */ + srslte_regs_t *regs; + + /* buffers */ + cf_t ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS][PCFICH_RE]; + cf_t symbols[SRSLTE_MAX_PORTS][PCFICH_RE]; + cf_t x[SRSLTE_MAX_PORTS][PCFICH_RE]; + cf_t d[PCFICH_RE]; + + // cfi table in floats + float cfi_table_float[3][PCFICH_CFI_LEN]; + + /* bit message */ + uint8_t data[PCFICH_CFI_LEN]; + + /* received soft bits */ + float data_f[PCFICH_CFI_LEN]; + + /* tx & rx objects */ + srslte_modem_table_t mod; + srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME]; + +} srslte_pcfich_t; + +SRSLTE_API int srslte_pcfich_init(srslte_pcfich_t *q, + uint32_t nof_rx_antennas); + +SRSLTE_API int srslte_pcfich_set_cell(srslte_pcfich_t *q, + srslte_regs_t *regs, + srslte_cell_t cell); + +SRSLTE_API void srslte_pcfich_free(srslte_pcfich_t *q); + +SRSLTE_API int srslte_pcfich_decode(srslte_pcfich_t *q, + cf_t *sf_symbols, + cf_t *ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t subframe, + uint32_t *cfi, + float *corr_result); + +SRSLTE_API int srslte_pcfich_decode_multi(srslte_pcfich_t *q, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t subframe, + uint32_t *cfi, + float *corr_result); + +SRSLTE_API int srslte_pcfich_encode(srslte_pcfich_t *q, + uint32_t cfi, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + uint32_t subframe); + +#endif // SRSLTE_PCFICH_H diff --git a/lib/include/srslte/phy/phch/pdcch.h b/lib/include/srslte/phy/phch/pdcch.h new file mode 100644 index 0000000..14f7b07 --- /dev/null +++ b/lib/include/srslte/phy/phch/pdcch.h @@ -0,0 +1,189 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pdcch.h + * + * Description: Physical downlink control channel. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.8 + *****************************************************************************/ + +#ifndef SRSLTE_PDCCH_H +#define SRSLTE_PDCCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/fec/rm_conv.h" +#include "srslte/phy/fec/convcoder.h" +#include "srslte/phy/fec/viterbi.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/regs.h" + + + + +typedef enum SRSLTE_API { + SEARCH_UE, SEARCH_COMMON +} srslte_pdcch_search_mode_t; + + +/* PDCCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + uint32_t nof_regs[3]; + uint32_t nof_cce[3]; + uint32_t max_bits; + uint32_t nof_rx_antennas; + bool is_ue; + + srslte_regs_t *regs; + + /* buffers */ + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + cf_t *symbols[SRSLTE_MAX_PORTS]; + cf_t *x[SRSLTE_MAX_PORTS]; + cf_t *d; + uint8_t *e; + float rm_f[3 * (SRSLTE_DCI_MAX_BITS + 16)]; + float *llr; + + /* tx & rx objects */ + srslte_modem_table_t mod; + srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME]; + srslte_viterbi_t decoder; + srslte_crc_t crc; + +} srslte_pdcch_t; + +SRSLTE_API int srslte_pdcch_init_ue(srslte_pdcch_t *q, + uint32_t max_prb, + uint32_t nof_rx_antennas); + +SRSLTE_API int srslte_pdcch_init_enb(srslte_pdcch_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_pdcch_set_cell(srslte_pdcch_t *q, + srslte_regs_t *regs, + srslte_cell_t cell); + +SRSLTE_API void srslte_pdcch_free(srslte_pdcch_t *q); + + +SRSLTE_API float srslte_pdcch_coderate(uint32_t nof_bits, + uint32_t l); + +/* Encoding function */ +SRSLTE_API int srslte_pdcch_encode(srslte_pdcch_t *q, + srslte_dci_msg_t *msg, + srslte_dci_location_t location, + uint16_t rnti, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + uint32_t nsubframe, + uint32_t cfi); + +/* Decoding functions: Extract the LLRs and save them in the srslte_pdcch_t object */ +SRSLTE_API int srslte_pdcch_extract_llr(srslte_pdcch_t *q, + cf_t *sf_symbols, + cf_t *ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t nsubframe, + uint32_t cfi); + +SRSLTE_API int srslte_pdcch_extract_llr_multi(srslte_pdcch_t *q, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t nsubframe, + uint32_t cfi); + +/* Decoding functions: Try to decode a DCI message after calling srslte_pdcch_extract_llr */ +SRSLTE_API int srslte_pdcch_decode_msg(srslte_pdcch_t *q, + srslte_dci_msg_t *msg, + srslte_dci_location_t *location, + srslte_dci_format_t format, + uint32_t cfi, + uint16_t *crc_rem); + +SRSLTE_API int srslte_pdcch_dci_decode(srslte_pdcch_t *q, + float *e, + uint8_t *data, + uint32_t E, + uint32_t nof_bits, + uint16_t *crc); + +SRSLTE_API int srslte_pdcch_dci_encode(srslte_pdcch_t *q, + uint8_t *data, + uint8_t *e, + uint32_t nof_bits, + uint32_t E, + uint16_t rnti); + +SRSLTE_API void srslte_pdcch_dci_encode_conv(srslte_pdcch_t *q, + uint8_t *data, + uint32_t nof_bits, + uint8_t *coded_data, + uint16_t rnti); + +/* Function for generation of UE-specific search space DCI locations */ +SRSLTE_API uint32_t srslte_pdcch_ue_locations(srslte_pdcch_t *q, + srslte_dci_location_t *locations, + uint32_t max_locations, + uint32_t nsubframe, + uint32_t cfi, + uint16_t rnti); + +SRSLTE_API uint32_t srslte_pdcch_ue_locations_ncce(uint32_t nof_cce, + srslte_dci_location_t *c, + uint32_t max_candidates, + uint32_t nsubframe, + uint16_t rnti); + +SRSLTE_API uint32_t srslte_pdcch_ue_locations_ncce_L(uint32_t nof_cce, + srslte_dci_location_t *c, + uint32_t max_candidates, + uint32_t nsubframe, + uint16_t rnti, + int L); + +/* Function for generation of common search space DCI locations */ +SRSLTE_API uint32_t srslte_pdcch_common_locations(srslte_pdcch_t *q, + srslte_dci_location_t *locations, + uint32_t max_locations, + uint32_t cfi); + +SRSLTE_API uint32_t srslte_pdcch_common_locations_ncce(uint32_t nof_cce, + srslte_dci_location_t *c, + uint32_t max_candidates); + + +#endif // SRSLTE_PDCCH_H diff --git a/lib/include/srslte/phy/phch/pdsch.h b/lib/include/srslte/phy/phch/pdsch.h new file mode 100644 index 0000000..f1ecbc3 --- /dev/null +++ b/lib/include/srslte/phy/phch/pdsch.h @@ -0,0 +1,177 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pdsch.h + * + * Description: Physical downlink shared channel + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.4 + *****************************************************************************/ + +#ifndef SRSLTE_PDSCH_H +#define SRSLTE_PDSCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/sch.h" +#include "srslte/phy/phch/pdsch_cfg.h" + +typedef struct { + srslte_sequence_t seq[SRSLTE_MAX_CODEWORDS][SRSLTE_NSUBFRAMES_X_FRAME]; + uint32_t cell_id; + bool sequence_generated; +} srslte_pdsch_user_t; + +/* PDSCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + + uint32_t nof_rx_antennas; + uint32_t last_nof_iterations[SRSLTE_MAX_CODEWORDS]; + + uint32_t max_re; + + uint16_t ue_rnti; + bool is_ue; + + /* Power allocation parameter 3GPP 36.213 Clause 5.2 Rho_b */ + float rho_a; + + /* buffers */ + // void buffers are shared for tx and rx + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; /* Channel estimation (Rx only) */ + cf_t *symbols[SRSLTE_MAX_PORTS]; /* PDSCH Encoded/Decoded Symbols */ + cf_t *x[SRSLTE_MAX_LAYERS]; /* Layer mapped */ + cf_t *d[SRSLTE_MAX_CODEWORDS]; /* Modulated/Demodulated codewords */ + void *e[SRSLTE_MAX_CODEWORDS]; + + bool csi_enabled; + float *csi[SRSLTE_MAX_CODEWORDS]; /* Channel Strengh Indicator */ + + /* tx & rx objects */ + srslte_modem_table_t mod[4]; + + // This is to generate the scrambling seq for multiple CRNTIs + srslte_pdsch_user_t **users; + + srslte_sequence_t tmp_seq; + + srslte_sch_t dl_sch; + + void *coworker_ptr; + +} srslte_pdsch_t; + + +SRSLTE_API int srslte_pdsch_init_ue(srslte_pdsch_t *q, + uint32_t max_prb, + uint32_t nof_rx_antennas); + +SRSLTE_API int srslte_pdsch_init_enb(srslte_pdsch_t *q, + uint32_t max_prb); + +SRSLTE_API void srslte_pdsch_free(srslte_pdsch_t *q); + +SRSLTE_API int srslte_pdsch_set_cell(srslte_pdsch_t *q, + srslte_cell_t cell); + +SRSLTE_API int srslte_pdsch_set_rnti(srslte_pdsch_t *q, + uint16_t rnti); + +SRSLTE_API void srslte_pdsch_set_power_allocation(srslte_pdsch_t *q, + float rho_a); + +SRSLTE_API int srslte_pdsch_enable_csi(srslte_pdsch_t *q, + bool enable); + +SRSLTE_API void srslte_pdsch_free_rnti(srslte_pdsch_t *q, + uint16_t rnti); + +SRSLTE_API int srslte_pdsch_cfg(srslte_pdsch_cfg_t *cfg, + srslte_cell_t cell, + srslte_ra_dl_grant_t *grant, + uint32_t cfi, + uint32_t sf_idx, + int rvidx); + +SRSLTE_API int srslte_pdsch_cfg_mimo(srslte_pdsch_cfg_t *cfg, + srslte_cell_t cell, + srslte_ra_dl_grant_t *grant, + uint32_t cfi, + uint32_t sf_idx, + int rvidx[SRSLTE_MAX_CODEWORDS], + srslte_mimo_type_t mimo_type, + uint32_t pmi); + +SRSLTE_API int srslte_pdsch_encode(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffers[SRSLTE_MAX_CODEWORDS], + uint8_t *data[SRSLTE_MAX_CODEWORDS], + uint16_t rnti, + cf_t *sf_symbols[SRSLTE_MAX_PORTS]); + +SRSLTE_API int srslte_pdsch_decode(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffers[SRSLTE_MAX_CODEWORDS], + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint16_t rnti, + uint8_t *data[SRSLTE_MAX_CODEWORDS], + bool acks[SRSLTE_MAX_CODEWORDS]); + +SRSLTE_API int srslte_pdsch_pmi_select(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t nof_ce, + uint32_t pmi[SRSLTE_MAX_LAYERS], + float sinr[SRSLTE_MAX_LAYERS][SRSLTE_MAX_CODEBOOKS]); + +SRSLTE_API int srslte_pdsch_cn_compute(srslte_pdsch_t *q, + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + uint32_t nof_ce, + float *cn); + +SRSLTE_API void srslte_pdsch_set_max_noi(srslte_pdsch_t *q, + uint32_t max_iter); + +SRSLTE_API float srslte_pdsch_last_noi(srslte_pdsch_t *q); + +SRSLTE_API int srslte_pdsch_enable_coworker(srslte_pdsch_t *q); + +SRSLTE_API uint32_t srslte_pdsch_last_noi_cw(srslte_pdsch_t *q, + uint32_t cw_idx); + +#endif // SRSLTE_PDSCH_H diff --git a/lib/include/srslte/phy/phch/pdsch_cfg.h b/lib/include/srslte/phy/phch/pdsch_cfg.h new file mode 100644 index 0000000..3e48490 --- /dev/null +++ b/lib/include/srslte/phy/phch/pdsch_cfg.h @@ -0,0 +1,62 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pdsch_cfg.h + * + * Description: Physical downlink shared channel configuration + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.4 + *****************************************************************************/ + +#ifndef SRSLTE_PDSCH_CFG_H +#define SRSLTE_PDSCH_CFG_H + +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/fec/softbuffer.h" +#include "srslte/phy/fec/cbsegm.h" + +/* 3GPP 36.213 Table 5.2-1: The cell-specific ratio rho_B / rho_A for 1, 2, or 4 cell specific antenna ports */ +static const float pdsch_cfg_cell_specific_ratio_table[2][4] = + { /* One antenna port */ {1.0f / 1.0f, 4.0f / 5.0f, 3.0f / 5.0f, 2.0f / 5.0f}, + /* Two or more antenna port */ {5.0f / 4.0f, 1.0f / 1.0f, 3.0f / 4.0f, 1.0f / 2.0f} + }; + + +typedef struct SRSLTE_API { + srslte_cbsegm_t cb_segm[SRSLTE_MAX_CODEWORDS]; + srslte_ra_dl_grant_t grant; + srslte_ra_nbits_t nbits[SRSLTE_MAX_CODEWORDS]; + uint32_t rv[SRSLTE_MAX_CODEWORDS]; + uint32_t sf_idx; + uint32_t nof_layers; + uint32_t codebook_idx; + srslte_mimo_type_t mimo_type; + bool tb_cw_swap; +} srslte_pdsch_cfg_t; + +#endif // SRSLTE_PDSCH_CFG_H + diff --git a/lib/include/srslte/phy/phch/phich.h b/lib/include/srslte/phy/phch/phich.h new file mode 100644 index 0000000..d131dc6 --- /dev/null +++ b/lib/include/srslte/phy/phch/phich.h @@ -0,0 +1,127 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: phich.h + * + * Description: Physical Hybrid ARQ indicator channel. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.9 + *****************************************************************************/ + +#ifndef SRSLTE_PHICH_H +#define SRSLTE_PHICH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "regs.h" + + + +#define SRSLTE_PHICH_NORM_NSEQUENCES 8 +#define SRSLTE_PHICH_EXT_NSEQUENCES 4 +#define SRSLTE_PHICH_NBITS 3 + +#define SRSLTE_PHICH_NORM_MSYMB SRSLTE_PHICH_NBITS * 4 +#define SRSLTE_PHICH_EXT_MSYMB SRSLTE_PHICH_NBITS * 2 +#define SRSLTE_PHICH_MAX_NSYMB SRSLTE_PHICH_NORM_MSYMB +#define SRSLTE_PHICH_NORM_C 1 +#define SRSLTE_PHICH_EXT_C 2 +#define SRSLTE_PHICH_NORM_NSF 4 +#define SRSLTE_PHICH_EXT_NSF 2 + +/* phich object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + + uint32_t nof_rx_antennas; + + /* handler to REGs resource mapper */ + srslte_regs_t *regs; + + /* buffers */ + cf_t ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS][SRSLTE_PHICH_MAX_NSYMB]; + cf_t sf_symbols[SRSLTE_MAX_PORTS][SRSLTE_PHICH_MAX_NSYMB]; + cf_t x[SRSLTE_MAX_PORTS][SRSLTE_PHICH_MAX_NSYMB]; + cf_t d[SRSLTE_PHICH_MAX_NSYMB]; + cf_t d0[SRSLTE_PHICH_MAX_NSYMB]; + cf_t z[SRSLTE_PHICH_NBITS]; + + /* bit message */ + uint8_t data[SRSLTE_PHICH_NBITS]; + float data_rx[SRSLTE_PHICH_NBITS]; + + /* tx & rx objects */ + srslte_modem_table_t mod; + srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME]; + +} srslte_phich_t; + +SRSLTE_API int srslte_phich_init(srslte_phich_t *q, + uint32_t nof_rx_antennas); + +SRSLTE_API void srslte_phich_free(srslte_phich_t *q); + +SRSLTE_API int srslte_phich_set_cell(srslte_phich_t *q, + srslte_regs_t *regs, + srslte_cell_t cell); + +SRSLTE_API void srslte_phich_calc(srslte_phich_t *q, + uint32_t n_prb_lowest, + uint32_t n_dmrs, + uint32_t *ngroup, + uint32_t *nseq); + +SRSLTE_API int srslte_phich_decode(srslte_phich_t *q, + cf_t *slot_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint32_t ngroup, + uint32_t nseq, + uint32_t nsubframe, + uint8_t *ack, + float *distance); + +SRSLTE_API int srslte_phich_encode(srslte_phich_t *q, + uint8_t ack, + uint32_t ngroup, + uint32_t nseq, + uint32_t nsubframe, + cf_t *slot_symbols[SRSLTE_MAX_PORTS]); + +SRSLTE_API void srslte_phich_reset(srslte_phich_t *q, + cf_t *slot_symbols[SRSLTE_MAX_PORTS]); + +SRSLTE_API uint32_t srslte_phich_ngroups(srslte_phich_t *q); + +SRSLTE_API uint32_t srslte_phich_nsf(srslte_phich_t *q); + +#endif // SRSLTE_PHICH_H diff --git a/lib/include/srslte/phy/phch/pmch.h b/lib/include/srslte/phy/phch/pmch.h new file mode 100644 index 0000000..22f1458 --- /dev/null +++ b/lib/include/srslte/phy/phch/pmch.h @@ -0,0 +1,152 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pmch.h + * + * Description: Physical multicast channel + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.5 + *****************************************************************************/ + +#ifndef SRSLTE_PMCH_H +#define SRSLTE_PMCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/sch.h" +#include "srslte/phy/common/sequence.h" + +typedef struct { + srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME]; +} srslte_pmch_seq_t; + +typedef struct SRSLTE_API { + srslte_cbsegm_t cb_segm; + srslte_ra_dl_grant_t grant; + srslte_ra_nbits_t nbits[SRSLTE_MAX_CODEWORDS]; + uint32_t sf_idx; +} srslte_pmch_cfg_t; + +/* PMCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + + uint32_t nof_rx_antennas; + + uint32_t max_re; + + /* buffers */ + // void buffers are shared for tx and rx + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + cf_t *symbols[SRSLTE_MAX_PORTS]; + cf_t *x[SRSLTE_MAX_PORTS]; + cf_t *d; + void *e; + + /* tx & rx objects */ + srslte_modem_table_t mod[4]; + + // This is to generate the scrambling seq for multiple MBSFN Area IDs + srslte_pmch_seq_t **seqs; + + srslte_sch_t dl_sch; + +} srslte_pmch_t; + + +SRSLTE_API int srslte_pmch_init(srslte_pmch_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_pmch_init_multi(srslte_pmch_t *q, + uint32_t max_prb, + uint32_t nof_rx_antennas); + +SRSLTE_API void srslte_pmch_free(srslte_pmch_t *q); + +SRSLTE_API int srslte_pmch_set_cell(srslte_pmch_t *q, srslte_cell_t cell); + +SRSLTE_API int srslte_pmch_set_area_id(srslte_pmch_t *q, uint16_t area_id); + +SRSLTE_API void srslte_pmch_free_area_id(srslte_pmch_t *q, uint16_t area_id); + + + +SRSLTE_API int srslte_pmch_get(srslte_pmch_t *q, cf_t *sf_symbols, cf_t *symbols, uint32_t lstart); + +SRSLTE_API int srslte_pmch_put(srslte_pmch_t *q, cf_t *symbols, cf_t *sf_symbols, uint32_t lstart); + +SRSLTE_API int srslte_pmch_cp(srslte_pmch_t *q, cf_t *input, cf_t *output, uint32_t lstart_grant, bool put); + + + +SRSLTE_API float srslte_pmch_coderate(uint32_t tbs, + uint32_t nof_re); + + +SRSLTE_API int srslte_pmch_cfg(srslte_pdsch_cfg_t *cfg, + srslte_cell_t cell, + srslte_ra_dl_grant_t *grant, + uint32_t cfi, + uint32_t sf_idx); + +SRSLTE_API int srslte_pmch_encode(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + uint16_t area_id, + cf_t *sf_symbols[SRSLTE_MAX_PORTS]); + +SRSLTE_API int srslte_pmch_decode(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols, + cf_t *ce[SRSLTE_MAX_PORTS], + float noise_estimate, + uint16_t area_id, + uint8_t *data); + +SRSLTE_API int srslte_pmch_decode_multi(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, + uint16_t area_id, + uint8_t *data); + +SRSLTE_API float srslte_pmch_average_noi(srslte_pmch_t *q); + +SRSLTE_API uint32_t srslte_pmch_last_noi(srslte_pmch_t *q); + +#endif // SRSLTE_PMCH_H diff --git a/lib/include/srslte/phy/phch/prach.h b/lib/include/srslte/phy/phch/prach.h new file mode 100644 index 0000000..27c59d3 --- /dev/null +++ b/lib/include/srslte/phy/phch/prach.h @@ -0,0 +1,181 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: prach.h + * + * Description: Physical random access channel. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.7 + *****************************************************************************/ + +#ifndef SRSLTE_PRACH_H +#define SRSLTE_PRACH_H + +#include +#include +#include +#include +#include "srslte/config.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/common/phy_common.h" + + +#define SRSLTE_PRACH_MAX_LEN (2*24576+21024) // Maximum Tcp + Tseq + +/** Generation and detection of RACH signals for uplink. + * Currently only supports preamble formats 0-3. + * Does not currently support high speed flag. + * Based on 3GPP TS 36.211 version 10.7.0 Release 10. + */ + +typedef struct SRSLTE_API { + // Parameters from higher layers (extracted from SIB2) + uint32_t config_idx; + uint32_t f; // preamble format + uint32_t rsi; // rootSequenceIndex + bool hs; // highSpeedFlag + uint32_t zczc; // zeroCorrelationZoneConfig + uint32_t N_ifft_ul; // IFFT size for uplink + uint32_t N_ifft_prach; // IFFT size for PRACH generation + + uint32_t max_N_ifft_ul; + + // Working parameters + uint32_t N_zc; // PRACH sequence length + uint32_t N_cs; // Cyclic shift size + uint32_t N_seq; // Preamble length + float T_seq; // Preamble length in seconds + float T_tot; // Total sequence length in seconds + uint32_t N_cp; // Cyclic prefix length + + // Generated tables + cf_t seqs[64][839]; // Our set of 64 preamble sequences + cf_t dft_seqs[64][839]; // DFT-precoded seqs + uint32_t root_seqs_idx[64]; // Indices of root seqs in seqs table + uint32_t N_roots; // Number of root sequences used in this configuration + + // Containers + cf_t *ifft_in; + cf_t *ifft_out; + cf_t *prach_bins; + cf_t *corr_spec; + float *corr; + + // PRACH IFFT + srslte_dft_plan_t fft; + srslte_dft_plan_t ifft; + + // ZC-sequence FFT and IFFT + srslte_dft_plan_t zc_fft; + srslte_dft_plan_t zc_ifft; + + cf_t *signal_fft; + float detect_factor; + + uint32_t deadzone; + float peak_values[65]; + uint32_t peak_offsets[65]; + +} srslte_prach_t; + +typedef struct SRSLTE_API { + int nof_sf; + uint32_t sf[5]; +} srslte_prach_sf_config_t; + +typedef enum SRSLTE_API { + SRSLTE_PRACH_SFN_EVEN = 0, + SRSLTE_PRACH_SFN_ANY, +} srslte_prach_sfn_t; + +typedef struct { + uint32_t config_idx; + uint32_t root_seq_idx; + uint32_t zero_corr_zone; + uint32_t freq_offset; + bool hs_flag; +} srslte_prach_cfg_t; + + +SRSLTE_API uint32_t srslte_prach_get_preamble_format(uint32_t config_idx); + +SRSLTE_API srslte_prach_sfn_t srslte_prach_get_sfn(uint32_t config_idx); + +SRSLTE_API bool srslte_prach_tti_opportunity(srslte_prach_t *p, + uint32_t current_tti, + int allowed_subframe); + +SRSLTE_API bool srslte_prach_tti_opportunity_config(uint32_t config_idx, + uint32_t current_tti, + int allowed_subframe); + +SRSLTE_API void srslte_prach_sf_config(uint32_t config_idx, + srslte_prach_sf_config_t *sf_config); + +SRSLTE_API int srslte_prach_init(srslte_prach_t *p, + uint32_t max_N_ifft_ul); + +SRSLTE_API int srslte_prach_set_cell(srslte_prach_t *p, + uint32_t N_ifft_ul, + uint32_t config_idx, + uint32_t root_seq_index, + bool high_speed_flag, + uint32_t zero_corr_zone_config); + +SRSLTE_API int srslte_prach_init_cfg(srslte_prach_t* p, + srslte_prach_cfg_t* cfg, + uint32_t nof_prb); + +SRSLTE_API int srslte_prach_gen(srslte_prach_t *p, + uint32_t seq_index, + uint32_t freq_offset, + cf_t *signal); + +SRSLTE_API int srslte_prach_detect(srslte_prach_t *p, + uint32_t freq_offset, + cf_t *signal, + uint32_t sig_len, + uint32_t *indices, + uint32_t *ind_len); + +SRSLTE_API int srslte_prach_detect_offset(srslte_prach_t *p, + uint32_t freq_offset, + cf_t *signal, + uint32_t sig_len, + uint32_t *indices, + float *t_offsets, + float *peak_to_avg, + uint32_t *ind_len); + +SRSLTE_API void srslte_prach_set_detect_factor(srslte_prach_t *p, + float factor); + +SRSLTE_API int srslte_prach_free(srslte_prach_t *p); + +SRSLTE_API int srslte_prach_print_seqs(srslte_prach_t *p); + +#endif // SRSLTE_PRACH_H diff --git a/lib/include/srslte/phy/phch/pucch.h b/lib/include/srslte/phy/phch/pucch.h new file mode 100644 index 0000000..fbb185c --- /dev/null +++ b/lib/include/srslte/phy/phch/pucch.h @@ -0,0 +1,214 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pucch.h + * + * Description: Physical uplink control channel. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.4 + *****************************************************************************/ + +#ifndef SRSLTE_PUCCH_H +#define SRSLTE_PUCCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/phch/cqi.h" +#include "srslte/phy/phch/uci.h" + +#define SRSLTE_PUCCH_N_SEQ 12 +#define SRSLTE_PUCCH2_NOF_BITS SRSLTE_UCI_CQI_CODED_PUCCH_B +#define SRSLTE_PUCCH_MAX_BITS SRSLTE_CQI_MAX_BITS +#define SRSLTE_PUCCH_MAX_SYMBOLS 128 + +typedef enum SRSLTE_API { + SRSLTE_PUCCH_FORMAT_1 = 0, + SRSLTE_PUCCH_FORMAT_1A, + SRSLTE_PUCCH_FORMAT_1B, + SRSLTE_PUCCH_FORMAT_2, + SRSLTE_PUCCH_FORMAT_2A, + SRSLTE_PUCCH_FORMAT_2B, + SRSLTE_PUCCH_FORMAT_ERROR, +} srslte_pucch_format_t; + +typedef struct SRSLTE_API { + bool sps_enabled; + uint32_t tpc_for_pucch; + uint32_t N_pucch_1; + uint32_t n_pucch_1[4]; // 4 n_pucch resources specified by RRC + uint32_t n_pucch_2; + uint32_t n_pucch_sr; +}srslte_pucch_sched_t; + +typedef struct SRSLTE_API { + // Common configuration + uint32_t delta_pucch_shift; + uint32_t n_rb_2; + uint32_t N_cs; + uint32_t n1_pucch_an; + + // SRS configuration + bool srs_configured; + uint32_t srs_cs_subf_cfg; + bool srs_simul_ack; +} srslte_pucch_cfg_t; + +typedef struct { + srslte_sequence_t seq_f2[SRSLTE_NSUBFRAMES_X_FRAME]; + bool sequence_generated; +} srslte_pucch_user_t; + +/* PUCCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + srslte_pucch_cfg_t pucch_cfg; + srslte_modem_table_t mod; + + srslte_uci_cqi_pucch_t cqi; + + srslte_pucch_user_t **users; + + uint8_t bits_scram[SRSLTE_PUCCH_MAX_BITS]; + cf_t d[SRSLTE_PUCCH_MAX_BITS/2]; + uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB]; + uint32_t f_gh[SRSLTE_NSLOTS_X_FRAME]; + float tmp_arg[SRSLTE_PUCCH_N_SEQ]; + + cf_t *z; + cf_t *z_tmp; + cf_t *ce; + + bool shortened; + bool group_hopping_en; + + float threshold_format1; + float last_corr; + uint32_t last_n_prb; + uint32_t last_n_pucch; + +}srslte_pucch_t; + + +SRSLTE_API int srslte_pucch_init(srslte_pucch_t *q); + +SRSLTE_API void srslte_pucch_free(srslte_pucch_t *q); + +SRSLTE_API int srslte_pucch_set_cell(srslte_pucch_t *q, + srslte_cell_t cell); + +SRSLTE_API bool srslte_pucch_set_cfg(srslte_pucch_t* q, + srslte_pucch_cfg_t* cfg, + bool group_hopping_en); + +SRSLTE_API void srslte_pucch_set_threshold(srslte_pucch_t *q, + float format1_threshold); + +SRSLTE_API int srslte_pucch_set_crnti(srslte_pucch_t *q, + uint16_t c_rnti); + +SRSLTE_API void srslte_pucch_clear_rnti(srslte_pucch_t *q, + uint16_t rnti); + +SRSLTE_API uint32_t srslte_pucch_nof_symbols(srslte_pucch_cfg_t *cfg, + srslte_pucch_format_t format, + bool shortened); + +SRSLTE_API float srslte_pucch_get_last_corr(srslte_pucch_t* q); + +SRSLTE_API int srslte_pucch_encode(srslte_pucch_t *q, + srslte_pucch_format_t format, + uint32_t n_pucch, // n_pucch_1 or n_pucch_2 depending on format + uint32_t sf_idx, + uint16_t rnti, + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], + cf_t *sf_symbols); + +SRSLTE_API int srslte_pucch_decode(srslte_pucch_t *q, + srslte_pucch_format_t format, + uint32_t n_pucch, // n_pucch_1 or n_pucch_2 depending on format + uint32_t sf_idx, + uint16_t rnti, + cf_t *sf_symbols, + cf_t *ce, + float noise_estimate, + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], + uint32_t nof_bits); + +SRSLTE_API float srslte_pucch_alpha_format1(uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB], + srslte_pucch_cfg_t *cfg, + uint32_t n_pucch, + srslte_cp_t cp, + bool is_dmrs, + uint32_t ns, + uint32_t l, + uint32_t *n_oc, + uint32_t *n_prime_ns); + +SRSLTE_API float srslte_pucch_alpha_format2(uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB], + srslte_pucch_cfg_t *cfg, + uint32_t n_pucch, + uint32_t ns, + uint32_t l); + +SRSLTE_API uint32_t srslte_pucch_m(srslte_pucch_cfg_t *cfg, + srslte_pucch_format_t format, + uint32_t n_pucch, + srslte_cp_t cp); + +SRSLTE_API srslte_pucch_format_t srslte_pucch_get_format(srslte_uci_data_t *uci_data, + srslte_cp_t cp); + +SRSLTE_API uint32_t srslte_pucch_get_npucch(uint32_t n_cce, + srslte_pucch_format_t format, + bool has_scheduling_request, + srslte_pucch_sched_t *pucch_sched); + +SRSLTE_API uint32_t srslte_pucch_n_prb(srslte_pucch_cfg_t *cfg, + srslte_pucch_format_t format, + uint32_t n_pucch, + uint32_t nof_prb, + srslte_cp_t cp, + uint32_t ns); + +SRSLTE_API int srslte_pucch_n_cs_cell(srslte_cell_t cell, + uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB]); + +SRSLTE_API int srslte_pucch_format2ab_mod_bits(srslte_pucch_format_t format, + uint8_t bits[2], + cf_t *d_10); + +SRSLTE_API bool srslte_pucch_cfg_isvalid(srslte_pucch_cfg_t *cfg, + uint32_t nof_prb); + +SRSLTE_API bool srslte_n_pucch_isvalid(srslte_pucch_t *q, + uint32_t n_pucch); + +SRSLTE_API void srslte_pucch_cfg_default(srslte_pucch_cfg_t *cfg); + +#endif // SRSLTE_PUCCH_H diff --git a/lib/include/srslte/phy/phch/pusch.h b/lib/include/srslte/phy/phch/pusch.h new file mode 100644 index 0000000..a5b8f04 --- /dev/null +++ b/lib/include/srslte/phy/phch/pusch.h @@ -0,0 +1,152 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pusch.h + * + * Description: Physical uplink shared channel. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.3 + *****************************************************************************/ + +#ifndef SRSLTE_PUSCH_H +#define SRSLTE_PUSCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/sch.h" +#include "srslte/phy/phch/pusch_cfg.h" +#include "srslte/phy/dft/dft_precoding.h" +#include "srslte/phy/ch_estimation/refsignal_ul.h" + +#define SRSLTE_PUSCH_MAX_TDEC_ITERS 5 + +typedef struct { + enum { + SRSLTE_PUSCH_HOP_MODE_INTER_SF = 1, + SRSLTE_PUSCH_HOP_MODE_INTRA_SF = 0 + } hop_mode; + uint32_t hopping_offset; + uint32_t n_sb; +} srslte_pusch_hopping_cfg_t; + +typedef struct { + srslte_sequence_t seq[SRSLTE_NSUBFRAMES_X_FRAME]; + uint32_t cell_id; + bool sequence_generated; +} srslte_pusch_user_t; + +/* PUSCH object */ +typedef struct SRSLTE_API { + srslte_cell_t cell; + + bool is_ue; + uint16_t ue_rnti; + uint32_t max_re; + + srslte_dft_precoding_t dft_precoding; + + /* buffers */ + // void buffers are shared for tx and rx + cf_t *ce; + cf_t *z; + cf_t *d; + + void *q; + void *g; + + /* tx & rx objects */ + srslte_modem_table_t mod[4]; + srslte_sequence_t seq_type2_fo; + + // This is to generate the scrambling seq for multiple CRNTIs + srslte_pusch_user_t **users; + srslte_sequence_t tmp_seq; + + srslte_sch_t ul_sch; + bool shortened; + +}srslte_pusch_t; + + +SRSLTE_API int srslte_pusch_init_ue(srslte_pusch_t *q, + uint32_t max_prb); + +SRSLTE_API int srslte_pusch_init_enb(srslte_pusch_t *q, + uint32_t max_prb); + +SRSLTE_API void srslte_pusch_free(srslte_pusch_t *q); + +SRSLTE_API int srslte_pusch_set_cell(srslte_pusch_t *q, + srslte_cell_t cell); + +SRSLTE_API int srslte_pusch_cfg(srslte_pusch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_ra_ul_grant_t *grant, + srslte_uci_cfg_t *uci_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg, + uint32_t tti, + uint32_t rv_idx, + uint32_t current_tx_nb); + +SRSLTE_API int srslte_pusch_set_rnti(srslte_pusch_t *q, + uint16_t rnti); + +SRSLTE_API void srslte_pusch_free_rnti(srslte_pusch_t *q, + uint16_t rnti); + +SRSLTE_API int srslte_pusch_encode(srslte_pusch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + srslte_uci_data_t uci_data, + uint16_t rnti, + cf_t *sf_symbols); + +SRSLTE_API int srslte_pusch_decode(srslte_pusch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols, + cf_t *ce, + float noise_estimate, + uint16_t rnti, + uint8_t *data, + srslte_cqi_value_t *cqi_value, + srslte_uci_data_t *uci_data); + +SRSLTE_API float srslte_pusch_average_noi(srslte_pusch_t *q); + +SRSLTE_API uint32_t srslte_pusch_last_noi(srslte_pusch_t *q); + +#endif // SRSLTE_PUSCH_H diff --git a/lib/include/srslte/phy/phch/pusch_cfg.h b/lib/include/srslte/phy/phch/pusch_cfg.h new file mode 100644 index 0000000..ed3239c --- /dev/null +++ b/lib/include/srslte/phy/phch/pusch_cfg.h @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pdsch_cfg.h + * + * Description: Physical downlink shared channel configuration + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.4 + *****************************************************************************/ + +#ifndef SRSLTE_PUSCH_CFG_H +#define SRSLTE_PUSCH_CFG_H + +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/fec/softbuffer.h" +#include "srslte/phy/fec/cbsegm.h" + +typedef struct SRSLTE_API { + uint32_t I_offset_cqi; + uint32_t I_offset_ri; + uint32_t I_offset_ack; +} srslte_uci_cfg_t; + +typedef struct SRSLTE_API { + srslte_cbsegm_t cb_segm; + srslte_ra_ul_grant_t grant; + srslte_ra_nbits_t nbits; + srslte_uci_cfg_t uci_cfg; + uint32_t rv; + uint32_t sf_idx; + uint32_t tti; + srslte_cp_t cp; + uint32_t last_O_cqi; +} srslte_pusch_cfg_t; + +#endif // SRSLTE_PUSCH_CFG_H + diff --git a/lib/include/srslte/phy/phch/ra.h b/lib/include/srslte/phy/phch/ra.h new file mode 100644 index 0000000..085f62b --- /dev/null +++ b/lib/include/srslte/phy/phch/ra.h @@ -0,0 +1,303 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ra.h + * + * Description: Structures and utility functions for DL/UL resource allocation. + * Convert an UL/DL unpacket DCI message to a resource allocation + * + * Reference: 3GPP TS 36.213 version 10.0.1 Release 10 + *****************************************************************************/ + +#ifndef SRSLTE_RA_H +#define SRSLTE_RA_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +/************************************************** + * Common structures used for Resource Allocation + **************************************************/ + +typedef struct SRSLTE_API { + srslte_mod_t mod; + int tbs; + uint32_t idx; +} srslte_ra_mcs_t; + + +/* Structure that gives the number of encoded bits and RE for a UL/DL grant */ +typedef struct { + uint32_t lstart; + uint32_t nof_symb; + uint32_t nof_bits; + uint32_t nof_re; +} srslte_ra_nbits_t; + +typedef enum SRSLTE_API { + SRSLTE_RA_ALLOC_TYPE0 = 0, + SRSLTE_RA_ALLOC_TYPE1 = 1, + SRSLTE_RA_ALLOC_TYPE2 = 2 +} srslte_ra_type_t; + +typedef struct SRSLTE_API { + uint32_t rbg_bitmask; +} srslte_ra_type0_t; + +typedef struct SRSLTE_API { + uint32_t vrb_bitmask; + uint32_t rbg_subset; + bool shift; +} srslte_ra_type1_t; + +typedef struct SRSLTE_API { + uint32_t riv; // if L_crb==0, DCI message packer will take this value directly + uint32_t L_crb; + uint32_t RB_start; + enum { + SRSLTE_RA_TYPE2_NPRB1A_2 = 0, SRSLTE_RA_TYPE2_NPRB1A_3 = 1 + } n_prb1a; + enum { + SRSLTE_RA_TYPE2_NG1 = 0, SRSLTE_RA_TYPE2_NG2 = 1 + } n_gap; + enum { + SRSLTE_RA_TYPE2_LOC = 0, SRSLTE_RA_TYPE2_DIST = 1 + } mode; +} srslte_ra_type2_t; + + + + +/************************************************** + * Structures used for Downlink Resource Allocation + **************************************************/ + +typedef struct SRSLTE_API { + bool prb_idx[2][SRSLTE_MAX_PRB]; + uint32_t nof_prb; + uint32_t Qm[SRSLTE_MAX_CODEWORDS]; + srslte_ra_mcs_t mcs[SRSLTE_MAX_CODEWORDS]; + srslte_sf_t sf_type; + bool tb_en[SRSLTE_MAX_CODEWORDS]; + uint32_t pinfo; + bool tb_cw_swap; +} srslte_ra_dl_grant_t; + +#define SRSLTE_RA_DL_GRANT_NOF_TB(G) ((((G)->tb_en[0])?1:0)+(((G)->tb_en[1])?1:0)) + +/** Unpacked DCI message for DL grant */ +typedef struct SRSLTE_API { + + srslte_ra_type_t alloc_type; + union { + srslte_ra_type0_t type0_alloc; + srslte_ra_type1_t type1_alloc; + srslte_ra_type2_t type2_alloc; + }; + + uint32_t harq_process; + uint32_t mcs_idx; + int rv_idx; + bool ndi; + uint32_t mcs_idx_1; + int rv_idx_1; + bool ndi_1; + + bool tb_cw_swap; + bool sram_id; + uint8_t pinfo; + bool pconf; + bool power_offset; + + uint8_t tpc_pucch; + + bool tb_en[2]; + + bool is_ra_order; + uint32_t ra_preamble; + uint32_t ra_mask_idx; + + bool dci_is_1a; + bool dci_is_1c; +} srslte_ra_dl_dci_t; + + + + + +/************************************************** + * Structures used for Uplink Resource Allocation + **************************************************/ + +typedef struct SRSLTE_API { + uint32_t n_prb[2]; + uint32_t n_prb_tilde[2]; + uint32_t L_prb; + uint32_t freq_hopping; + uint32_t M_sc; + uint32_t M_sc_init; + uint32_t Qm; + srslte_ra_mcs_t mcs; + uint32_t ncs_dmrs; +} srslte_ra_ul_grant_t; + +/** Unpacked DCI Format0 message */ +typedef struct SRSLTE_API { + /* 36.213 Table 8.4-2: SRSLTE_RA_PUSCH_HOP_HALF is 0 for < 10 Mhz and 10 for > 10 Mhz. + * SRSLTE_RA_PUSCH_HOP_QUART is 00 for > 10 Mhz and SRSLTE_RA_PUSCH_HOP_QUART_NEG is 01 for > 10 Mhz. + */ + enum { + SRSLTE_RA_PUSCH_HOP_DISABLED = -1, + SRSLTE_RA_PUSCH_HOP_QUART = 0, + SRSLTE_RA_PUSCH_HOP_QUART_NEG = 1, + SRSLTE_RA_PUSCH_HOP_HALF = 2, + SRSLTE_RA_PUSCH_HOP_TYPE2 = 3 + } freq_hop_fl; + + srslte_ra_ul_grant_t prb_alloc; + + srslte_ra_type2_t type2_alloc; + uint32_t mcs_idx; + uint32_t rv_idx; + uint32_t n_dmrs; + bool ndi; + bool cqi_request; + uint8_t tpc_pusch; + +} srslte_ra_ul_dci_t; + +typedef union { + srslte_ra_ul_grant_t ul; + srslte_ra_dl_grant_t dl; +} srslte_phy_grant_t; + +#define SRSLTE_PHY_GRANT_LEN sizeof(srslte_phy_grant_t) + + +/************************************************** + * Functions + **************************************************/ + +SRSLTE_API int srslte_ra_dl_dci_to_grant(srslte_ra_dl_dci_t *dci, + uint32_t nof_prb, + uint16_t rnti, + srslte_ra_dl_grant_t *grant); + +SRSLTE_API void srslte_ra_dl_grant_to_nbits(srslte_ra_dl_grant_t *grant, + uint32_t cfi, + srslte_cell_t cell, + uint32_t sf_idx, + srslte_ra_nbits_t nbits[SRSLTE_MAX_CODEWORDS]); + +SRSLTE_API uint32_t srslte_ra_dl_approx_nof_re(srslte_cell_t cell, + uint32_t nof_prb, + uint32_t nof_ctrl_symbols); + +SRSLTE_API uint32_t srslte_ra_dl_grant_nof_re(srslte_ra_dl_grant_t *grant, + srslte_cell_t cell, + uint32_t sf_idx, + uint32_t nof_ctrl_symbols); + +SRSLTE_API int srslte_ra_ul_dci_to_grant(srslte_ra_ul_dci_t *dci, + uint32_t nof_prb, + uint32_t n_rb_ho, + srslte_ra_ul_grant_t *grant); + +SRSLTE_API void srslte_ra_ul_grant_to_nbits(srslte_ra_ul_grant_t *grant, + srslte_cp_t cp, + uint32_t N_srs, + srslte_ra_nbits_t *nbits); + +SRSLTE_API int srslte_ra_ul_dci_to_grant_prb_allocation(srslte_ra_ul_dci_t *dci, + srslte_ra_ul_grant_t *grant, + uint32_t n_rb_ho, + uint32_t nof_prb); + +SRSLTE_API int srslte_ra_dl_dci_to_grant_prb_allocation(srslte_ra_dl_dci_t *dci, + srslte_ra_dl_grant_t *grant, + uint32_t nof_prb); + +SRSLTE_API int srslte_ra_tbs_idx_from_mcs(uint32_t mcs); + +SRSLTE_API srslte_mod_t srslte_ra_mod_from_mcs(uint32_t mcs); + +SRSLTE_API int srslte_ra_mcs_from_tbs_idx(uint32_t tbs_idx); + +SRSLTE_API int srslte_ra_tbs_from_idx(uint32_t tbs_idx, + uint32_t n_prb); + +SRSLTE_API int srslte_ra_tbs_to_table_idx(uint32_t tbs, + uint32_t n_prb); + +SRSLTE_API uint32_t srslte_ra_type0_P(uint32_t nof_prb); + +SRSLTE_API uint32_t srslte_ra_type2_to_riv(uint32_t L_crb, + uint32_t RB_start, + uint32_t nof_prb); + +SRSLTE_API void srslte_ra_type2_from_riv(uint32_t riv, + uint32_t *L_crb, + uint32_t *RB_start, + uint32_t nof_prb, + uint32_t nof_vrb); + +SRSLTE_API uint32_t srslte_ra_type2_n_vrb_dl(uint32_t nof_prb, + bool ngap_is_1); + +SRSLTE_API uint32_t srslte_ra_type2_n_rb_step(uint32_t nof_prb); + +SRSLTE_API uint32_t srslte_ra_type2_ngap(uint32_t nof_prb, + bool ngap_is_1); + +SRSLTE_API uint32_t srslte_ra_type1_N_rb(uint32_t nof_prb); + +SRSLTE_API void srslte_ra_pdsch_fprint(FILE *f, + srslte_ra_dl_dci_t *ra, + uint32_t nof_prb); + +SRSLTE_API void srslte_ra_dl_grant_fprint(FILE *f, + srslte_ra_dl_grant_t *grant); + +SRSLTE_API void srslte_ra_prb_fprint(FILE *f, + srslte_ra_dl_grant_t *grant); + +SRSLTE_API void srslte_ra_pusch_fprint(FILE *f, + srslte_ra_ul_dci_t *ra, + uint32_t nof_prb); + +SRSLTE_API void srslte_ra_ul_grant_fprint(FILE *f, + srslte_ra_ul_grant_t *grant); + +SRSLTE_API int srslte_dl_fill_ra_mcs_pmch(srslte_ra_mcs_t *mcs, uint32_t nprb); + +SRSLTE_API int srslte_dl_fill_ra_mcs(srslte_ra_mcs_t *mcs, uint32_t nprb); + + +#endif // SRSLTE_RA_H \ No newline at end of file diff --git a/lib/include/srslte/phy/phch/regs.h b/lib/include/srslte/phy/phch/regs.h new file mode 100644 index 0000000..91e6f33 --- /dev/null +++ b/lib/include/srslte/phy/phch/regs.h @@ -0,0 +1,141 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: regs.h + * + * Description: Resource element mapping functions. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 + *****************************************************************************/ + +#ifndef SRSLTE_REGS_H +#define SRSLTE_REGS_H + +#include +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" + +#define REGS_PHICH_NSYM 12 +#define REGS_PHICH_REGS_X_GROUP 3 + +#define REGS_PCFICH_NSYM 16 +#define REGS_PCFICH_NREGS 4 + +#define REGS_RE_X_REG 4 + + +typedef struct SRSLTE_API { + uint32_t k[4]; + uint32_t k0; + uint32_t l; + bool assigned; +}srslte_regs_reg_t; + +typedef struct SRSLTE_API { + uint32_t nof_regs; + srslte_regs_reg_t **regs; +}srslte_regs_ch_t; + +typedef struct SRSLTE_API { + srslte_cell_t cell; + uint32_t max_ctrl_symbols; + uint32_t ngroups_phich; + + srslte_phich_resources_t phich_res; + srslte_phich_length_t phich_len; + + srslte_regs_ch_t pcfich; + srslte_regs_ch_t *phich; // there are several phich + srslte_regs_ch_t pdcch[3]; /* PDCCH indexing, permutation and interleaving is computed for + the three possible CFI value */ + + uint32_t nof_regs; + srslte_regs_reg_t *regs; +}srslte_regs_t; + +SRSLTE_API int srslte_regs_init(srslte_regs_t *h, + srslte_cell_t cell); + +SRSLTE_API void srslte_regs_free(srslte_regs_t *h); + + +SRSLTE_API int srslte_regs_pdcch_nregs(srslte_regs_t *h, + uint32_t cfi); + +SRSLTE_API int srslte_regs_pdcch_ncce(srslte_regs_t *h, + uint32_t cfi); + +SRSLTE_API int srslte_regs_pcfich_put(srslte_regs_t *h, + cf_t symbols[REGS_PCFICH_NSYM], + cf_t *slot_symbols); + +SRSLTE_API int srslte_regs_pcfich_get(srslte_regs_t *h, + cf_t *slot_symbols, + cf_t symbols[REGS_PCFICH_NSYM]); + +SRSLTE_API uint32_t srslte_regs_phich_nregs(srslte_regs_t *h); +SRSLTE_API int srslte_regs_phich_add(srslte_regs_t *h, + cf_t symbols[REGS_PHICH_NSYM], + uint32_t ngroup, + cf_t *slot_symbols); + +SRSLTE_API int srslte_regs_phich_get(srslte_regs_t *h, + cf_t *slot_symbols, + cf_t symbols[REGS_PHICH_NSYM], + uint32_t ngroup); + +SRSLTE_API uint32_t srslte_regs_phich_ngroups(srslte_regs_t *h); +SRSLTE_API int srslte_regs_phich_reset(srslte_regs_t *h, + cf_t *slot_symbols); + +SRSLTE_API int srslte_regs_pdcch_put(srslte_regs_t *h, + uint32_t cfi, + cf_t *d, + cf_t *slot_symbols); + +SRSLTE_API int srslte_regs_pdcch_put_offset(srslte_regs_t *h, + uint32_t cfi, + cf_t *d, + cf_t *slot_symbols, + uint32_t start_reg, + uint32_t nof_regs); + +SRSLTE_API int srslte_regs_pdcch_get(srslte_regs_t *h, + uint32_t cfi, + cf_t *slot_symbols, + cf_t *d); + +SRSLTE_API int srslte_regs_pdcch_get_offset(srslte_regs_t *h, + uint32_t cfi, + cf_t *slot_symbols, + cf_t *d, + uint32_t start_reg, + uint32_t nof_regs); + +#endif // SRSLTE_REGS_H + + diff --git a/lib/include/srslte/phy/phch/sch.h b/lib/include/srslte/phy/phch/sch.h new file mode 100644 index 0000000..003e9b7 --- /dev/null +++ b/lib/include/srslte/phy/phch/sch.h @@ -0,0 +1,162 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: sch.h + * + * Description: Common UL and DL shared channel encode/decode functions. + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 + *****************************************************************************/ + +#ifndef SRSLTE_SCH_H +#define SRSLTE_SCH_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/fec/rm_turbo.h" +#include "srslte/phy/fec/turbocoder.h" +#include "srslte/phy/fec/turbodecoder.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/phch/pdsch_cfg.h" +#include "srslte/phy/phch/pusch_cfg.h" +#include "srslte/phy/phch/uci.h" + +#ifndef SRSLTE_RX_NULL +#define SRSLTE_RX_NULL 10000 +#endif + +#ifndef SRSLTE_TX_NULL +#define SRSLTE_TX_NULL 100 +#endif + +/* DL-SCH AND UL-SCH common functions */ +typedef struct SRSLTE_API { + + uint32_t max_iterations; + uint32_t nof_iterations; + + /* buffers */ + uint8_t *cb_in; + uint8_t *parity_bits; + void *e; + uint8_t *temp_g_bits; + uint32_t *ul_interleaver; + srslte_uci_bit_t ack_ri_bits[12*288]; + uint32_t nof_ri_ack_bits; + + srslte_tcod_t encoder; + srslte_tdec_t decoder; + srslte_crc_t crc_tb; + srslte_crc_t crc_cb; + + srslte_uci_cqi_pusch_t uci_cqi; + +} srslte_sch_t; +#include "srslte/phy/phch/pmch.h" + +SRSLTE_API int srslte_sch_init(srslte_sch_t *q); + +SRSLTE_API void srslte_sch_free(srslte_sch_t *q); + + +SRSLTE_API void srslte_sch_set_max_noi(srslte_sch_t *q, + uint32_t max_iterations); + +SRSLTE_API uint32_t srslte_sch_last_noi(srslte_sch_t *q); + +SRSLTE_API int srslte_dlsch_encode(srslte_sch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + uint8_t *e_bits); + +SRSLTE_API int srslte_dlsch_encode2(srslte_sch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + uint8_t *e_bits, + int codeword_idx); + +SRSLTE_API int srslte_dlsch_decode(srslte_sch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + int16_t *e_bits, + uint8_t *data); + +SRSLTE_API int srslte_dlsch_decode2(srslte_sch_t *q, + srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + int16_t *e_bits, + uint8_t *data, + int codeword_idx); + +SRSLTE_API int srslte_ulsch_encode(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + uint8_t *g_bits, + uint8_t *q_bits); + +SRSLTE_API int srslte_ulsch_uci_encode(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, + srslte_uci_data_t uci_data, + uint8_t *g_bits, + uint8_t *q_bits); + +SRSLTE_API int srslte_ulsch_decode(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + int16_t *q_bits, + int16_t *g_bits, + uint8_t *data); + +SRSLTE_API int srslte_ulsch_uci_decode(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + int16_t *q_bits, + int16_t *g_bits, + uint8_t *data, + srslte_uci_data_t *uci_data); + +SRSLTE_API int srslte_ulsch_uci_decode_ri_ack(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_softbuffer_rx_t *softbuffer, + int16_t *q_bits, + uint8_t *c_seq, + srslte_uci_data_t *uci_data); + +SRSLTE_API float srslte_sch_beta_cqi(uint32_t I_cqi); + +SRSLTE_API uint32_t srslte_sch_find_Ioffset_ack(float beta); + +SRSLTE_API uint32_t srslte_sch_find_Ioffset_cqi(float beta); + +SRSLTE_API uint32_t srslte_sch_find_Ioffset_ri(float beta); + +#endif // SRSLTE_SCH_H diff --git a/lib/include/srslte/phy/phch/uci.h b/lib/include/srslte/phy/phch/uci.h new file mode 100644 index 0000000..4f211ff --- /dev/null +++ b/lib/include/srslte/phy/phch/uci.h @@ -0,0 +1,156 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: uci.h + * + * Description: Uplink control information. Only 1-bit ACK for UCI on PUSCCH is supported + * + * Reference: 3GPP TS 36.212 version 10.0.0 Release 10 Sec. 5.2.3, 5.2.4 + *****************************************************************************/ + +#ifndef SRSLTE_UCI_H +#define SRSLTE_UCI_H + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/pusch_cfg.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/viterbi.h" +#include "srslte/phy/phch/cqi.h" + +#define SRSLTE_UCI_MAX_CQI_LEN_PUSCH 512 +#define SRSLTE_UCI_MAX_CQI_LEN_PUCCH 13 +#define SRSLTE_UCI_CQI_CODED_PUCCH_B 20 + +typedef struct SRSLTE_API { + srslte_crc_t crc; + srslte_viterbi_t viterbi; + uint8_t tmp_cqi[SRSLTE_UCI_MAX_CQI_LEN_PUSCH]; + uint8_t encoded_cqi[3*SRSLTE_UCI_MAX_CQI_LEN_PUSCH]; + int16_t encoded_cqi_s[3*SRSLTE_UCI_MAX_CQI_LEN_PUSCH]; + uint8_t *cqi_table[11]; + int16_t *cqi_table_s[11]; +} srslte_uci_cqi_pusch_t; + +typedef struct SRSLTE_API { + uint8_t **cqi_table; + int16_t **cqi_table_s; +} srslte_uci_cqi_pucch_t; + +typedef struct SRSLTE_API { + uint8_t uci_cqi[SRSLTE_CQI_MAX_BITS]; + uint32_t uci_cqi_len; + uint8_t uci_ri; // Only 1-bit supported for RI + uint32_t uci_ri_len; + uint8_t uci_ack; // 1st codeword bit for HARQ-ACK + uint8_t uci_ack_2; // 2st codeword bit for HARQ-ACK + uint32_t uci_ack_len; + bool ri_periodic_report; + bool scheduling_request; + bool channel_selection; + bool cqi_ack; +} srslte_uci_data_t; + +typedef enum { + UCI_BIT_1 = 0, UCI_BIT_0, UCI_BIT_REPETITION, UCI_BIT_PLACEHOLDER +} srslte_uci_bit_type_t; + +typedef struct { + uint32_t position; + srslte_uci_bit_type_t type; +} srslte_uci_bit_t; + +SRSLTE_API void srslte_uci_cqi_pucch_init(srslte_uci_cqi_pucch_t *q); + +SRSLTE_API void srslte_uci_cqi_pucch_free(srslte_uci_cqi_pucch_t *q); + +SRSLTE_API int srslte_uci_encode_cqi_pucch(uint8_t *cqi_data, + uint32_t cqi_len, + uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]); + +SRSLTE_API int srslte_uci_encode_cqi_pucch_from_table(srslte_uci_cqi_pucch_t *q, + uint8_t *cqi_data, + uint32_t cqi_len, + uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]); + +SRSLTE_API int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t *q, + int16_t b_bits[32], // aligned for simd + uint8_t *cqi_data, + uint32_t cqi_len); + + +SRSLTE_API int srslte_uci_cqi_init(srslte_uci_cqi_pusch_t *q); + +SRSLTE_API void srslte_uci_cqi_free(srslte_uci_cqi_pusch_t *q); + +SRSLTE_API int srslte_uci_encode_cqi_pusch(srslte_uci_cqi_pusch_t *q, + srslte_pusch_cfg_t *cfg, + uint8_t *cqi_data, + uint32_t cqi_len, + float beta, + uint32_t Q_prime_ri, + uint8_t *q_bits); + +SRSLTE_API int srslte_uci_decode_cqi_pusch(srslte_uci_cqi_pusch_t *q, + srslte_pusch_cfg_t *cfg, + int16_t *q_bits, + float beta, + uint32_t Q_prime_ri, + uint32_t cqi_len, + uint8_t *cqi_data, + bool *cqi_ack); + +SRSLTE_API int srslte_uci_encode_ack(srslte_pusch_cfg_t *cfg, + uint8_t acks[2], + uint32_t nof_acks, + uint32_t O_cqi, + float beta, + uint32_t H_prime_total, + srslte_uci_bit_t *ri_bits); + +SRSLTE_API int srslte_uci_encode_ack_ri(srslte_pusch_cfg_t *cfg, + uint8_t *data, + uint32_t data_len, + uint32_t O_cqi, + float beta, + uint32_t H_prime_total, + srslte_uci_bit_t *ri_bits, + bool is_ri); + +SRSLTE_API int srslte_uci_decode_ack_ri(srslte_pusch_cfg_t *cfg, + int16_t *q_bits, + uint8_t *c_seq, + float beta, + uint32_t H_prime_total, + uint32_t O_cqi, + srslte_uci_bit_t *ack_ri_bits, + uint8_t data[2], + uint32_t nof_bits, + bool is_ri); + + +#endif // SRSLTE_UCI_H diff --git a/lib/include/srslte/phy/resampling/decim.h b/lib/include/srslte/phy/resampling/decim.h new file mode 100644 index 0000000..1394c64 --- /dev/null +++ b/lib/include/srslte/phy/resampling/decim.h @@ -0,0 +1,51 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: decim.h + * + * Description: Integer linear decimation + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_DECIM_H +#define SRSLTE_DECIM_H + +#include "srslte/config.h" + + +SRSLTE_API void srslte_decim_c(cf_t *input, + cf_t *output, + int M, + int len); + +SRSLTE_API void srslte_decim_f(float *input, + float *output, + int M, + int len); + +#endif // SRSLTE_DECIM_H diff --git a/lib/include/srslte/phy/resampling/interp.h b/lib/include/srslte/phy/resampling/interp.h new file mode 100644 index 0000000..8cdb821 --- /dev/null +++ b/lib/include/srslte/phy/resampling/interp.h @@ -0,0 +1,136 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: interp.h + * + * Description: Linear and vector interpolation + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_INTERP_H +#define SRSLTE_INTERP_H + +#include +#include + +#include "srslte/config.h" + + +/************* STATIC LINEAR INTERPOLATION FUNCTIONS */ + +SRSLTE_API cf_t srslte_interp_linear_onesample(cf_t input0, + cf_t input1); + +SRSLTE_API cf_t srslte_interp_linear_onesample_cabs(cf_t input0, + cf_t input1); + +SRSLTE_API void srslte_interp_linear_offset_cabs(cf_t *input, + cf_t *output, + uint32_t M, + uint32_t len, + uint32_t off_st, + uint32_t off_end); + +SRSLTE_API void srslte_interp_linear_f(float *input, + float *output, + uint32_t M, + uint32_t len); + + + +/* Interpolation between vectors */ + +typedef struct { + cf_t *diff_vec; + uint32_t vector_len; + uint32_t max_vector_len; +} srslte_interp_linsrslte_vec_t; + +SRSLTE_API int srslte_interp_linear_vector_init(srslte_interp_linsrslte_vec_t *q, + uint32_t vector_len); + +SRSLTE_API void srslte_interp_linear_vector_free(srslte_interp_linsrslte_vec_t *q); + +SRSLTE_API int srslte_interp_linear_vector_resize(srslte_interp_linsrslte_vec_t *q, + uint32_t vector_len); + +SRSLTE_API void srslte_interp_linear_vector(srslte_interp_linsrslte_vec_t *q, + cf_t *in0, + cf_t *in1, + cf_t *between, + uint32_t in1_in0_d, + uint32_t M); + +SRSLTE_API void srslte_interp_linear_vector2(srslte_interp_linsrslte_vec_t *q, + cf_t *in0, + cf_t *in1, + cf_t *start, + cf_t *between, + uint32_t in1_in0_d, + uint32_t M); + +SRSLTE_API void srslte_interp_linear_vector3(srslte_interp_linsrslte_vec_t *q, + cf_t *in0, + cf_t *in1, + cf_t *start, + cf_t *between, + uint32_t in1_in0_d, + uint32_t M, + bool to_right, + uint32_t len); + +/* Interpolation within a vector */ + +typedef struct { + cf_t *diff_vec; + cf_t *diff_vec2; + float *ramp; + uint32_t vector_len; + uint32_t M; + uint32_t max_vector_len; + uint32_t max_M; +} srslte_interp_lin_t; + +SRSLTE_API int srslte_interp_linear_init(srslte_interp_lin_t *q, + uint32_t vector_len, + uint32_t M); + +SRSLTE_API void srslte_interp_linear_free(srslte_interp_lin_t *q); + +SRSLTE_API int srslte_interp_linear_resize(srslte_interp_lin_t *q, + uint32_t vector_len, + uint32_t M); + +SRSLTE_API void srslte_interp_linear_offset(srslte_interp_lin_t *q, + cf_t *input, + cf_t *output, + uint32_t off_st, + uint32_t off_end); + + +#endif // SRSLTE_INTERP_H diff --git a/lib/include/srslte/phy/resampling/resample_arb.h b/lib/include/srslte/phy/resampling/resample_arb.h new file mode 100644 index 0000000..76d8730 --- /dev/null +++ b/lib/include/srslte/phy/resampling/resample_arb.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: resample_arb.h + * + * Description: Arbitrary rate resampler using a polyphase filter bank + * implementation. + * + * Reference: Multirate Signal Processing for Communication Systems + * fredric j. harris + *****************************************************************************/ + +#ifndef SRSLTE_RESAMPLE_ARB_H +#define SRSLTE_RESAMPLE_ARB_H + +#include +#include +#include + +#include "srslte/config.h" + + +#define SRSLTE_RESAMPLE_ARB_N_35 35 +#define SRSLTE_RESAMPLE_ARB_N 32 // Polyphase filter rows +#define SRSLTE_RESAMPLE_ARB_M 8 // Polyphase filter columns + +typedef struct SRSLTE_API { + float rate; // Resample rate + float step; // Step increment through filter + float acc; // Index into filter + bool interpolate; + cf_t reg[SRSLTE_RESAMPLE_ARB_M]; // Our window of samples + +} srslte_resample_arb_t; + +SRSLTE_API void srslte_resample_arb_init(srslte_resample_arb_t *q, + float rate, bool interpolate); + +SRSLTE_API int srslte_resample_arb_compute(srslte_resample_arb_t *q, + cf_t *input, + cf_t *output, + int n_in); + +#endif // SRSLTE_RESAMPLE_ARB_ diff --git a/lib/include/srslte/phy/rf/rf.h b/lib/include/srslte/phy/rf/rf.h new file mode 100644 index 0000000..f093d1d --- /dev/null +++ b/lib/include/srslte/phy/rf/rf.h @@ -0,0 +1,227 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_RF_H +#define SRSLTE_RF_H + +#include +#include +#include +#include + +#include "srslte/config.h" + +typedef struct { + void *handler; + void *dev; + + // The following variables are for threaded RX gain control + pthread_t thread_gain; + pthread_cond_t cond; + pthread_mutex_t mutex; + double cur_rx_gain; + double new_rx_gain; + bool tx_gain_same_rx; + float tx_rx_gain_offset; +} srslte_rf_t; + +typedef struct { + float dc_gain; + float dc_phase; + float iq_i; + float iq_q; +} srslte_rf_cal_t; + + +typedef struct { + double min_tx_gain; + double max_tx_gain; + double min_rx_gain; + double max_rx_gain; +} srslte_rf_info_t; + +typedef struct { + enum { + SRSLTE_RF_ERROR_LATE, + SRSLTE_RF_ERROR_UNDERFLOW, + SRSLTE_RF_ERROR_OVERFLOW, + SRSLTE_RF_ERROR_RX, + SRSLTE_RF_ERROR_OTHER + } type; + int opt; + const char *msg; +} srslte_rf_error_t; + +typedef void (*srslte_rf_error_handler_t)(srslte_rf_error_t error); + +SRSLTE_API int srslte_rf_open(srslte_rf_t *h, + char *args); + +SRSLTE_API int srslte_rf_open_multi(srslte_rf_t *h, + char *args, + uint32_t nof_channels); + +SRSLTE_API int srslte_rf_open_devname(srslte_rf_t *h, + char *devname, + char *args, + uint32_t nof_channels); + +SRSLTE_API const char *srslte_rf_name(srslte_rf_t *h); + +SRSLTE_API int srslte_rf_start_gain_thread(srslte_rf_t *rf, + bool tx_gain_same_rx); + +SRSLTE_API int srslte_rf_close(srslte_rf_t *h); + +SRSLTE_API void srslte_rf_set_tx_cal(srslte_rf_t *h, srslte_rf_cal_t *cal); + +SRSLTE_API void srslte_rf_set_rx_cal(srslte_rf_t *h, srslte_rf_cal_t *cal); + +SRSLTE_API int srslte_rf_start_rx_stream(srslte_rf_t *h, bool now); + +SRSLTE_API int srslte_rf_stop_rx_stream(srslte_rf_t *h); + +SRSLTE_API void srslte_rf_flush_buffer(srslte_rf_t *h); + +SRSLTE_API bool srslte_rf_has_rssi(srslte_rf_t *h); + +SRSLTE_API float srslte_rf_get_rssi(srslte_rf_t *h); + +SRSLTE_API bool srslte_rf_rx_wait_lo_locked(srslte_rf_t *h); + +SRSLTE_API void srslte_rf_set_master_clock_rate(srslte_rf_t *h, + double rate); + +SRSLTE_API bool srslte_rf_is_master_clock_dynamic(srslte_rf_t *h); + +SRSLTE_API double srslte_rf_set_rx_srate(srslte_rf_t *h, + double freq); + +SRSLTE_API double srslte_rf_set_rx_gain(srslte_rf_t *h, + double gain); + +SRSLTE_API void srslte_rf_set_tx_rx_gain_offset(srslte_rf_t *h, + double offset); + +SRSLTE_API double srslte_rf_set_rx_gain_th(srslte_rf_t *h, + double gain); + +SRSLTE_API double srslte_rf_get_rx_gain(srslte_rf_t *h); + +SRSLTE_API double srslte_rf_get_tx_gain(srslte_rf_t *h); + +SRSLTE_API srslte_rf_info_t *srslte_rf_get_info(srslte_rf_t *h); + +SRSLTE_API void srslte_rf_suppress_stdout(srslte_rf_t *h); + +SRSLTE_API void srslte_rf_register_error_handler(srslte_rf_t *h, + srslte_rf_error_handler_t error_handler); + +SRSLTE_API double srslte_rf_set_rx_freq(srslte_rf_t *h, + double freq); + +SRSLTE_API int srslte_rf_recv(srslte_rf_t *h, + void *data, + uint32_t nsamples, + bool blocking); + +SRSLTE_API int srslte_rf_recv_with_time(srslte_rf_t *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int srslte_rf_recv_with_time_multi(srslte_rf_t *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double srslte_rf_set_tx_srate(srslte_rf_t *h, + double freq); + +SRSLTE_API double srslte_rf_set_tx_gain(srslte_rf_t *h, + double gain); + +SRSLTE_API double srslte_rf_set_tx_freq(srslte_rf_t *h, + double freq); + +SRSLTE_API void srslte_rf_get_time(srslte_rf_t *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int srslte_rf_send(srslte_rf_t *h, + void *data, + uint32_t nsamples, + bool blocking); + +SRSLTE_API int srslte_rf_send2(srslte_rf_t *h, + void *data, + uint32_t nsamples, + bool blocking, + bool start_of_burst, + bool end_of_burst); + +SRSLTE_API int srslte_rf_send(srslte_rf_t *h, + void *data, + uint32_t nsamples, + bool blocking); + + +SRSLTE_API int srslte_rf_send_timed(srslte_rf_t *h, + void *data, + int nsamples, + time_t secs, + double frac_secs); + +SRSLTE_API int srslte_rf_send_timed2(srslte_rf_t *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSLTE_API int srslte_rf_send_timed_multi(srslte_rf_t *rf, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSLTE_API int srslte_rf_send_multi(srslte_rf_t *rf, + void *data[4], + int nsamples, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +#endif // SRSLTE_RF_H + diff --git a/lib/include/srslte/phy/rf/rf_utils.h b/lib/include/srslte/phy/rf/rf_utils.h new file mode 100644 index 0000000..4cfe164 --- /dev/null +++ b/lib/include/srslte/phy/rf/rf_utils.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_RF_UTILS_H +#define SRSLTE_RF_UTILS_H + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" + +typedef struct SRSLTE_API { + uint32_t max_frames_pbch; // timeout in number of 5ms frames for MIB decoding + uint32_t max_frames_pss; // timeout in number of 5ms frames for synchronization + uint32_t nof_valid_pss_frames; // number of required synchronized frames + float init_agc; // 0 or negative to disable AGC +} cell_search_cfg_t; + +SRSLTE_API int rf_rssi_scan(srslte_rf_t *rf, + float *freqs, + float *rssi, + int nof_bands, + double fs, + int nsamp); + +SRSLTE_API int rf_mib_decoder(srslte_rf_t *rf, + uint32_t nof_rx_antennas, + cell_search_cfg_t *config, + srslte_cell_t *cell, + float *cfo); + +SRSLTE_API int rf_cell_search(srslte_rf_t *rf, + uint32_t nof_rx_antennas, + cell_search_cfg_t *config, + int force_N_id_2, + srslte_cell_t *cell, + float *cfo); + +SRSLTE_API int rf_search_and_decode_mib(srslte_rf_t *rf, + uint32_t nof_rx_antennas, + cell_search_cfg_t *config, + int force_N_id_2, + srslte_cell_t *cell, + float *cfo); + +#endif // SRSLTE_RF_UTILS_H + diff --git a/lib/include/srslte/phy/scrambling/scrambling.h b/lib/include/srslte/phy/scrambling/scrambling.h new file mode 100644 index 0000000..c38bbe8 --- /dev/null +++ b/lib/include/srslte/phy/scrambling/scrambling.h @@ -0,0 +1,79 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: scrambling.h + * + * Description: Generic scrambling functions used by UL and DL channels. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 5.3.1, 6.3.1 + *****************************************************************************/ + +#ifndef SRSLTE_SCRAMBLING_H +#define SRSLTE_SCRAMBLING_H + +#include "srslte/config.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/common/phy_common.h" + +/* Scrambling has no state */ +SRSLTE_API void srslte_scrambling_b(srslte_sequence_t *s, + uint8_t *data); + +SRSLTE_API void srslte_scrambling_b_offset(srslte_sequence_t *s, + uint8_t *data, + int offset, + int len); + +SRSLTE_API void srslte_scrambling_bytes(srslte_sequence_t *s, + uint8_t *data, + int len); + +SRSLTE_API void srslte_scrambling_f(srslte_sequence_t *s, + float *data); + +SRSLTE_API void srslte_scrambling_f_offset(srslte_sequence_t *s, + float *data, + int offset, + int len); + +SRSLTE_API void srslte_scrambling_s(srslte_sequence_t *s, + short *data); + +SRSLTE_API void srslte_scrambling_s_offset(srslte_sequence_t *s, + short *data, + int offset, + int len); + +SRSLTE_API void srslte_scrambling_c(srslte_sequence_t *s, + cf_t *data); + +SRSLTE_API void srslte_scrambling_c_offset(srslte_sequence_t *s, + cf_t *data, + int offset, + int len); + +#endif // SRSLTE_SCRAMBLING_H diff --git a/lib/include/srslte/phy/sync/cfo.h b/lib/include/srslte/phy/sync/cfo.h new file mode 100644 index 0000000..b28d57d --- /dev/null +++ b/lib/include/srslte/phy/sync/cfo.h @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: cfo.h + * + * Description: Carrier frequency offset correction using complex exponentials. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_CFO_H +#define SRSLTE_CFO_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/utils/cexptab.h" + +#define SRSLTE_CFO_CEXPTAB_SIZE 4096 + +typedef struct SRSLTE_API { + float last_freq; + float tol; + int nsamples; + int max_samples; + srslte_cexptab_t tab; + cf_t *cur_cexp; +}srslte_cfo_t; + +SRSLTE_API int srslte_cfo_init(srslte_cfo_t *h, + uint32_t nsamples); + +SRSLTE_API void srslte_cfo_free(srslte_cfo_t *h); + +SRSLTE_API int srslte_cfo_resize(srslte_cfo_t *h, + uint32_t samples); + +SRSLTE_API void srslte_cfo_set_tol(srslte_cfo_t *h, + float tol); + +SRSLTE_API void srslte_cfo_correct(srslte_cfo_t *h, + const cf_t *input, + cf_t *output, + float freq); + +#endif // SRSLTE_CFO_H diff --git a/lib/include/srslte/phy/sync/cp.h b/lib/include/srslte/phy/sync/cp.h new file mode 100644 index 0000000..511efb4 --- /dev/null +++ b/lib/include/srslte/phy/sync/cp.h @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_CP_H +#define SRSLTE_CP_H + +#include +#include + +#include "srslte/config.h" + +typedef struct { + cf_t *corr; + uint32_t symbol_sz; + uint32_t max_symbol_sz; +} srslte_cp_synch_t; + +SRSLTE_API int srslte_cp_synch_init(srslte_cp_synch_t *q, + uint32_t symbol_sz); + +SRSLTE_API void srslte_cp_synch_free(srslte_cp_synch_t *q); + +SRSLTE_API int srslte_cp_synch_resize(srslte_cp_synch_t *q, + uint32_t symbol_sz); + +SRSLTE_API uint32_t srslte_cp_synch(srslte_cp_synch_t *q, + const cf_t *input, + uint32_t max_offset, + uint32_t nof_symbols, + uint32_t cp_len); + +SRSLTE_API cf_t srslte_cp_synch_corr_output(srslte_cp_synch_t *q, + uint32_t offset); + +#endif // SRSLTE_CP_H diff --git a/lib/include/srslte/phy/sync/pss.h b/lib/include/srslte/phy/sync/pss.h new file mode 100644 index 0000000..520e2bc --- /dev/null +++ b/lib/include/srslte/phy/sync/pss.h @@ -0,0 +1,179 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: pss.h + * + * Description: Primary synchronization signal (PSS) generation and detection. + * + * The srslte_pss_t object provides functions for fast + * computation of the crosscorrelation between the PSS and received + * signal and CFO estimation. Also, the function srslte_pss_tperiodic() + * is designed to be called periodically every subframe, taking + * care of the correct data alignment with respect to the PSS sequence. + * + * The object is designed to work with signals sampled at 1.92 Mhz + * centered at the carrier frequency. Thus, downsampling is required + * if the signal is sampled at higher frequencies. + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.11.1 + *****************************************************************************/ + +#ifndef SRSLTE_PSS_H +#define SRSLTE_PSS_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/convolution.h" +#include "srslte/phy/utils/filter.h" + +#define CONVOLUTION_FFT + +#define SRSLTE_PSS_LEN 62 +#define SRSLTE_PSS_RE (6*12) + + +/* PSS processing options */ + +#define SRSLTE_PSS_ACCUMULATE_ABS // If enabled, accumulates the correlation absolute value on consecutive calls to srslte_pss_find_pss + +#define SRSLTE_PSS_RETURN_PSR // If enabled returns peak to side-lobe ratio, otherwise returns absolute peak value + + +/* Low-level API */ +typedef struct SRSLTE_API { + +#ifdef CONVOLUTION_FFT + srslte_conv_fft_cc_t conv_fft; + srslte_filt_cc_t filter; + +#endif + int decimate; + + uint32_t max_frame_size; + uint32_t max_fft_size; + + uint32_t frame_size; + uint32_t N_id_2; + uint32_t fft_size; + cf_t *pss_signal_freq_full[3]; + + cf_t *pss_signal_time[3]; + cf_t *pss_signal_time_scale[3]; + + cf_t pss_signal_freq[3][SRSLTE_PSS_LEN]; // One sequence for each N_id_2 + cf_t *tmp_input; + cf_t *conv_output; + float *conv_output_abs; + float ema_alpha; + float *conv_output_avg; + float peak_value; + + bool filter_pss_enable; + srslte_dft_plan_t dftp_input; + srslte_dft_plan_t idftp_input; + cf_t tmp_fft[SRSLTE_SYMBOL_SZ_MAX]; + cf_t tmp_fft2[SRSLTE_SYMBOL_SZ_MAX]; + + cf_t tmp_ce[SRSLTE_PSS_LEN]; + + bool chest_on_filter; + +}srslte_pss_t; + +typedef enum { PSS_TX, PSS_RX } pss_direction_t; + +/* Basic functionality */ +SRSLTE_API int srslte_pss_init_fft(srslte_pss_t *q, + uint32_t frame_size, + uint32_t fft_size); + +SRSLTE_API int srslte_pss_init_fft_offset(srslte_pss_t *q, + uint32_t frame_size, + uint32_t fft_size, + int cfo_i); + +SRSLTE_API int srslte_pss_init_fft_offset_decim(srslte_pss_t *q, + uint32_t frame_size, + uint32_t fft_size, + int cfo_i, + int decimate); + +SRSLTE_API int srslte_pss_resize(srslte_pss_t *q, uint32_t frame_size, + uint32_t fft_size, + int offset); + +SRSLTE_API int srslte_pss_init(srslte_pss_t *q, + uint32_t frame_size); + +SRSLTE_API void srslte_pss_free(srslte_pss_t *q); + +SRSLTE_API void srslte_pss_reset(srslte_pss_t *q); + +SRSLTE_API void srslte_pss_filter_enable(srslte_pss_t *q, + bool enable); + +SRSLTE_API void srslte_pss_sic(srslte_pss_t *q, + cf_t *input); + +SRSLTE_API void srslte_pss_filter(srslte_pss_t *q, + const cf_t *input, + cf_t *output); + +SRSLTE_API int srslte_pss_generate(cf_t *signal, + uint32_t N_id_2); + +SRSLTE_API void srslte_pss_get_slot(cf_t *slot, + cf_t *pss_signal, + uint32_t nof_prb, + srslte_cp_t cp); + +SRSLTE_API void srslte_pss_put_slot(cf_t *pss_signal, + cf_t *slot, + uint32_t nof_prb, + srslte_cp_t cp); + +SRSLTE_API void srslte_pss_set_ema_alpha(srslte_pss_t *q, + float alpha); + +SRSLTE_API int srslte_pss_set_N_id_2(srslte_pss_t *q, + uint32_t N_id_2); + +SRSLTE_API int srslte_pss_find_pss(srslte_pss_t *q, + const cf_t *input, + float *corr_peak_value); + +SRSLTE_API int srslte_pss_chest(srslte_pss_t *q, + const cf_t *input, + cf_t ce[SRSLTE_PSS_LEN]); + +SRSLTE_API float srslte_pss_cfo_compute(srslte_pss_t* q, + const cf_t *pss_recv); + +#endif // SRSLTE_PSS_H diff --git a/lib/include/srslte/phy/sync/sfo.h b/lib/include/srslte/phy/sync/sfo.h new file mode 100644 index 0000000..a42ced4 --- /dev/null +++ b/lib/include/srslte/phy/sync/sfo.h @@ -0,0 +1,49 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: sfo.h + * + * Description: Sampling frequency offset estimation. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_SFO_H +#define SRSLTE_SFO_H + +#include "srslte/config.h" + +SRSLTE_API float srslte_sfo_estimate(int *t0, + int len, + float period); + +SRSLTE_API float srslte_sfo_estimate_period(int *t0, + int *t, + int len, + float period); + +#endif // SRSLTE_SFO_H diff --git a/lib/include/srslte/phy/sync/sss.h b/lib/include/srslte/phy/sync/sss.h new file mode 100644 index 0000000..a0062d6 --- /dev/null +++ b/lib/include/srslte/phy/sync/sss.h @@ -0,0 +1,157 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: sss.h + * + * Description: Secondary synchronization signal (SSS) generation and detection. + * + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.11.2 + *****************************************************************************/ + +#ifndef SRSLTE_SSS_H +#define SRSLTE_SSS_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" + + +#define SRSLTE_SSS_N 31 +#define SRSLTE_SSS_LEN 2*SRSLTE_SSS_N + +typedef struct SRSLTE_API { + int z1[SRSLTE_SSS_N][SRSLTE_SSS_N]; + int c[2][SRSLTE_SSS_N]; + int s[SRSLTE_SSS_N][SRSLTE_SSS_N]; +}srslte_sss_tables_t; + +/* Allocate 32 complex to make it multiple of 32-byte AVX instructions alignment requirement. + * Should use srslte_vec_malloc() to make it platform agnostic. + */ +typedef struct SRSLTE_API { + float z1[SRSLTE_SSS_N][SRSLTE_SSS_N]; + float c[2][SRSLTE_SSS_N]; + float s[SRSLTE_SSS_N][SRSLTE_SSS_N]; + float sd[SRSLTE_SSS_N][SRSLTE_SSS_N-1]; +}srslte_sss_fc_tables_t; + + +/* Low-level API */ +typedef struct SRSLTE_API { + + srslte_dft_plan_t dftp_input; + + uint32_t fft_size; + uint32_t max_fft_size; + + float corr_peak_threshold; + uint32_t symbol_sz; + uint32_t subframe_sz; + uint32_t N_id_2; + + uint32_t N_id_1_table[30][30]; + srslte_sss_fc_tables_t fc_tables[3]; // one for each N_id_2 + + float corr_output_m0[SRSLTE_SSS_N]; + float corr_output_m1[SRSLTE_SSS_N]; + +}srslte_sss_t; + + +/* Basic functionality */ +SRSLTE_API int srslte_sss_init(srslte_sss_t *q, + uint32_t fft_size); + +SRSLTE_API int srslte_sss_resize(srslte_sss_t *q, + uint32_t fft_size); + +SRSLTE_API void srslte_sss_free(srslte_sss_t *q); + +SRSLTE_API void srslte_sss_generate(float *signal0, + float *signal5, + uint32_t cell_id); + +SRSLTE_API void srslte_sss_put_slot(float *sss, + cf_t *symbol, + uint32_t nof_prb, + srslte_cp_t cp); + +SRSLTE_API int srslte_sss_set_N_id_2(srslte_sss_t *q, + uint32_t N_id_2); + +SRSLTE_API int srslte_sss_m0m1_partial(srslte_sss_t *q, + const cf_t *input, + uint32_t M, + cf_t ce[2*SRSLTE_SSS_N], + uint32_t *m0, + float *m0_value, + uint32_t *m1, + float *m1_value); + +SRSLTE_API int srslte_sss_m0m1_diff_coh(srslte_sss_t *q, + const cf_t *input, + cf_t ce[2*SRSLTE_SSS_N], + uint32_t *m0, + float *m0_value, + uint32_t *m1, + float *m1_value); + +SRSLTE_API int srslte_sss_m0m1_diff(srslte_sss_t *q, + const cf_t *input, + uint32_t *m0, + float *m0_value, + uint32_t *m1, + float *m1_value); + + +SRSLTE_API uint32_t srslte_sss_subframe(uint32_t m0, + uint32_t m1); + +SRSLTE_API int srslte_sss_N_id_1(srslte_sss_t *q, + uint32_t m0, + uint32_t m1); + +SRSLTE_API int srslte_sss_frame(srslte_sss_t *q, + cf_t *input, + uint32_t *subframe_idx, + uint32_t *N_id_1); + +SRSLTE_API void srslte_sss_set_threshold(srslte_sss_t *q, + float threshold); + +SRSLTE_API void srslte_sss_set_symbol_sz(srslte_sss_t *q, + uint32_t symbol_sz); + +SRSLTE_API void srslte_sss_set_subframe_sz(srslte_sss_t *q, + uint32_t subframe_sz); + +#endif // SRSLTE_SSS_H + diff --git a/lib/include/srslte/phy/sync/sync.h b/lib/include/srslte/phy/sync/sync.h new file mode 100644 index 0000000..f99bfc1 --- /dev/null +++ b/lib/include/srslte/phy/sync/sync.h @@ -0,0 +1,244 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: sync.h + * + * Description: Time and frequency synchronization using the PSS and SSS signals. + * + * The object is designed to work with signals sampled at 1.92 Mhz + * centered at the carrier frequency. Thus, downsampling is required + * if the signal is sampled at higher frequencies. + * + * Correlation peak is detected comparing the maximum at the output + * of the correlator with a threshold. The comparison accepts two + * modes: absolute value or peak-to-mean ratio, which are configured + * with the functions sync_pss_det_absolute() and sync_pss_det_peakmean(). + * + * + * Reference: 3GPP TS 36.211 version 10.0.0 Release 10 Sec. 6.11.1, 6.11.2 + *****************************************************************************/ + +#ifndef SRSLTE_SYNC_H +#define SRSLTE_SYNC_H + +#include +#include + +#include "srslte/config.h" +#include "srslte/phy/sync/pss.h" +#include "srslte/phy/sync/sss.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/sync/cp.h" + +#define SRSLTE_SYNC_FFT_SZ_MIN 64 +#define SRSLTE_SYNC_FFT_SZ_MAX 2048 + +typedef enum {SSS_DIFF=0, SSS_PARTIAL_3=2, SSS_FULL=1} sss_alg_t; + +typedef struct SRSLTE_API { + srslte_pss_t pss; + srslte_pss_t pss_i[2]; + srslte_sss_t sss; + srslte_cp_synch_t cp_synch; + cf_t *cfo_i_corr[2]; + int decimate; + float threshold; + float peak_value; + uint32_t N_id_2; + uint32_t N_id_1; + uint32_t sf_idx; + uint32_t fft_size; + uint32_t frame_size; + uint32_t max_offset; + uint32_t nof_symbols; + uint32_t cp_len; + float current_cfo_tol; + sss_alg_t sss_alg; + bool detect_cp; + bool sss_en; + srslte_cp_t cp; + uint32_t m0; + uint32_t m1; + float m0_value; + float m1_value; + float M_norm_avg; + float M_ext_avg; + cf_t *temp; + + uint32_t max_frame_size; + + + // variables for various CFO estimation methods + bool cfo_cp_enable; + bool cfo_pss_enable; + bool cfo_i_enable; + + bool cfo_cp_is_set; + bool cfo_pss_is_set; + bool cfo_i_initiated; + + float cfo_cp_mean; + float cfo_pss; + float cfo_pss_mean; + int cfo_i_value; + + float cfo_ema_alpha; + + uint32_t cfo_cp_nsymbols; + + srslte_cfo_t cfo_corr_frame; + srslte_cfo_t cfo_corr_symbol; + + bool sss_channel_equalize; + bool pss_filtering_enabled; + cf_t sss_filt[SRSLTE_SYMBOL_SZ_MAX]; + cf_t pss_filt[SRSLTE_SYMBOL_SZ_MAX]; + +}srslte_sync_t; + +typedef enum { + SRSLTE_SYNC_FOUND = 1, + SRSLTE_SYNC_FOUND_NOSPACE = 2, + SRSLTE_SYNC_NOFOUND = 0, + SRSLTE_SYNC_ERROR = -1 +} srslte_sync_find_ret_t; + + +SRSLTE_API int srslte_sync_init(srslte_sync_t *q, + uint32_t frame_size, + uint32_t max_offset, + uint32_t fft_size); + +SRSLTE_API int srslte_sync_init_decim(srslte_sync_t *q, + uint32_t frame_size, + uint32_t max_offset, + uint32_t fft_size, + int decimate); + + +SRSLTE_API void srslte_sync_free(srslte_sync_t *q); + +SRSLTE_API int srslte_sync_resize(srslte_sync_t *q, + uint32_t frame_size, + uint32_t max_offset, + uint32_t fft_size); + +SRSLTE_API void srslte_sync_reset(srslte_sync_t *q); + +/* Finds a correlation peak in the input signal around position find_offset */ +SRSLTE_API srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, + const cf_t *input, + uint32_t find_offset, + uint32_t *peak_position); + +/* Estimates the CP length */ +SRSLTE_API srslte_cp_t srslte_sync_detect_cp(srslte_sync_t *q, + const cf_t *input, + uint32_t peak_pos); + +/* Sets the threshold for peak comparison */ +SRSLTE_API void srslte_sync_set_threshold(srslte_sync_t *q, + float threshold); + +/* Gets the subframe idx (0 or 5) */ +SRSLTE_API uint32_t srslte_sync_get_sf_idx(srslte_sync_t *q); + +/* Gets the peak value */ +SRSLTE_API float srslte_sync_get_peak_value(srslte_sync_t *q); + +/* Choose SSS detection algorithm */ +SRSLTE_API void srslte_sync_set_sss_algorithm(srslte_sync_t *q, + sss_alg_t alg); + +/* Sets PSS exponential averaging alpha weight */ +SRSLTE_API void srslte_sync_set_em_alpha(srslte_sync_t *q, + float alpha); + +/* Sets the N_id_2 to search for */ +SRSLTE_API int srslte_sync_set_N_id_2(srslte_sync_t *q, + uint32_t N_id_2); + +/* Gets the Physical CellId from the last call to synch_run() */ +SRSLTE_API int srslte_sync_get_cell_id(srslte_sync_t *q); + +/* Enables/disables filtering of the central PRBs before PSS CFO estimation or SSS correlation*/ +SRSLTE_API void srslte_sync_set_pss_filt_enable(srslte_sync_t *q, + bool enable); + +SRSLTE_API void srslte_sync_set_sss_eq_enable(srslte_sync_t *q, + bool enable); + +/* Gets the CFO estimation from the last call to synch_run() */ +SRSLTE_API float srslte_sync_get_cfo(srslte_sync_t *q); + +/* Resets internal CFO state */ +SRSLTE_API void srslte_sync_cfo_reset(srslte_sync_t *q); + +/* Copies CFO internal state from another object to avoid long transients */ +SRSLTE_API void srslte_sync_copy_cfo(srslte_sync_t *q, + srslte_sync_t *src_obj); + +/* Enable different CFO estimation stages */ +SRSLTE_API void srslte_sync_set_cfo_i_enable(srslte_sync_t *q, + bool enable); +SRSLTE_API void srslte_sync_set_cfo_cp_enable(srslte_sync_t *q, + bool enable, + uint32_t nof_symbols); + +SRSLTE_API void srslte_sync_set_cfo_pss_enable(srslte_sync_t *q, + bool enable); + +/* Sets CFO correctors tolerance (in Hz) */ +SRSLTE_API void srslte_sync_set_cfo_tol(srslte_sync_t *q, + float tol); + +/* Sets the exponential moving average coefficient for CFO averaging */ +SRSLTE_API void srslte_sync_set_cfo_ema_alpha(srslte_sync_t *q, + float alpha); + + +/* Gets the CP length estimation from the last call to synch_run() */ +SRSLTE_API srslte_cp_t srslte_sync_get_cp(srslte_sync_t *q); + +/* Sets the CP length estimation (must do it if disabled) */ +SRSLTE_API void srslte_sync_set_cp(srslte_sync_t *q, + srslte_cp_t cp); + +/* Enables/Disables SSS detection */ +SRSLTE_API void srslte_sync_sss_en(srslte_sync_t *q, + bool enabled); + +SRSLTE_API srslte_pss_t* srslte_sync_get_cur_pss_obj(srslte_sync_t *q); + +SRSLTE_API bool srslte_sync_sss_detected(srslte_sync_t *q); + +/* Enables/Disables CP detection */ +SRSLTE_API void srslte_sync_cp_en(srslte_sync_t *q, + bool enabled); + +#endif // SRSLTE_SYNC_H + diff --git a/lib/include/srslte/phy/ue/ue_cell_search.h b/lib/include/srslte/phy/ue/ue_cell_search.h new file mode 100644 index 0000000..e99e916 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_cell_search.h @@ -0,0 +1,114 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_cell_search.h + * + * Description: Wrapper for the ue_sync object. + * + * This object is a wrapper to the ue_sync object. It receives + * several synchronized frames and obtains the most common cell_id + * and cp length. + * + * The I/O stream device sampling frequency must be set to 1.92 MHz + * (SRSLTE_CS_SAMP_FREQ constant) before calling to + * srslte_ue_cellsearch_scan() functions. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_UE_CELL_SEARCH_H +#define SRSLTE_UE_CELL_SEARCH_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/ue/ue_sync.h" +#include "srslte/phy/ue/ue_mib.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/dft/ofdm.h" + +#define SRSLTE_CS_NOF_PRB 6 +#define SRSLTE_CS_SAMP_FREQ 1920000.0 + +typedef struct SRSLTE_API { + uint32_t cell_id; + srslte_cp_t cp; + float peak; + float mode; + float psr; + float cfo; +} srslte_ue_cellsearch_result_t; + + +typedef struct SRSLTE_API { + srslte_ue_sync_t ue_sync; + + cf_t *sf_buffer[SRSLTE_MAX_PORTS]; + uint32_t nof_rx_antennas; + + uint32_t max_frames; + uint32_t nof_valid_frames; // number of 5 ms frames to scan + + uint32_t *mode_ntimes; + uint8_t *mode_counted; + + srslte_ue_cellsearch_result_t *candidates; +} srslte_ue_cellsearch_t; + +SRSLTE_API int srslte_ue_cellsearch_init(srslte_ue_cellsearch_t *q, + uint32_t max_frames_total, + int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*), + void *stream_handler); + +SRSLTE_API int srslte_ue_cellsearch_init_multi(srslte_ue_cellsearch_t *q, + uint32_t max_frames_total, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t,srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler); + +SRSLTE_API void srslte_ue_cellsearch_free(srslte_ue_cellsearch_t *q); + +SRSLTE_API int srslte_ue_cellsearch_scan_N_id_2(srslte_ue_cellsearch_t *q, + uint32_t N_id_2, + srslte_ue_cellsearch_result_t *found_cell); + +SRSLTE_API int srslte_ue_cellsearch_scan(srslte_ue_cellsearch_t * q, + srslte_ue_cellsearch_result_t found_cells[3], + uint32_t *max_N_id_2); + +SRSLTE_API int srslte_ue_cellsearch_set_nof_valid_frames(srslte_ue_cellsearch_t *q, + uint32_t nof_frames); + + + + + + +#endif // SRSLTE_UE_CELL_SEARCH_H + diff --git a/lib/include/srslte/phy/ue/ue_dl.h b/lib/include/srslte/phy/ue/ue_dl.h new file mode 100644 index 0000000..197a650 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_dl.h @@ -0,0 +1,257 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_dl.h + * + * Description: UE downlink object. + * + * This module is a frontend to all the downlink data and control + * channel processing modules. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_UE_DL_H +#define SRSLTE_UE_DL_H + +#include + +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/common/phy_common.h" + +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/pcfich.h" +#include "srslte/phy/phch/pdcch.h" +#include "srslte/phy/phch/pdsch.h" +#include "srslte/phy/phch/pmch.h" +#include "srslte/phy/phch/pdsch_cfg.h" +#include "srslte/phy/phch/phich.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/phch/regs.h" + +#include "srslte/phy/sync/cfo.h" + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#include "srslte/config.h" + + +#define MAX_CANDIDATES_UE 16 // From 36.213 Table 9.1.1-1 +#define MAX_CANDIDATES_COM 6 // From 36.213 Table 9.1.1-1 +#define MAX_CANDIDATES (MAX_CANDIDATES_UE + MAX_CANDIDATES_COM) + + +typedef struct { + srslte_dci_format_t format; + srslte_dci_location_t loc[MAX_CANDIDATES]; + uint32_t nof_locations; +} dci_blind_search_t; + +typedef struct SRSLTE_API { + srslte_pcfich_t pcfich; + srslte_pdcch_t pdcch; + srslte_pdsch_t pdsch; + srslte_pmch_t pmch; + srslte_phich_t phich; + srslte_regs_t regs; + srslte_ofdm_t fft[SRSLTE_MAX_PORTS]; + srslte_ofdm_t fft_mbsfn; + srslte_chest_dl_t chest; + + srslte_pdsch_cfg_t pdsch_cfg; + srslte_pdsch_cfg_t pmch_cfg; + srslte_softbuffer_rx_t *softbuffers[SRSLTE_MAX_CODEWORDS]; + srslte_ra_dl_dci_t dl_dci; + srslte_cell_t cell; + + uint32_t nof_rx_antennas; + + cf_t *sf_symbols; // this is for backwards compatibility + cf_t *sf_symbols_m[SRSLTE_MAX_PORTS]; + cf_t *ce[SRSLTE_MAX_PORTS]; // compatibility + cf_t *ce_m[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + + /* RI, PMI and SINR for MIMO statistics */ + float sinr[SRSLTE_MAX_LAYERS][SRSLTE_MAX_CODEBOOKS]; + uint32_t pmi[SRSLTE_MAX_LAYERS]; + uint32_t ri; + + /* Power allocation parameter 3GPP 36.213 Clause 5.2 Rho_b */ + float rho_b; + + srslte_dci_format_t dci_format; + uint64_t pkt_errors; + uint64_t pkts_total; + uint64_t pdsch_pkt_errors; + uint64_t pdsch_pkts_total; + uint64_t pmch_pkt_errors; + uint64_t pmch_pkts_total; + uint64_t nof_detected; + + uint16_t current_rnti; + uint16_t current_mbsfn_area_id; + dci_blind_search_t current_ss_ue[3][10]; + dci_blind_search_t current_ss_common[3]; + srslte_dci_location_t last_location; + srslte_dci_location_t last_location_ul; + + srslte_dci_msg_t pending_ul_dci_msg; + uint16_t pending_ul_dci_rnti; + + float last_phich_corr; +}srslte_ue_dl_t; + +/* This function shall be called just after the initial synchronization */ +SRSLTE_API int srslte_ue_dl_init(srslte_ue_dl_t *q, + cf_t *input[SRSLTE_MAX_PORTS], + uint32_t max_prb, + uint32_t nof_rx_antennas); + +SRSLTE_API void srslte_ue_dl_free(srslte_ue_dl_t *q); + +SRSLTE_API int srslte_ue_dl_set_cell(srslte_ue_dl_t *q, + srslte_cell_t cell); + +int srslte_ue_dl_decode_fft_estimate(srslte_ue_dl_t *q, + uint32_t sf_idx, + uint32_t *cfi); + +SRSLTE_API int srslte_ue_dl_decode_fft_estimate_mbsfn(srslte_ue_dl_t *q, + uint32_t sf_idx, + uint32_t *cfi, + srslte_sf_t sf_type); + +SRSLTE_API int srslte_ue_dl_decode_fft_estimate_noguru(srslte_ue_dl_t *q, + cf_t *input[SRSLTE_MAX_PORTS], + uint32_t sf_idx, + uint32_t *cfi); + +SRSLTE_API int srslte_ue_dl_decode_estimate(srslte_ue_dl_t *q, + uint32_t sf_idx, + uint32_t *cfi); + +SRSLTE_API int srslte_ue_dl_decode_estimate_mbsfn(srslte_ue_dl_t *q, + uint32_t sf_idx, + uint32_t *cfi, + srslte_sf_t sf_type); + +SRSLTE_API int srslte_ue_dl_cfg_grant(srslte_ue_dl_t *q, + srslte_ra_dl_grant_t *grant, + uint32_t cfi, + uint32_t sf_idx, + int rvidx[SRSLTE_MAX_CODEWORDS], + srslte_mimo_type_t mimo_type); + +SRSLTE_API int srslte_ue_dl_find_ul_dci(srslte_ue_dl_t *q, + uint32_t cfi, + uint32_t sf_idx, + uint16_t rnti, + srslte_dci_msg_t *dci_msg); + +SRSLTE_API int srslte_ue_dl_find_dl_dci(srslte_ue_dl_t *q, + uint32_t tm, + uint32_t cfi, + uint32_t sf_idx, + uint16_t rnti, + srslte_dci_msg_t *dci_msg); + +SRSLTE_API int srslte_ue_dl_find_dl_dci_type(srslte_ue_dl_t *q, + uint32_t tm, + uint32_t cfi, + uint32_t sf_idx, + uint16_t rnti, + srslte_rnti_type_t rnti_type, + srslte_dci_msg_t *dci_msg); + +SRSLTE_API uint32_t srslte_ue_dl_get_ncce(srslte_ue_dl_t *q); + +SRSLTE_API int srslte_ue_dl_decode(srslte_ue_dl_t *q, + uint8_t *data[SRSLTE_MAX_CODEWORDS], + uint32_t tm, + uint32_t tti, + bool acks[SRSLTE_MAX_CODEWORDS]); + +SRSLTE_API int srslte_ue_dl_decode_rnti(srslte_ue_dl_t *q, + uint8_t *data[SRSLTE_MAX_CODEWORDS], + uint32_t tm, + uint32_t tti, + uint16_t rnti, + bool acks[SRSLTE_MAX_CODEWORDS]); + +/* Used by example applications - full PMCH decode for a given MBSFN area ID + * srslte_ue_dl_decode_fft_estimate_multi, + * srslte_chest_dl_get_noise_estimate, + * srslte_ue_dl_cfg_grant, + * srslte_pmch_decode_multi + */ +SRSLTE_API int srslte_ue_dl_decode_mbsfn(srslte_ue_dl_t * q, + uint8_t *data, + uint32_t tti); + + +SRSLTE_API int srslte_ue_dl_ri_pmi_select(srslte_ue_dl_t *q, + uint8_t *ri, + uint8_t *pmi, + float *current_sinr); + +SRSLTE_API int srslte_ue_dl_ri_select(srslte_ue_dl_t *q, + uint8_t *ri, + float *cn); + +SRSLTE_API bool srslte_ue_dl_decode_phich(srslte_ue_dl_t *q, + uint32_t sf_idx, + uint32_t n_prb_lowest, + uint32_t n_dmrs); + +SRSLTE_API void srslte_ue_dl_reset(srslte_ue_dl_t *q); + +SRSLTE_API void srslte_ue_dl_set_rnti(srslte_ue_dl_t *q, + uint16_t rnti); + +/* Generate signals if required, store in q->current_mbsfn_area_id */ +SRSLTE_API int srslte_ue_dl_set_mbsfn_area_id(srslte_ue_dl_t *q, + uint16_t mbsfn_area_id); + +SRSLTE_API void srslte_ue_dl_set_non_mbsfn_region(srslte_ue_dl_t *q, + uint8_t non_mbsfn_region_length); + +SRSLTE_API void srslte_ue_dl_set_power_alloc(srslte_ue_dl_t *q, + float rho_a, + float rho_b); + + +SRSLTE_API void srslte_ue_dl_save_signal(srslte_ue_dl_t *q, + srslte_softbuffer_rx_t *softbuffer, + uint32_t tti, + uint32_t rv_idx, + uint16_t rnti, + uint32_t cfi); + + +#endif // SRSLTE_UE_DL_H diff --git a/lib/include/srslte/phy/ue/ue_mib.h b/lib/include/srslte/phy/ue/ue_mib.h new file mode 100644 index 0000000..38f27d0 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_mib.h @@ -0,0 +1,133 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_mib.h + * + * Description: This object decodes the MIB from the PBCH of an LTE signal. + * + * The function srslte_ue_mib_decode() shall be called multiple + * times, each passing a number of samples multiple of 19200, + * sampled at 1.92 MHz (that is, 10 ms of samples). + * + * The function uses the sync_t object to find the PSS sequence and + * decode the PBCH to obtain the MIB. + * + * The function returns 0 until the MIB is decoded. + * + * See ue_cell_detect.c for an example. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_UE_MIB_H +#define SRSLTE_UE_MIB_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/ue/ue_sync.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/dft/ofdm.h" + + +#define SRSLTE_UE_MIB_NOF_PRB 6 + +#define SRSLTE_UE_MIB_FOUND 1 +#define SRSLTE_UE_MIB_NOTFOUND 0 + +typedef struct SRSLTE_API { + srslte_sync_t sfind; + + cf_t *sf_symbols; + cf_t *ce[SRSLTE_MAX_PORTS]; + + srslte_ofdm_t fft; + srslte_chest_dl_t chest; + srslte_pbch_t pbch; + + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + uint32_t nof_tx_ports; + uint32_t sfn_offset; + + uint32_t frame_cnt; +} srslte_ue_mib_t; + +SRSLTE_API int srslte_ue_mib_init(srslte_ue_mib_t *q, + cf_t *in_buffer[SRSLTE_MAX_PORTS], + uint32_t max_prb); + +SRSLTE_API void srslte_ue_mib_free(srslte_ue_mib_t *q); + +SRSLTE_API int srslte_ue_mib_set_cell(srslte_ue_mib_t * q, + srslte_cell_t cell); + +SRSLTE_API void srslte_ue_mib_reset(srslte_ue_mib_t * q); + +SRSLTE_API int srslte_ue_mib_decode(srslte_ue_mib_t * q, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], + uint32_t *nof_tx_ports, + int *sfn_offset); + + +/* This interface uses ue_mib and ue_sync to first get synchronized subframes + * and then decode MIB + * + * This object calls the pbch object with nof_ports=0 for blind nof_ports determination +*/ +typedef struct { + srslte_ue_mib_t ue_mib; + srslte_ue_sync_t ue_sync; + cf_t *sf_buffer[SRSLTE_MAX_PORTS]; + uint32_t nof_rx_antennas; +} srslte_ue_mib_sync_t; + +SRSLTE_API int srslte_ue_mib_sync_init_multi(srslte_ue_mib_sync_t *q, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t *), + uint32_t nof_rx_antennas, + void *stream_handler); + +SRSLTE_API void srslte_ue_mib_sync_free(srslte_ue_mib_sync_t *q); + +SRSLTE_API int srslte_ue_mib_sync_set_cell(srslte_ue_mib_sync_t *q, + uint32_t cell_id, + srslte_cp_t cp); + +SRSLTE_API void srslte_ue_mib_sync_reset(srslte_ue_mib_sync_t * q); + +SRSLTE_API int srslte_ue_mib_sync_decode(srslte_ue_mib_sync_t * q, + uint32_t max_frames_timeout, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], + uint32_t *nof_tx_ports, + int *sfn_offset); + + + + +#endif // SRSLTE_UE_MIB_H + diff --git a/lib/include/srslte/phy/ue/ue_phy.h b/lib/include/srslte/phy/ue/ue_phy.h new file mode 100644 index 0000000..0a82175 --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_phy.h @@ -0,0 +1,168 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_phy.h + * + * Description: Top-level class wrapper for PHY layer. + * + * Reference: + *****************************************************************************/ + +#include "srslte/srslte.h" +#include "srslte/phy/utils/queue.h" + +#ifndef SRSLTE_UE_PHY_H +#define SRSLTE_UE_PHY_H + +#define SYNC_MODE_CV 0 +#define SYNC_MODE_CALLBACK 1 +#define SYNC_MODE SYNC_MODE_CV + +namespace srslte { + +typedef _Complex float cf_t; + +class ue_phy +{ +public: + + typedef enum {DOWNLINK, UPLINK} direction_t; + + typedef enum { + PDCCH_UL_SEARCH_CRNTI = 0, + PDCCH_UL_SEARCH_RA_PROC, + PDCCH_UL_SEARCH_SPS, + PDCCH_UL_SEARCH_TEMPORAL, + PDCCH_UL_SEARCH_TPC_PUSCH, + PDCCH_UL_SEARCH_TPC_PUCCH + } pdcch_ul_search_t; + + typedef enum { + PDCCH_DL_SEARCH_CRNTI = 0, + PDCCH_DL_SEARCH_SIRNTI, + PDCCH_DL_SEARCH_PRNTI, + PDCCH_DL_SEARCH_RARNTI, + PDCCH_DL_SEARCH_TEMPORAL, + PDCCH_DL_SEARCH_SPS + } pdcch_dl_search_t; + + /* Uplink/Downlink scheduling grant generated by a successfully decoded PDCCH */ + class sched_grant { + public: + uint16_t get_rnti(); + uint32_t get_rv(); + void set_rv(uint32_t rv); + bool get_ndi(); + bool get_cqi_request(); + uint32_t get_harq_process(); + private: + union { + srslte_ra_ul_dci_t ul_grant; + srslte_ra_dl_dci_t dl_grant; + }; + direction_t dir; + }; + + + /* Uplink scheduling assignment. The MAC instructs the PHY to prepare an UL packet (PUSCH or PUCCH) + * for transmission. The MAC must call generate_pusch() to set the packet ready for transmission + */ + class ul_buffer : public queue::element { + public: + ul_buffer(srslte_cell_t cell); + void generate_pusch(sched_grant pusch_grant, uint8_t *payload, srslte_uci_data_t uci_data); + void generate_pucch(srslte_uci_data_t uci_data); + private: + srslte_ue_ul_t ue_ul; + bool signal_generated = false; + cf_t* signal_buffer = NULL; + uint32_t tti = 0; + }; + + /* Class for the processing of Downlink buffers. The MAC obtains a buffer for a given TTI and then + * gets ul/dl scheduling grants and/or processes phich/pdsch channels + */ + class dl_buffer : public queue::element { + public: + dl_buffer(srslte_cell_t cell); + sched_grant get_ul_grant(pdcch_ul_search_t mode, uint32_t rnti); + sched_grant get_dl_grant(pdcch_dl_search_t mode, uint32_t rnti); + bool decode_phich(sched_grant pusch_grant); + bool decode_pdsch(sched_grant pdsch_grant, uint8_t *payload); // returns true or false for CRC OK/KO + private: + srslte_ue_dl_t ue_dl; + srslte_phich_t phich; + cf_t *signal_buffer = NULL; + bool sf_symbols_and_ce_done = false; + bool pdcch_llr_extracted = false; + uint32_t tti = 0; + }; + + +#if SYNC_MODE==SYNC_MODE_CALLBACK + typedef (*ue_phy_tti_clock_fcn_t) (void); + ue_phy(ue_phy_tti_clock_fcn_t tti_clock_callback); +#else + ue_phy(); +#endif + ~ue_phy(); + + void measure(); // TBD + void dl_bch(); + void start_rxtx(); + void stop_rxtx(); + void init_prach(); + void send_prach(/* prach_cfg_t in prach.h with power, seq idx, etc */); + void set_param(); + + uint32_t get_tti(); +#if SYNC_MODE==SYNC_MODE_CV + std::condition_variable tti_cv; + std::mutex tti_mutex; +#endif + + ul_buffer get_ul_buffer(uint32_t tti); + dl_buffer get_dl_buffer(uint32_t tti); + +private: + enum { + IDLE, MEASURE, RX_BCH, RXTX + } phy_state; + + bool prach_initiated = false; + bool prach_ready_to_send = false; + srslte_prach_t prach; + + queue ul_buffer_queue; + queue dl_buffer_queue; + + pthread_t radio_thread; + void *radio_handler; +}; + +} +#endif // SRSLTE_UE_PHY_H diff --git a/lib/include/srslte/phy/ue/ue_sync.h b/lib/include/srslte/phy/ue/ue_sync.h new file mode 100644 index 0000000..45aabfb --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_sync.h @@ -0,0 +1,265 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_sync.h + * + * Description: This object automatically manages the cell synchronization + * procedure. + * + * The main function is srslte_ue_sync_get_buffer(), which returns + * a pointer to the aligned subframe of samples (before FFT). This + * function should be called regularly, returning every 1 ms. + * It reads from the USRP, aligns the samples to the subframe and + * performs time/freq synch. + * + * It is also possible to read the signal from a file using the + * init function srslte_ue_sync_init_file(). The sampling frequency + * is derived from the number of PRB. + * + * The function returns 1 when the signal is correctly acquired and + * the returned buffer is aligned with the subframe. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_UE_SYNC_H +#define SRSLTE_UE_SYNC_H + +#include + +#include "srslte/config.h" +#include "srslte/phy/sync/sync.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/agc/agc.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/common/timestamp.h" +#include "srslte/phy/io/filesource.h" + +#define DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD 10 +#define DEFAULT_SFO_EMA_COEFF 0.1 + +#define DEFAULT_CFO_BW_PSS 0.05 +#define DEFAULT_CFO_PSS_MIN 400 // typical bias of PSS estimation. +#define DEFAULT_CFO_BW_REF 0.08 +#define DEFAULT_CFO_REF_MIN 0 // typical bias of REF estimation +#define DEFAULT_CFO_REF_MAX DEFAULT_CFO_PSS_MIN // Maximum detection offset of REF based estimation + +#define DEFAULT_PSS_STABLE_TIMEOUT 20 // Time after which the PSS is considered to be stable and we accept REF-CFO + +#define DEFAULT_CFO_EMA_TRACK 0.05 + +typedef enum SRSLTE_API { SF_FIND, SF_TRACK} srslte_ue_sync_state_t; + +//#define MEASURE_EXEC_TIME + +typedef struct SRSLTE_API { + srslte_sync_t sfind; + srslte_sync_t strack; + + uint32_t max_prb; + + srslte_agc_t agc; + bool do_agc; + uint32_t agc_period; + int decimate; + void *stream; + void *stream_single; + int (*recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*); + int (*recv_callback_single)(void*, void*, uint32_t, srslte_timestamp_t*); + srslte_timestamp_t last_timestamp; + + uint32_t nof_rx_antennas; + + srslte_filesource_t file_source; + bool file_mode; + float file_cfo; + bool file_wrap_enable; + srslte_cfo_t file_cfo_correct; + + srslte_ue_sync_state_t state; + + uint32_t frame_len; + uint32_t fft_size; + uint32_t nof_recv_sf; // Number of subframes received each call to srslte_ue_sync_get_buffer + uint32_t nof_avg_find_frames; + uint32_t frame_find_cnt; + uint32_t sf_len; + + /* These count half frames (5ms) */ + uint64_t frame_ok_cnt; + uint32_t frame_no_cnt; + uint32_t frame_total_cnt; + + /* this is the system frame number (SFN) */ + uint32_t frame_number; + + srslte_cell_t cell; + uint32_t sf_idx; + + bool decode_sss_on_track; + + bool cfo_is_copied; + bool cfo_correct_enable_track; + bool cfo_correct_enable_find; + float cfo_current_value; + float cfo_loop_bw_pss; + float cfo_loop_bw_ref; + float cfo_pss_min; + float cfo_ref_min; + float cfo_ref_max; + + uint32_t pss_stable_cnt; + uint32_t pss_stable_timeout; + bool pss_is_stable; + + uint32_t peak_idx; + int next_rf_sample_offset; + int last_sample_offset; + float mean_sample_offset; + uint32_t sample_offset_correct_period; + float sfo_ema; + + + #ifdef MEASURE_EXEC_TIME + float mean_exec_time; + #endif +} srslte_ue_sync_t; + +SRSLTE_API int srslte_ue_sync_init(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, void*, uint32_t, srslte_timestamp_t*), + void *stream_handler); + +SRSLTE_API int srslte_ue_sync_init_multi(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler); + +SRSLTE_API int srslte_ue_sync_init_multi_decim(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler, + int decimate); + +SRSLTE_API int srslte_ue_sync_init_file(srslte_ue_sync_t *q, + uint32_t nof_prb, + char *file_name, + int offset_time, + float offset_freq); + +SRSLTE_API int srslte_ue_sync_init_file_multi(srslte_ue_sync_t *q, + uint32_t nof_prb, + char *file_name, + int offset_time, + float offset_freq, + uint32_t nof_rx_ant); + +SRSLTE_API void srslte_ue_sync_free(srslte_ue_sync_t *q); + +SRSLTE_API void srslte_ue_sync_file_wrap(srslte_ue_sync_t *q, + bool enable); + +SRSLTE_API int srslte_ue_sync_set_cell(srslte_ue_sync_t *q, + srslte_cell_t cell); + +SRSLTE_API void srslte_ue_sync_cfo_reset(srslte_ue_sync_t *q); + +SRSLTE_API void srslte_ue_sync_reset(srslte_ue_sync_t *q); + +SRSLTE_API int srslte_ue_sync_start_agc(srslte_ue_sync_t *q, + double (set_gain_callback)(void*, double), + double min_gain, + double max_gain, + double init_gain_value); + +SRSLTE_API uint32_t srslte_ue_sync_sf_len(srslte_ue_sync_t *q); + +SRSLTE_API void srslte_ue_sync_set_agc_period(srslte_ue_sync_t *q, + uint32_t period); + +/* CAUTION: input_buffer MUST have space for 2 subframes */ +SRSLTE_API int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, + cf_t *input_buffer); + +SRSLTE_API int srslte_ue_sync_zerocopy_multi(srslte_ue_sync_t *q, + cf_t *input_buffer[SRSLTE_MAX_PORTS]); + +SRSLTE_API void srslte_ue_sync_set_cfo_tol(srslte_ue_sync_t *q, + float tol); + +SRSLTE_API void srslte_ue_sync_copy_cfo(srslte_ue_sync_t *q, + srslte_ue_sync_t *src_obj); + +SRSLTE_API void srslte_ue_sync_set_cfo_loop_bw(srslte_ue_sync_t *q, + float bw_pss, + float bw_ref, + float pss_tol, + float ref_tol, + float ref_max, + uint32_t pss_stable_timeout); + +SRSLTE_API void srslte_ue_sync_set_cfo_ema(srslte_ue_sync_t *q, + float ema); + +SRSLTE_API void srslte_ue_sync_set_cfo_ref(srslte_ue_sync_t *q, float res_cfo); + +SRSLTE_API void srslte_ue_sync_set_cfo_i_enable(srslte_ue_sync_t *q, + bool enable); + +SRSLTE_API void srslte_ue_sync_set_N_id_2(srslte_ue_sync_t *q, + uint32_t N_id_2); + +SRSLTE_API void srslte_ue_sync_decode_sss_on_track(srslte_ue_sync_t *q, + bool enabled); + +SRSLTE_API uint32_t srslte_ue_sync_get_sfidx(srslte_ue_sync_t *q); + +SRSLTE_API float srslte_ue_sync_get_cfo(srslte_ue_sync_t *q); + +SRSLTE_API float srslte_ue_sync_get_sfo(srslte_ue_sync_t *q); + +SRSLTE_API int srslte_ue_sync_get_last_sample_offset(srslte_ue_sync_t *q); + +SRSLTE_API void srslte_ue_sync_set_sfo_correct_period(srslte_ue_sync_t *q, + uint32_t nof_subframes); + +SRSLTE_API void srslte_ue_sync_set_sfo_ema(srslte_ue_sync_t *q, + float ema_coefficient); + +SRSLTE_API void srslte_ue_sync_get_last_timestamp(srslte_ue_sync_t *q, + srslte_timestamp_t *timestamp); + + +#endif // SRSLTE_UE_SYNC_H + diff --git a/lib/include/srslte/phy/ue/ue_ul.h b/lib/include/srslte/phy/ue/ue_ul.h new file mode 100644 index 0000000..c6eb96a --- /dev/null +++ b/lib/include/srslte/phy/ue/ue_ul.h @@ -0,0 +1,217 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: ue_ul.h + * + * Description: UE uplink object. + * + * This module is a frontend to all the uplink data and control + * channel processing modules. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_UE_UL_H +#define SRSLTE_UE_UL_H + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/ch_estimation/refsignal_ul.h" +#include "srslte/phy/phch/pusch.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#include "srslte/config.h" + +/* UE UL power control */ +typedef struct { + // Common configuration + float p0_nominal_pusch; + float alpha; + float p0_nominal_pucch; + float delta_f_pucch[5]; + float delta_preamble_msg3; + + // Dedicated configuration + float p0_ue_pusch; + bool delta_mcs_based; + bool acc_enabled; + float p0_ue_pucch; + float p_srs_offset; +} srslte_ue_ul_powerctrl_t; + +typedef struct SRSLTE_API { + srslte_ofdm_t fft; + srslte_cfo_t cfo; + srslte_cell_t cell; + + bool normalize_en; + bool cfo_en; + + float current_cfo_tol; + float current_cfo; + srslte_pucch_format_t last_pucch_format; + + srslte_pusch_cfg_t pusch_cfg; + srslte_refsignal_ul_t signals; + srslte_refsignal_ul_dmrs_pregen_t pregen_drms; + srslte_refsignal_srs_pregen_t pregen_srs; + + srslte_softbuffer_tx_t softbuffer; + + srslte_pusch_t pusch; + srslte_pucch_t pucch; + + srslte_pucch_sched_t pucch_sched; + srslte_refsignal_srs_cfg_t srs_cfg; + srslte_uci_cfg_t uci_cfg; + srslte_pusch_hopping_cfg_t hopping_cfg; + srslte_ue_ul_powerctrl_t power_ctrl; + + cf_t *refsignal; + cf_t *srs_signal; + cf_t *sf_symbols; + + float last_amplitude; + + uint16_t current_rnti; + bool signals_pregenerated; +}srslte_ue_ul_t; + + + +/* This function shall be called just after the initial synchronization */ +SRSLTE_API int srslte_ue_ul_init(srslte_ue_ul_t *q, + cf_t *out_buffer, + uint32_t max_prb); + +SRSLTE_API void srslte_ue_ul_free(srslte_ue_ul_t *q); + +SRSLTE_API int srslte_ue_ul_set_cell(srslte_ue_ul_t *q, + srslte_cell_t cell); + +SRSLTE_API void srslte_ue_ul_set_cfo_tol(srslte_ue_ul_t *q, + float tol); + +SRSLTE_API void srslte_ue_ul_set_cfo(srslte_ue_ul_t *q, + float cur_cfo); + +SRSLTE_API void srslte_ue_ul_set_cfo_enable(srslte_ue_ul_t *q, + bool enabled); + +SRSLTE_API void srslte_ue_ul_set_normalization(srslte_ue_ul_t *q, + bool enabled); + +SRSLTE_API float srslte_ue_ul_get_last_amplitude(srslte_ue_ul_t *q); + +SRSLTE_API void srslte_ue_ul_set_cfg(srslte_ue_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *dmrs_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_uci_cfg_t *uci_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_ue_ul_powerctrl_t *power_ctrl); + +SRSLTE_API int srslte_ue_ul_cfg_grant(srslte_ue_ul_t *q, + srslte_ra_ul_grant_t *grant, + uint32_t tti, + uint32_t rvidx, + uint32_t current_tx_nb); + +SRSLTE_API int srslte_ue_ul_pucch_encode(srslte_ue_ul_t *q, + srslte_uci_data_t uci_data, + uint32_t pdcch_n_cce, /* Ncce of the last PDCCH message received */ + uint32_t tti, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_pusch_encode(srslte_ue_ul_t *q, + uint8_t *data, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_pusch_encode_rnti(srslte_ue_ul_t *q, + uint8_t *data, + uint16_t rnti, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_pusch_uci_encode(srslte_ue_ul_t *q, + uint8_t *data, + srslte_uci_data_t uci_data, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_pusch_uci_encode_rnti(srslte_ue_ul_t *q, + uint8_t *data, + srslte_uci_data_t uci_data, + uint16_t rnti, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_pusch_encode_rnti_softbuffer(srslte_ue_ul_t *q, + uint8_t *data, + srslte_uci_data_t uci_data, + srslte_softbuffer_tx_t *softbuffer, + uint16_t rnti, + cf_t *output_signal); + +SRSLTE_API int srslte_ue_ul_srs_encode(srslte_ue_ul_t *q, + uint32_t tti, + cf_t *output_signal); + +SRSLTE_API void srslte_ue_ul_reset(srslte_ue_ul_t *q); + +SRSLTE_API int srslte_ue_ul_pregen_signals(srslte_ue_ul_t *q); + +SRSLTE_API void srslte_ue_ul_set_rnti(srslte_ue_ul_t *q, + uint16_t rnti); + +/* Power control procedure */ +SRSLTE_API float srslte_ue_ul_pusch_power(srslte_ue_ul_t *q, + float PL, + float p0_preamble); + +SRSLTE_API float srslte_ue_ul_pucch_power(srslte_ue_ul_t *q, + float PL, + srslte_pucch_format_t format, + uint32_t n_cqi, + uint32_t n_harq); + +SRSLTE_API float srslte_ue_ul_srs_power(srslte_ue_ul_t *q, + float PL); + +/* Other static functions for UL PHY procedures defined in 36.213 */ + +SRSLTE_API int srslte_ue_ul_sr_send_tti(uint32_t I_sr, + uint32_t current_tti); + +SRSLTE_API bool srslte_ue_ul_srs_tx_enabled(srslte_refsignal_srs_cfg_t *srs_cfg, + uint32_t tti); + + +#endif // SRSLTE_UE_UL_H diff --git a/lib/include/srslte/phy/utils/bit.h b/lib/include/srslte/phy/utils/bit.h new file mode 100644 index 0000000..8a4ff78 --- /dev/null +++ b/lib/include/srslte/phy/utils/bit.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: bit.h + * + * Description: Bit-level utilities. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_BIT_H +#define SRSLTE_BIT_H + +#include +#include + +#include "srslte/config.h" + +typedef struct { + uint32_t nof_bits; + uint16_t *interleaver; + uint16_t *byte_idx; + uint8_t *bit_mask; + uint8_t n_128; +} srslte_bit_interleaver_t; + +SRSLTE_API void srslte_bit_interleaver_init(srslte_bit_interleaver_t *q, + uint16_t *interleaver, + uint32_t nof_bits); + +SRSLTE_API void srslte_bit_interleaver_free(srslte_bit_interleaver_t *q); + +SRSLTE_API void srslte_bit_interleaver_run(srslte_bit_interleaver_t *q, + uint8_t *input, + uint8_t *output, + uint16_t w_offset); + +SRSLTE_API void srslte_bit_interleave(uint8_t *input, + uint8_t *output, + uint16_t *interleaver, + uint32_t nof_bits); + +SRSLTE_API void srslte_bit_copy(uint8_t *dst, + uint32_t dst_offset, + uint8_t *src, + uint32_t src_offset, + uint32_t nof_bits); + +SRSLTE_API void srslte_bit_interleave_i(uint8_t *input, + uint8_t *output, + uint32_t *interleaver, + uint32_t nof_bits); + +SRSLTE_API void srslte_bit_interleave_i_w_offset(uint8_t *input, + uint8_t *output, + uint32_t *interleaver, + uint32_t nof_bits, + uint32_t w_offset); + +SRSLTE_API void srslte_bit_interleave_w_offset(uint8_t *input, + uint8_t *output, + uint16_t *interleaver, + uint32_t nof_bits, + uint32_t w_offset); + +SRSLTE_API void srslte_bit_unpack_vector(uint8_t *packed, + uint8_t *unpacked, + int nof_bits); + +SRSLTE_API void srslte_bit_pack_vector(uint8_t *unpacked, + uint8_t *packed, + int nof_bits); + +SRSLTE_API uint32_t srslte_bit_pack(uint8_t **bits, + int nof_bits); + +SRSLTE_API uint64_t srslte_bit_pack_l(uint8_t **bits, + int nof_bits); + +SRSLTE_API void srslte_bit_unpack_l(uint64_t value, + uint8_t **bits, + int nof_bits); + +SRSLTE_API void srslte_bit_unpack(uint32_t value, + uint8_t **bits, + int nof_bits); + +SRSLTE_API void srslte_bit_fprint(FILE *stream, + uint8_t *bits, + int nof_bits); + +SRSLTE_API uint32_t srslte_bit_diff(uint8_t *x, + uint8_t *y, + int nbits); + +SRSLTE_API uint32_t srslte_bit_count(uint32_t n); + +#endif // SRSLTE_BIT_H + diff --git a/lib/include/srslte/phy/utils/cexptab.h b/lib/include/srslte/phy/utils/cexptab.h new file mode 100644 index 0000000..b96b330 --- /dev/null +++ b/lib/include/srslte/phy/utils/cexptab.h @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: cexptab.h + * + * Description: Utility module for generation of complex exponential tables. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_CEXPTAB_H +#define SRSLTE_CEXPTAB_H + +#include +#include +#include "srslte/config.h" + +typedef struct SRSLTE_API { + uint32_t size; + cf_t *tab; +}srslte_cexptab_t; + +SRSLTE_API int srslte_cexptab_init(srslte_cexptab_t *nco, + uint32_t size); + +SRSLTE_API void srslte_cexptab_free(srslte_cexptab_t *nco); + +SRSLTE_API void srslte_cexptab_gen(srslte_cexptab_t *nco, + cf_t *x, + float freq, + uint32_t len); + +SRSLTE_API void srslte_cexptab_gen_direct(cf_t *x, + float freq, + uint32_t len); + +#endif // SRSLTE_CEXPTAB_H diff --git a/lib/include/srslte/phy/utils/convolution.h b/lib/include/srslte/phy/utils/convolution.h new file mode 100644 index 0000000..3f0cad7 --- /dev/null +++ b/lib/include/srslte/phy/utils/convolution.h @@ -0,0 +1,97 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: convolution.h + * + * Description: Utility module for fast convolution using FFT. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_CONVOLUTION_H +#define SRSLTE_CONVOLUTION_H + +#include "srslte/config.h" +#include "srslte/phy/dft/dft.h" + +typedef struct SRSLTE_API { + cf_t *input_fft; + cf_t *filter_fft; + cf_t *output_fft; + cf_t *output_fft2; + uint32_t input_len; + uint32_t filter_len; + uint32_t output_len; + uint32_t max_input_len; + uint32_t max_filter_len; + srslte_dft_plan_t input_plan; + srslte_dft_plan_t filter_plan; + srslte_dft_plan_t output_plan; + //cf_t *pss_signal_time_fft[3]; // One sequence for each N_id_2 + //cf_t *pss_signal_time[3]; + +}srslte_conv_fft_cc_t; + +SRSLTE_API int srslte_conv_fft_cc_init(srslte_conv_fft_cc_t *q, + uint32_t input_len, + uint32_t filter_len); + +SRSLTE_API int srslte_conv_fft_cc_replan(srslte_conv_fft_cc_t *q, + uint32_t input_len, + uint32_t filter_len); + +SRSLTE_API void srslte_conv_fft_cc_free(srslte_conv_fft_cc_t *q); + +SRSLTE_API uint32_t srslte_conv_fft_cc_run(srslte_conv_fft_cc_t *q, + const cf_t *input, + const cf_t *filter, + cf_t *output); + +SRSLTE_API uint32_t srslte_conv_fft_cc_run_opt(srslte_conv_fft_cc_t *q, + const cf_t *input, + const cf_t *filter_freq, + cf_t *output); + +SRSLTE_API uint32_t srslte_conv_cc(const cf_t *input, + const cf_t *filter, + cf_t *output, + uint32_t input_len, + uint32_t filter_len); + +SRSLTE_API uint32_t srslte_conv_same_cf(cf_t *input, + float *filter, + cf_t *output, + uint32_t input_len, + uint32_t filter_len); + +SRSLTE_API uint32_t srslte_conv_same_cc(cf_t *input, + cf_t *filter, + cf_t *output, + uint32_t input_len, + uint32_t filter_len); + +#endif // SRSLTE_CONVOLUTION_H diff --git a/lib/include/srslte/phy/utils/debug.h b/lib/include/srslte/phy/utils/debug.h new file mode 100644 index 0000000..9a07272 --- /dev/null +++ b/lib/include/srslte/phy/utils/debug.h @@ -0,0 +1,83 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: debug.h + * + * Description: Debug output utilities. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_DEBUG_H +#define SRSLTE_DEBUG_H + +#include +#include "srslte/config.h" +#include "srslte/phy/common/phy_logger.h" + +#define SRSLTE_VERBOSE_DEBUG 2 +#define SRSLTE_VERBOSE_INFO 1 +#define SRSLTE_VERBOSE_NONE 0 + +#include +SRSLTE_API void get_time_interval(struct timeval * tdata); + +#define SRSLTE_DEBUG_ENABLED 1 + +SRSLTE_API extern int srslte_verbose; +SRSLTE_API extern int handler_registered; + +#define SRSLTE_VERBOSE_ISINFO() (srslte_verbose>=SRSLTE_VERBOSE_INFO) +#define SRSLTE_VERBOSE_ISDEBUG() (srslte_verbose>=SRSLTE_VERBOSE_DEBUG) +#define SRSLTE_VERBOSE_ISNONE() (srslte_verbose==SRSLTE_VERBOSE_NONE) + +#define PRINT_DEBUG srslte_verbose=SRSLTE_VERBOSE_DEBUG +#define PRINT_INFO srslte_verbose=SRSLTE_VERBOSE_INFO +#define PRINT_NONE srslte_verbose=SRSLTE_VERBOSE_NONE + +#define DEBUG(_fmt, ...) if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_DEBUG && !handler_registered)\ + { fprintf(stdout, "[DEBUG]: " _fmt, ##__VA_ARGS__); }\ + else{ srslte_phy_log_print(LOG_LEVEL_DEBUG, _fmt, ##__VA_ARGS__); } + +#define INFO(_fmt, ...) if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_INFO && !handler_registered) \ + { fprintf(stdout, "[INFO]: " _fmt, ##__VA_ARGS__); }\ + else{ srslte_phy_log_print(LOG_LEVEL_INFO, _fmt, ##__VA_ARGS__); } + +#if CMAKE_BUILD_TYPE==Debug +/* In debug mode, it prints out the */ +#define ERROR(_fmt, ...) if (!handler_registered)\ + { fprintf(stderr, "\e[31m%s.%d: " _fmt "\e[0m\n", __FILE__, __LINE__, ##__VA_ARGS__);}\ + else {srslte_phy_log_print(LOG_LEVEL_ERROR, _fmt, ##__VA_ARGS__);} // +#else +#define ERROR(_fmt, ...) if (!handler_registered)\ + { fprintf(stderr, "[ERROR in %s]:" _fmt "\n", __FUNCTION__, ##__VA_ARGS__);}\ + else{srslte_phy_log_print(LOG_LEVEL_ERROR, _fmt, ##__VA_ARGS__);} // +#endif /* CMAKE_BUILD_TYPE==Debug */ + +void srslte_debug_handle_crash(int argc, char **argv); + +#endif // SRSLTE_DEBUG_H diff --git a/lib/include/srslte/phy/utils/filter.h b/lib/include/srslte/phy/utils/filter.h new file mode 100644 index 0000000..a126e17 --- /dev/null +++ b/lib/include/srslte/phy/utils/filter.h @@ -0,0 +1,61 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: debug.h + * + * Description: Debug output utilities. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_FILTER_H +#define SRSLTE_FILTER_H + +#include +#include +#include "srslte/config.h" +#include +#include "srslte/phy/utils/vector.h" +typedef struct SRSLTE_API{ + cf_t *filter_input; + cf_t *downsampled_input; + cf_t *filter_output; + bool is_decimator; + int factor; + int num_taps; + float *taps; + +}srslte_filt_cc_t; + +void srslte_filt_decim_cc_init(srslte_filt_cc_t *q, int factor, int order); + +void srslte_filt_decim_cc_free(srslte_filt_cc_t *q); + +void srslte_filt_decim_cc_execute(srslte_filt_cc_t *q, cf_t *input, cf_t *downsampled_input, cf_t *output, int size); + +void srslte_downsample_cc(cf_t *input, cf_t *output, int M, int size) ; +#endif // SRSLTE_FILTER_H \ No newline at end of file diff --git a/lib/include/srslte/phy/utils/mat.h b/lib/include/srslte/phy/utils/mat.h new file mode 100644 index 0000000..8db0205 --- /dev/null +++ b/lib/include/srslte/phy/utils/mat.h @@ -0,0 +1,221 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_MAT_H +#define SRSLTE_MAT_H + +#include "srslte/config.h" +#include "srslte/phy/utils/simd.h" + +/* Generic implementation for complex reciprocal */ +SRSLTE_API cf_t srslte_mat_cf_recip_gen(cf_t a); + +/* Generic implementation for 2x2 determinant */ +SRSLTE_API cf_t srslte_mat_2x2_det_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11); + +/* Generic implementation for 2x2 Matrix Inversion */ +SRSLTE_API void srslte_mat_2x2_inv_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11, + cf_t *r00, cf_t *r01, cf_t *r10, cf_t *r11); + +/* Generic implementation for Zero Forcing (ZF) solver */ +SRSLTE_API void srslte_mat_2x2_zf_gen(cf_t y0, cf_t y1, + cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, + float norm); + +/* Generic implementation for Minimum Mean Squared Error (MMSE) solver */ +SRSLTE_API void srslte_mat_2x2_mmse_gen(cf_t y0, cf_t y1, + cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, + float noise_estimate, + float norm); + +SRSLTE_API void srslte_mat_2x2_mmse_csi_gen(cf_t y0, cf_t y1, + cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, float *csi0, float *csi1, + float noise_estimate, + float norm); + +SRSLTE_API float srslte_mat_2x2_cn(cf_t h00, + cf_t h01, + cf_t h10, + cf_t h11); + +#ifdef LV_HAVE_SSE + +/* SSE implementation for complex reciprocal */ +SRSLTE_API __m128 srslte_mat_cf_recip_sse(__m128 a); + +/* SSE implementation for 2x2 determinant */ +SRSLTE_API __m128 srslte_mat_2x2_det_sse(__m128 a00, __m128 a01, __m128 a10, __m128 a11); + +/* SSE implementation for Zero Forcing (ZF) solver */ +SRSLTE_API void srslte_mat_2x2_zf_sse(__m128 y0, __m128 y1, + __m128 h00, __m128 h01, __m128 h10, __m128 h11, + __m128 *x0, __m128 *x1, + float norm); + +/* SSE implementation for Minimum Mean Squared Error (MMSE) solver */ +SRSLTE_API void srslte_mat_2x2_mmse_sse(__m128 y0, __m128 y1, + __m128 h00, __m128 h01, __m128 h10, __m128 h11, + __m128 *x0, __m128 *x1, + float noise_estimate, float norm); + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + +/* AVX implementation for complex reciprocal */ +SRSLTE_API __m256 srslte_mat_cf_recip_avx(__m256 a); + +/* AVX implementation for 2x2 determinant */ +SRSLTE_API __m256 srslte_mat_2x2_det_avx(__m256 a00, __m256 a01, __m256 a10, __m256 a11); + +/* AVX implementation for Zero Forcing (ZF) solver */ +SRSLTE_API void srslte_mat_2x2_zf_avx(__m256 y0, __m256 y1, + __m256 h00, __m256 h01, __m256 h10, __m256 h11, + __m256 *x0, __m256 *x1, + float norm); + +/* AVX implementation for Minimum Mean Squared Error (MMSE) solver */ +SRSLTE_API void srslte_mat_2x2_mmse_avx(__m256 y0, __m256 y1, + __m256 h00, __m256 h01, __m256 h10, __m256 h11, + __m256 *x0, __m256 *x1, + float noise_estimate, float norm); + +#endif /* LV_HAVE_AVX */ + +#if SRSLTE_SIMD_CF_SIZE != 0 + +/* Generic SIMD implementation for 2x2 determinant */ +static inline simd_cf_t srslte_mat_2x2_det_simd(simd_cf_t a00, simd_cf_t a01, simd_cf_t a10, simd_cf_t a11) { + return srslte_simd_cf_sub(srslte_simd_cf_prod(a00, a11), srslte_simd_cf_prod(a01, a10)); +} + +/* Generic SIMD implementation for Zero Forcing (ZF) solver */ +static inline void srslte_mat_2x2_zf_csi_simd(simd_cf_t y0, + simd_cf_t y1, + simd_cf_t h00, + simd_cf_t h01, + simd_cf_t h10, + simd_cf_t h11, + simd_cf_t *x0, + simd_cf_t *x1, + simd_f_t *csi0, + simd_f_t *csi1, + float norm) { + simd_cf_t det = srslte_mat_2x2_det_simd(h00, h01, h10, h11); + simd_cf_t detrec = srslte_simd_cf_mul(srslte_simd_cf_rcp(det), srslte_simd_f_set1(norm)); + + *x0 = srslte_simd_cf_prod(srslte_simd_cf_sub(srslte_simd_cf_prod(h11, y0), srslte_simd_cf_prod(h01, y1)), detrec); + *x1 = srslte_simd_cf_prod(srslte_simd_cf_sub(srslte_simd_cf_prod(h00, y1), srslte_simd_cf_prod(h10, y0)), detrec); + + *csi0 = srslte_simd_f_set1(1.0f); + *csi1 = srslte_simd_f_set1(1.0f); +} + +static inline void srslte_mat_2x2_zf_simd(simd_cf_t y0, + simd_cf_t y1, + simd_cf_t h00, + simd_cf_t h01, + simd_cf_t h10, + simd_cf_t h11, + simd_cf_t *x0, + simd_cf_t *x1, + float norm) { + simd_f_t csi1, csi2; + srslte_mat_2x2_zf_csi_simd(y0, y1, h00, h01, h10, h11, x0, x1, &csi1, &csi2, norm); +} + +/* Generic SIMD implementation for Minimum Mean Squared Error (MMSE) solver */ +static inline void srslte_mat_2x2_mmse_csi_simd(simd_cf_t y0, + simd_cf_t y1, + simd_cf_t h00, + simd_cf_t h01, + simd_cf_t h10, + simd_cf_t h11, + simd_cf_t *x0, + simd_cf_t *x1, + simd_f_t *csi0, + simd_f_t *csi1, + float noise_estimate, + float norm) { + simd_cf_t _noise_estimate; + simd_f_t _norm = srslte_simd_f_set1(norm); + + _noise_estimate.re = srslte_simd_f_set1(noise_estimate); + _noise_estimate.im = srslte_simd_f_zero(); + + /* 1. A = H' x H + No*/ + simd_cf_t a00 = + srslte_simd_cf_add(srslte_simd_cf_add(srslte_simd_cf_conjprod(h00, h00), srslte_simd_cf_conjprod(h10, h10)), + _noise_estimate); + simd_cf_t a01 = srslte_simd_cf_add(srslte_simd_cf_conjprod(h01, h00), srslte_simd_cf_conjprod(h11, h10)); + simd_cf_t a10 = srslte_simd_cf_add(srslte_simd_cf_conjprod(h00, h01), srslte_simd_cf_conjprod(h10, h11)); + simd_cf_t a11 = + srslte_simd_cf_add(srslte_simd_cf_add(srslte_simd_cf_conjprod(h01, h01), srslte_simd_cf_conjprod(h11, h11)), + _noise_estimate); + simd_cf_t a_det_rcp = srslte_simd_cf_rcp(srslte_mat_2x2_det_simd(a00, a01, a10, a11)); + + /* 2. B = inv(H' x H + No) = inv(A) */ + simd_cf_t _norm2 = srslte_simd_cf_mul(a_det_rcp, _norm); + simd_cf_t b00 = srslte_simd_cf_prod(a11, _norm2); + simd_cf_t b01 = srslte_simd_cf_prod(srslte_simd_cf_neg(a01), _norm2); + simd_cf_t b10 = srslte_simd_cf_prod(srslte_simd_cf_neg(a10), _norm2); + simd_cf_t b11 = srslte_simd_cf_prod(a00, _norm2); + + + /* 3. W = inv(H' x H + No) x H' = B x H' */ + simd_cf_t w00 = srslte_simd_cf_add(srslte_simd_cf_conjprod(b00, h00), srslte_simd_cf_conjprod(b01, h01)); + simd_cf_t w01 = srslte_simd_cf_add(srslte_simd_cf_conjprod(b00, h10), srslte_simd_cf_conjprod(b01, h11)); + simd_cf_t w10 = srslte_simd_cf_add(srslte_simd_cf_conjprod(b10, h00), srslte_simd_cf_conjprod(b11, h01)); + simd_cf_t w11 = srslte_simd_cf_add(srslte_simd_cf_conjprod(b10, h10), srslte_simd_cf_conjprod(b11, h11)); + + /* 4. X = W x Y */ + *x0 = srslte_simd_cf_add(srslte_simd_cf_prod(y0, w00), srslte_simd_cf_prod(y1, w01)); + *x1 = srslte_simd_cf_add(srslte_simd_cf_prod(y0, w10), srslte_simd_cf_prod(y1, w11)); + + /* 5. Extract CSI */ + *csi0 = srslte_simd_f_rcp(srslte_simd_cf_re(b00)); + *csi1 = srslte_simd_f_rcp(srslte_simd_cf_re(b11)); +} + +static inline void srslte_mat_2x2_mmse_simd(simd_cf_t y0, + simd_cf_t y1, + simd_cf_t h00, + simd_cf_t h01, + simd_cf_t h10, + simd_cf_t h11, + simd_cf_t *x0, + simd_cf_t *x1, + float noise_estimate, + float norm) { + simd_f_t csi0, csi1; + srslte_mat_2x2_mmse_csi_simd(y0, y1, h00, h01, h10, h11, x0, x1, &csi0, &csi1, noise_estimate, norm); +} + +#endif /* SRSLTE_SIMD_CF_SIZE != 0 */ +#endif /* SRSLTE_MAT_H */ diff --git a/lib/include/srslte/phy/utils/ringbuffer.h b/lib/include/srslte/phy/utils/ringbuffer.h new file mode 100644 index 0000000..b7a8521 --- /dev/null +++ b/lib/include/srslte/phy/utils/ringbuffer.h @@ -0,0 +1,45 @@ + +#ifndef SRSLTE_RINGBUFFER_H +#define SRSLTE_RINGBUFFER_H + +#include "srslte/config.h" +#include +#include +#include + +typedef struct { + uint8_t *buffer; + bool active; + int capacity; + int count; + int wpm; + int rpm; + pthread_mutex_t mutex; + pthread_cond_t cvar; +} srslte_ringbuffer_t; + + +SRSLTE_API int srslte_ringbuffer_init(srslte_ringbuffer_t *q, + int capacity); + +SRSLTE_API void srslte_ringbuffer_free(srslte_ringbuffer_t *q); + +SRSLTE_API void srslte_ringbuffer_reset(srslte_ringbuffer_t *q); + +SRSLTE_API int srslte_ringbuffer_status(srslte_ringbuffer_t *q); + +SRSLTE_API int srslte_ringbuffer_space(srslte_ringbuffer_t *q); + +SRSLTE_API int srslte_ringbuffer_write(srslte_ringbuffer_t *q, + void *ptr, + int nof_bytes); + +SRSLTE_API int srslte_ringbuffer_read(srslte_ringbuffer_t *q, + void *ptr, + int nof_bytes); + +SRSLTE_API void srslte_ringbuffer_stop(srslte_ringbuffer_t *q); + +#endif // SRSLTE_RINGBUFFER_H + + diff --git a/lib/include/srslte/phy/utils/simd.h b/lib/include/srslte/phy/utils/simd.h new file mode 100644 index 0000000..2a7566e --- /dev/null +++ b/lib/include/srslte/phy/utils/simd.h @@ -0,0 +1,1779 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_SIMD_H +#define SRSLTE_SIMD_H + +#ifdef LV_HAVE_SSE /* AVX, AVX2, FMA, AVX512 are in this group */ +#ifndef __OPTIMIZE__ +#define __OPTIMIZE__ +#endif +#include +#endif /* LV_HAVE_SSE */ +#include + +#ifdef HAVE_NEON +#include +#endif + +/* + * SSE Macros + */ +#ifdef LV_HAVE_SSE +#define _MM_SWAP(X) ((__m128)_mm_shuffle_ps(X, X, _MM_SHUFFLE(2,3,0,1))) +#define _MM_PERM(X) ((__m128)_mm_shuffle_ps(X, X, _MM_SHUFFLE(2,1,3,0))) +#define _MM_MULJ_PS(X) _MM_SWAP(_MM_CONJ_PS(X)) +#define _MM_CONJ_PS(X) (_mm_xor_ps(X, _mm_set_ps(-0.0f, 0.0f, -0.0f, 0.0f))) +#define _MM_SQMOD_PS(X) _MM_PERM(_mm_hadd_ps(_mm_mul_ps(X,X), _mm_set_ps(0.0f, 0.0f, 0.0f, 0.0f))) +#define _MM_PROD_PS(a, b) _mm_addsub_ps(_mm_mul_ps(a,_mm_moveldup_ps(b)),_mm_mul_ps(\ + _mm_shuffle_ps(a,a,0xB1),_mm_movehdup_ps(b))) + +#endif /* LV_HAVE_SSE */ + +/* + * AVX Macros + */ +#ifdef LV_HAVE_AVX + +#define _MM256_MULJ_PS(X) _mm256_permute_ps(_MM256_CONJ_PS(X), 0b10110001) +#define _MM256_CONJ_PS(X) (_mm256_xor_ps(X, _mm256_set_ps(-0.0f, 0.0f, -0.0f, 0.0f, -0.0f, 0.0f, -0.0f, 0.0f))) + +#ifdef LV_HAVE_FMA +#define _MM256_SQMOD_PS(A, B) _mm256_permute_ps(_mm256_hadd_ps(_mm256_fmadd_ps(A, A, _mm256_mul_ps(B,B)), \ + _mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100) +#define _MM256_PROD_PS(a, b) _mm256_fmaddsub_ps(a,_mm256_moveldup_ps(b),\ + _mm256_mul_ps(_mm256_shuffle_ps(a,a,0xB1),_mm256_movehdup_ps(b))) +#else +#define _MM256_SQMOD_PS(A, B) _mm256_permute_ps(_mm256_hadd_ps(_mm256_add_ps(_mm256_mul_ps(A,A), _mm256_mul_ps(B,B)), \ + _mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100) +#define _MM256_PROD_PS(a, b) _mm256_addsub_ps(_mm256_mul_ps(a,_mm256_moveldup_ps(b)),\ + _mm256_mul_ps(_mm256_shuffle_ps(a,a,0xB1),_mm256_movehdup_ps(b))) +#endif /* LV_HAVE_FMA */ +#endif /* LV_HAVE_AVX */ + + +/* + * AVX extension with FMA Macros + */ +#ifdef LV_HAVE_FMA + +#define _MM256_SQMOD_ADD_PS(A, B, C) _mm256_permute_ps(_mm256_hadd_ps(_mm256_fmadd_ps(A, A, _mm256_fmadd_ps(B, B, C)),\ + _mm256_set_ps(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)), 0b11011100) + +#define _MM256_PROD_ADD_PS(A, B, C) _mm256_fmaddsub_ps(A,_mm256_moveldup_ps(B),\ + _mm256_fmaddsub_ps(_mm256_shuffle_ps(A,A,0xB1),_mm256_movehdup_ps(B), C)) + +#define _MM256_PROD_SUB_PS(A, B, C) _mm256_fmaddsub_ps(A,_mm256_moveldup_ps(B),\ + _mm256_fmsubadd_ps(_mm256_shuffle_ps(A,A,0xB1),_mm256_movehdup_ps(B), C)) +#endif /* LV_HAVE_FMA */ + + + +/* Memory Sizes for Single Floating Point and fixed point */ +#ifdef LV_HAVE_AVX512 + +#define SRSLTE_SIMD_F_SIZE 16 +#define SRSLTE_SIMD_CF_SIZE 16 + +#define SRSLTE_SIMD_I_SIZE 16 + +#define SRSLTE_SIMD_S_SIZE 32 +#define SRSLTE_SIMD_C16_SIZE 0 + +#else +#ifdef LV_HAVE_AVX2 + +#define SRSLTE_SIMD_F_SIZE 8 +#define SRSLTE_SIMD_CF_SIZE 8 + +#define SRSLTE_SIMD_I_SIZE 8 + +#define SRSLTE_SIMD_S_SIZE 16 +#define SRSLTE_SIMD_C16_SIZE 16 + +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + +#define SRSLTE_SIMD_F_SIZE 4 +#define SRSLTE_SIMD_CF_SIZE 4 + +#define SRSLTE_SIMD_I_SIZE 4 + +#define SRSLTE_SIMD_S_SIZE 8 +#define SRSLTE_SIMD_C16_SIZE 8 + +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + +#define SRSLTE_SIMD_F_SIZE 4 +#define SRSLTE_SIMD_CF_SIZE 4 + +#define SRSLTE_SIMD_I_SIZE 4 + +#define SRSLTE_SIMD_S_SIZE 8 +#define SRSLTE_SIMD_C16_SIZE 8 + +#else /* LV_HAVE_NEON */ +#define SRSLTE_SIMD_F_SIZE 0 +#define SRSLTE_SIMD_CF_SIZE 0 + +#define SRSLTE_SIMD_I_SIZE 0 + +#define SRSLTE_SIMD_S_SIZE 0 +#define SRSLTE_SIMD_C16_SIZE 0 + +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + +#ifndef ENABLE_C16 +#undef SRSLTE_SIMD_C16_SIZE +#define SRSLTE_SIMD_C16_SIZE 0 +#endif /* ENABLE_C16 */ + +#if SRSLTE_SIMD_F_SIZE + +/* Data types */ +#ifdef LV_HAVE_AVX512 +typedef __m512 simd_f_t; +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 +typedef __m256 simd_f_t; +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE +typedef __m128 simd_f_t; +#else /* HAVE_NEON */ +#ifdef HAVE_NEON +typedef float32x4_t simd_f_t; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + +/* Single precision Floating point functions */ +static inline simd_f_t srslte_simd_f_load(const float *ptr) { +#ifdef LV_HAVE_AVX512 + return _mm512_load_ps(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_load_ps(ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_load_ps(ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_f32(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_loadu(const float *ptr) { +#ifdef LV_HAVE_AVX512 + return _mm512_loadu_ps(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_loadu_ps(ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_loadu_ps(ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_f32(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_f_store(float *ptr, simd_f_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_store_ps(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_store_ps(ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_store_ps(ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_f32(ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_f_storeu(float *ptr, simd_f_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_storeu_ps(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_storeu_ps(ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_storeu_ps(ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_f32(ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_set1(float x) { +#ifdef LV_HAVE_AVX512 + return _mm512_set1_ps(x); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_set1_ps(x); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_set1_ps(x); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vdupq_n_f32(x); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_mul(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_mul_ps(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_mul_ps(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_mul_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vmulq_f32(a,b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_rcp(simd_f_t a) { +#ifdef LV_HAVE_AVX512 + return _mm512_rcp14_ps(a); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_rcp_ps(a); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_rcp_ps(a); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vmulq_f32(vrecpeq_f32(a), vrecpsq_f32(vrecpeq_f32(a), a)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_addsub(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + __m512 r = _mm512_add_ps(a, b); + return _mm512_mask_sub_ps(r, 0b0101010101010101, a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_addsub_ps(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_addsub_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON + float* a_ptr = (float*) &a; + float* b_ptr = (float*) &b; + simd_f_t ret; + float* c_ptr = (float*) &ret; + for(int i = 0; i<4;i++){ + if(i%2==0){ + c_ptr[i] = a_ptr[i] - b_ptr[i]; + }else{ + c_ptr[i] = a_ptr[i] + b_ptr[i]; + } + } + + return ret; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + + + + +static inline simd_f_t srslte_simd_f_sub(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_sub_ps(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_sub_ps(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_sub_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vsubq_f32(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_add(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_add_ps(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_add_ps(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_add_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vaddq_f32(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_zero (void) { +#ifdef LV_HAVE_AVX512 + return _mm512_setzero_ps(); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_setzero_ps(); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_setzero_ps(); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vdupq_n_f32(0); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_swap(simd_f_t a) { +#ifdef LV_HAVE_AVX512 + return _mm512_permute_ps(a, 0b10110001); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_permute_ps(a, 0b10110001); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_shuffle_ps(a, a, 0b10110001); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vcombine_f32(vrev64_f32(vget_low_f32(a)), vrev64_f32(vget_high_f32(a))); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_hadd(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + const __m512i idx1 = _mm512_setr_epi32((0b00000), (0b00010), + (0b00100), (0b00110), + (0b01000), (0b01010), + (0b01100), (0b01110), + (0b10000), (0b10010), + (0b10100), (0b10110), + (0b11000), (0b11010), + (0b11100), (0b11110)); + const __m512i idx2 = _mm512_or_epi32(idx1, _mm512_set1_epi32(1)); + + simd_f_t a1 = _mm512_permutex2var_ps(a, idx1, b); + simd_f_t b1 = _mm512_permutex2var_ps(a, idx2, b); + return _mm512_add_ps(a1, b1); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + simd_f_t a1 = _mm256_permute2f128_ps(a, b, 0b00100000); + simd_f_t b1 = _mm256_permute2f128_ps(a, b, 0b00110001); + return _mm256_hadd_ps(a1, b1); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_hadd_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vcombine_f32( vpadd_f32( vget_low_f32(a), vget_high_f32(a) ), vpadd_f32( vget_low_f32(b), vget_high_f32(b) ) ); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_sqrt(simd_f_t a) { +#ifdef LV_HAVE_AVX512 + return _mm512_sqrt_ps(a); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_sqrt_ps(a); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_sqrt_ps(a); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + float32x4_t sqrt_reciprocal = vrsqrteq_f32(a); + sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a,sqrt_reciprocal), sqrt_reciprocal),sqrt_reciprocal); + float32x4_t result = vmulq_f32(a,sqrt_reciprocal); + + /* Detect zeros in NEON 1/sqrtf for preventing NaN */ + float32x4_t zeros = vmovq_n_f32(0); /* Zero vector */ + uint32x4_t mask = vceqq_f32(a, zeros); /* Zero vector mask */ + return vbslq_f32(mask, zeros, result); /* Force zero results and return */ +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_neg(simd_f_t a) { +#ifdef LV_HAVE_AVX512 + return _mm512_xor_ps(_mm512_set1_ps(-0.0f), a); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_xor_ps(_mm256_set1_ps(-0.0f), a); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_xor_ps(_mm_set1_ps(-0.0f), a); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vnegq_f32(a); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_neg_mask(simd_f_t a, simd_f_t mask) { +#ifdef LV_HAVE_AVX512 + return _mm512_xor_ps(mask, a); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_xor_ps(mask, a); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_xor_ps(mask, a); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return (float32x4_t) veorq_s32((int32x4_t) a, (int32x4_t) mask); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_f_abs(simd_f_t a) { +#ifdef LV_HAVE_AVX512 + return _mm512_andnot_ps(_mm512_set1_ps(-0.0f), a); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_andnot_ps(_mm256_set1_ps(-0.0f), a); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_andnot_ps(_mm_set1_ps(-0.0f), a); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vqabsq_s32(a); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + + +#endif /* SRSLTE_SIMD_F_SIZE */ + + +#if SRSLTE_SIMD_CF_SIZE + +#ifdef HAVE_NEON +typedef float32x4x2_t simd_cf_t; +#else +typedef struct { + simd_f_t re; + simd_f_t im; +} simd_cf_t; +#endif + +/* Complex Single precission Floating point functions */ +static inline simd_cf_t srslte_simd_cfi_load(const cf_t *ptr) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + __m512 in1 = _mm512_load_ps((float*)(ptr)); + __m512 in2 = _mm512_load_ps((float*)(ptr + SRSLTE_SIMD_CF_SIZE/2)); + ret.re = _mm512_permutex2var_ps(in1, _mm512_setr_epi32(0x00, 0x02, 0x04, 0x06, + 0x08, 0x0A, 0x0C, 0x0E, + 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1A, 0x1C, 0x1E), in2); + ret.im = _mm512_permutex2var_ps(in1, _mm512_setr_epi32(0x01, 0x03, 0x05, 0x07, + 0x09, 0x0B, 0x0D, 0x0F, + 0x11, 0x13, 0x15, 0x17, + 0x19, 0x1B, 0x1D, 0x1F), in2); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + __m256 in1 = _mm256_permute_ps(_mm256_load_ps((float*)(ptr)), 0b11011000); + __m256 in2 = _mm256_permute_ps(_mm256_load_ps((float*)(ptr + 4)), 0b11011000); + ret.re = _mm256_unpacklo_ps(in1, in2); + ret.im = _mm256_unpackhi_ps(in1, in2); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + __m128 i1 = _mm_load_ps((float*)(ptr)); + __m128 i2 = _mm_load_ps((float*)(ptr + 2)); + ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2,0,2,0)); + ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3,1,3,1)); +#else +#ifdef HAVE_NEON + ret = vld2q_f32((float*)(ptr)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +/* Complex Single precission Floating point functions */ +static inline simd_cf_t srslte_simd_cfi_loadu(const cf_t *ptr) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + __m512 in1 = _mm512_loadu_ps((float*)(ptr)); + __m512 in2 = _mm512_loadu_ps((float*)(ptr + SRSLTE_SIMD_CF_SIZE/2)); + ret.re = _mm512_permutex2var_ps(in1, _mm512_setr_epi32(0x00, 0x02, 0x04, 0x06, + 0x08, 0x0A, 0x0C, 0x0E, + 0x10, 0x12, 0x14, 0x16, + 0x18, 0x1A, 0x1C, 0x1E), in2); + ret.im = _mm512_permutex2var_ps(in1, _mm512_setr_epi32(0x01, 0x03, 0x05, 0x07, + 0x09, 0x0B, 0x0D, 0x0F, + 0x11, 0x13, 0x15, 0x17, + 0x19, 0x1B, 0x1D, 0x1F), in2); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + __m256 in1 = _mm256_permute_ps(_mm256_loadu_ps((float*)(ptr)), 0b11011000); + __m256 in2 = _mm256_permute_ps(_mm256_loadu_ps((float*)(ptr + 4)), 0b11011000); + ret.re = _mm256_unpacklo_ps(in1, in2); + ret.im = _mm256_unpackhi_ps(in1, in2); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + __m128 i1 = _mm_loadu_ps((float*)(ptr)); + __m128 i2 = _mm_loadu_ps((float*)(ptr + 2)); + ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2,0,2,0)); + ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3,1,3,1)); +#else +#ifdef HAVE_NEON + ret = vld2q_f32((float*)(ptr)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_load(const float *re, const float *im) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_load_ps(re); + ret.im = _mm512_load_ps(im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_load_ps(re); + ret.im = _mm256_load_ps(im); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_load_ps(re); + ret.im = _mm_load_ps(im); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + ret.val[0] = vld1q_f32(re); + ret.val[1] = vld1q_f32(im); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_loadu(const float *re, const float *im) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_loadu_ps(re); + ret.im = _mm512_loadu_ps(im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_loadu_ps(re); + ret.im = _mm256_loadu_ps(im); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_loadu_ps(re); + ret.im = _mm_loadu_ps(im); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + ret.val[0] = vld1q_f32(re); + ret.val[1] = vld1q_f32(im); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline void srslte_simd_cfi_store(cf_t *ptr, simd_cf_t simdreg) { +#ifdef LV_HAVE_AVX512 + __m512 s1 = _mm512_permutex2var_ps(simdreg.re, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, + 0x02, 0x12, 0x03, 0x13, + 0x04, 0x14, 0x05, 0x15, + 0x06, 0x16, 0x07, 0x17), simdreg.im); + __m512 s2 = _mm512_permutex2var_ps(simdreg.re, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, + 0x0A, 0x1A, 0x0B, 0x1B, + 0x0C, 0x1C, 0x0D, 0x1D, + 0x0E, 0x1E, 0x0F, 0x1F), simdreg.im); + _mm512_store_ps((float*)(ptr), s1); + _mm512_store_ps((float*)(ptr + 8), s2); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); + __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); + _mm256_store_ps((float*)(ptr), _mm256_unpacklo_ps(out1, out2)); + _mm256_store_ps((float*)(ptr + 4), _mm256_unpackhi_ps(out1, out2)); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_store_ps((float*)(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); + _mm_store_ps((float*)(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst2q_f32((float*)(ptr), simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_cfi_storeu(cf_t *ptr, simd_cf_t simdreg) { +#ifdef LV_HAVE_AVX512 + __m512 s1 = _mm512_permutex2var_ps(simdreg.re, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, + 0x02, 0x12, 0x03, 0x13, + 0x04, 0x14, 0x05, 0x15, + 0x06, 0x16, 0x07, 0x17), simdreg.im); + __m512 s2 = _mm512_permutex2var_ps(simdreg.re, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, + 0x0A, 0x1A, 0x0B, 0x1B, + 0x0C, 0x1C, 0x0D, 0x1D, + 0x0E, 0x1E, 0x0F, 0x1F), simdreg.im); + _mm512_storeu_ps((float*)(ptr), s1); + _mm512_storeu_ps((float*)(ptr + 8), s2); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); + __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); + _mm256_storeu_ps((float*)(ptr), _mm256_unpacklo_ps(out1, out2)); + _mm256_storeu_ps((float*)(ptr + 4), _mm256_unpackhi_ps(out1, out2)); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_storeu_ps((float*)(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); + _mm_storeu_ps((float*)(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst2q_f32((float*)(ptr), simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_cf_store(float *re, float *im, simd_cf_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_store_ps(re, simdreg.re); + _mm512_store_ps(im, simdreg.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_store_ps(re, simdreg.re); + _mm256_store_ps(im, simdreg.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_SSE + _mm_store_ps((float *) re, simdreg.re); + _mm_store_ps((float *) im, simdreg.im); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst1q_f32((float *) re, simdreg.val[0]); + vst1q_f32((float *) im, simdreg.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_cf_storeu(float *re, float *im, simd_cf_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_storeu_ps(re, simdreg.re); + _mm512_storeu_ps(im, simdreg.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_storeu_ps(re, simdreg.re); + _mm256_storeu_ps(im, simdreg.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_SSE + _mm_storeu_ps((float *) re, simdreg.re); + _mm_storeu_ps((float *) im, simdreg.im); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst1q_f32((float *) re, simdreg.val[0]); + vst1q_f32((float *) im, simdreg.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_f_t srslte_simd_cf_re(simd_cf_t in) { +#ifdef HAVE_NEON + simd_f_t out = in.val[0]; +#else + simd_f_t out = in.re; +#endif /*HAVE_NEON*/ +#ifndef LV_HAVE_AVX512 +#ifdef LV_HAVE_AVX2 + /* Permute for AVX registers (mis SSE registers) */ + const __m256i idx = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7); + out = _mm256_permutevar8x32_ps(out, idx); +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return out; +} + +static inline simd_cf_t srslte_simd_cf_set1 (cf_t x) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_set1_ps(__real__ x); + ret.im = _mm512_set1_ps(__imag__ x); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_set1_ps(__real__ x); + ret.im = _mm256_set1_ps(__imag__ x); +#else +#ifdef LV_HAVE_SSE + ret.re = _mm_set1_ps(__real__ x); + ret.im = _mm_set1_ps(__imag__ x); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + ret.val[0] = vdupq_n_f32(__real__ x); + ret.val[1] = vdupq_n_f32(__imag__ x); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_prod (simd_cf_t a, simd_cf_t b) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_sub_ps(_mm512_mul_ps(a.re, b.re), + _mm512_mul_ps(a.im, b.im)); + ret.im = _mm512_add_ps(_mm512_mul_ps(a.re, b.im), + _mm512_mul_ps(a.im, b.re)); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 +#ifdef LV_HAVE_FMA + ret.re = _mm256_fmsub_ps(a.re, b.re, _mm256_mul_ps(a.im, b.im)); + ret.im = _mm256_fmadd_ps(a.re, b.im, _mm256_mul_ps(a.im, b.re)); +#else /* LV_HAVE_FMA */ + ret.re = _mm256_sub_ps(_mm256_mul_ps(a.re, b.re), + _mm256_mul_ps(a.im, b.im)); + ret.im = _mm256_add_ps(_mm256_mul_ps(a.re, b.im), + _mm256_mul_ps(a.im, b.re)); +#endif /* LV_HAVE_FMA */ +#else +#ifdef LV_HAVE_SSE + ret.re = _mm_sub_ps(_mm_mul_ps(a.re, b.re), + _mm_mul_ps(a.im, b.im)); + ret.im = _mm_add_ps(_mm_mul_ps(a.re, b.im), + _mm_mul_ps(a.im, b.re)); +#else +#ifdef HAVE_NEON + ret.val[0] = vsubq_f32(vmulq_f32(a.val[0],b.val[0]), + vmulq_f32(a.val[1],b.val[1])); + ret.val[1] = vaddq_f32(vmulq_f32(a.val[0],b.val[1]), + vmulq_f32(a.val[1],b.val[0])); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_conjprod (simd_cf_t a, simd_cf_t b) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_add_ps(_mm512_mul_ps(a.re, b.re), + _mm512_mul_ps(a.im, b.im)); + ret.im = _mm512_sub_ps(_mm512_mul_ps(a.im, b.re), + _mm512_mul_ps(a.re, b.im)); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_add_ps(_mm256_mul_ps(a.re, b.re), + _mm256_mul_ps(a.im, b.im)); + ret.im = _mm256_sub_ps(_mm256_mul_ps(a.im, b.re), + _mm256_mul_ps(a.re, b.im)); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_add_ps(_mm_mul_ps(a.re, b.re), + _mm_mul_ps(a.im, b.im)); + ret.im = _mm_sub_ps(_mm_mul_ps(a.im, b.re), + _mm_mul_ps(a.re, b.im)); +#else +#ifdef HAVE_NEON + ret.val[0] = vaddq_f32(vmulq_f32(a.val[0],b.val[0]), + vmulq_f32(a.val[1],b.val[1])); + ret.val[1] = vsubq_f32(vmulq_f32(a.val[1],b.val[0]), + vmulq_f32(a.val[0],b.val[1])); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_add (simd_cf_t a, simd_cf_t b) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_add_ps(a.re, b.re); + ret.im = _mm512_add_ps(a.im, b.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_add_ps(a.re, b.re); + ret.im = _mm256_add_ps(a.im, b.im); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_add_ps(a.re, b.re); + ret.im = _mm_add_ps(a.im, b.im); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + ret.val[0] = vaddq_f32(a.val[0],b.val[0]); + ret.val[1] = vaddq_f32(a.val[1],b.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_sub (simd_cf_t a, simd_cf_t b) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_sub_ps(a.re, b.re); + ret.im = _mm512_sub_ps(a.im, b.im); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_sub_ps(a.re, b.re); + ret.im = _mm256_sub_ps(a.im, b.im); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_sub_ps(a.re, b.re); + ret.im = _mm_sub_ps(a.im, b.im); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + ret.val[0] = vsubq_f32(a.val[0],b.val[0]); + ret.val[1] = vsubq_f32(a.val[1],b.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_mul (simd_cf_t a, simd_f_t b) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_mul_ps(a.re, b); + ret.im = _mm512_mul_ps(a.im, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + b = _mm256_permutevar8x32_ps(b, _mm256_setr_epi32(0,4,1,5,2,6,3,7)); + ret.re = _mm256_mul_ps(a.re, b); + ret.im = _mm256_mul_ps(a.im, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_mul_ps(a.re, b); + ret.im = _mm_mul_ps(a.im, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + ret.val[0] = vmulq_f32(a.val[0],b); + ret.val[1] = vmulq_f32(a.val[1],b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_rcp (simd_cf_t a) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + simd_f_t a2re = _mm512_mul_ps(a.re, a.re); + simd_f_t a2im = _mm512_mul_ps(a.im, a.im); + simd_f_t mod2 = _mm512_add_ps(a2re, a2im); + simd_f_t rcp = _mm512_rcp14_ps(mod2); + simd_f_t neg_a_im = _mm512_xor_ps(_mm512_set1_ps(-0.0f), a.im); + ret.re = _mm512_mul_ps(a.re, rcp); + ret.im = _mm512_mul_ps(neg_a_im, rcp); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + simd_f_t a2re = _mm256_mul_ps(a.re, a.re); + simd_f_t a2im = _mm256_mul_ps(a.im, a.im); + simd_f_t mod2 = _mm256_add_ps(a2re, a2im); + simd_f_t rcp = _mm256_rcp_ps(mod2); + simd_f_t neg_a_im = _mm256_xor_ps(_mm256_set1_ps(-0.0f), a.im); + ret.re = _mm256_mul_ps(a.re, rcp); + ret.im = _mm256_mul_ps(neg_a_im, rcp); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + simd_f_t a2re = _mm_mul_ps(a.re, a.re); + simd_f_t a2im = _mm_mul_ps(a.im, a.im); + simd_f_t mod2 = _mm_add_ps(a2re, a2im); + simd_f_t rcp = _mm_rcp_ps(mod2); + simd_f_t neg_a_im = _mm_xor_ps(_mm_set1_ps(-0.0f), a.im); + ret.re = _mm_mul_ps(a.re, rcp); + ret.im = _mm_mul_ps(neg_a_im, rcp); + #else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + simd_f_t a2re = vmulq_f32(a.val[0], a.val[0]); + simd_f_t a2im = vmulq_f32(a.val[1], a.val[1]); + simd_f_t mod2 = vaddq_f32(a2re, a2im); + simd_f_t rcp = vmulq_f32(vrecpeq_f32(mod2), vrecpsq_f32(vrecpeq_f32(mod2), mod2)); + simd_f_t neg_a_im = vnegq_f32(a.val[1]); + ret.val[0] = vmulq_f32(a.val[0], rcp); + ret.val[1] = vmulq_f32(neg_a_im, rcp); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_neg (simd_cf_t a) { + simd_cf_t ret; +#if LV_HAVE_NEON + ret.val[0] = srslte_simd_f_neg(a.val[0]); + ret.val[1] = srslte_simd_f_neg(a.val[1]); +#else /* LV_HAVE_NEON */ + ret.re = srslte_simd_f_neg(a.re); + ret.im = srslte_simd_f_neg(a.im); +#endif /* LV_HAVE_NEON */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_neg_mask (simd_cf_t a, simd_f_t mask) { + simd_cf_t ret; +#ifndef LV_HAVE_AVX512 +#ifdef LV_HAVE_AVX2 + mask = _mm256_permutevar8x32_ps(mask, _mm256_setr_epi32(0,4,1,5,2,6,3,7)); +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +#if LV_HAVE_NEON + ret.val[0] = srslte_simd_f_neg_mask(a.val[0], mask); + ret.val[1] = srslte_simd_f_neg_mask(a.val[1], mask); +#else /* LV_HAVE_NEON */ + ret.re = srslte_simd_f_neg_mask(a.re, mask); + ret.im = srslte_simd_f_neg_mask(a.im, mask); +#endif /* LV_HAVE_NEON */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_conj (simd_cf_t a) { + simd_cf_t ret; +#if LV_HAVE_NEON + ret.val[0] = a.val[0]; + ret.val[1] = srslte_simd_f_neg(a.val[1]); +#else /* LV_HAVE_NEON */ + ret.re = a.re; + ret.im = srslte_simd_f_neg(a.im); +#endif /* LV_HAVE_NEON */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_mulj (simd_cf_t a) { + simd_cf_t ret; +#if LV_HAVE_NEON + ret.val[0] = srslte_simd_f_neg(a.val[1]); + ret.val[1] = a.val[0]; +#else /* LV_HAVE_NEON */ + ret.re = srslte_simd_f_neg(a.im); + ret.im = a.re; +#endif /* LV_HAVE_NEON */ + return ret; +} + +static inline simd_cf_t srslte_simd_cf_zero (void) { + simd_cf_t ret; +#ifdef LV_HAVE_AVX512 + ret.re = _mm512_setzero_ps(); + ret.im = _mm512_setzero_ps(); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + ret.re = _mm256_setzero_ps(); + ret.im = _mm256_setzero_ps(); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + ret.re = _mm_setzero_ps(); + ret.im = _mm_setzero_ps(); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + ret.val[0] = vdupq_n_f32(0); + ret.val[1] = vdupq_n_f32(0); +#endif /* HAVE_NEON */ +#endif /* HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +#endif /* SRSLTE_SIMD_CF_SIZE */ + +#if SRSLTE_SIMD_I_SIZE + +#ifdef LV_HAVE_AVX512 +typedef __m512i simd_i_t; +typedef __mmask16 simd_sel_t; +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 +typedef __m256i simd_i_t; +typedef __m256 simd_sel_t; +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE +typedef __m128i simd_i_t; +typedef __m128 simd_sel_t; +#else /* LV_HAVE_AVX2 */ +#ifdef HAVE_NEON +typedef int32x4_t simd_i_t; +typedef int32x4_t simd_sel_t; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + +static inline simd_i_t srslte_simd_i_load(int *x) { +#ifdef LV_HAVE_AVX512 + return _mm512_load_epi32((__m512i*)x); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_load_si256((__m256i*)x); +#else +#ifdef LV_HAVE_SSE + return _mm_load_si128((__m128i*)x); +#else +#ifdef HAVE_NEON + return vld1q_s32((int*)x); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_i_store(int *x, simd_i_t reg) { +#ifdef LV_HAVE_AVX512 + _mm512_store_epi32((__m512i*)x, reg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_store_si256((__m256i*)x, reg); +#else +#ifdef LV_HAVE_SSE + _mm_store_si128((__m128i*)x, reg); +#else +#ifdef HAVE_NEON + vst1q_s32((int*)x, reg); +#endif /*HAVE_NEON*/ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_i_t srslte_simd_i_set1(int x) { +#ifdef LV_HAVE_AVX512 + return _mm512_set1_epi32(x); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_set1_epi32(x); +#else +#ifdef LV_HAVE_SSE + return _mm_set1_epi32(x); +#else +#ifdef HAVE_NEON + return vdupq_n_s32(x); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_i_t srslte_simd_i_add(simd_i_t a, simd_i_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_add_epi32(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_add_epi32(a, b); +#else +#ifdef LV_HAVE_SSE + return _mm_add_epi32(a, b); +#else +#ifdef HAVE_NEON + return vaddq_s32(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_sel_t srslte_simd_f_max(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_cmp_ps_mask(a, b, _CMP_GT_OS); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_cmp_ps(a, b, _CMP_GT_OS); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return (simd_sel_t) _mm_cmpgt_ps(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return (simd_sel_t) vcgtq_f32(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_i_t srslte_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t selector) { +#ifdef LV_HAVE_AVX512 + return (__m512i) _mm512_mask_blend_ps( selector, (__m512)a, (__m512) b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return (__m256i) _mm256_blendv_ps((__m256) a,(__m256) b, selector); +#else +#ifdef LV_HAVE_SSE + return (__m128i) _mm_blendv_ps((__m128)a, (__m128)b, selector); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON + int* a_ptr = (int*) &a; + int* b_ptr = (int*) &b; + simd_i_t ret; + int* sel = (int*) &selector; + int* c_ptr = (int*) &ret; + for(int i = 0;i<4;i++) + { + if(sel[i] == -1){ + c_ptr[i] = b_ptr[i]; + }else{ + c_ptr[i] = a_ptr[i]; + } + } + return ret; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +#endif /* SRSLTE_SIMD_I_SIZE*/ + + +#if SRSLTE_SIMD_S_SIZE + + +#ifdef LV_HAVE_AVX512 +typedef __m512i simd_s_t; +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 +typedef __m256i simd_s_t; +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE +typedef __m128i simd_s_t; +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON +typedef int16x8_t simd_s_t; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + +static inline simd_s_t srslte_simd_s_load(const int16_t *ptr) { +#ifdef LV_HAVE_AVX512 + return _mm512_load_si512(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_load_si256((__m256i*) ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_load_si128((__m128i*) ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_s16(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_s_t srslte_simd_s_loadu(const int16_t *ptr) { +#ifdef LV_HAVE_AVX512 + return _mm512_loadu_si512(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_loadu_si256((__m256i*) ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_loadu_si128((__m128i*) ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_s16(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_s_store(int16_t *ptr, simd_s_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_store_si512(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_store_si256((__m256i*) ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_store_si128((__m128i*) ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_s16( ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_s_storeu(int16_t *ptr, simd_s_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_storeu_si512(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_storeu_si256((__m256i*) ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_storeu_si128((__m128i*) ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_s16(ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} +static inline simd_s_t srslte_simd_s_zero(void) { +#ifdef LV_HAVE_AVX512 + return _mm512_setzero_si512(); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_setzero_si256(); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_setzero_si128(); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vdupq_n_s16(0); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_s_t srslte_simd_s_mul(simd_s_t a, simd_s_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_mullo_epi16(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_mullo_epi16(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_mullo_epi16(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vmulq_s16(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_s_t srslte_simd_s_add(simd_s_t a, simd_s_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_add_epi16(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_add_epi16(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_add_epi16(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vaddq_s16(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_s_t srslte_simd_s_sub(simd_s_t a, simd_s_t b) { +#ifdef LV_HAVE_AVX512 + return _mm512_sub_epi16(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_sub_epi16(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_sub_epi16(a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vsubq_s16(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +#endif /* SRSLTE_SIMD_S_SIZE */ + + +#if SRSLTE_SIMD_C16_SIZE + +typedef +#ifdef LV_HAVE_AVX512 + struct { + union { + __m512i m512; + int16_t i16[32]; + } re; + union { + __m512i m512; + int16_t i16[32]; + } im; +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + struct { + union { + __m256i m256; + int16_t i16[16]; + } re; + union { + __m256i m256; + int16_t i16[16]; + } im; +#else +#ifdef LV_HAVE_SSE + struct { + union { + __m128i m128; + int16_t i16[8]; + } re; + union { + __m128i m128; + int16_t i16[8]; + } im; +#else +#ifdef HAVE_NEON + union { + int16x8x2_t m128; + int16_t i16[16]; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} simd_c16_t; + +/* Fixed point precision (16-bit) functions */ +static inline simd_c16_t srslte_simd_c16i_load(const c16_t *ptr) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX512 + __m512i in1 = _mm512_load_si512((__m512i*)(ptr)); + __m512i in2 = _mm512_load_si512((__m512i*)(ptr + 8)); + ret.re.m512 = _mm512_mask_blend_epi16(0xAAAAAAAA, in1,_mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in2, 0b10100000), 0b10100000)); + ret.im.m512 = _mm512_mask_blend_epi16(0xAAAAAAAA, _mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in1, 0b11110101), 0b11110101),in2); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_AVX2 + __m256i in1 = _mm256_load_si256((__m256i*)(ptr)); + __m256i in2 = _mm256_load_si256((__m256i*)(ptr + 8)); + ret.re.m256 = _mm256_blend_epi16(in1,_mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); + ret.im.m256 = _mm256_blend_epi16(_mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in1, 0b11110101), 0b11110101),in2, 0b10101010); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + __m128i in1 = _mm_load_si128((__m128i*)(ptr)); + __m128i in2 = _mm_load_si128((__m128i*)(ptr + 8)); + ret.re.m128 = _mm_blend_epi16(in1,_mm_shufflelo_epi16(_mm_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); + ret.im.m128 = _mm_blend_epi16(_mm_shufflelo_epi16(_mm_shufflehi_epi16(in1, 0b11110101), 0b11110101),in2, 0b10101010); +#else /* LV_HAVE_SSE*/ +#ifdef HAVE_NEON + ret.m128 = vld2q_s16((int16_t*)(ptr)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + return ret; +} + +static inline simd_c16_t srslte_simd_c16_load(const int16_t *re, const int16_t *im) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX2 + ret.re.m256 = _mm256_load_si256((__m256i*)(re)); + ret.im.m256 = _mm256_load_si256((__m256i*)(im)); +#else +#ifdef LV_HAVE_SSE + ret.re.m128 = _mm_load_si128((__m128i*)(re)); + ret.im.m128 = _mm_load_si128((__m128i*)(im)); +#else /* LV_HAVE_SSE*/ +#ifdef HAVE_NEON + ret.m128.val[0] = vld1q_s16((int16_t*)(re)); + ret.m128.val[1] = vld1q_s16((int16_t*)(im)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ + return ret; +} + +static inline simd_c16_t srslte_simd_c16_loadu(const int16_t *re, const int16_t *im) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX2 + ret.re.m256 = _mm256_loadu_si256((__m256i*)(re)); + ret.im.m256 = _mm256_loadu_si256((__m256i*)(im)); +#else +#ifdef LV_HAVE_SSE + ret.re.m128 = _mm_loadu_si128((__m128i*)(re)); + ret.im.m128 = _mm_loadu_si128((__m128i*)(im)); +#else /* LV_HAVE_SSE*/ +#ifdef HAVE_NEON + ret.m128.val[0] = vld1q_s16((int16_t*)(re)); + ret.m128.val[1] = vld1q_s16((int16_t*)(im)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ + return ret; +} + +static inline void srslte_simd_c16i_store(c16_t *ptr, simd_c16_t simdreg) { +#ifdef LV_HAVE_AVX2 + __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re.m256, 0b10110001), 0b10110001); + __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im.m256, 0b10110001), 0b10110001); + _mm256_store_si256((__m256i *) (ptr), _mm256_blend_epi16(simdreg.re.m256, im_sw, 0b10101010)); + _mm256_store_si256((__m256i *) (ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im.m256, 0b10101010)); +#else +#ifdef LV_HAVE_SSE + __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re.m128, 0b10110001), 0b10110001); + __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im.m128, 0b10110001), 0b10110001); + _mm_store_si128((__m128i *) (ptr), _mm_blend_epi16(simdreg.re.m128, im_sw, 0b10101010)); + _mm_store_si128((__m128i *) (ptr + 8), _mm_blend_epi16(re_sw, simdreg.im.m128, 0b10101010)); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst2q_s16((int16_t*)(ptr) ,simdreg.m128); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +} + +static inline void srslte_simd_c16i_storeu(c16_t *ptr, simd_c16_t simdreg) { +#ifdef LV_HAVE_AVX2 + __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re.m256, 0b10110001), 0b10110001); + __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im.m256, 0b10110001), 0b10110001); + _mm256_storeu_si256((__m256i *) (ptr), _mm256_blend_epi16(simdreg.re.m256, im_sw, 0b10101010)); + _mm256_storeu_si256((__m256i *) (ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im.m256, 0b10101010)); +#else +#ifdef LV_HAVE_SSE + __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re.m128, 0b10110001), 0b10110001); + __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im.m128, 0b10110001), 0b10110001); + _mm_storeu_si128((__m128i *) (ptr), _mm_blend_epi16(simdreg.re.m128, im_sw, 0b10101010)); + _mm_storeu_si128((__m128i *) (ptr + 8), _mm_blend_epi16(re_sw, simdreg.im.m128, 0b10101010)); +#else /*HAVE_NEON*/ +#ifdef HAVE_NEON + vst2q_s16((int16_t*)(ptr) ,simdreg.m128); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +} + +static inline void srslte_simd_c16_store(int16_t *re, int16_t *im, simd_c16_t simdreg) { +#ifdef LV_HAVE_AVX2 + _mm256_store_si256((__m256i *) re, simdreg.re.m256); + _mm256_store_si256((__m256i *) im, simdreg.im.m256); +#else +#ifdef LV_HAVE_SSE + _mm_store_si128((__m128i *) re, simdreg.re.m128); + _mm_store_si128((__m128i *) im, simdreg.im.m128); +#else +#ifdef HAVE_NEON + vst1q_s16((int16_t *) re, simdreg.m128.val[0]); + vst1q_s16((int16_t *) im, simdreg.m128.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +} + +static inline void srslte_simd_c16_storeu(int16_t *re, int16_t *im, simd_c16_t simdreg) { +#ifdef LV_HAVE_AVX2 + _mm256_storeu_si256((__m256i *) re, simdreg.re.m256); + _mm256_storeu_si256((__m256i *) im, simdreg.im.m256); +#else +#ifdef LV_HAVE_SSE + _mm_storeu_si128((__m128i *) re, simdreg.re.m128); + _mm_storeu_si128((__m128i *) im, simdreg.im.m128); +#else +#ifdef HAVE_NEON + vst1q_s16((int16_t *) re, simdreg.m128.val[0]); + vst1q_s16((int16_t *) im, simdreg.m128.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +} + +static inline simd_c16_t srslte_simd_c16_prod (simd_c16_t a, simd_c16_t b) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX2 + ret.re.m256 = _mm256_sub_epi16(_mm256_mulhrs_epi16(a.re.m256, _mm256_slli_epi16(b.re.m256, 1)), + _mm256_mulhrs_epi16(a.im.m256, _mm256_slli_epi16(b.im.m256, 1))); + ret.im.m256 = _mm256_add_epi16(_mm256_mulhrs_epi16(a.re.m256, _mm256_slli_epi16(b.im.m256, 1)), + _mm256_mulhrs_epi16(a.im.m256, _mm256_slli_epi16(b.re.m256, 1))); +#else +#ifdef LV_HAVE_SSE + ret.re.m128 = _mm_sub_epi16(_mm_mulhrs_epi16(a.re.m128, _mm_slli_epi16(b.re.m128, 1)), + _mm_mulhrs_epi16(a.im.m128, _mm_slli_epi16(b.im.m128, 1))); + ret.im.m128 = _mm_add_epi16(_mm_mulhrs_epi16(a.re.m128, _mm_slli_epi16(b.im.m128, 1)), + _mm_mulhrs_epi16(a.im.m128, _mm_slli_epi16(b.re.m128, 1))); +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ + return ret; +} + +static inline simd_c16_t srslte_simd_c16_add (simd_c16_t a, simd_c16_t b) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX2 + ret.re.m256 = _mm256_add_epi16(a.re.m256, b.re.m256); + ret.im.m256 = _mm256_add_epi16(a.im.m256, b.im.m256); +#else +#ifdef LV_HAVE_SSE + ret.re.m128 = _mm_add_epi16(a.re.m128, b.re.m128); + ret.im.m128 = _mm_add_epi16(a.im.m128, b.im.m128); +#else +#ifdef HAVE_NEON + ret.m128.val[0] = vaddq_s16(a.m128.val[0],a.m128.val[0]); + ret.m128.val[1] = vaddq_s16(a.m128.val[1],a.m128.val[1]); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ + return ret; +} + +static inline simd_c16_t srslte_simd_c16_zero (void) { + simd_c16_t ret; +#ifdef LV_HAVE_AVX2 + ret.re.m256 = _mm256_setzero_si256(); + ret.im.m256 = _mm256_setzero_si256(); +#else +#ifdef LV_HAVE_SSE + ret.re.m128 = _mm_setzero_si128(); + ret.im.m128 = _mm_setzero_si128(); +#else +#ifdef HAVE_NEON + ret.m128.val[0] = vdupq_n_s16(0); + ret.m128.val[1] = vdupq_n_s16(0); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ + return ret; +} + +#endif /* SRSLTE_SIMD_C16_SIZE */ + +#if SRSLTE_SIMD_F_SIZE && SRSLTE_SIMD_S_SIZE + +static inline simd_s_t srslte_simd_convert_2f_s(simd_f_t a, simd_f_t b) { +#ifdef LV_HAVE_AVX512 + __m512 aa = _mm512_permutex2var_ps(a, _mm512_setr_epi32(0x00, 0x01, 0x02, 0x03, + 0x08, 0x09, 0x0A, 0x0B, + 0x10, 0x11, 0x12, 0x13, + 0x18, 0x19, 0x1A, 0x1B), b); + __m512 bb = _mm512_permutex2var_ps(a, _mm512_setr_epi32(0x04, 0x05, 0x06, 0x07, + 0x0C, 0x0D, 0x0E, 0x0F, + 0x14, 0x15, 0x16, 0x17, + 0x1C, 0x1D, 0x1E, 0x1F), b); + __m512i ai = _mm512_cvttps_epi32(aa); + __m512i bi = _mm512_cvttps_epi32(bb); + return _mm512_packs_epi32(ai, bi); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + __m256 aa = _mm256_permute2f128_ps(a, b, 0x20); + __m256 bb = _mm256_permute2f128_ps(a, b, 0x31); + __m256i ai = _mm256_cvttps_epi32(aa); + __m256i bi = _mm256_cvttps_epi32(bb); + return _mm256_packs_epi32(ai, bi); +#else +#ifdef LV_HAVE_SSE + __m128i ai = _mm_cvttps_epi32(a); + __m128i bi = _mm_cvttps_epi32(b); + return _mm_packs_epi32(ai, bi); + #else +#ifdef HAVE_NEON + int32x4_t ai = vcvtq_s32_f32(a); + int32x4_t bi = vcvtq_s32_f32(b); + return (simd_s_t)vcombine_s16(vqmovn_s32(ai), vqmovn_s32(bi)); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +#endif /* SRSLTE_SIMD_F_SIZE && SRSLTE_SIMD_C16_SIZE */ + +#if SRSLTE_SIMD_B_SIZE +/* Data types */ +#ifdef LV_HAVE_AVX512 +typedef __m512i simd_b_t; +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 +typedef __m256i simd_b_t; +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE +typedef __m128i simd_b_t; +#else /* HAVE_NEON */ +#ifdef HAVE_NEON +typedef int8x16_t simd_b_t; +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ + + + +static inline simd_b_t srslte_simd_b_load(int8_t *ptr){ +#ifdef LV_HAVE_AVX512 + return _mm512_load_si512(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_load_si256((__m256i*) ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_load_si128((__m128i*) ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_s8(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline simd_b_t srslte_simd_b_loadu(int8_t *ptr){ +#ifdef LV_HAVE_AVX512 + return _mm512_loadu_si512(ptr); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_loadu_si256((__m256i*) ptr); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_loadu_si128((__m128i*) ptr); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return vld1q_s8(ptr); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_b_store(int8_t *ptr, simd_b_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_store_si512(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_store_si256((__m256i*) ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_store_si128((__m128i*) ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_s8( ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +static inline void srslte_simd_b_storeu(int8_t *ptr, simd_b_t simdreg) { +#ifdef LV_HAVE_AVX512 + _mm512_storeu_si512(ptr, simdreg); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + _mm256_storeu_si256((__m256i*) ptr, simdreg); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + _mm_storeu_si128((__m128i*) ptr, simdreg); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + vst1q_s8(ptr, simdreg); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + + +static inline simd_b_t srslte_simd_b_xor(simd_b_t a, simd_b_t b) { + +#ifdef LV_HAVE_AVX512 + return _mm512_xor_epi32(a, b); +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX2 + return _mm256_xor_si256(a, b); +#else /* LV_HAVE_AVX2 */ +#ifdef LV_HAVE_SSE + return _mm_xor_si128 (a, b); +#else /* LV_HAVE_SSE */ +#ifdef HAVE_NEON + return veorq_s8(a, b); +#endif /* HAVE_NEON */ +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX2 */ +#endif /* LV_HAVE_AVX512 */ +} + +#endif /*SRSLTE_SIMD_B_SIZE */ + + +#endif // SRSLTE_SIMD_H diff --git a/lib/include/srslte/phy/utils/vector.h b/lib/include/srslte/phy/utils/vector.h new file mode 100644 index 0000000..32629da --- /dev/null +++ b/lib/include/srslte/phy/utils/vector.h @@ -0,0 +1,167 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/****************************************************************************** + * File: vector.h + * + * Description: Vector functions using SIMD instructions where possible. + * + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_VECTOR_H +#define SRSLTE_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "srslte/config.h" + + +#define SRSLTE_MAX(a,b) ((a)>(b)?(a):(b)) +#define SRSLTE_MIN(a,b) ((a)<(b)?(a):(b)) + +// Cumulative moving average +#define SRSLTE_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n)+1)) + +// Exponential moving average +#define SRSLTE_VEC_EMA(data, average, alpha) ((alpha)*(data)+(1-alpha)*(average)) + + +/*logical operations */ +SRSLTE_API void srslte_vec_xor_bbb(int8_t *x,int8_t *y,int8_t *z, const uint32_t len); + +/** Return the sum of all the elements */ +SRSLTE_API float srslte_vec_acc_ff(const float *x, const uint32_t len); +SRSLTE_API cf_t srslte_vec_acc_cc(const cf_t *x, const uint32_t len); + +SRSLTE_API void *srslte_vec_malloc(uint32_t size); + +SRSLTE_API void *srslte_vec_realloc(void *ptr, uint32_t old_size, uint32_t new_size); + +/* print vectors */ +SRSLTE_API void srslte_vec_fprint_c(FILE *stream, cf_t *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_f(FILE *stream, float *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_b(FILE *stream, uint8_t *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_byte(FILE *stream, uint8_t *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_i(FILE *stream, int *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_s(FILE *stream, short *x, const uint32_t len); +SRSLTE_API void srslte_vec_fprint_hex(FILE *stream, uint8_t *x, const uint32_t len); +SRSLTE_API void srslte_vec_sprint_hex(char *str, const uint32_t max_str_len, uint8_t *x, const uint32_t len); + +/* Saves/loads a vector to a file */ +SRSLTE_API void srslte_vec_save_file(char *filename, const void *buffer, const uint32_t len); +SRSLTE_API void srslte_vec_load_file(char *filename, void *buffer, const uint32_t len); + +/* sum two vectors */ +SRSLTE_API void srslte_vec_sum_fff(const float *x, const float *y, float *z, const uint32_t len); +SRSLTE_API void srslte_vec_sum_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_sub_sss(const int16_t *x, const int16_t *y, int16_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_sum_sss(const int16_t *x, const int16_t *y, int16_t *z, const uint32_t len); + +/* substract two vectors z=x-y */ +SRSLTE_API void srslte_vec_sub_fff(const float *x, const float *y, float *z, const uint32_t len); +SRSLTE_API void srslte_vec_sub_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len); + +/* scalar product */ +SRSLTE_API void srslte_vec_sc_prod_cfc(const cf_t *x, const float h, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_sc_prod_ccc(const cf_t *x, const cf_t h, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_sc_prod_fff(const float *x, const float h, float *z, const uint32_t len); + + +SRSLTE_API void srslte_vec_convert_fi(const float *x, const float scale, int16_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_convert_if(const int16_t *x, const float scale, float *z, const uint32_t len); + +SRSLTE_API void srslte_vec_lut_sss(const short *x, const unsigned short *lut, short *y, const uint32_t len); +SRSLTE_API void srslte_vec_lut_sis(const short *x, const unsigned int *lut, short *y, const uint32_t len); + +/* vector product (element-wise) */ +SRSLTE_API void srslte_vec_prod_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_prod_ccc_split(const float *x_re, const float *x_im, const float *y_re, const float *y_im, float *z_re, float *z_im, const uint32_t len); + +/* vector product (element-wise) */ +SRSLTE_API void srslte_vec_prod_cfc(const cf_t *x, const float *y, cf_t *z, const uint32_t len); + +/* conjugate vector product (element-wise) */ +SRSLTE_API void srslte_vec_prod_conj_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len); + +/* real vector product (element-wise) */ +SRSLTE_API void srslte_vec_prod_fff(const float *x, const float *y, float *z, const uint32_t len); +SRSLTE_API void srslte_vec_prod_sss(const int16_t *x, const int16_t *y, int16_t *z, const uint32_t len); + +/* Dot-product */ +SRSLTE_API cf_t srslte_vec_dot_prod_cfc(const cf_t *x, const float *y, const uint32_t len); +SRSLTE_API cf_t srslte_vec_dot_prod_ccc(const cf_t *x, const cf_t *y, const uint32_t len); +SRSLTE_API cf_t srslte_vec_dot_prod_conj_ccc(const cf_t *x, const cf_t *y, const uint32_t len); +SRSLTE_API float srslte_vec_dot_prod_fff(const float *x, const float *y, const uint32_t len); +SRSLTE_API int32_t srslte_vec_dot_prod_sss(const int16_t *x, const int16_t *y, const uint32_t len); + +/* z=x/y vector division (element-wise) */ +SRSLTE_API void srslte_vec_div_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_div_cfc(const cf_t *x, const float *y, cf_t *z, const uint32_t len); +SRSLTE_API void srslte_vec_div_fff(const float *x, const float *y, float *z, const uint32_t len); + +/* conjugate */ +SRSLTE_API void srslte_vec_conj_cc(const cf_t *x, cf_t *y, const uint32_t len); + +/* average vector power */ +SRSLTE_API float srslte_vec_avg_power_cf(const cf_t *x, const uint32_t len); + +/* Correlation between complex vectors x and y */ +SRSLTE_API float srslte_vec_corr_ccc(const cf_t *x, cf_t *y, const uint32_t len); + +/* return the index of the maximum value in the vector */ +SRSLTE_API uint32_t srslte_vec_max_fi(const float *x, const uint32_t len); +SRSLTE_API uint32_t srslte_vec_max_abs_fi(const float *x, const uint32_t len); +SRSLTE_API uint32_t srslte_vec_max_abs_ci(const cf_t *x, const uint32_t len); + +/* quantify vector of floats or int16 and convert to uint8_t */ +SRSLTE_API void srslte_vec_quant_fuc(const float *in, uint8_t *out, const float gain, const float offset, const float clip, const uint32_t len); +SRSLTE_API void srslte_vec_quant_fus(float *in, uint16_t *out, float gain, float offset, float clip, uint32_t len); +SRSLTE_API void srslte_vec_quant_suc(const int16_t *in, uint8_t *out, const float gain, const int16_t offset, const int16_t clip, const uint32_t len); +SRSLTE_API void srslte_vec_quant_sus(const int16_t *in, uint16_t *out, const float gain, const int16_t offset, const uint32_t len); +/* magnitude of each vector element */ +SRSLTE_API void srslte_vec_abs_cf(const cf_t *x, float *abs, const uint32_t len); +SRSLTE_API void srslte_vec_abs_square_cf(const cf_t *x, float *abs_square, const uint32_t len); + +/* Copy 256 bit aligned vector */ +SRSLTE_API void srs_vec_cf_cpy(const cf_t *src, cf_t *dst, const int len); + +SRSLTE_API void srslte_vec_interleave(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_interleave_add(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_apply_cfo(const cf_t *x, float cfo, cf_t *z, int len); + + +#ifdef __cplusplus +} +#endif + +#endif // SRSLTE_VECTOR_H diff --git a/lib/include/srslte/phy/utils/vector_simd.h b/lib/include/srslte/phy/utils/vector_simd.h new file mode 100644 index 0000000..68ddbde --- /dev/null +++ b/lib/include/srslte/phy/utils/vector_simd.h @@ -0,0 +1,147 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_VECTOR_SIMD_H +#define SRSLTE_VECTOR_SIMD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "srslte/config.h" + +#ifdef LV_HAVE_AVX512 +#define SRSLTE_SIMD_BIT_ALIGN 512 +#define SRSLTE_IS_ALIGNED(PTR) (((size_t)(PTR) & 0x3F) == 0) +#else /* LV_HAVE_AVX512 */ +#ifdef LV_HAVE_AVX +#define SRSLTE_SIMD_BIT_ALIGN 256 +#define SRSLTE_IS_ALIGNED(PTR) (((size_t)(PTR) & 0x1F) == 0) +#else /* LV_HAVE_AVX */ +#ifdef LV_HAVE_SSE +#define SRSLTE_SIMD_BIT_ALIGN 128 +#define SRSLTE_IS_ALIGNED(PTR) (((size_t)(PTR) & 0x0F) == 0) +#else /* LV_HAVE_SSE */ +#define SRSLTE_SIMD_BIT_ALIGN 64 +#define SRSLTE_IS_ALIGNED(PTR) (1) +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX */ +#endif /* LV_HAVE_AVX512 */ + + +/*SIMD Logical operations*/ +SRSLTE_API void srslte_vec_xor_bbb_simd(const int8_t *x, const int8_t *y, int8_t *z, int len); + +/* SIMD Basic vector math */ +SRSLTE_API void srslte_vec_sum_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, int len); + +SRSLTE_API void srslte_vec_sub_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, int len); + +SRSLTE_API float srslte_vec_acc_ff_simd(const float *x, int len); + +SRSLTE_API cf_t srslte_vec_acc_cc_simd(const cf_t *x, int len); + +SRSLTE_API void srslte_vec_add_fff_simd(const float *x, const float *y, float *z, int len); + +SRSLTE_API void srslte_vec_sub_fff_simd(const float *x, const float *y, float *z, int len); + +/* SIMD Vector Scalar Product */ +SRSLTE_API void srslte_vec_sc_prod_cfc_simd(const cf_t *x, const float h,cf_t *y, const int len); + +SRSLTE_API void srslte_vec_sc_prod_fff_simd(const float *x, const float h, float *z, const int len); + +SRSLTE_API void srslte_vec_sc_prod_ccc_simd(const cf_t *x, const cf_t h, cf_t *z, const int len); + +/* SIMD Vector Product */ +SRSLTE_API void srslte_vec_prod_ccc_split_simd(const float *a_re, const float *a_im, const float *b_re, const float *b_im, + float *r_re, float *r_im, const int len); + +SRSLTE_API void srslte_vec_prod_ccc_c16_simd(const int16_t *a_re, const int16_t *a_im, const int16_t *b_re, const int16_t *b_im, + int16_t *r_re, int16_t *r_im, const int len); + +SRSLTE_API void srslte_vec_prod_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, const int len); + +SRSLTE_API void srslte_vec_prod_cfc_simd(const cf_t *x, const float *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_prod_fff_simd(const float *x, const float *y, float *z, const int len); + +SRSLTE_API void srslte_vec_prod_ccc_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_prod_conj_ccc_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +/* SIMD Division */ +SRSLTE_API void srslte_vec_div_ccc_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_div_cfc_simd(const cf_t *x, const float *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_div_fff_simd(const float *x, const float *y, float *z, const int len); + +/* SIMD Dot product */ +SRSLTE_API cf_t srslte_vec_dot_prod_conj_ccc_simd(const cf_t *x, const cf_t *y, const int len); + +SRSLTE_API cf_t srslte_vec_dot_prod_ccc_simd(const cf_t *x, const cf_t *y, const int len); + +#ifdef ENABLE_C16 +SRSLTE_API c16_t srslte_vec_dot_prod_ccc_c16i_simd(const c16_t *x, const c16_t *y, const int len); +#endif /* ENABLE_C16 */ + +SRSLTE_API int srslte_vec_dot_prod_sss_simd(const int16_t *x, const int16_t *y, const int len); + +/* SIMD Modulus functions */ +SRSLTE_API void srslte_vec_abs_cf_simd(const cf_t *x, float *z, const int len); + +SRSLTE_API void srslte_vec_abs_square_cf_simd(const cf_t *x, float *z, const int len); + +/* Other Functions */ +SRSLTE_API void srslte_vec_lut_sss_simd(const short *x, const unsigned short *lut, short *y, const int len); + +SRSLTE_API void srslte_vec_convert_if_simd(const int16_t *x, float *z, const float scale, const int len); + +SRSLTE_API void srslte_vec_convert_fi_simd(const float *x, int16_t *z, const float scale, const int len); + +SRSLTE_API void srslte_vec_cp_simd(const cf_t *src, cf_t *dst, int len); + +SRSLTE_API void srslte_vec_interleave_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_interleave_add_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len); + +SRSLTE_API void srslte_vec_apply_cfo_simd(const cf_t *x, float cfo, cf_t *z, int len); + + +/* SIMD Find Max functions */ +SRSLTE_API uint32_t srslte_vec_max_fi_simd(const float *x, const int len); + +SRSLTE_API uint32_t srslte_vec_max_abs_fi_simd(const float *x, const int len); + +SRSLTE_API uint32_t srslte_vec_max_ci_simd(const cf_t *x, const int len); + +#ifdef __cplusplus +} +#endif + +#endif // SRSLTE_VECTOR_SIMD_H diff --git a/lib/include/srslte/radio/radio.h b/lib/include/srslte/radio/radio.h new file mode 100644 index 0000000..658bf73 --- /dev/null +++ b/lib/include/srslte/radio/radio.h @@ -0,0 +1,187 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" +#include "srslte/common/trace.h" + +#ifndef SRSLTE_RADIO_H +#define SRSLTE_RADIO_H + +typedef struct { + float tx_corr_dc_gain; + float tx_corr_dc_phase; + float tx_corr_iq_i; + float tx_corr_iq_q; + float rx_corr_dc_gain; + float rx_corr_dc_phase; + float rx_corr_iq_i; + float rx_corr_iq_q; +} rf_cal_t; + +namespace srslte { + +/* Interface to the RF frontend. + */ +class radio { + public: + radio() : tr_local_time(1024 * 10), tr_usrp_time(1024 * 10), tr_tx_time(1024 * 10), tr_is_eob(1024 * 10) { + bzero(&rf_device, sizeof(srslte_rf_t)); + bzero(&end_of_burst_time, sizeof(srslte_timestamp_t)); + zeros = (cf_t *) srslte_vec_malloc(burst_preamble_max_samples * sizeof (cf_t)); + bzero(zeros, burst_preamble_max_samples * sizeof(cf_t)); + + burst_preamble_sec = 0; + is_start_of_burst = false; + burst_preamble_samples = 0; + burst_preamble_time_rounded = 0; + + cur_tx_srate = 0; + tx_adv_sec = 0; + tx_adv_nsamples = 0; + tx_adv_auto = false; + tx_adv_negative = false; + tx_freq = 0; + rx_freq = 0; + trace_enabled = false; + tti = 0; + agc_enabled = false; + radio_is_streaming = false; + is_initialized = false; + continuous_tx = false; + }; + + bool init(char *args = NULL, char *devname = NULL, uint32_t nof_channels = 1); + void stop(); + void reset(); + bool start_agc(bool tx_gain_same_rx); + + void set_burst_preamble(double preamble_us); + void set_tx_adv(int nsamples); + void set_tx_adv_neg(bool tx_adv_is_neg); + + void set_manual_calibration(rf_cal_t *calibration); + + bool is_continuous_tx(); + void set_continuous_tx(bool enable); + + void get_time(srslte_timestamp_t *now); + bool tx_single(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + bool tx(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time); + void tx_end(); + bool rx_now(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t *rxd_time); + bool rx_at(void *buffer, uint32_t nof_samples, srslte_timestamp_t rx_time); + + void set_tx_gain(float gain); + void set_rx_gain(float gain); + void set_tx_rx_gain_offset(float offset); + double set_rx_gain_th(float gain); + + void set_freq_offset(double freq); + void set_tx_freq(double freq); + void set_rx_freq(double freq); + + double get_freq_offset(); + double get_tx_freq(); + double get_rx_freq(); + + void set_master_clock_rate(double rate); + void set_tx_srate(double srate); + void set_rx_srate(double srate); + + float get_tx_gain(); + float get_rx_gain(); + srslte_rf_info_t *get_info(); + + float get_max_tx_power(); + float set_tx_power(float power); + float get_rssi(); + bool has_rssi(); + + void start_trace(); + void write_trace(std::string filename); + + void set_tti(uint32_t tti); + + bool is_first_of_burst(); + + bool is_init(); + + void register_error_handler(srslte_rf_error_handler_t h); + + protected: + + void save_trace(uint32_t is_eob, srslte_timestamp_t *usrp_time); + + srslte_rf_t rf_device; + + const static uint32_t burst_preamble_max_samples = 30720000; // 30.72 MHz is maximum frequency + double burst_preamble_sec;// Start of burst preamble time (off->on RF transition time) + srslte_timestamp_t end_of_burst_time; + bool is_start_of_burst; + uint32_t burst_preamble_samples; + double burst_preamble_time_rounded; // preamble time rounded to sample time + cf_t *zeros; + double cur_tx_srate; + + double tx_adv_sec; // Transmission time advance to compensate for antenna->timestamp delay + int tx_adv_nsamples; // Transmision time advance in number of samples + + // Define default values for known radios + bool tx_adv_auto; + bool tx_adv_negative; + const static double uhd_default_burst_preamble_sec = 600 * 1e-6; + const static double uhd_default_tx_adv_samples = 98; + const static double uhd_default_tx_adv_offset_sec = 4 * 1e-6; + + const static double blade_default_burst_preamble_sec = 0.0; + const static double blade_default_tx_adv_samples = 27; + const static double blade_default_tx_adv_offset_sec = 1e-6; + + double tx_freq, rx_freq, freq_offset; + + trace tr_local_time; + trace tr_usrp_time; + trace tr_tx_time; + trace tr_is_eob; + bool trace_enabled; + uint32_t tti; + bool agc_enabled; + + bool continuous_tx; + bool is_initialized; + bool radio_is_streaming; + + uint32_t saved_nof_channels; + char saved_args[128]; + char saved_devname[128]; + +}; +} + +#endif // SRSLTE_RADIO_H diff --git a/lib/include/srslte/radio/radio_multi.h b/lib/include/srslte/radio/radio_multi.h new file mode 100644 index 0000000..097fe55 --- /dev/null +++ b/lib/include/srslte/radio/radio_multi.h @@ -0,0 +1,55 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/srslte.h" +extern "C" { +#include "srslte/phy/rf/rf.h" +} +#include "srslte/common/trace.h" + +#include "srslte/radio/radio.h" + +#ifndef SRSLTE_RADIO_MULTI_H +#define SRSLTE_RADIO_MULTI_H + + +namespace srslte { + +/* Interface to the RF frontend. + */ + class radio_multi : public radio + { + public: + radio_multi() {} + ~radio_multi() {} + bool init_multi(uint32_t nof_rx_antennas, char *args = NULL, char *devname = NULL); + bool rx_now(cf_t *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t *rxd_time); + }; +} + +#endif // SRSLTE_RADIO_MULTI_H diff --git a/lib/include/srslte/srslte.h b/lib/include/srslte/srslte.h new file mode 100644 index 0000000..f87a1bf --- /dev/null +++ b/lib/include/srslte/srslte.h @@ -0,0 +1,131 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +#ifndef SRSLTE_SRSLTE_H +#define SRSLTE_SRSLTE_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include +#include + +#include "srslte/config.h" +#include "srslte/version.h" + +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/ringbuffer.h" +#include "srslte/phy/utils/convolution.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/cexptab.h" +#include "srslte/phy/utils/vector.h" + +#include "srslte/phy/common/timestamp.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/common/phy_logger.h" + +#include "srslte/phy/ch_estimation/chest_ul.h" +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/ch_estimation/refsignal_dl.h" +#include "srslte/phy/ch_estimation/refsignal_ul.h" + +#include "srslte/phy/resampling/interp.h" +#include "srslte/phy/resampling/decim.h" +#include "srslte/phy/resampling/resample_arb.h" + +#include "srslte/phy/channel/ch_awgn.h" + +#include "srslte/phy/fec/viterbi.h" +#include "srslte/phy/fec/convcoder.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/turbocoder.h" +#include "srslte/phy/fec/turbodecoder.h" +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/fec/rm_conv.h" +#include "srslte/phy/fec/rm_turbo.h" + +#include "srslte/phy/dft/dft_precoding.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/dft/dft.h" + +#include "srslte/phy/io/binsource.h" +#include "srslte/phy/io/filesink.h" +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/io/netsink.h" +#include "srslte/phy/io/netsource.h" + +#include "srslte/phy/modem/demod_hard.h" +#include "srslte/phy/modem/demod_soft.h" +#include "srslte/phy/modem/mod.h" +#include "srslte/phy/modem/modem_table.h" + +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/mimo/layermap.h" + +#include "srslte/phy/phch/cqi.h" +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/fec/softbuffer.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/phch/pcfich.h" +#include "srslte/phy/phch/pdcch.h" +#include "srslte/phy/phch/pdsch.h" +#include "srslte/phy/phch/phich.h" +#include "srslte/phy/phch/pusch.h" +#include "srslte/phy/phch/pucch.h" +#include "srslte/phy/phch/prach.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/sch.h" +#include "srslte/phy/phch/uci.h" + +#include "srslte/phy/ue/ue_sync.h" +#include "srslte/phy/ue/ue_mib.h" +#include "srslte/phy/ue/ue_cell_search.h" +#include "srslte/phy/ue/ue_dl.h" +#include "srslte/phy/ue/ue_ul.h" + +#include "srslte/phy/enb/enb_dl.h" +#include "srslte/phy/enb/enb_ul.h" + +#include "srslte/phy/scrambling/scrambling.h" + +#include "srslte/phy/sync/pss.h" +#include "srslte/phy/sync/sfo.h" +#include "srslte/phy/sync/sss.h" +#include "srslte/phy/sync/sync.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/sync/cp.h" + +#ifdef __cplusplus +} +#undef I // Fix complex.h #define I nastiness when using C++ +#endif + +#endif // SRSLTE_SRSLTE_H diff --git a/lib/include/srslte/upper/gtpu.h b/lib/include/srslte/upper/gtpu.h new file mode 100644 index 0000000..24b9c5c --- /dev/null +++ b/lib/include/srslte/upper/gtpu.h @@ -0,0 +1,96 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_GTPU_H +#define SRSLTE_GTPU_H + +#include +#include "srslte/common/common.h" +#include "srslte/common/log.h" + +namespace srslte { + +/**************************************************************************** + * GTPU Header + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version |PT | * | E | S |PN | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * 5 | TEID (1st Octet) | + * 6 | TEID (2nd Octet) | + * 7 | TEID (3rd Octet) | + * 8 | TEID (4th Octet) | + ***************************************************************************/ + +#define GTPU_HEADER_LEN 8 + +typedef struct{ + uint8_t flags; // Only support 0x30 - v1, PT1 (GTP), no other flags + uint8_t message_type; // Only support 0xFF - T-PDU type + uint16_t length; + uint32_t teid; +}gtpu_header_t; + + +bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header, srslte::log *gtpu_log); +bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log); + +inline void uint8_to_uint32(uint8_t *buf, uint32_t *i) +{ + *i = (uint32_t)buf[0] << 24 | + (uint32_t)buf[1] << 16 | + (uint32_t)buf[2] << 8 | + (uint32_t)buf[3]; +} + +inline void uint32_to_uint8(uint32_t i, uint8_t *buf) +{ + buf[0] = (i >> 24) & 0xFF; + buf[1] = (i >> 16) & 0xFF; + buf[2] = (i >> 8) & 0xFF; + buf[3] = i & 0xFF; +} + +inline void uint8_to_uint16(uint8_t *buf, uint16_t *i) +{ + *i = (uint32_t)buf[0] << 8 | + (uint32_t)buf[1]; +} + +inline void uint16_to_uint8(uint16_t i, uint8_t *buf) +{ + buf[0] = (i >> 8) & 0xFF; + buf[1] = i & 0xFF; +} + + +}//namespace + +#endif diff --git a/lib/include/srslte/upper/pdcp.h b/lib/include/srslte/upper/pdcp.h new file mode 100644 index 0000000..d4cca95 --- /dev/null +++ b/lib/include/srslte/upper/pdcp.h @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_PDCP_H +#define SRSLTE_PDCP_H + +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/upper/pdcp_entity.h" + +namespace srslte { + +class pdcp + :public srsue::pdcp_interface_gw + ,public srsue::pdcp_interface_rlc + ,public srsue::pdcp_interface_rrc +{ +public: + pdcp(); + virtual ~pdcp(){} + void init(srsue::rlc_interface_pdcp *rlc_, + srsue::rrc_interface_pdcp *rrc_, + srsue::gw_interface_pdcp *gw_, + log *pdcp_log_, + uint32_t lcid_, + uint8_t direction_); + void stop(); + + // GW interface + bool is_drb_enabled(uint32_t lcid); + + // RRC interface + void reestablish(); + void reset(); + void write_sdu(uint32_t lcid, byte_buffer_t *sdu); + void write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu); + void add_bearer(uint32_t lcid, srslte_pdcp_config_t cnfg = srslte_pdcp_config_t()); + void add_bearer_mrb(uint32_t lcid, srslte_pdcp_config_t cnfg = srslte_pdcp_config_t()); + void config_security(uint32_t lcid, + uint8_t *k_enc, + uint8_t *k_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + void config_security_all(uint8_t *k_enc, + uint8_t *k_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + void enable_integrity(uint32_t lcid); + void enable_encryption(uint32_t lcid); + + // RLC interface + void write_pdu(uint32_t lcid, byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, byte_buffer_t *sdu); + void write_pdu_bcch_bch(byte_buffer_t *sdu); + void write_pdu_bcch_dlsch(byte_buffer_t *sdu); + void write_pdu_pcch(byte_buffer_t *sdu); + + +private: + srsue::rlc_interface_pdcp *rlc; + srsue::rrc_interface_pdcp *rrc; + srsue::gw_interface_pdcp *gw; + + log *pdcp_log; + pdcp_entity pdcp_array[SRSLTE_N_RADIO_BEARERS]; + pdcp_entity pdcp_array_mrb[SRSLTE_N_MCH_LCIDS]; + uint32_t lcid; // default LCID that is maintained active by PDCP instance + uint8_t direction; + + bool valid_lcid(uint32_t lcid); + bool valid_mch_lcid(uint32_t lcid); +}; + +} // namespace srslte + + +#endif // SRSLTE_PDCP_H diff --git a/lib/include/srslte/upper/pdcp_entity.h b/lib/include/srslte/upper/pdcp_entity.h new file mode 100644 index 0000000..de51ed1 --- /dev/null +++ b/lib/include/srslte/upper/pdcp_entity.h @@ -0,0 +1,149 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_PDCP_ENTITY_H +#define SRSLTE_PDCP_ENTITY_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" +#include "srslte/common/threads.h" + + +namespace srslte { + +/**************************************************************************** + * Structs and Defines + * Ref: 3GPP TS 36.323 v10.1.0 + ***************************************************************************/ + +#define PDCP_CONTROL_MAC_I 0x00000000 + +#define PDCP_PDU_TYPE_PDCP_STATUS_REPORT 0x0 +#define PDCP_PDU_TYPE_INTERSPERSED_ROHC_FEEDBACK_PACKET 0x1 + +typedef enum{ + PDCP_D_C_CONTROL_PDU = 0, + PDCP_D_C_DATA_PDU, + PDCP_D_C_N_ITEMS, +}pdcp_d_c_t; +static const char pdcp_d_c_text[PDCP_D_C_N_ITEMS][20] = {"Control PDU", + "Data PDU"}; + +/**************************************************************************** + * PDCP Entity interface + * Common interface for all PDCP entities + ***************************************************************************/ +class pdcp_entity +{ +public: + pdcp_entity(); + void init(srsue::rlc_interface_pdcp *rlc_, + srsue::rrc_interface_pdcp *rrc_, + srsue::gw_interface_pdcp *gw_, + srslte::log *log_, + uint32_t lcid_, + srslte_pdcp_config_t cfg_); + void reset(); + void reestablish(); + + bool is_active(); + + // RRC interface + void write_sdu(byte_buffer_t *sdu); + void config_security(uint8_t *k_enc_, + uint8_t *k_int_, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo_); + void enable_integrity(); + void enable_encryption(); + + // RLC interface + void write_pdu(byte_buffer_t *pdu); + +private: + byte_buffer_pool *pool; + srslte::log *log; + + srsue::rlc_interface_pdcp *rlc; + srsue::rrc_interface_pdcp *rrc; + srsue::gw_interface_pdcp *gw; + + bool active; + uint32_t lcid; + srslte_pdcp_config_t cfg; + uint8_t sn_len_bytes; + bool do_integrity; + bool do_encryption; + + uint32_t rx_count; + uint32_t tx_count; + uint8_t k_enc[32]; + uint8_t k_int[32]; + + CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + + void integrity_generate(uint8_t *msg, + uint32_t msg_len, + uint8_t *mac); + + bool integrity_verify(uint8_t *msg, + uint32_t count, + uint32_t msg_len, + uint8_t *mac); + + void cipher_encrypt(uint8_t *msg, + uint32_t msg_len, + uint8_t *ct); + + void cipher_decrypt(uint8_t *ct, + uint32_t count, + uint32_t ct_len, + uint8_t *msg); + + uint8_t get_bearer_id(uint8_t lcid); +}; + +/**************************************************************************** + * Pack/Unpack helper functions + * Ref: 3GPP TS 36.323 v10.1.0 + ***************************************************************************/ + +void pdcp_pack_control_pdu(uint32_t sn, byte_buffer_t *sdu); +void pdcp_unpack_control_pdu(byte_buffer_t *sdu, uint32_t *sn); + +void pdcp_pack_data_pdu_short_sn(uint32_t sn, byte_buffer_t *sdu); +void pdcp_unpack_data_pdu_short_sn(byte_buffer_t *sdu, uint32_t *sn); +void pdcp_pack_data_pdu_long_sn(uint32_t sn, byte_buffer_t *sdu); +void pdcp_unpack_data_pdu_long_sn(byte_buffer_t *sdu, uint32_t *sn); + +} // namespace srslte + + +#endif // SRSLTE_PDCP_ENTITY_H diff --git a/lib/include/srslte/upper/rlc.h b/lib/include/srslte/upper/rlc.h new file mode 100644 index 0000000..8d6eb2f --- /dev/null +++ b/lib/include/srslte/upper/rlc.h @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_H +#define SRSLTE_RLC_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/upper/rlc_entity.h" +#include "srslte/upper/rlc_metrics.h" +#include "srslte/upper/rlc_common.h" + +namespace srslte { + +/**************************************************************************** + * RLC Layer + * Ref: 3GPP TS 36.322 v10.0.0 + * Single interface for RLC layer - contains separate RLC entities for + * each bearer. + ***************************************************************************/ +class rlc + :public srsue::rlc_interface_mac + ,public srsue::rlc_interface_pdcp + ,public srsue::rlc_interface_rrc +{ +public: + rlc(); + virtual ~rlc() {} + void init(srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + srsue::ue_interface *ue_, + log *rlc_log_, + mac_interface_timers *mac_timers_, + uint32_t lcid_, + int buffer_size = -1); // -1 to use default buffer sizes + void stop(); + + void get_metrics(rlc_metrics_t &m); + + // PDCP interface + void write_sdu(uint32_t lcid, byte_buffer_t *sdu); + void write_sdu_nb(uint32_t lcid, byte_buffer_t *sdu); + void write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu); + bool rb_is_um(uint32_t lcid); + + // MAC interface + uint32_t get_buffer_state(uint32_t lcid); + uint32_t get_total_buffer_state(uint32_t lcid); + uint32_t get_total_mch_buffer_state(uint32_t lcid); + int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + int read_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + int get_increment_sequence_num(); + void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes); + void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes); + void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes); + void write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + + // RRC interface + void reestablish(); + void reset(); + void empty_queue(); + void add_bearer(uint32_t lcid); + void add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg); + void add_bearer_mrb(uint32_t lcid); + void add_bearer_mrb_enb(uint32_t lcid); +private: + void reset_metrics(); + + byte_buffer_pool *pool; + srslte::log *rlc_log; + srsue::pdcp_interface_rlc *pdcp; + srsue::rrc_interface_rlc *rrc; + srslte::mac_interface_timers *mac_timers; + srsue::ue_interface *ue; + srslte::rlc_entity rlc_array[SRSLTE_N_RADIO_BEARERS]; + srslte::rlc_um rlc_array_mrb[SRSLTE_N_MCH_LCIDS]; + uint32_t default_lcid; + int buffer_size; + + long ul_tput_bytes[SRSLTE_N_RADIO_BEARERS]; + long dl_tput_bytes[SRSLTE_N_RADIO_BEARERS]; + long dl_tput_bytes_mrb[SRSLTE_N_MCH_LCIDS]; + struct timeval metrics_time[3]; + + bool valid_lcid(uint32_t lcid); + bool valid_lcid_mrb(uint32_t lcid); +}; + +} // namespace srsue + + +#endif // SRSLTE_RLC_H diff --git a/lib/include/srslte/upper/rlc_am.h b/lib/include/srslte/upper/rlc_am.h new file mode 100644 index 0000000..cfaee6c --- /dev/null +++ b/lib/include/srslte/upper/rlc_am.h @@ -0,0 +1,230 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_AM_H +#define SRSLTE_RLC_AM_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/upper/rlc_tx_queue.h" +#include "srslte/common/timeout.h" +#include "srslte/upper/rlc_common.h" +#include +#include +#include + +namespace srslte { + +#undef RLC_AM_BUFFER_DEBUG + +struct rlc_amd_rx_pdu_t{ + rlc_amd_pdu_header_t header; + byte_buffer_t *buf; +}; + +struct rlc_amd_rx_pdu_segments_t{ + std::list segments; +}; + +struct rlc_amd_tx_pdu_t{ + rlc_amd_pdu_header_t header; + byte_buffer_t *buf; + uint32_t retx_count; + bool is_acked; +}; + +struct rlc_amd_retx_t{ + uint32_t sn; + bool is_segment; + uint32_t so_start; + uint32_t so_end; +}; + + +class rlc_am + :public rlc_common +{ +public: + rlc_am(uint32_t queue_len = 16); + ~rlc_am(); + void init(log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers); + void configure(srslte_rlc_config_t cnfg); + void reestablish(); + void stop(); + + void empty_queue(); + + rlc_mode_t get_mode(); + uint32_t get_bearer(); + + // PDCP interface + void write_sdu(byte_buffer_t *sdu); + void write_sdu_nb(byte_buffer_t *sdu); + + // MAC interface + uint32_t get_buffer_state(); + uint32_t get_total_buffer_state(); + int read_pdu(uint8_t *payload, uint32_t nof_bytes); + void write_pdu(uint8_t *payload, uint32_t nof_bytes); + +private: + + byte_buffer_pool *pool; + srslte::log *log; + uint32_t lcid; + srsue::pdcp_interface_rlc *pdcp; + srsue::rrc_interface_rlc *rrc; + + // TX SDU buffers + rlc_tx_queue tx_sdu_queue; + byte_buffer_t *tx_sdu; + + // PDU being resegmented + rlc_amd_tx_pdu_t tx_pdu_segments; + + // Tx and Rx windows + std::map tx_window; + std::deque retx_queue; + std::map rx_window; + std::map rx_segments; + + // RX SDU buffers + byte_buffer_t *rx_sdu; + + // Mutexes + pthread_mutex_t mutex; + + bool tx_enabled; + bool poll_received; + bool do_status; + rlc_status_pdu_t status; + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + srslte_rlc_am_config_t cfg; + + /**************************************************************************** + * State variables and counters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + // Tx state variables + uint32_t vt_a; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window. + uint32_t vt_ms; // Max send state. High edge of tx window. vt_a + window_size. + uint32_t vt_s; // Send state. SN to be assigned for next PDU. + uint32_t poll_sn; // Poll send state. SN of most recent PDU txed with poll bit set. + + // Tx counters + uint32_t pdu_without_poll; + uint32_t byte_without_poll; + + // Rx state variables + uint32_t vr_r; // Receive state. SN following last in-sequence received PDU. Low edge of rx window + uint32_t vr_mr; // Max acceptable receive state. High edge of rx window. vr_r + window size. + uint32_t vr_x; // t_reordering state. SN following PDU which triggered t_reordering. + uint32_t vr_ms; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU. + uint32_t vr_h; // Highest rx state. SN following PDU with highest SN among rxed PDUs. + + /**************************************************************************** + * Timers + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + timeout poll_retx_timeout; + timeout reordering_timeout; + timeout status_prohibit_timeout; + + static const int reordering_timeout_id = 1; + + static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested + + // Timer checks + bool status_prohibited(); + bool poll_retx(); + void check_reordering_timeout(); + + // Helpers + bool poll_required(); + + int prepare_status(); + int build_status_pdu(uint8_t *payload, uint32_t nof_bytes); + int build_retx_pdu(uint8_t *payload, uint32_t nof_bytes); + int build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx); + int build_data_pdu(uint8_t *payload, uint32_t nof_bytes); + + void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t &header); + void handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t &header); + void handle_control_pdu(uint8_t *payload, uint32_t nof_bytes); + + void reassemble_rx_sdus(); + + bool inside_tx_window(uint16_t sn); + bool inside_rx_window(uint16_t sn); + void debug_state(); + void print_rx_segments(); + + bool add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pdu_t *segment); + int required_buffer_size(rlc_amd_retx_t retx); + bool retx_queue_has_sn(uint32_t sn); +}; + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ +void rlc_am_read_data_pdu_header(byte_buffer_t *pdu, rlc_amd_pdu_header_t *header); +void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd_pdu_header_t *header); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, byte_buffer_t *pdu); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payload); +void rlc_am_read_status_pdu(byte_buffer_t *pdu, rlc_status_pdu_t *status); +void rlc_am_read_status_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_status_pdu_t *status); +void rlc_am_write_status_pdu(rlc_status_pdu_t *status, byte_buffer_t *pdu ); +int rlc_am_write_status_pdu(rlc_status_pdu_t *status, uint8_t *payload); + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header); +uint32_t rlc_am_packed_length(rlc_status_pdu_t *status); +uint32_t rlc_am_packed_length(rlc_amd_retx_t retx); +bool rlc_am_is_control_pdu(byte_buffer_t *pdu); +bool rlc_am_is_control_pdu(uint8_t *payload); +bool rlc_am_is_pdu_segment(uint8_t *payload); +std::string rlc_am_to_string(rlc_status_pdu_t *status); +bool rlc_am_start_aligned(const uint8_t fi); +bool rlc_am_end_aligned(const uint8_t fi); +bool rlc_am_is_unaligned(const uint8_t fi); +bool rlc_am_not_start_aligned(const uint8_t fi); + +} // namespace srslte + + +#endif // SRSLTE_RLC_AM_H diff --git a/lib/include/srslte/upper/rlc_common.h b/lib/include/srslte/upper/rlc_common.h new file mode 100644 index 0000000..c9655ef --- /dev/null +++ b/lib/include/srslte/upper/rlc_common.h @@ -0,0 +1,185 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_COMMON_H +#define SRSLTE_RLC_COMMON_H + +#include "srslte/upper/rlc_interface.h" + +namespace srslte { + +/**************************************************************************** + * Structs and Defines + * Ref: 3GPP TS 36.322 v10.0.0 + ***************************************************************************/ + +#define RLC_AM_WINDOW_SIZE 512 + +typedef enum{ + RLC_MODE_TM = 0, + RLC_MODE_UM, + RLC_MODE_AM, + RLC_MODE_N_ITEMS, +}rlc_mode_t; +static const char rlc_mode_text[RLC_MODE_N_ITEMS][20] = {"Transparent Mode", + "Unacknowledged Mode", + "Acknowledged Mode"}; + +typedef enum{ + RLC_FI_FIELD_START_AND_END_ALIGNED = 0, + RLC_FI_FIELD_NOT_END_ALIGNED, + RLC_FI_FIELD_NOT_START_ALIGNED, + RLC_FI_FIELD_NOT_START_OR_END_ALIGNED, + RLC_FI_FIELD_N_ITEMS, +}rlc_fi_field_t; +static const char rlc_fi_field_text[RLC_FI_FIELD_N_ITEMS][32] = {"Start and end aligned", + "Not end aligned", + "Not start aligned", + "Not start or end aligned"}; + +typedef enum{ + RLC_DC_FIELD_CONTROL_PDU = 0, + RLC_DC_FIELD_DATA_PDU, + RLC_DC_FIELD_N_ITEMS, +}rlc_dc_field_t; +static const char rlc_dc_field_text[RLC_DC_FIELD_N_ITEMS][20] = {"Control PDU", + "Data PDU"}; + +// UMD PDU Header +typedef struct{ + uint8_t fi; // Framing info + rlc_umd_sn_size_t sn_size; // Sequence number size (5 or 10 bits) + uint16_t sn; // Sequence number + uint32_t N_li; // Number of length indicators + uint16_t li[RLC_AM_WINDOW_SIZE]; // Array of length indicators +}rlc_umd_pdu_header_t; + +// AMD PDU Header +struct rlc_amd_pdu_header_t{ + rlc_dc_field_t dc; // Data or control + uint8_t rf; // Resegmentation flag + uint8_t p; // Polling bit + uint8_t fi; // Framing info + uint16_t sn; // Sequence number + uint8_t lsf; // Last segment flag + uint16_t so; // Segment offset + uint32_t N_li; // Number of length indicators + uint16_t li[RLC_AM_WINDOW_SIZE]; // Array of length indicators + + rlc_amd_pdu_header_t(){ + dc = RLC_DC_FIELD_CONTROL_PDU; + rf = 0; + p = 0; + fi = 0; + sn = 0; + lsf = 0; + so = 0; + N_li=0; + for(int i=0;irlc_mode), am(), um() + { + switch(rlc_mode) + { + case LIBLTE_RRC_RLC_MODE_AM: + am.t_poll_retx = liblte_rrc_t_poll_retransmit_num[cnfg->ul_am_rlc.t_poll_retx]; + am.poll_pdu = liblte_rrc_poll_pdu_num[cnfg->ul_am_rlc.poll_pdu]; + am.poll_byte = liblte_rrc_poll_byte_num[cnfg->ul_am_rlc.poll_byte]*1000; // KB + am.max_retx_thresh = liblte_rrc_max_retx_threshold_num[cnfg->ul_am_rlc.max_retx_thresh]; + am.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_am_rlc.t_reordering]; + am.t_status_prohibit = liblte_rrc_t_status_prohibit_num[cnfg->dl_am_rlc.t_status_prohibit]; + break; + case LIBLTE_RRC_RLC_MODE_UM_BI: + um.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_bi_rlc.t_reordering]; + um.rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_bi_rlc.sn_field_len; + um.rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 16 : 512; + um.rx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 32 : 1024; + um.tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_bi_rlc.sn_field_len; + um.tx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.tx_sn_field_length) ? 32 : 1024; + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_UL: + um.tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_uni_rlc.sn_field_len; + um.tx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.tx_sn_field_length) ? 32 : 1024; + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_DL: + um.t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_uni_rlc.t_reordering]; + um.rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_uni_rlc.sn_field_len; + um.rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 16 : 512; + um.rx_mod = (RLC_UMD_SN_SIZE_5_BITS == um.rx_sn_field_length) ? 32 : 1024; + break; + default: + // Handle default case + break; + } + } + + // Factory for MCH + static srslte_rlc_config_t mch_config() + { + srslte_rlc_config_t cfg; + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_DL; + cfg.um.t_reordering = 0; + cfg.um.rx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cfg.um.rx_window_size = 0; + cfg.um.rx_mod = 1; + cfg.um.tx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cfg.um.tx_mod = 1; + cfg.um.is_mrb = true; + return cfg; + } +}; + +} // namespace srslte + +#endif // SRSLTE_RLC_INTERFACE_H diff --git a/lib/include/srslte/upper/rlc_metrics.h b/lib/include/srslte/upper/rlc_metrics.h new file mode 100644 index 0000000..ab6e83c --- /dev/null +++ b/lib/include/srslte/upper/rlc_metrics.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_METRICS_H +#define SRSLTE_RLC_METRICS_H + + +namespace srslte { + +struct rlc_metrics_t +{ + float dl_tput_mbps; + float ul_tput_mbps; +}; + +} // namespace srslte + +#endif // SRSLTE_RLC_METRICS_H diff --git a/lib/include/srslte/upper/rlc_tm.h b/lib/include/srslte/upper/rlc_tm.h new file mode 100644 index 0000000..d78ab59 --- /dev/null +++ b/lib/include/srslte/upper/rlc_tm.h @@ -0,0 +1,85 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_TM_H +#define SRSLTE_RLC_TM_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/upper/rlc_tx_queue.h" +#include "srslte/upper/rlc_common.h" + +namespace srslte { + +class rlc_tm + :public rlc_common +{ +public: + rlc_tm(uint32_t queue_len = 16); + ~rlc_tm(); + void init(log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers); + void configure(srslte_rlc_config_t cnfg); + void stop(); + void reestablish(); + void empty_queue(); + + rlc_mode_t get_mode(); + uint32_t get_bearer(); + + // PDCP interface + void write_sdu(byte_buffer_t *sdu); + void write_sdu_nb(byte_buffer_t *sdu); + + // MAC interface + uint32_t get_buffer_state(); + uint32_t get_total_buffer_state(); + int read_pdu(uint8_t *payload, uint32_t nof_bytes); + void write_pdu(uint8_t *payload, uint32_t nof_bytes); + +private: + + byte_buffer_pool *pool; + srslte::log *log; + uint32_t lcid; + srsue::pdcp_interface_rlc *pdcp; + srsue::rrc_interface_rlc *rrc; + + bool tx_enabled; + + // Thread-safe queues for MAC messages + rlc_tx_queue ul_queue; +}; + +} // namespace srsue + + +#endif // SRSLTE_RLC_TM_H diff --git a/lib/include/srslte/upper/rlc_tx_queue.h b/lib/include/srslte/upper/rlc_tx_queue.h new file mode 100644 index 0000000..afabcd8 --- /dev/null +++ b/lib/include/srslte/upper/rlc_tx_queue.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: rlc_tx_queue.h + * Description: Queue used in RLC TM/UM/AM TX queues. + * Uses a blocking queue with bounded capacity to block higher layers + * when pushing Uplink traffic + * Reference: + *****************************************************************************/ + +#ifndef SRSLTE_MSG_QUEUE_H +#define SRSLTE_MSG_QUEUE_H + +#include "srslte/common/block_queue.h" +#include "srslte/common/common.h" +#include + +namespace srslte { + +class rlc_tx_queue : public block_queue::call_mutexed_itf +{ +public: + rlc_tx_queue(int capacity = 128) : queue(capacity) { + unread_bytes = 0; + queue.set_mutexed_itf(this); + } + // increase/decrease unread_bytes inside push/pop mutexed operations + void pushing(byte_buffer_t *msg) { + unread_bytes += msg->N_bytes; + } + void popping(byte_buffer_t *msg) { + if (unread_bytes > msg->N_bytes) { + unread_bytes -= msg->N_bytes; + } else { + unread_bytes = 0; + } + } + void write(byte_buffer_t *msg) + { + queue.push(msg); + } + + bool try_write(byte_buffer_t *msg) + { + return queue.try_push(msg); + } + + void read(byte_buffer_t **msg) + { + byte_buffer_t *m = queue.wait_pop(); + *msg = m; + } + + bool try_read(byte_buffer_t **msg) + { + return queue.try_pop(msg); + } + + void resize(uint32_t capacity) + { + queue.resize(capacity); + } + uint32_t size() + { + return (uint32_t) queue.size(); + } + + uint32_t size_bytes() + { + return unread_bytes; + } + + uint32_t size_tail_bytes() + { + if (!queue.empty()) { + byte_buffer_t *m = queue.front(); + if (m) { + return m->N_bytes; + } + } + return 0; + } + + // This is a hack to reset N_bytes counter when queue is corrupted (see line 89) + void reset() { + unread_bytes = 0; + } + +private: + bool is_empty() { return queue.empty(); } + + block_queue queue; + uint32_t unread_bytes; +}; + +} // namespace srslte + + +#endif // SRSLTE_MSG_QUEUE_H diff --git a/lib/include/srslte/upper/rlc_um.h b/lib/include/srslte/upper/rlc_um.h new file mode 100644 index 0000000..f099f60 --- /dev/null +++ b/lib/include/srslte/upper/rlc_um.h @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSLTE_RLC_UM_H +#define SRSLTE_RLC_UM_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/upper/rlc_tx_queue.h" +#include "srslte/upper/rlc_common.h" +#include +#include +#include + +namespace srslte { + +struct rlc_umd_pdu_t{ + rlc_umd_pdu_header_t header; + byte_buffer_t *buf; +}; + +class rlc_um + :public timer_callback + ,public rlc_common +{ +public: + rlc_um(uint32_t queue_len = 32); + ~rlc_um(); + void init(log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers_); + void configure(srslte_rlc_config_t cnfg); + void reestablish(); + void stop(); + void empty_queue(); + bool is_mrb(); + + rlc_mode_t get_mode(); + uint32_t get_bearer(); + + // PDCP interface + void write_sdu(byte_buffer_t *sdu); + void write_sdu_nb(byte_buffer_t *sdu); + + // MAC interface + uint32_t get_buffer_state(); + uint32_t get_total_buffer_state(); + int read_pdu(uint8_t *payload, uint32_t nof_bytes); + void write_pdu(uint8_t *payload, uint32_t nof_bytes); + int get_increment_sequence_num(); + // Timeout callback interface + void timer_expired(uint32_t timeout_id); + + bool reordering_timeout_running(); + +private: + + byte_buffer_pool *pool; + srslte::log *log; + uint32_t lcid; + srsue::pdcp_interface_rlc *pdcp; + srsue::rrc_interface_rlc *rrc; + mac_interface_timers *mac_timers; + + // TX SDU buffers + rlc_tx_queue tx_sdu_queue; + byte_buffer_t *tx_sdu; + byte_buffer_t tx_sdu_temp; + + // Rx window + std::map rx_window; + + // RX SDU buffers + byte_buffer_t *rx_sdu; + uint32_t vr_ur_in_rx_sdu; + + // Mutexes + pthread_mutex_t mutex; + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + srslte_rlc_um_config_t cfg; + + /**************************************************************************** + * State variables and counters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + // Tx state variables + uint32_t vt_us; // Send state. SN to be assigned for next PDU. + + // Rx state variables + uint32_t vr_ur; // Receive state. SN of earliest PDU still considered for reordering. + uint32_t vr_ux; // t_reordering state. SN following PDU which triggered t_reordering. + uint32_t vr_uh; // Highest rx state. SN following PDU with highest SN among rxed PDUs. + + /**************************************************************************** + * Timers + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + srslte::timers::timer *reordering_timer; + uint32_t reordering_timer_id; + + bool tx_enabled; + bool pdu_lost; + + int build_data_pdu(uint8_t *payload, uint32_t nof_bytes); + void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes); + void reassemble_rx_sdus(); + bool inside_reordering_window(uint16_t sn); + void debug_state(); + + std::string rb_name(); +}; + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ +void rlc_um_read_data_pdu_header(byte_buffer_t *pdu, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header); +void rlc_um_read_data_pdu_header(uint8_t *payload, uint32_t nof_bytes, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header); +void rlc_um_write_data_pdu_header(rlc_umd_pdu_header_t *header, byte_buffer_t *pdu); + +uint32_t rlc_um_packed_length(rlc_umd_pdu_header_t *header); +bool rlc_um_start_aligned(uint8_t fi); +bool rlc_um_end_aligned(uint8_t fi); + +} // namespace srsue + + +#endif // SRSLTE_RLC_UM_H diff --git a/lib/include/srslte/version.h.in b/lib/include/srslte/version.h.in new file mode 100644 index 0000000..f50a6f9 --- /dev/null +++ b/lib/include/srslte/version.h.in @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef SRSLTE_VERSION_H_IN +#define SRSLTE_VERSION_H_IN + +// the configured options and settings for SRSLTE +#define SRSLTE_VERSION_MAJOR @SRSLTE_VERSION_MAJOR@ +#define SRSLTE_VERSION_MINOR @SRSLTE_VERSION_MINOR@ +#define SRSLTE_VERSION_PATCH @SRSLTE_VERSION_PATCH@ +#define SRSLTE_VERSION_STRING "@SRSLTE_VERSION_STRING@" + +#define SRSLTE_VERSION_ENCODE(major, minor, patch) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((patch) * 1)) + +#define SRSLTE_VERSION SRSLTE_VERSION_ENCODE( \ + SRSLTE_VERSION_MAJOR, \ + SRSLTE_VERSION_MINOR, \ + SRSLTE_VERSION_PATCH) + +#define SRSLTE_VERSION_CHECK(major,minor,patch) \ + (SRSLTE_VERSION >= SRSLTE_VERSION_ENCODE(major,minor,patch)) + +#include "srslte/config.h" + +SRSLTE_API char* srslte_get_version(); +SRSLTE_API int srslte_get_version_major(); +SRSLTE_API int srslte_get_version_minor(); +SRSLTE_API int srslte_get_version_patch(); +SRSLTE_API int srslte_check_version(int major, int minor, int patch); + +#endif // VERSION_ diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt new file mode 100644 index 0000000..2c576f7 --- /dev/null +++ b/lib/src/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(asn1) +add_subdirectory(common) +add_subdirectory(phy) +add_subdirectory(radio) +add_subdirectory(upper) diff --git a/lib/src/asn1/CMakeLists.txt b/lib/src/asn1/CMakeLists.txt new file mode 100644 index 0000000..caddb38 --- /dev/null +++ b/lib/src/asn1/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2013-2017 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/. +# + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch -Wno-unused-but-set-variable -Wno-unused-variable -Wno-return-type -Wno-sign-compare -Wno-reorder -Wno-parantheses") +add_library(srslte_asn1 STATIC + liblte_common.cc + liblte_rrc.cc + liblte_mme.cc + liblte_s1ap.cc + gtpc.cc +) +install(TARGETS srslte_asn1 DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/asn1/gtpc.cc b/lib/src/asn1/gtpc.cc new file mode 100644 index 0000000..ac01168 --- /dev/null +++ b/lib/src/asn1/gtpc.cc @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/asn1/gtpc.h" +#include "srslte/common/common.h" + +namespace srslte{ + + +int +gtpc_pack_create_session_request(struct gtpc_create_session_request *cs_req, srslte::byte_buffer_t) +{ + //FIXME + return 0; +} + +}; diff --git a/lib/src/asn1/liblte_common.cc b/lib/src/asn1/liblte_common.cc new file mode 100644 index 0000000..0e618bf --- /dev/null +++ b/lib/src/asn1/liblte_common.cc @@ -0,0 +1,198 @@ +/******************************************************************************* + + Copyright 2014 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_common.cc + + Description: Contains all the implementations for the LTE common library. + + Revision History + ---------- ------------- -------------------------------------------- + 08/03/2014 Ben Wojtowicz Created file. + 11/29/2014 Ben Wojtowicz Added liblte prefix to value_2_bits and + bits_2_value. + +*******************************************************************************/ + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/asn1/liblte_common.h" + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + + +/******************************************************************************* + GLOBAL VARIABLES +*******************************************************************************/ + + +/******************************************************************************* + FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + Name: liblte_value_2_bits + + Description: Converts a value to a bit string +*********************************************************************/ +void liblte_value_2_bits(uint32 value, + uint8 **bits, + uint32 N_bits) +{ + uint32 i; + + for(i=0; i> (N_bits-i-1)) & 0x1; + } + *bits += N_bits; +} + +/********************************************************************* + Name: liblte_bits_2_value + + Description: Converts a bit string to a value +*********************************************************************/ +uint32 liblte_bits_2_value(uint8 **bits, + uint32 N_bits) +{ + uint32 value = 0; + uint32 i; + + for(i=0; imsg; + uint32_t i; + + for(i=0; iN_bits/8; i++) + { + bytes->msg[i] = liblte_bits_2_value(&bit_ptr, 8); + } + bytes->N_bytes = bits->N_bits/8; + if(bits->N_bits%8 > 0) + { + bytes->msg[bytes->N_bytes] = liblte_bits_2_value(&bit_ptr, bits->N_bits%8); + bytes->N_bytes++; + } +} + +/********************************************************************* + Name: liblte_unpack + + Description: Unpack a byte array into a bit array +*********************************************************************/ +void liblte_unpack(LIBLTE_BYTE_MSG_STRUCT *bytes, + LIBLTE_BIT_MSG_STRUCT *bits) +{ + uint8_t *bit_ptr = bits->msg; + uint32_t i; + + for(i=0; iN_bytes; i++) + { + liblte_value_2_bits(bytes->msg[i], &bit_ptr, 8); + } + bits->N_bits = bytes->N_bytes*8; +} + +/********************************************************************* + Name: liblte_pack + + Description: Pack a bit array into a byte array +*********************************************************************/ +void liblte_pack(uint8_t *bits, uint32_t n_bits, uint8_t *bytes) +{ + uint8_t* bit_ptr = bits; + uint32_t i; + + for(i=0; i 0) + { + bytes[n_bits/8] = liblte_bits_2_value(&bit_ptr, n_bits%8); + } +} + +/********************************************************************* + Name: liblte_unpack + + Description: Unpack a byte array into a bit array +*********************************************************************/ +void liblte_unpack(uint8_t *bytes, uint32_t n_bytes, uint8_t *bits) +{ + uint8_t *bit_ptr = bits; + uint32_t i; + + for(i=0; i 0) + { + (*ptr)++; + } +} + +/********************************************************************* + Name: liblte_align_up_zero + + Description: Aligns a pointer to a multibyte boundary and zeros + bytes skipped +*********************************************************************/ +void liblte_align_up_zero(uint8_t **ptr, uint32_t align) +{ + while( (uint64_t)(*ptr) % align > 0) + { + **ptr = 0; + (*ptr)++; + } +} diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc new file mode 100644 index 0000000..189f913 --- /dev/null +++ b/lib/src/asn1/liblte_mme.cc @@ -0,0 +1,11191 @@ +/******************************************************************************* + + Copyright 2014-2015 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_mme.cc + + Description: Contains all the implementations for the LTE Mobility + Management Entity library. + + Revision History + ---------- ------------- -------------------------------------------- + 06/15/2014 Ben Wojtowicz Created file. + 08/03/2014 Ben Wojtowicz Added more decoding/encoding. + 09/03/2014 Ben Wojtowicz Added more decoding/encoding and fixed MCC + and MNC packing. + 11/01/2014 Ben Wojtowicz Added more decoding/encoding. + 11/29/2014 Ben Wojtowicz Added more decoding/encoding. + 12/16/2014 Ben Wojtowicz Added more decoding/encoding. + 12/24/2014 Ben Wojtowicz Cleaned up the Time Zone and Time IE. + 02/15/2015 Ben Wojtowicz Added more decoding/encoding. + +*******************************************************************************/ + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/asn1/liblte_mme.h" +#include "srslte/common/liblte_security.h" + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + + +/******************************************************************************* + GLOBAL VARIABLES +*******************************************************************************/ + + +/******************************************************************************* + INFORMATION ELEMENT FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + IE Name: Additional Information + + Description: Provides additional information to upper layers in + relation to the generic NAS message transport + mechanism. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.0 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_information_ie(LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT *add_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(add_info != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = add_info->N_octets; + for(i=0; iN_octets; i++) + { + (*ie_ptr)[1+i] = add_info->info[i]; + } + *ie_ptr += add_info->N_octets + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_information_ie(uint8 **ie_ptr, + LIBLTE_MME_ADDITIONAL_INFORMATION_STRUCT *add_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + add_info != NULL) + { + add_info->N_octets = (*ie_ptr)[0]; + for(i=0; iN_octets; i++) + { + add_info->info[i] = (*ie_ptr)[1+i]; + } + *ie_ptr += add_info->N_octets + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Device Properties + + Description: Indicates if the UE is configured for NAS signalling + low priority. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.0A + 24.008 v10.2.0 Section 10.5.7.8 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_device_properties_ie(LIBLTE_MME_DEVICE_PROPERTIES_ENUM device_props, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= device_props << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_device_properties_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_DEVICE_PROPERTIES_ENUM *device_props) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + device_props != NULL) + { + *device_props = (LIBLTE_MME_DEVICE_PROPERTIES_ENUM)((**ie_ptr >> bit_offset) & 0x01); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Bearer Context Status + + Description: Indicates the state of each EPS bearer context that + can be identified by an EPS bearer identity. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_bearer_context_status_ie(LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT *ebcs, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ebcs != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 2; + (*ie_ptr)[1] = (ebcs->ebi[7] << 7); + (*ie_ptr)[1] |= (ebcs->ebi[6] << 6); + (*ie_ptr)[1] |= (ebcs->ebi[5] << 5); + (*ie_ptr)[2] = (ebcs->ebi[15] << 7); + (*ie_ptr)[2] |= (ebcs->ebi[14] << 6); + (*ie_ptr)[2] |= (ebcs->ebi[13] << 5); + (*ie_ptr)[2] |= (ebcs->ebi[12] << 4); + (*ie_ptr)[2] |= (ebcs->ebi[11] << 3); + (*ie_ptr)[2] |= (ebcs->ebi[10] << 2); + (*ie_ptr)[2] |= (ebcs->ebi[9] << 1); + (*ie_ptr)[2] |= ebcs->ebi[8]; + *ie_ptr += 3; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_bearer_context_status_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT *ebcs) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ebcs != NULL) + { + ebcs->ebi[5] = ((*ie_ptr)[1] >> 5) & 0x01; + ebcs->ebi[6] = ((*ie_ptr)[1] >> 6) & 0x01; + ebcs->ebi[7] = ((*ie_ptr)[1] >> 7) & 0x01; + ebcs->ebi[8] = (*ie_ptr)[2] & 0x01; + ebcs->ebi[9] = ((*ie_ptr)[2] >> 1) & 0x01; + ebcs->ebi[10] = ((*ie_ptr)[2] >> 2) & 0x01; + ebcs->ebi[11] = ((*ie_ptr)[2] >> 3) & 0x01; + ebcs->ebi[12] = ((*ie_ptr)[2] >> 4) & 0x01; + ebcs->ebi[13] = ((*ie_ptr)[2] >> 5) & 0x01; + ebcs->ebi[14] = ((*ie_ptr)[2] >> 6) & 0x01; + ebcs->ebi[15] = ((*ie_ptr)[2] >> 7) & 0x01; + *ie_ptr += 3; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Location Area Identification + + Description: Provides an unambiguous identification of location + areas within the area covered by the 3GPP system. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.2 + 24.008 v10.2.0 Section 10.5.1.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_location_area_id_ie(LIBLTE_MME_LOCATION_AREA_ID_STRUCT *lai, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(lai != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = (((lai->mcc/10) % 10) << 4) | ((lai->mcc/100) % 10); + if(lai->mnc < 100) + { + (*ie_ptr)[1] = 0xF0 | (lai->mcc % 10); + (*ie_ptr)[2] = ((lai->mnc % 10) << 4) | ((lai->mnc/10) % 10); + }else{ + (*ie_ptr)[1] = ((lai->mnc % 10) << 4) | (lai->mcc % 10); + (*ie_ptr)[2] = (((lai->mnc/10) % 10) << 4) | ((lai->mnc/100) % 10); + } + (*ie_ptr)[3] = (lai->lac >> 8) & 0xFF; + (*ie_ptr)[4] = lai->lac & 0xFF; + *ie_ptr += 5; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_location_area_id_ie(uint8 **ie_ptr, + LIBLTE_MME_LOCATION_AREA_ID_STRUCT *lai) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + lai != NULL) + { + lai->mcc = ((*ie_ptr)[0] & 0x0F)*100; + lai->mcc += (((*ie_ptr)[0] >> 4) & 0x0F)*10; + lai->mcc += (*ie_ptr)[1] & 0x0F; + if((((*ie_ptr)[1] >> 4) & 0x0F) == 0x0F) + { + lai->mnc = ((*ie_ptr)[2] & 0x0F)*10; + lai->mnc += ((*ie_ptr)[2] >> 4) & 0x0F; + }else{ + lai->mnc = ((*ie_ptr)[1] >> 4) & 0x0F; + lai->mnc += ((*ie_ptr)[2] & 0x0F)*100; + lai->mnc += (((*ie_ptr)[2] >> 4) & 0x0F)*10; + } + lai->lac = (*ie_ptr)[3] << 8; + lai->lac |= (*ie_ptr)[4]; + *ie_ptr += 5; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Mobile Identity + + Description: Provides either the IMSI, TMSI/P-TMSI/M-TMSI, IMEI, + IMEISV, or TMGI, associated with the optional MBMS + session identity. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.3 + 24.008 v10.2.0 Section 10.5.1.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_id_ie(LIBLTE_MME_MOBILE_ID_STRUCT *mobile_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *id = NULL; + uint32 id32 = 0; + uint32 i; + uint8 length = 0; + bool odd = false; + + if(mobile_id != NULL && + ie_ptr != NULL) + { + if(LIBLTE_MME_MOBILE_ID_TYPE_IMSI == mobile_id->type_of_id) + { + id = mobile_id->imsi; + length = 8; + odd = true; + }else if(LIBLTE_MME_MOBILE_ID_TYPE_IMEI == mobile_id->type_of_id){ + id = mobile_id->imei; + length = 8; + odd = true; + }else if(LIBLTE_MME_MOBILE_ID_TYPE_IMEISV == mobile_id->type_of_id){ + id = mobile_id->imeisv; + length = 9; + odd = false; + }else if(LIBLTE_MME_MOBILE_ID_TYPE_TMSI == mobile_id->type_of_id){ + id32 = mobile_id->tmsi; + length = 4; + odd = false; + } + }else{ + // FIXME: Not handling these IDs + return(err); + } + + // Length + **ie_ptr = length; + *ie_ptr += 1; + if(LIBLTE_MME_MOBILE_ID_TYPE_TMSI != mobile_id->type_of_id) + { + // | Identity digit 1 | odd/even | Id type | + if(odd) + { + **ie_ptr = (id[0] << 4) | (1 << 3) | mobile_id->type_of_id; + }else{ + **ie_ptr = (id[0] << 4) | (0 << 3) | mobile_id->type_of_id; + } + *ie_ptr += 1; + + + // | Identity digit p+1 | Identity digit p | + for(i=0; i<7; i++) + { + (*ie_ptr)[i] = (id[i*2+2] << 4) | id[i*2+1]; + } + *ie_ptr += 7; + if(!odd) + { + **ie_ptr = 0xF0 | id[15]; + *ie_ptr += 1; + } + + err = LIBLTE_SUCCESS; + } + else{ + + **ie_ptr = (0xFF << 4) | (0 << 3) | mobile_id->type_of_id; + *ie_ptr += 1; + //4-Byte based ids + **ie_ptr = (id32 >> 24) & 0xFF; + *ie_ptr += 1; + **ie_ptr = (id32 >> 16) & 0xFF; + *ie_ptr += 1; + **ie_ptr = (id32 >> 8) & 0xFF; + *ie_ptr += 1; + **ie_ptr = id32 & 0xFF; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_id_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_ID_STRUCT *mobile_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *id; + uint32 length; + uint32 i; + bool odd = false; + + if(ie_ptr != NULL && + mobile_id != NULL) + { + length = **ie_ptr; + *ie_ptr += 1; + + mobile_id->type_of_id = **ie_ptr & 0x07; + + if(LIBLTE_MME_MOBILE_ID_TYPE_IMSI == mobile_id->type_of_id) + { + id = mobile_id->imsi; + odd = true; + }else if(LIBLTE_MME_MOBILE_ID_TYPE_IMEI == mobile_id->type_of_id){ + id = mobile_id->imei; + odd = true; + }else if(LIBLTE_MME_MOBILE_ID_TYPE_IMEISV == mobile_id->type_of_id){ + id = mobile_id->imeisv; + odd = false; + }else{ + // FIXME: Not handling these IDs + return(err); + } + + id[0] = **ie_ptr >> 4; + *ie_ptr += 1; + for(i=0; i<7; i++) + { + id[i*2+1] = (*ie_ptr)[i] & 0x0F; + id[i*2+2] = (*ie_ptr)[i] >> 4; + } + if(odd) + { + *ie_ptr += 7; + }else{ + id[i*2+1] = (*ie_ptr)[i] & 0xF; + *ie_ptr += 8; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Mobile Station Classmark 2 + + Description: Provides the network with information concerning + aspects of both high and low priority of the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.4 + 24.008 v10.2.0 Section 10.5.1.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_station_classmark_2_ie(LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT *ms_cm2, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ms_cm2 != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 3; + (*ie_ptr)[1] = (ms_cm2->rev_lev & 0x03) << 5; + (*ie_ptr)[1] |= ms_cm2->es_ind << 4; + (*ie_ptr)[1] |= ms_cm2->a5_1 << 3; + (*ie_ptr)[1] |= ms_cm2->rf_power_cap & 0x07; + (*ie_ptr)[2] = ms_cm2->ps_cap << 6; + (*ie_ptr)[2] |= (ms_cm2->ss_screen_ind & 0x03) << 4; + (*ie_ptr)[2] |= ms_cm2->sm_cap << 3; + (*ie_ptr)[2] |= ms_cm2->vbs << 2; + (*ie_ptr)[2] |= ms_cm2->vgcs << 1; + (*ie_ptr)[2] |= ms_cm2->fc; + (*ie_ptr)[3] = ms_cm2->cm3 << 7; + (*ie_ptr)[3] |= ms_cm2->lcsva_cap << 5; + (*ie_ptr)[3] |= ms_cm2->ucs2 << 4; + (*ie_ptr)[3] |= ms_cm2->solsa << 3; + (*ie_ptr)[3] |= ms_cm2->cmsp << 2; + (*ie_ptr)[3] |= ms_cm2->a5_3 << 1; + (*ie_ptr)[3] |= ms_cm2->a5_2; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_station_classmark_2_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_STATION_CLASSMARK_2_STRUCT *ms_cm2) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ms_cm2 != NULL) + { + ms_cm2->rev_lev = (LIBLTE_MME_REVISION_LEVEL_ENUM)(((*ie_ptr)[1] >> 5) & 0x03); + ms_cm2->es_ind = ((*ie_ptr)[1] >> 4) & 0x01; + ms_cm2->a5_1 = ((*ie_ptr)[1] >> 3) & 0x01; + ms_cm2->rf_power_cap = (LIBLTE_MME_RF_POWER_CAPABILITY_ENUM)((*ie_ptr)[1] & 0x07); + ms_cm2->ps_cap = ((*ie_ptr)[2] >> 6) & 0x01; + ms_cm2->ss_screen_ind = (LIBLTE_MME_SS_SCREEN_INDICATOR_ENUM)(((*ie_ptr)[2] >> 4) & 0x03); + ms_cm2->sm_cap = ((*ie_ptr)[2] >> 3) & 0x01; + ms_cm2->vbs = ((*ie_ptr)[2] >> 2) & 0x01; + ms_cm2->vgcs = ((*ie_ptr)[2] >> 1) & 0x01; + ms_cm2->fc = (*ie_ptr)[2] & 0x01; + ms_cm2->cm3 = ((*ie_ptr)[3] >> 7) & 0x01; + ms_cm2->lcsva_cap = ((*ie_ptr)[3] >> 5) & 0x01; + ms_cm2->ucs2 = ((*ie_ptr)[3] >> 4) & 0x01; + ms_cm2->solsa = ((*ie_ptr)[3] >> 3) & 0x01; + ms_cm2->cmsp = ((*ie_ptr)[3] >> 2) & 0x01; + ms_cm2->a5_3 = ((*ie_ptr)[3] >> 1) & 0x01; + ms_cm2->a5_2 = (*ie_ptr)[3] & 0x01; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Mobile Station Classmark 3 + + Description: Provides the network with information concerning + aspects of the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.5 + 24.008 v10.2.0 Section 10.5.1.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_mobile_station_classmark_3_ie(LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT *ms_cm3, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ms_cm3 != NULL && + ie_ptr != NULL) + { + // FIXME + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_station_classmark_3_ie(uint8 **ie_ptr, + LIBLTE_MME_MOBILE_STATION_CLASSMARK_3_STRUCT *ms_cm3) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ms_cm3 != NULL) + { + // FIXME + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: NAS Security Parameters From E-UTRA + + Description: Provides the UE with information that enables the UE + to create a mapped UMTS security context. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_parameters_from_eutra_ie(uint8 dl_nas_count, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = dl_nas_count & 0x0F; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_parameters_from_eutra_ie(uint8 **ie_ptr, + uint8 *dl_nas_count) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + dl_nas_count != NULL) + { + *dl_nas_count = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: NAS Security Parameters To E-UTRA + + Description: Provides the UE with parameters that enables the UE + to create a mapped EPS security context and take + this context into use after inter-system handover to + S1 mode. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_parameters_to_eutra_ie(LIBLTE_MME_NAS_SECURITY_PARAMETERS_TO_EUTRA_STRUCT *sec_params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(sec_params != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = (sec_params->nonce_mme >> 24) & 0xFF; + (*ie_ptr)[1] = (sec_params->nonce_mme >> 16) & 0xFF; + (*ie_ptr)[2] = (sec_params->nonce_mme >> 8) & 0xFF; + (*ie_ptr)[3] = sec_params->nonce_mme & 0xFF; + (*ie_ptr)[4] = (sec_params->eea & 0x07) << 4; + (*ie_ptr)[4] |= sec_params->eia & 0x07; + (*ie_ptr)[5] = (sec_params->tsc_flag & 0x01) << 3; + (*ie_ptr)[5] |= sec_params->nas_ksi & 0x07; + *ie_ptr += 6; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_parameters_to_eutra_ie(uint8 **ie_ptr, + LIBLTE_MME_NAS_SECURITY_PARAMETERS_TO_EUTRA_STRUCT *sec_params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + sec_params != NULL) + { + sec_params->nonce_mme = (*ie_ptr)[0] << 24; + sec_params->nonce_mme |= (*ie_ptr)[1] << 16; + sec_params->nonce_mme |= (*ie_ptr)[2] << 8; + sec_params->nonce_mme |= (*ie_ptr)[3]; + sec_params->eea = (LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_ENUM)(((*ie_ptr)[4] >> 4) & 0x07); + sec_params->eia = (LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_ENUM)((*ie_ptr)[4] & 0x07); + sec_params->tsc_flag = (LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_ENUM)(((*ie_ptr)[5] >> 3) & 0x01); + sec_params->nas_ksi = (*ie_ptr)[5] & 0x07; + *ie_ptr += 6; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PLMN List + + Description: Provides a list of PLMN codes to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.8 + 24.008 v10.2.0 Section 10.5.1.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_plmn_list_ie(LIBLTE_MME_PLMN_LIST_STRUCT *plmn_list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(plmn_list != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = plmn_list->N_plmns * 3; + for(i=0; iN_plmns; i++) + { + (*ie_ptr)[i*3+0] = (((plmn_list->mcc[i]/10) % 10) << 4) | ((plmn_list->mcc[i]/100) % 10); + if(plmn_list->mnc[i] < 100) + { + (*ie_ptr)[i*3+1] = 0xF0 | (plmn_list->mcc[i] % 10); + (*ie_ptr)[i*3+2] = ((plmn_list->mnc[i] % 10) << 4) | ((plmn_list->mnc[i]/10) % 10); + }else{ + (*ie_ptr)[i*3+1] = ((plmn_list->mnc[i] % 10) << 4) | (plmn_list->mcc[i] % 10); + (*ie_ptr)[i*3+2] = (((plmn_list->mnc[i]/10) % 10) << 4) | ((plmn_list->mnc[i]/100) % 10); + } + } + *ie_ptr += (plmn_list->N_plmns * 3) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_plmn_list_ie(uint8 **ie_ptr, + LIBLTE_MME_PLMN_LIST_STRUCT *plmn_list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + plmn_list != NULL) + { + plmn_list->N_plmns = (*ie_ptr)[0] / 3; + for(i=0; iN_plmns; i++) + { + plmn_list->mcc[i] = ((*ie_ptr)[i*3+0] & 0x0F)*100; + plmn_list->mcc[i] += (((*ie_ptr)[i*3+0] >> 4) & 0x0F)*10; + plmn_list->mcc[i] += (*ie_ptr)[i*3+1] & 0x0F; + if((((*ie_ptr)[i*3+1] >> 4) & 0x0F) == 0x0F) + { + plmn_list->mnc[i] = ((*ie_ptr)[i*3+2] & 0x0F)*10; + plmn_list->mnc[i] += ((*ie_ptr)[i*3+2] >> 4) & 0x0F; + }else{ + plmn_list->mnc[i] = ((*ie_ptr)[i*3+1] >> 4) & 0x0F; + plmn_list->mnc[i] += ((*ie_ptr)[i*3+2] & 0x0F)*100; + plmn_list->mnc[i] += (((*ie_ptr)[i*3+2] >> 4) & 0x0F)*10; + } + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Spare Half Octet + + Description: Used in the description of EMM and ESM messages when + an odd number of half octet type 1 information + elements are used. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.9 +*********************************************************************/ + +/********************************************************************* + IE Name: Supported Codec List + + Description: Provides the network with information about the + speech codecs supported by the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.2.10 + 24.008 v10.2.0 Section 10.5.4.32 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_supported_codec_list_ie(LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT *supported_codec_list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(supported_codec_list != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = supported_codec_list->N_supported_codecs*4; + for(i=0; iN_supported_codecs; i++) + { + (*ie_ptr)[1+i*4+0] = supported_codec_list->supported_codec[i].sys_id; + (*ie_ptr)[1+i*4+1] = 2; + (*ie_ptr)[1+i*4+2] = (supported_codec_list->supported_codec[i].codec_bitmap >> 8) & 0xFF; + (*ie_ptr)[1+i*4+3] = supported_codec_list->supported_codec[i].codec_bitmap & 0xFF; + } + *ie_ptr += (supported_codec_list->N_supported_codecs*4) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_supported_codec_list_ie(uint8 **ie_ptr, + LIBLTE_MME_SUPPORTED_CODEC_LIST_STRUCT *supported_codec_list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + supported_codec_list != NULL) + { + supported_codec_list->N_supported_codecs = ((*ie_ptr)[0]/4); + for(i=0; iN_supported_codecs; i++) + { + supported_codec_list->supported_codec[i].sys_id = (*ie_ptr)[1+i*4+0]; + supported_codec_list->supported_codec[i].codec_bitmap = (*ie_ptr)[1+i*4+2] << 8; + supported_codec_list->supported_codec[i].codec_bitmap |= (*ie_ptr)[1+i*4+3]; + } + *ie_ptr += (supported_codec_list->N_supported_codecs*4) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Additional Update Result + + Description: Provides additional information about the result of + a combined attached procedure or a combined tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.0A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_update_result_ie(LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM result, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= result << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_update_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM *result) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(result != NULL && + ie_ptr != NULL) + { + *result = (LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM)((**ie_ptr >> bit_offset) & 0x03); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Additional Update Type + + Description: Provides additional information about the type of + request for a combined attach or a combined tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.0B +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_additional_update_type_ie(LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM aut, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= aut << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_additional_update_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM *aut) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + aut != NULL) + { + *aut = (LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_ENUM)((**ie_ptr >> bit_offset) & 0x01); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Authentication Failure Parameter + + Description: Provides the network with the necessary information + to begin a re-authentication procedure in the case + of a 'Synch failure', following a UMTS or EPS + authentication challenge. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.1 + 24.008 v10.2.0 Section 10.5.3.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_failure_parameter_ie(uint8 *auth_fail_param, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(auth_fail_param != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 14; + for(i=0; i<14; i++) + { + (*ie_ptr)[i+1] = auth_fail_param[i]; + } + *ie_ptr += 15; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_failure_parameter_ie(uint8 **ie_ptr, + uint8 *auth_fail_param) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + auth_fail_param != NULL) + { + for(i=0; i<14; i++) + { + auth_fail_param[i] = (*ie_ptr)[i+1]; + } + *ie_ptr += 15; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Authentication Parameter AUTN + + Description: Provides the UE with a means of authenticating the + network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.2 + 24.008 v10.2.0 Section 10.5.3.1.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_parameter_autn_ie(uint8 *autn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(autn != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 16; + for(i=0; i<16; i++) + { + (*ie_ptr)[i+1] = autn[i]; + } + *ie_ptr += 17; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_autn_ie(uint8 **ie_ptr, + uint8 *autn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + autn != NULL) + { + for(i=0; i<16; i++) + { + autn[i] = (*ie_ptr)[i+1]; + } + *ie_ptr += 17; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Authentication Parameter RAND + + Description: Provides the UE with a non-predictable number to be + used to calculate the authentication signature SRES + and the ciphering key Kc (for a GSM authentication + challenge), or the response RES and both the + ciphering key CK and the integrity key IK (for a + UMTS authentication challenge). + + Document Reference: 24.301 v10.2.0 Section 9.9.3.3 + 24.008 v10.2.0 Section 10.5.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_parameter_rand_ie(uint8 *rand_val, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(rand_val != NULL && + ie_ptr != NULL) + { + for(i=0; i<16; i++) + { + (*ie_ptr)[i] = rand_val[i]; + } + *ie_ptr += 16; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_parameter_rand_ie(uint8 **ie_ptr, + uint8 *rand_val) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + rand_val != NULL) + { + for(i=0; i<16; i++) + { + rand_val[i] = (*ie_ptr)[i]; + } + *ie_ptr += 16; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Authentication Response Parameter + + Description: Provides the network with the authentication + response calculated in the USIM. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_parameter_ie(uint8 *res, + int res_len, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(res != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = res_len; + *ie_ptr += 1; + for(i=0; i> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CSFB Response + + Description: Indicates whether the UE accepts or rejects a paging + for CS fallback. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_csfb_response_ie(uint8 csfb_resp, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= (csfb_resp & 0x07) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_csfb_response_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *csfb_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + csfb_resp != NULL) + { + *csfb_resp = ((*ie_ptr)[0] & 0x07) >> bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Daylight Saving Time + + Description: Encodes the daylight saving time in steps of 1 hour. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.6 + 24.008 v10.2.0 Section 10.5.3.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_daylight_saving_time_ie(LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM dst, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = 1; + (*ie_ptr)[1] = dst & 0x03; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_daylight_saving_time_ie(uint8 **ie_ptr, + LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM *dst) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + dst != NULL) + { + *dst = (LIBLTE_MME_DAYLIGHT_SAVING_TIME_ENUM)((*ie_ptr)[1] & 0x03); + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Detach Type + + Description: Indicates the type of detach. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_type_ie(LIBLTE_MME_DETACH_TYPE_STRUCT *detach_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(detach_type != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] |= (detach_type->switch_off & 0x01) << (3 + bit_offset); + (*ie_ptr)[0] |= (detach_type->type_of_detach & 0x07) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_DETACH_TYPE_STRUCT *detach_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + detach_type != NULL) + { + detach_type->switch_off = ((*ie_ptr)[0] >> (3 + bit_offset)) & 0x01; + detach_type->type_of_detach = ((*ie_ptr)[0] >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: DRX Parameter + + Description: Indicates whether the UE uses DRX mode or not. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.8 + 24.008 v10.2.0 Section 10.5.5.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_drx_parameter_ie(LIBLTE_MME_DRX_PARAMETER_STRUCT *drx_param, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(drx_param != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = drx_param->split_pg_cycle_code; + (*ie_ptr)[1] = (drx_param->drx_cycle_len_coeff_and_value & 0x0F) << 4; + (*ie_ptr)[1] |= (drx_param->split_on_ccch & 0x01) << 3; + (*ie_ptr)[1] |= drx_param->non_drx_timer & 0x07; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_drx_parameter_ie(uint8 **ie_ptr, + LIBLTE_MME_DRX_PARAMETER_STRUCT *drx_param) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + drx_param != NULL) + { + drx_param->split_pg_cycle_code = (*ie_ptr)[0]; + drx_param->drx_cycle_len_coeff_and_value = ((*ie_ptr)[1] >> 4) & 0x0F; + drx_param->split_on_ccch = ((*ie_ptr)[1] >> 3) & 0x01; + drx_param->non_drx_timer = (LIBLTE_MME_NON_DRX_TIMER_ENUM)((*ie_ptr)[1] & 0x07); + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EMM Cause + + Description: Indicates the reason why an EMM request from the UE + is rejected by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_cause_ie(uint8 emm_cause, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr = emm_cause; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_cause_ie(uint8 **ie_ptr, + uint8 *emm_cause) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + emm_cause != NULL) + { + *emm_cause = **ie_ptr; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Attach Result + + Description: Specifies the result of an attach procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.10 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_attach_result_ie(uint8 result, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= result << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_attach_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *result) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + result != NULL) + { + *result = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Attach Type + + Description: Indicates the type of the requested attach. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.11 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_attach_type_ie(uint8 attach_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= attach_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_attach_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *attach_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + attach_type != NULL) + { + *attach_type = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Mobile Identity + + Description: Provides either the IMSI, the GUTI, or the IMEI. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_mobile_id_ie(LIBLTE_MME_EPS_MOBILE_ID_STRUCT *eps_mobile_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *id; + uint32 i; + + if(eps_mobile_id != NULL && + ie_ptr != NULL) + { + if(LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI == eps_mobile_id->type_of_id) + { + **ie_ptr = 11; + *ie_ptr += 1; + **ie_ptr = 0xF0 | eps_mobile_id->type_of_id; + *ie_ptr += 1; + **ie_ptr = (((eps_mobile_id->guti.mcc/10) % 10) << 4) | ((eps_mobile_id->guti.mcc/100) % 10); + *ie_ptr += 1; + if(eps_mobile_id->guti.mnc < 100) + { + **ie_ptr = 0xF0 | (eps_mobile_id->guti.mcc % 10); + *ie_ptr += 1; + **ie_ptr = ((eps_mobile_id->guti.mnc % 10) << 4) | ((eps_mobile_id->guti.mnc/10) % 10); + *ie_ptr += 1; + }else{ + **ie_ptr = ((eps_mobile_id->guti.mnc % 10) << 4) | (eps_mobile_id->guti.mcc % 10); + *ie_ptr += 1; + **ie_ptr = (((eps_mobile_id->guti.mnc/10) % 10) << 4) | ((eps_mobile_id->guti.mnc/100) % 10); + *ie_ptr += 1; + } + **ie_ptr = (eps_mobile_id->guti.mme_group_id >> 8) & 0xFF; + *ie_ptr += 1; + **ie_ptr = eps_mobile_id->guti.mme_group_id & 0xFF; + *ie_ptr += 1; + **ie_ptr = eps_mobile_id->guti.mme_code; + *ie_ptr += 1; + **ie_ptr = (eps_mobile_id->guti.m_tmsi >> 24) & 0xFF; + *ie_ptr += 1; + **ie_ptr = (eps_mobile_id->guti.m_tmsi >> 16) & 0xFF; + *ie_ptr += 1; + **ie_ptr = (eps_mobile_id->guti.m_tmsi >> 8) & 0xFF; + *ie_ptr += 1; + **ie_ptr = eps_mobile_id->guti.m_tmsi & 0xFF; + *ie_ptr += 1; + }else{ + if(LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI == eps_mobile_id->type_of_id) + { + id = eps_mobile_id->imsi; + }else{ + id = eps_mobile_id->imei; + } + + **ie_ptr = 8; + *ie_ptr += 1; + **ie_ptr = (id[0] << 4) | (1 << 3) | eps_mobile_id->type_of_id; + *ie_ptr += 1; + for(i=0; i<7; i++) + { + **ie_ptr = (id[i*2+2] << 4) | id[i*2+1]; + *ie_ptr += 1; + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_mobile_id_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_MOBILE_ID_STRUCT *eps_mobile_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *id; + uint32 length; + uint32 i; + + if(ie_ptr != NULL && + eps_mobile_id != NULL) + { + length = **ie_ptr; + *ie_ptr += 1; + + eps_mobile_id->type_of_id = **ie_ptr & 0x07; + + if(LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI == eps_mobile_id->type_of_id) + { + *ie_ptr += 1; + eps_mobile_id->guti.mcc = (**ie_ptr & 0x0F)*100; + eps_mobile_id->guti.mcc += ((**ie_ptr >> 4) & 0x0F)*10; + *ie_ptr += 1; + eps_mobile_id->guti.mcc += **ie_ptr & 0x0F; + if(((**ie_ptr >> 4) & 0x0F) == 0x0F) + { + *ie_ptr += 1; + eps_mobile_id->guti.mnc = (**ie_ptr & 0x0F)*10; + eps_mobile_id->guti.mnc += (**ie_ptr >> 4) & 0x0F; + *ie_ptr += 1; + }else{ + eps_mobile_id->guti.mnc = (**ie_ptr >> 4) & 0x0F; + *ie_ptr += 1; + eps_mobile_id->guti.mnc += (**ie_ptr & 0x0F)*100; + eps_mobile_id->guti.mnc += ((**ie_ptr >> 4) & 0x0F)*10; + *ie_ptr += 1; + } + eps_mobile_id->guti.mme_group_id = **ie_ptr << 8; + *ie_ptr += 1; + eps_mobile_id->guti.mme_group_id |= **ie_ptr; + *ie_ptr += 1; + eps_mobile_id->guti.mme_code = **ie_ptr; + *ie_ptr += 1; + eps_mobile_id->guti.m_tmsi = **ie_ptr << 24; + *ie_ptr += 1; + eps_mobile_id->guti.m_tmsi |= **ie_ptr << 16; + *ie_ptr += 1; + eps_mobile_id->guti.m_tmsi |= **ie_ptr << 8; + *ie_ptr += 1; + eps_mobile_id->guti.m_tmsi |= **ie_ptr; + *ie_ptr += 1; + }else{ + if(LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI == eps_mobile_id->type_of_id) + { + id = eps_mobile_id->imsi; + }else{ + id = eps_mobile_id->imei; + } + + id[0] = **ie_ptr >> 4; + *ie_ptr += 1; + for(i=0; i<7; i++) + { + id[i*2+1] = **ie_ptr & 0x0F; + id[i*2+2] = **ie_ptr >> 4; + *ie_ptr += 1; + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Network Feature Support + + Description: Indicates whether certain features are supported by + the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.12A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_network_feature_support_ie(LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT *eps_nfs, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(eps_nfs != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 1; + (*ie_ptr)[1] = eps_nfs->esrps << 5; + (*ie_ptr)[1] |= (eps_nfs->cs_lcs & 0x03) << 3; + (*ie_ptr)[1] |= eps_nfs->epc_lcs << 2; + (*ie_ptr)[1] |= eps_nfs->emc_bs << 1; + (*ie_ptr)[1] |= eps_nfs->ims_vops; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_network_feature_support_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT *eps_nfs) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + eps_nfs != NULL) + { + eps_nfs->esrps = ((*ie_ptr)[1] >> 5) & 0x01; + eps_nfs->cs_lcs = (LIBLTE_MME_CS_LCS_ENUM)(((*ie_ptr)[1] >> 3) & 0x03); + eps_nfs->epc_lcs = ((*ie_ptr)[1] >> 2) & 0x01; + eps_nfs->emc_bs = ((*ie_ptr)[1] >> 1) & 0x01; + eps_nfs->ims_vops = (*ie_ptr)[1] & 0x01; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Update Result + + Description: Specifies the result of the associated updating + procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_update_result_ie(uint8 eps_update_res, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= (eps_update_res & 0x07) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_update_result_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *eps_update_res) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + eps_update_res != NULL) + { + *eps_update_res = ((*ie_ptr)[0] >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Update Type + + Description: Specifies the area the updating procedure is + associated with. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.14 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_update_type_ie(LIBLTE_MME_EPS_UPDATE_TYPE_STRUCT *eps_update_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(eps_update_type != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] |= (eps_update_type->active_flag & 0x01) << (bit_offset + 3); + (*ie_ptr)[0] |= (eps_update_type->type & 0x07) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_update_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_EPS_UPDATE_TYPE_STRUCT *eps_update_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + eps_update_type != NULL) + { + eps_update_type->active_flag = ((*ie_ptr)[0] >> (bit_offset + 3)) & 0x01; + eps_update_type->type = (LIBLTE_MME_EPS_UPDATE_TYPE_ENUM)(((*ie_ptr)[0] >> bit_offset) & 0x07); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ESM Message Container + + Description: Enables piggybacked transfer of a single ESM message + within an EMM message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.15 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *esm_msg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(esm_msg != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = esm_msg->N_bytes >> 8; + (*ie_ptr)[1] = esm_msg->N_bytes & 0xFF; + for(i=0; iN_bytes; i++) + { + (*ie_ptr)[2+i] = esm_msg->msg[i]; + } + *ie_ptr += esm_msg->N_bytes + 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *esm_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + esm_msg != NULL) + { + esm_msg->N_bytes = (*ie_ptr)[0] << 8; + esm_msg->N_bytes |= (*ie_ptr)[1]; + for(i=0; iN_bytes; i++) + { + esm_msg->msg[i] = (*ie_ptr)[2+i]; + } + *ie_ptr += esm_msg->N_bytes + 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: GPRS Timer + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16 + 24.008 v10.2.0 Section 10.5.7.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_ie(LIBLTE_MME_GPRS_TIMER_STRUCT *timer, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(timer != NULL && + ie_ptr != NULL) + { + **ie_ptr = ((timer->unit & 0x07) << 5) | (timer->value & 0x1F); + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_ie(uint8 **ie_ptr, + LIBLTE_MME_GPRS_TIMER_STRUCT *timer) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + timer != NULL) + { + timer->unit = **ie_ptr >> 5; + timer->value = **ie_ptr & 0x1F; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: GPRS Timer 2 + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16A + 24.008 v10.2.0 Section 10.5.7.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_2_ie(uint8 value, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr = 1; + *ie_ptr += 1; + **ie_ptr = value; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_2_ie(uint8 **ie_ptr, + uint8 *value) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + value != NULL) + { + *ie_ptr += 1; + *value = **ie_ptr; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: GPRS Timer 3 + + Description: Specifies GPRS specific timer values. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.16B + 24.008 v10.2.0 Section 10.5.7.4A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_gprs_timer_3_ie(LIBLTE_MME_GPRS_TIMER_3_STRUCT *timer, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(timer != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 1; + (*ie_ptr)[1] = ((timer->unit & 0x07) << 5) | (timer->value & 0x1F); + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_gprs_timer_3_ie(uint8 **ie_ptr, + LIBLTE_MME_GPRS_TIMER_3_STRUCT *timer) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + timer != NULL) + { + timer->unit = (*ie_ptr)[1] >> 5; + timer->value = (*ie_ptr)[1] & 0x1F; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Identity Type 2 + + Description: Specifies which identity is requested. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.17 + 24.008 v10.2.0 Section 10.5.5.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_type_2_ie(uint8 id_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= id_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_type_2_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *id_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + id_type != NULL) + { + *id_type = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: IMEISV Request + + Description: Indicates that the IMEISV shall be included by the + UE in the authentication and ciphering response + message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.18 + 24.008 v10.2.0 Section 10.5.5.10 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_imeisv_request_ie(LIBLTE_MME_IMEISV_REQUEST_ENUM imeisv_req, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= imeisv_req << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_imeisv_request_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_IMEISV_REQUEST_ENUM *imeisv_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + imeisv_req != NULL) + { + *imeisv_req = (LIBLTE_MME_IMEISV_REQUEST_ENUM)((**ie_ptr >> bit_offset) & 0x07); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: KSI And Sequence Number + + Description: Provides the network with the key set identifier + (KSI) value of the current EPS security context and + the 5 least significant bits of the NAS COUNT value + applicable for the message including this information + element. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.19 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ksi_and_sequence_number_ie(LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT *ksi_and_seq_num, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ksi_and_seq_num != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = (ksi_and_seq_num->ksi & 0x07) << 5; + (*ie_ptr)[0] |= ksi_and_seq_num->seq_num & 0x1F; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ksi_and_sequence_number_ie(uint8 **ie_ptr, + LIBLTE_MME_KSI_AND_SEQUENCE_NUMBER_STRUCT *ksi_and_seq_num) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ksi_and_seq_num != NULL) + { + ksi_and_seq_num->ksi = ((*ie_ptr)[0] >> 5) & 0x07; + ksi_and_seq_num->seq_num = (*ie_ptr)[0] & 0x1F; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MS Network Capability + + Description: Provides the network with information concerning + aspects of the UE related to GPRS. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.20 + 24.008 v10.2.0 Section 10.5.5.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ms_network_capability_ie(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT *ms_network_cap, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ms_network_cap != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = 3; + (*ie_ptr)[1] = ms_network_cap->gea[1] << 7; + (*ie_ptr)[1] |= ms_network_cap->sm_cap_ded << 6; + (*ie_ptr)[1] |= ms_network_cap->sm_cap_gprs << 5; + (*ie_ptr)[1] |= ms_network_cap->ucs2 << 4; + (*ie_ptr)[1] |= (ms_network_cap->ss_screening & 0x03) << 2; + (*ie_ptr)[1] |= ms_network_cap->solsa << 1; + (*ie_ptr)[1] |= ms_network_cap->revision; + (*ie_ptr)[2] = ms_network_cap->pfc << 7; + (*ie_ptr)[2] |= ms_network_cap->gea[2] << 6; + (*ie_ptr)[2] |= ms_network_cap->gea[3] << 5; + (*ie_ptr)[2] |= ms_network_cap->gea[4] << 4; + (*ie_ptr)[2] |= ms_network_cap->gea[5] << 3; + (*ie_ptr)[2] |= ms_network_cap->gea[6] << 2; + (*ie_ptr)[2] |= ms_network_cap->gea[7] << 1; + (*ie_ptr)[2] |= ms_network_cap->lcsva; + (*ie_ptr)[3] = ms_network_cap->ho_g2u_via_iu << 7; + (*ie_ptr)[3] |= ms_network_cap->ho_g2e_via_s1 << 6; + (*ie_ptr)[3] |= ms_network_cap->emm_comb << 5; + (*ie_ptr)[3] |= ms_network_cap->isr << 4; + (*ie_ptr)[3] |= ms_network_cap->srvcc << 3; + (*ie_ptr)[3] |= ms_network_cap->epc << 2; + (*ie_ptr)[3] |= ms_network_cap->nf << 1; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ms_network_capability_ie(uint8 **ie_ptr, + LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT *ms_network_cap) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ms_network_cap != NULL) + { + ms_network_cap->gea[1] = ((*ie_ptr)[1] >> 7) & 0x01; + ms_network_cap->sm_cap_ded = ((*ie_ptr)[1] >> 6) & 0x01; + ms_network_cap->sm_cap_gprs = ((*ie_ptr)[1] >> 5) & 0x01; + ms_network_cap->ucs2 = ((*ie_ptr)[1] >> 4) & 0x01; + ms_network_cap->ss_screening = (LIBLTE_MME_SS_SCREENING_INDICATOR_ENUM)(((*ie_ptr)[1] >> 2) & 0x03); + ms_network_cap->solsa = ((*ie_ptr)[1] >> 1) & 0x01; + ms_network_cap->revision = (*ie_ptr)[1] & 0x01; + ms_network_cap->pfc = ((*ie_ptr)[2] >> 7) & 0x01; + ms_network_cap->gea[2] = ((*ie_ptr)[2] >> 6) & 0x01; + ms_network_cap->gea[3] = ((*ie_ptr)[2] >> 5) & 0x01; + ms_network_cap->gea[4] = ((*ie_ptr)[2] >> 4) & 0x01; + ms_network_cap->gea[5] = ((*ie_ptr)[2] >> 3) & 0x01; + ms_network_cap->gea[6] = ((*ie_ptr)[2] >> 2) & 0x01; + ms_network_cap->gea[7] = ((*ie_ptr)[2] >> 1) & 0x01; + ms_network_cap->lcsva = (*ie_ptr)[2] & 0x01; + ms_network_cap->ho_g2u_via_iu = ((*ie_ptr)[3] >> 7) & 0x01; + ms_network_cap->ho_g2e_via_s1 = ((*ie_ptr)[3] >> 6) & 0x01; + ms_network_cap->emm_comb = ((*ie_ptr)[3] >> 5) & 0x01; + ms_network_cap->isr = ((*ie_ptr)[3] >> 4) & 0x01; + ms_network_cap->srvcc = ((*ie_ptr)[3] >> 3) & 0x01; + ms_network_cap->epc = ((*ie_ptr)[3] >> 2) & 0x01; + ms_network_cap->nf = ((*ie_ptr)[3] >> 1) & 0x01; + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: NAS Key Set Identifier + + Description: Provides the NAS key set identifier that is allocated + by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.21 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_key_set_id_ie(LIBLTE_MME_NAS_KEY_SET_ID_STRUCT *nas_ksi, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(nas_ksi != NULL && + ie_ptr != NULL) + { + **ie_ptr |= nas_ksi->tsc_flag << (bit_offset + 3); + **ie_ptr |= nas_ksi->nas_ksi << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_key_set_id_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_NAS_KEY_SET_ID_STRUCT *nas_ksi) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + nas_ksi != NULL) + { + nas_ksi->tsc_flag = (LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_ENUM)((**ie_ptr >> (bit_offset + 3)) & 0x01); + nas_ksi->nas_ksi = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: NAS Message Container + + Description: Encapsulates the SMS messages transferred between + the UE and the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.22 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *nas_msg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(nas_msg != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = nas_msg->N_bytes & 0xFF; + for(i=0; iN_bytes; i++) + { + (*ie_ptr)[1+i] = nas_msg->msg[i]; + } + *ie_ptr += nas_msg->N_bytes + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *nas_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + nas_msg != NULL) + { + nas_msg->N_bytes = (*ie_ptr)[0]; + for(i=0; iN_bytes; i++) + { + nas_msg->msg[i] = (*ie_ptr)[1+i]; + } + *ie_ptr += nas_msg->N_bytes + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: NAS Security Algorithms + + Description: Indicates the algorithms to be used for ciphering + and integrity protection. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.23 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nas_security_algorithms_ie(LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT *nas_sec_algs, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(nas_sec_algs != NULL && + ie_ptr != NULL) + { + **ie_ptr = (nas_sec_algs->type_of_eea << 4) | (nas_sec_algs->type_of_eia); + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nas_security_algorithms_ie(uint8 **ie_ptr, + LIBLTE_MME_NAS_SECURITY_ALGORITHMS_STRUCT *nas_sec_algs) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + nas_sec_algs != NULL) + { + nas_sec_algs->type_of_eea = (LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_ENUM)((**ie_ptr >> 4) & 0x07); + nas_sec_algs->type_of_eia = (LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_ENUM)(**ie_ptr & 0x07); + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Network Name + + Description: Passes a text string to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.24 + 24.008 v10.2.0 Section 10.5.3.5A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_network_name_ie(LIBLTE_MME_NETWORK_NAME_STRUCT *net_name, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 bit_offset; + uint32 byte_offset; + const char *char_str = net_name->name; + + if(net_name != NULL && + ie_ptr != NULL) + { + bit_offset = 0; + byte_offset = 2; + for(i=0; i= 0x20 && + char_str[i] <= 0x3F) || + (char_str[i] >= 0x41 && + char_str[i] <= 0x5A) || + (char_str[i] >= 0x61 && + char_str[i] <= 0x7A)) + { + switch(bit_offset) + { + case 0: + (*ie_ptr)[byte_offset] = char_str[i]; + bit_offset = 7; + break; + case 1: + (*ie_ptr)[byte_offset] |= (char_str[i] << 1); + bit_offset = 0; + byte_offset++; + break; + case 2: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 2) & 0xFC); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 6) & 0x01); + bit_offset = 1; + break; + case 3: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 3) & 0xF8); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 5) & 0x03); + bit_offset = 2; + break; + case 4: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 4) & 0xF0); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 4) & 0x07); + bit_offset = 3; + break; + case 5: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 5) & 0xE0); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 3) & 0x0F); + bit_offset = 4; + break; + case 6: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 6) & 0xC0); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 2) & 0x1F); + bit_offset = 5; + break; + case 7: + (*ie_ptr)[byte_offset] |= ((char_str[i] << 7) & 0x80); + byte_offset++; + (*ie_ptr)[byte_offset] = ((char_str[i] >> 1) & 0x3F); + bit_offset = 6; + break; + } + } + } + if(0 == bit_offset) + { + (*ie_ptr)[0] = byte_offset - 1; + (*ie_ptr)[1] = 0x80 | ((net_name->add_ci & 0x01) << 3); + *ie_ptr += byte_offset; + }else{ + (*ie_ptr)[0] = byte_offset; + (*ie_ptr)[1] = 0x80 | ((net_name->add_ci & 0x01) << 3) | ((8 - bit_offset) & 0x07); + *ie_ptr += byte_offset + 1; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_network_name_ie(uint8 **ie_ptr, + LIBLTE_MME_NETWORK_NAME_STRUCT *net_name) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 bit_offset; + uint32 byte_offset; + uint32 N_bytes; + uint8 spare_field; + char tmp_char; + uint32 str_cnt; + + if(ie_ptr != NULL && + net_name != NULL) + { + net_name->add_ci = (LIBLTE_MME_ADD_CI_ENUM)(((*ie_ptr)[1] >> 3) & 0x01); + spare_field = (*ie_ptr)[1] & 0x07; + N_bytes = (*ie_ptr)[0]; + bit_offset = 0; + byte_offset = 2; + str_cnt = 0; + + while(byte_offset < N_bytes && str_cnt < LIBLTE_STRING_LEN) + { + switch(bit_offset) + { + case 0: + tmp_char = (*ie_ptr)[byte_offset] & 0x7F; + bit_offset = 7; + break; + case 1: + tmp_char = ((*ie_ptr)[byte_offset] >> 1) & 0x7F; + bit_offset = 0; + byte_offset++; + break; + case 2: + tmp_char = ((*ie_ptr)[byte_offset] >> 2) & 0x3F; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 6) & 0x40; + bit_offset = 1; + break; + case 3: + tmp_char = ((*ie_ptr)[byte_offset] >> 3) & 0x1F; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 5) & 0x60; + bit_offset = 2; + break; + case 4: + tmp_char = ((*ie_ptr)[byte_offset] >> 4) & 0x0F; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 4) & 0x70; + bit_offset = 3; + break; + case 5: + tmp_char = ((*ie_ptr)[byte_offset] >> 5) & 0x07; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 3) & 0x78; + bit_offset = 4; + break; + case 6: + tmp_char = ((*ie_ptr)[byte_offset] >> 6) & 0x03; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 2) & 0x7C; + bit_offset = 5; + break; + case 7: + tmp_char = ((*ie_ptr)[byte_offset] >> 7) & 0x01; + byte_offset++; + tmp_char |= ((*ie_ptr)[byte_offset] << 1) & 0x7E; + bit_offset = 6; + break; + } + + if(tmp_char == 0x0A || + tmp_char == 0x0D || + (tmp_char >= 0x20 && + tmp_char <= 0x3F) || + (tmp_char >= 0x41 && + tmp_char <= 0x5A) || + (tmp_char >= 0x61 && + tmp_char <= 0x7A)) + { + if (str_cnt < LIBLTE_STRING_LEN) { + net_name->name[str_cnt] = tmp_char; + str_cnt++; + } + } + } + + if(0 == bit_offset || + (1 == bit_offset && + 0 == spare_field)) + { + if(0 == bit_offset) + { + tmp_char = (*ie_ptr)[byte_offset] & 0x7F; + }else{ + tmp_char = ((*ie_ptr)[byte_offset] >> 1) & 0x7F; + } + if(tmp_char == 0x0A || + tmp_char == 0x0D || + (tmp_char >= 0x20 && + tmp_char <= 0x3F) || + (tmp_char >= 0x41 && + tmp_char <= 0x5A) || + (tmp_char >= 0x61 && + tmp_char <= 0x7A)) + { + if (str_cnt < LIBLTE_STRING_LEN) { + net_name->name[str_cnt] = tmp_char; + str_cnt++; + } + } + } + + if (str_cnt < LIBLTE_STRING_LEN) { + net_name->name[str_cnt] = '\0'; + str_cnt++; + } + + *ie_ptr += byte_offset + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Nonce + + Description: Transfers a 32-bit nonce value to support deriving + a new mapped EPS security context. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.25 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_nonce_ie(uint32 nonce, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = (nonce >> 24) & 0xFF; + (*ie_ptr)[1] = (nonce >> 16) & 0xFF; + (*ie_ptr)[2] = (nonce >> 8) & 0xFF; + (*ie_ptr)[3] = nonce & 0xFF; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_nonce_ie(uint8 **ie_ptr, + uint32 *nonce) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + nonce != NULL) + { + *nonce = (*ie_ptr)[0] << 24; + *nonce |= (*ie_ptr)[1] << 16; + *nonce |= (*ie_ptr)[2] << 8; + *nonce |= (*ie_ptr)[3]; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Paging Identity + + Description: Indicates the identity used for paging for non-EPS + services. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.25A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_paging_identity_ie(uint8 paging_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = paging_id & 0x01; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_paging_identity_ie(uint8 **ie_ptr, + uint8 *paging_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + paging_id != NULL) + { + *paging_id = (*ie_ptr)[0] & 0x01; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: P-TMSI Signature + + Description: Identifies a GMM context of a UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.26 + 24.008 v10.2.0 Section 10.5.5.8 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_p_tmsi_signature_ie(uint32 p_tmsi_signature, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = (p_tmsi_signature >> 24) & 0xFF; + (*ie_ptr)[1] = (p_tmsi_signature >> 16) & 0xFF; + (*ie_ptr)[2] = (p_tmsi_signature >> 8) & 0xFF; + (*ie_ptr)[3] = p_tmsi_signature & 0xFF; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_p_tmsi_signature_ie(uint8 **ie_ptr, + uint32 *p_tmsi_signature) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + p_tmsi_signature != NULL) + { + *p_tmsi_signature = (*ie_ptr)[0] << 24; + *p_tmsi_signature |= (*ie_ptr)[1] << 16; + *p_tmsi_signature |= (*ie_ptr)[2] << 8; + *p_tmsi_signature |= (*ie_ptr)[3]; + *ie_ptr += 4; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Service Type + + Description: Specifies the purpose of the service request + procedure. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.27 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_service_type_ie(uint8 value, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= (value & 0x0F) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *value) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + value != NULL) + { + *value = ((*ie_ptr)[0] >> bit_offset) & 0x0F; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Short MAC + + Description: Protects the integrity of a SERVICE REQUEST message. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.28 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_short_mac_ie(uint16 short_mac, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = (short_mac >> 8) & 0xFF; + (*ie_ptr)[1] = short_mac & 0xFF; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_short_mac_ie(uint8 **ie_ptr, + uint16 *short_mac) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + short_mac != NULL) + { + *short_mac = (*ie_ptr)[0] << 8; + *short_mac |= (*ie_ptr)[1]; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Time Zone + + Description: Encodes the offset between universal time and local + time in steps of 15 minutes. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.29 + 24.008 v10.2.0 Section 10.5.3.8 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_time_zone_ie(uint8 tz, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = tz; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_time_zone_ie(uint8 **ie_ptr, + uint8 *tz) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tz != NULL) + { + *tz = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Time Zone And Time + + Description: Encodes the offset between universal time and local + time in steps of 15 minutes and encodes the universal + time at which the IE may have been sent by the + network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.30 + 24.008 v10.2.0 Section 10.5.3.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_time_zone_and_time_ie(LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT *ttz, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ttz != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = ((ttz->year % 10) << 4) | ((ttz->year % 100) / 10); + (*ie_ptr)[1] = ((ttz->month % 10) << 4) | (ttz->month / 10); + (*ie_ptr)[2] = ((ttz->day % 10) << 4) | (ttz->day / 10); + (*ie_ptr)[3] = ((ttz->hour % 10) << 4) | (ttz->hour / 10); + (*ie_ptr)[4] = ((ttz->minute % 10) << 4) | (ttz->minute / 10); + (*ie_ptr)[5] = ((ttz->second % 10) << 4) | (ttz->second / 10); + (*ie_ptr)[6] = ttz->tz; + *ie_ptr += 7; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_time_zone_and_time_ie(uint8 **ie_ptr, + LIBLTE_MME_TIME_ZONE_AND_TIME_STRUCT *ttz) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ttz != NULL) + { + ttz->year = 2000 + (((*ie_ptr)[0] & 0x0F) * 10) + (((*ie_ptr)[0] >> 4) & 0x0F); + ttz->month = (((*ie_ptr)[1] & 0x0F) * 10) + (((*ie_ptr)[1] >> 4) & 0x0F); + ttz->day = (((*ie_ptr)[2] & 0x0F) * 10) + (((*ie_ptr)[2] >> 4) & 0x0F); + ttz->hour = (((*ie_ptr)[3] & 0x0F) * 10) + (((*ie_ptr)[3] >> 4) & 0x0F); + ttz->minute = (((*ie_ptr)[4] & 0x0F) * 10) + (((*ie_ptr)[4] >> 4) & 0x0F); + ttz->second = (((*ie_ptr)[5] & 0x0F) * 10) + (((*ie_ptr)[5] >> 4) & 0x0F); + ttz->tz = (*ie_ptr)[6]; + *ie_ptr += 7; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: TMSI Status + + Description: Indicates whether a valid TMSI is available in the + UE or not. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.31 + 24.008 v10.2.0 Section 10.5.5.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tmsi_status_ie(LIBLTE_MME_TMSI_STATUS_ENUM tmsi_status, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= tmsi_status << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tmsi_status_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_TMSI_STATUS_ENUM *tmsi_status) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tmsi_status != NULL) + { + *tmsi_status = (LIBLTE_MME_TMSI_STATUS_ENUM)((**ie_ptr >> bit_offset) & 0x01); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Tracking Area Identity + + Description: Provides an unambiguous identification of tracking + areas within the area covered by the 3GPP system. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.32 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_id_ie(LIBLTE_MME_TRACKING_AREA_ID_STRUCT *tai, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(tai != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = (((tai->mcc/10) % 10) << 4) | ((tai->mcc/100) % 10); + if(tai->mnc < 100) + { + (*ie_ptr)[1] = 0xF0 | (tai->mcc % 10); + (*ie_ptr)[2] = ((tai->mnc % 10) << 4) | ((tai->mnc/10) % 10); + }else{ + (*ie_ptr)[1] = ((tai->mnc % 10) << 4) | (tai->mcc % 10); + (*ie_ptr)[2] = (((tai->mnc/10) % 10) << 4) | ((tai->mnc/100) % 10); + } + (*ie_ptr)[3] = (tai->tac >> 8) & 0xFF; + (*ie_ptr)[4] = tai->tac & 0xFF; + *ie_ptr += 5; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_id_ie(uint8 **ie_ptr, + LIBLTE_MME_TRACKING_AREA_ID_STRUCT *tai) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tai != NULL) + { + tai->mcc = ((*ie_ptr)[0] & 0x0F)*100; + tai->mcc += (((*ie_ptr)[0] >> 4) & 0x0F)*10; + tai->mcc += (*ie_ptr)[1] & 0x0F; + if((((*ie_ptr)[1] >> 4) & 0x0F) == 0x0F) + { + tai->mnc = ((*ie_ptr)[2] & 0x0F)*10; + tai->mnc += ((*ie_ptr)[2] >> 4) & 0x0F; + }else{ + tai->mnc = ((*ie_ptr)[1] >> 4) & 0x0F; + tai->mnc += ((*ie_ptr)[2] & 0x0F)*100; + tai->mnc += (((*ie_ptr)[2] >> 4) & 0x0F)*10; + } + tai->tac = (*ie_ptr)[3] << 8; + tai->tac |= (*ie_ptr)[4]; + *ie_ptr += 5; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Tracking Area Identity List + + Description: Transfers a list of tracking areas from the network + to the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.33 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_identity_list_ie(LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT *tai_list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(tai_list != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = (tai_list->N_tais*5) + 1; + // FIXME: Support all types + if(1 == tai_list->N_tais) + { + (*ie_ptr)[1] = (LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ONE_PLMN_NON_CONSECUTIVE_TACS << 5) | ((tai_list->N_tais - 1) & 0x1F); + }else{ + (*ie_ptr)[1] = (LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_DIFFERENT_PLMNS << 5) | ((tai_list->N_tais - 1) & 0x1F); + } + *ie_ptr += 2; + for(i=0; iN_tais; i++) + { + liblte_mme_pack_tracking_area_id_ie(&tai_list->tai[i], ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_identity_list_ie(uint8 **ie_ptr, + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT *tai_list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ENUM type; + uint32 sent_length; + uint32 length; + uint32 i; + uint32 N_elems; + uint16 mcc; + uint16 mnc; + uint16 tac; + + if(ie_ptr != NULL && + tai_list != NULL) + { + sent_length = (*ie_ptr)[0] + 1; + length = 1; + tai_list->N_tais = 0; + while(length < sent_length) + { + type = (LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ENUM)(((*ie_ptr)[length] >> 5) & 0x03); + N_elems = (*ie_ptr)[length++] & 0x1F; + if(LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ONE_PLMN_NON_CONSECUTIVE_TACS == type) + { + mcc = ((*ie_ptr)[length] & 0x0F)*100; + mcc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + mcc += (*ie_ptr)[length] & 0x0F; + if((((*ie_ptr)[length] >> 4) & 0x0F) == 0x0F) + { + length++; + mnc = ((*ie_ptr)[length] & 0x0F)*10; + mnc += ((*ie_ptr)[length++] >> 4) & 0x0F; + }else{ + mnc = ((*ie_ptr)[length++] >> 4) & 0x0F; + mnc += ((*ie_ptr)[length] & 0x0F)*100; + mnc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + } + for(i=0; itai[tai_list->N_tais].mcc = mcc; + tai_list->tai[tai_list->N_tais].mnc = mnc; + tai_list->tai[tai_list->N_tais].tac = (*ie_ptr)[length++] << 8; + tai_list->tai[tai_list->N_tais].tac |= (*ie_ptr)[length++]; + tai_list->N_tais++; + } + }else if(LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_TYPE_ONE_PLMN_CONSECUTIVE_TACS == type){ + mcc = ((*ie_ptr)[length] & 0x0F)*100; + mcc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + mcc += (*ie_ptr)[length] & 0x0F; + if((((*ie_ptr)[length] >> 4) & 0x0F) == 0x0F) + { + length++; + mnc = ((*ie_ptr)[length] & 0x0F)*10; + mnc += ((*ie_ptr)[length++] >> 4) & 0x0F; + }else{ + mnc = ((*ie_ptr)[length++] >> 4) & 0x0F; + mnc += ((*ie_ptr)[length] & 0x0F)*100; + mnc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + } + tac = (*ie_ptr)[length++] << 8; + tac |= (*ie_ptr)[length++]; + for(i=0; itai[tai_list->N_tais].mcc = mcc; + tai_list->tai[tai_list->N_tais].mnc = mnc; + tai_list->tai[tai_list->N_tais].tac = tac + i; + tai_list->N_tais++; + } + }else{ + for(i=0; itai[tai_list->N_tais].mcc = ((*ie_ptr)[length] & 0x0F)*100; + tai_list->tai[tai_list->N_tais].mcc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + tai_list->tai[tai_list->N_tais].mcc += (*ie_ptr)[length] & 0x0F; + if((((*ie_ptr)[length] >> 4) & 0x0F) == 0x0F) + { + length++; + tai_list->tai[tai_list->N_tais].mnc = ((*ie_ptr)[length] & 0x0F)*10; + tai_list->tai[tai_list->N_tais].mnc += ((*ie_ptr)[length++] >> 4) & 0x0F; + }else{ + tai_list->tai[tai_list->N_tais].mnc = ((*ie_ptr)[length++] >> 4) & 0x0F; + tai_list->tai[tai_list->N_tais].mnc += ((*ie_ptr)[length] & 0x0F)*100; + tai_list->tai[tai_list->N_tais].mnc += (((*ie_ptr)[length++] >> 4) & 0x0F)*10; + } + tai_list->tai[tai_list->N_tais].tac = (*ie_ptr)[length++] << 8; + tai_list->tai[tai_list->N_tais].tac |= (*ie_ptr)[length++]; + tai_list->N_tais++; + } + } + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: UE Network Capability + + Description: Provides the network with information concerning + aspects of the UE related to EPS or interworking with + GPRS. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.34 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_network_capability_ie(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT *ue_network_cap, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ue_network_cap != NULL && + ie_ptr != NULL) + { + if(ue_network_cap->uea_present && + (ue_network_cap->ucs2_present || + ue_network_cap->uia_present) && + (ue_network_cap->lpp_present || + ue_network_cap->lcs_present || + ue_network_cap->onexsrvcc_present || + ue_network_cap->nf_present)) + { + **ie_ptr = 5; + }else if(ue_network_cap->uea_present && + (ue_network_cap->ucs2_present || + ue_network_cap->uia_present)){ + **ie_ptr = 4; + }else if(ue_network_cap->uea_present){ + **ie_ptr = 3; + }else{ + **ie_ptr = 2; + } + *ie_ptr += 1; + **ie_ptr = ue_network_cap->eea[0] << 7; + **ie_ptr |= ue_network_cap->eea[1] << 6; + **ie_ptr |= ue_network_cap->eea[2] << 5; + **ie_ptr |= ue_network_cap->eea[3] << 4; + **ie_ptr |= ue_network_cap->eea[4] << 3; + **ie_ptr |= ue_network_cap->eea[5] << 2; + **ie_ptr |= ue_network_cap->eea[6] << 1; + **ie_ptr |= ue_network_cap->eea[7]; + *ie_ptr += 1; + **ie_ptr = ue_network_cap->eia[0] << 7; + **ie_ptr |= ue_network_cap->eia[1] << 6; + **ie_ptr |= ue_network_cap->eia[2] << 5; + **ie_ptr |= ue_network_cap->eia[3] << 4; + **ie_ptr |= ue_network_cap->eia[4] << 3; + **ie_ptr |= ue_network_cap->eia[5] << 2; + **ie_ptr |= ue_network_cap->eia[6] << 1; + **ie_ptr |= ue_network_cap->eia[7]; + *ie_ptr += 1; + if(ue_network_cap->uea_present) + { + **ie_ptr = ue_network_cap->uea[0] << 7; + **ie_ptr |= ue_network_cap->uea[1] << 6; + **ie_ptr |= ue_network_cap->uea[2] << 5; + **ie_ptr |= ue_network_cap->uea[3] << 4; + **ie_ptr |= ue_network_cap->uea[4] << 3; + **ie_ptr |= ue_network_cap->uea[5] << 2; + **ie_ptr |= ue_network_cap->uea[6] << 1; + **ie_ptr |= ue_network_cap->uea[7]; + *ie_ptr += 1; + } + if(ue_network_cap->ucs2_present || + ue_network_cap->uia_present) + { + **ie_ptr = ue_network_cap->ucs2 << 7; + **ie_ptr |= ue_network_cap->uia[1] << 6; + **ie_ptr |= ue_network_cap->uia[2] << 5; + **ie_ptr |= ue_network_cap->uia[3] << 4; + **ie_ptr |= ue_network_cap->uia[4] << 3; + **ie_ptr |= ue_network_cap->uia[5] << 2; + **ie_ptr |= ue_network_cap->uia[6] << 1; + **ie_ptr |= ue_network_cap->uia[7]; + *ie_ptr += 1; + } + if(ue_network_cap->lpp_present || + ue_network_cap->lcs_present || + ue_network_cap->onexsrvcc_present || + ue_network_cap->nf_present) + { + **ie_ptr = ue_network_cap->lpp << 3; + **ie_ptr |= ue_network_cap->lcs << 2; + **ie_ptr |= ue_network_cap->onexsrvcc << 1; + **ie_ptr |= ue_network_cap->nf; + *ie_ptr += 1; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_network_capability_ie(uint8 **ie_ptr, + LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT *ue_network_cap) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 length; + + if(ie_ptr != NULL && + ue_network_cap != NULL) + { + length = **ie_ptr; + *ie_ptr += 1; + ue_network_cap->eea[0] = (**ie_ptr >> 7) & 0x01; + ue_network_cap->eea[1] = (**ie_ptr >> 6) & 0x01; + ue_network_cap->eea[2] = (**ie_ptr >> 5) & 0x01; + ue_network_cap->eea[3] = (**ie_ptr >> 4) & 0x01; + ue_network_cap->eea[4] = (**ie_ptr >> 3) & 0x01; + ue_network_cap->eea[5] = (**ie_ptr >> 2) & 0x01; + ue_network_cap->eea[6] = (**ie_ptr >> 1) & 0x01; + ue_network_cap->eea[7] = **ie_ptr & 0x01; + *ie_ptr += 1; + ue_network_cap->eia[0] = (**ie_ptr >> 7) & 0x01; + ue_network_cap->eia[1] = (**ie_ptr >> 6) & 0x01; + ue_network_cap->eia[2] = (**ie_ptr >> 5) & 0x01; + ue_network_cap->eia[3] = (**ie_ptr >> 4) & 0x01; + ue_network_cap->eia[4] = (**ie_ptr >> 3) & 0x01; + ue_network_cap->eia[5] = (**ie_ptr >> 2) & 0x01; + ue_network_cap->eia[6] = (**ie_ptr >> 1) & 0x01; + ue_network_cap->eia[7] = **ie_ptr & 0x01; + *ie_ptr += 1; + if(length > 2) + { + ue_network_cap->uea[0] = (**ie_ptr >> 7) & 0x01; + ue_network_cap->uea[1] = (**ie_ptr >> 6) & 0x01; + ue_network_cap->uea[2] = (**ie_ptr >> 5) & 0x01; + ue_network_cap->uea[3] = (**ie_ptr >> 4) & 0x01; + ue_network_cap->uea[4] = (**ie_ptr >> 3) & 0x01; + ue_network_cap->uea[5] = (**ie_ptr >> 2) & 0x01; + ue_network_cap->uea[6] = (**ie_ptr >> 1) & 0x01; + ue_network_cap->uea[7] = **ie_ptr & 0x01; + ue_network_cap->uea_present = true; + *ie_ptr += 1; + }else{ + ue_network_cap->uea_present = false; + } + if(length > 3) + { + ue_network_cap->ucs2 = (**ie_ptr >> 7) & 0x01; + ue_network_cap->ucs2_present = true; + ue_network_cap->uia[1] = (**ie_ptr >> 6) & 0x01; + ue_network_cap->uia[2] = (**ie_ptr >> 5) & 0x01; + ue_network_cap->uia[3] = (**ie_ptr >> 4) & 0x01; + ue_network_cap->uia[4] = (**ie_ptr >> 3) & 0x01; + ue_network_cap->uia[5] = (**ie_ptr >> 2) & 0x01; + ue_network_cap->uia[6] = (**ie_ptr >> 1) & 0x01; + ue_network_cap->uia[7] = **ie_ptr & 0x01; + ue_network_cap->uia_present = true; + *ie_ptr += 1; + }else{ + ue_network_cap->ucs2_present = false; + ue_network_cap->uia_present = false; + } + if(length > 4) + { + ue_network_cap->lpp = (**ie_ptr >> 3) & 0x01; + ue_network_cap->lpp_present = true; + ue_network_cap->lcs = (**ie_ptr >> 2) & 0x01; + ue_network_cap->lcs_present = true; + ue_network_cap->onexsrvcc = (**ie_ptr >> 1) & 0x01; + ue_network_cap->onexsrvcc_present = true; + ue_network_cap->nf = **ie_ptr >> 1; + ue_network_cap->nf_present = true; + *ie_ptr += 1; + }else{ + ue_network_cap->lpp_present = false; + ue_network_cap->lcs_present = false; + ue_network_cap->onexsrvcc_present = false; + ue_network_cap->nf_present = false; + } + if(length > 5) + { + *ie_ptr += length-5; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: UE Radio Capability Update Needed + + Description: Indicates whether the MME shall delete the stored + UE radio capability information, if any. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.35 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_radio_capability_update_needed_ie(uint8 urc_update, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= (urc_update & 0x01) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_radio_capability_update_needed_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *urc_update) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + urc_update != NULL) + { + *urc_update = ((*ie_ptr)[0] >> bit_offset) & 0x01; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: UE Security Capability + + Description: Indicates which security algorithms are supported by + the UE in S1 mode. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.36 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ue_security_capabilities_ie(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *ue_sec_cap, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 idx; + + if(ue_sec_cap != NULL && + ie_ptr != NULL) + { + if(ue_sec_cap->uea_present && + ue_sec_cap->uia_present && + ue_sec_cap->gea_present) + { + (*ie_ptr)[0] = 5; + }else if(ue_sec_cap->uea_present && + ue_sec_cap->uia_present){ + (*ie_ptr)[0] = 4; + }else if(ue_sec_cap->uea_present){ + (*ie_ptr)[0] = 3; + }else{ + (*ie_ptr)[0] = 2; + } + idx = 1; + (*ie_ptr)[idx] = ue_sec_cap->eea[0] << 7; + (*ie_ptr)[idx] |= ue_sec_cap->eea[1] << 6; + (*ie_ptr)[idx] |= ue_sec_cap->eea[2] << 5; + (*ie_ptr)[idx] |= ue_sec_cap->eea[3] << 4; + (*ie_ptr)[idx] |= ue_sec_cap->eea[4] << 3; + (*ie_ptr)[idx] |= ue_sec_cap->eea[5] << 2; + (*ie_ptr)[idx] |= ue_sec_cap->eea[6] << 1; + (*ie_ptr)[idx] |= ue_sec_cap->eea[7]; + idx++; + (*ie_ptr)[idx] = ue_sec_cap->eia[0] << 7; + (*ie_ptr)[idx] |= ue_sec_cap->eia[1] << 6; + (*ie_ptr)[idx] |= ue_sec_cap->eia[2] << 5; + (*ie_ptr)[idx] |= ue_sec_cap->eia[3] << 4; + (*ie_ptr)[idx] |= ue_sec_cap->eia[4] << 3; + (*ie_ptr)[idx] |= ue_sec_cap->eia[5] << 2; + (*ie_ptr)[idx] |= ue_sec_cap->eia[6] << 1; + (*ie_ptr)[idx] |= ue_sec_cap->eia[7]; + idx++; + if(ue_sec_cap->uea_present) + { + (*ie_ptr)[idx] = ue_sec_cap->uea[0] << 7; + (*ie_ptr)[idx] |= ue_sec_cap->uea[1] << 6; + (*ie_ptr)[idx] |= ue_sec_cap->uea[2] << 5; + (*ie_ptr)[idx] |= ue_sec_cap->uea[3] << 4; + (*ie_ptr)[idx] |= ue_sec_cap->uea[4] << 3; + (*ie_ptr)[idx] |= ue_sec_cap->uea[5] << 2; + (*ie_ptr)[idx] |= ue_sec_cap->uea[6] << 1; + (*ie_ptr)[idx] |= ue_sec_cap->uea[7]; + idx++; + } + if(ue_sec_cap->uia_present) + { + (*ie_ptr)[idx] = ue_sec_cap->uia[1] << 6; + (*ie_ptr)[idx] |= ue_sec_cap->uia[2] << 5; + (*ie_ptr)[idx] |= ue_sec_cap->uia[3] << 4; + (*ie_ptr)[idx] |= ue_sec_cap->uia[4] << 3; + (*ie_ptr)[idx] |= ue_sec_cap->uia[5] << 2; + (*ie_ptr)[idx] |= ue_sec_cap->uia[6] << 1; + (*ie_ptr)[idx] |= ue_sec_cap->uia[7]; + idx++; + } + if(ue_sec_cap->gea_present) + { + (*ie_ptr)[idx] = ue_sec_cap->gea[1] << 6; + (*ie_ptr)[idx] |= ue_sec_cap->gea[2] << 5; + (*ie_ptr)[idx] |= ue_sec_cap->gea[3] << 4; + (*ie_ptr)[idx] |= ue_sec_cap->gea[4] << 3; + (*ie_ptr)[idx] |= ue_sec_cap->gea[5] << 2; + (*ie_ptr)[idx] |= ue_sec_cap->gea[6] << 1; + (*ie_ptr)[idx] |= ue_sec_cap->gea[7]; + idx++; + } + *ie_ptr += idx; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ue_security_capabilities_ie(uint8 **ie_ptr, + LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *ue_sec_cap) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 length; + + if(ie_ptr != NULL && + ue_sec_cap != NULL) + { + length = (*ie_ptr)[0]; + ue_sec_cap->eea[0] = ((*ie_ptr)[1] >> 7) & 0x01; + ue_sec_cap->eea[1] = ((*ie_ptr)[1] >> 6) & 0x01; + ue_sec_cap->eea[2] = ((*ie_ptr)[1] >> 5) & 0x01; + ue_sec_cap->eea[3] = ((*ie_ptr)[1] >> 4) & 0x01; + ue_sec_cap->eea[4] = ((*ie_ptr)[1] >> 3) & 0x01; + ue_sec_cap->eea[5] = ((*ie_ptr)[1] >> 2) & 0x01; + ue_sec_cap->eea[6] = ((*ie_ptr)[1] >> 1) & 0x01; + ue_sec_cap->eea[7] = (*ie_ptr)[1] & 0x01; + ue_sec_cap->eia[0] = ((*ie_ptr)[2] >> 7) & 0x01; + ue_sec_cap->eia[1] = ((*ie_ptr)[2] >> 6) & 0x01; + ue_sec_cap->eia[2] = ((*ie_ptr)[2] >> 5) & 0x01; + ue_sec_cap->eia[3] = ((*ie_ptr)[2] >> 4) & 0x01; + ue_sec_cap->eia[4] = ((*ie_ptr)[2] >> 3) & 0x01; + ue_sec_cap->eia[5] = ((*ie_ptr)[2] >> 2) & 0x01; + ue_sec_cap->eia[6] = ((*ie_ptr)[2] >> 1) & 0x01; + ue_sec_cap->eia[7] = (*ie_ptr)[2] & 0x01; + if(length > 2) + { + ue_sec_cap->uea[0] = ((*ie_ptr)[3] >> 7) & 0x01; + ue_sec_cap->uea[1] = ((*ie_ptr)[3] >> 6) & 0x01; + ue_sec_cap->uea[2] = ((*ie_ptr)[3] >> 5) & 0x01; + ue_sec_cap->uea[3] = ((*ie_ptr)[3] >> 4) & 0x01; + ue_sec_cap->uea[4] = ((*ie_ptr)[3] >> 3) & 0x01; + ue_sec_cap->uea[5] = ((*ie_ptr)[3] >> 2) & 0x01; + ue_sec_cap->uea[6] = ((*ie_ptr)[3] >> 1) & 0x01; + ue_sec_cap->uea[7] = (*ie_ptr)[3] & 0x01; + ue_sec_cap->uea_present = true; + }else{ + ue_sec_cap->uea_present = false; + } + if(length > 3) + { + ue_sec_cap->uia[1] = ((*ie_ptr)[4] >> 6) & 0x01; + ue_sec_cap->uia[2] = ((*ie_ptr)[4] >> 5) & 0x01; + ue_sec_cap->uia[3] = ((*ie_ptr)[4] >> 4) & 0x01; + ue_sec_cap->uia[4] = ((*ie_ptr)[4] >> 3) & 0x01; + ue_sec_cap->uia[5] = ((*ie_ptr)[4] >> 2) & 0x01; + ue_sec_cap->uia[6] = ((*ie_ptr)[4] >> 1) & 0x01; + ue_sec_cap->uia[7] = (*ie_ptr)[4] & 0x01; + ue_sec_cap->uia_present = true; + }else{ + ue_sec_cap->uia_present = false; + } + if(length > 4) + { + ue_sec_cap->gea[1] = ((*ie_ptr)[5] >> 6) & 0x01; + ue_sec_cap->gea[2] = ((*ie_ptr)[5] >> 5) & 0x01; + ue_sec_cap->gea[3] = ((*ie_ptr)[5] >> 4) & 0x01; + ue_sec_cap->gea[4] = ((*ie_ptr)[5] >> 3) & 0x01; + ue_sec_cap->gea[5] = ((*ie_ptr)[5] >> 2) & 0x01; + ue_sec_cap->gea[6] = ((*ie_ptr)[5] >> 1) & 0x01; + ue_sec_cap->gea[7] = (*ie_ptr)[5] & 0x01; + ue_sec_cap->gea_present = true; + }else{ + ue_sec_cap->gea_present = false; + } + *ie_ptr += length + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Emergency Number List + + Description: Encodes emergency number(s) for use within the + country (as indicated by MCC) where the IE is + received. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.37 + 24.008 v10.2.0 Section 10.5.3.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_emergency_number_list_ie(LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT *emerg_num_list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 j; + uint32 length; + + if(emerg_num_list != NULL && + ie_ptr != NULL) + { + length = 1; + for(i=0; iN_emerg_nums; i++) + { + if((emerg_num_list->emerg_num[i].N_emerg_num_digits % 2) == 0) + { + (*ie_ptr)[length++] = (emerg_num_list->emerg_num[i].N_emerg_num_digits/2) + 1; + (*ie_ptr)[length++] = emerg_num_list->emerg_num[i].emerg_service_cat; + for(j=0; jemerg_num[i].N_emerg_num_digits/2; j++) + { + (*ie_ptr)[length] = emerg_num_list->emerg_num[i].emerg_num[j*2+0]; + (*ie_ptr)[length++] |= emerg_num_list->emerg_num[i].emerg_num[j*2+1] << 4; + } + }else{ + (*ie_ptr)[length++] = (emerg_num_list->emerg_num[i].N_emerg_num_digits/2) + 2; + (*ie_ptr)[length++] = emerg_num_list->emerg_num[i].emerg_service_cat; + for(j=0; jemerg_num[i].N_emerg_num_digits/2; j++) + { + (*ie_ptr)[length] = emerg_num_list->emerg_num[i].emerg_num[j*2+0]; + (*ie_ptr)[length++] |= emerg_num_list->emerg_num[i].emerg_num[j*2+1] << 4; + } + (*ie_ptr)[length++] = 0xF0 | emerg_num_list->emerg_num[i].emerg_num[j*2]; + } + } + (*ie_ptr)[0] = length - 2; + *ie_ptr += length - 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_emergency_number_list_ie(uint8 **ie_ptr, + LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT *emerg_num_list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 sent_length; + uint32 length; + uint32 idx; + uint32 i; + + if(ie_ptr != NULL && + emerg_num_list != NULL) + { + sent_length = (*ie_ptr)[0] + 1; + length = 1; + emerg_num_list->N_emerg_nums = 0; + while(length < sent_length) + { + idx = emerg_num_list->N_emerg_nums; + emerg_num_list->emerg_num[idx].N_emerg_num_digits = ((*ie_ptr)[length++] - 1) * 2; + emerg_num_list->emerg_num[idx].emerg_service_cat = (LIBLTE_MME_EMERGENCY_SERVICE_CATEGORY_ENUM)((*ie_ptr)[length++] & 0x1F); + for(i=0; iemerg_num[idx].N_emerg_num_digits/2; i++) + { + emerg_num_list->emerg_num[idx].emerg_num[i*2+0] = (*ie_ptr)[length] & 0x0F; + emerg_num_list->emerg_num[idx].emerg_num[i*2+1] = (*ie_ptr)[length++] >> 4; + } + // Added by Ismael: if i==0 this is negative + if (i > 0) { + if(emerg_num_list->emerg_num[idx].emerg_num[i*2-1] == 0x0F) + { + emerg_num_list->emerg_num[idx].N_emerg_num_digits--; + } + } + emerg_num_list->N_emerg_nums++; + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CLI + + Description: Conveys information about the calling line for a + terminated call to a CS fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.38 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: SS Code + + Description: Conveys information related to a network initiated + supplementary service request to a CS fallback + capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.39 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_ss_code_ie(uint8 code, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = code; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_ss_code_ie(uint8 **ie_ptr, + uint8 *code) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + code != NULL) + { + *code = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: LCS Indicator + + Description: Indicates that the origin of the message is due to a + LCS request and the type of this request to a CS + fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.40 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_lcs_indicator_ie(uint8 lcs_ind, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = lcs_ind; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_lcs_indicator_ie(uint8 **ie_ptr, + uint8 *lcs_ind) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + lcs_ind != NULL) + { + *lcs_ind = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: LCS Client Identity + + Description: Conveys information related to the client of a LCS + request for a CS fallback capable UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.41 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Generic Message Container Type + + Description: Specifies the type of message contained in the + generic message container IE. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.42 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_generic_message_container_type_ie(uint8 msg_cont_type, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = msg_cont_type; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_generic_message_container_type_ie(uint8 **ie_ptr, + uint8 *msg_cont_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + msg_cont_type != NULL) + { + *msg_cont_type = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Generic Message Container + + Description: Encapsulates the application message transferred + between the UE and the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.43 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_generic_message_container_ie(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(msg != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[0] = msg->N_bytes >> 8; + (*ie_ptr)[1] = msg->N_bytes & 0xFF; + for(i=0; iN_bytes; i++) + { + (*ie_ptr)[2+i] = msg->msg[i]; + } + *ie_ptr += msg->N_bytes + 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_generic_message_container_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + msg != NULL) + { + msg->N_bytes = (*ie_ptr)[0] << 8; + msg->N_bytes |= (*ie_ptr)[1]; + for(i=0; iN_bytes; i++) + { + msg->msg[i] = (*ie_ptr)[2+i]; + } + *ie_ptr += msg->N_bytes + 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Voice Domain Preference and UE's Usage Setting + + Description: Provides the network with the UE's usage setting and + the voice domain preference for the E-UTRAN. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.44 + 24.008 v10.2.0 Section 10.5.5.28 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_voice_domain_pref_and_ue_usage_setting_ie(LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT *voice_domain_pref_and_ue_usage_setting, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(voice_domain_pref_and_ue_usage_setting != NULL && + ie_ptr != NULL) + { + **ie_ptr = 1; + *ie_ptr += 1; + **ie_ptr = voice_domain_pref_and_ue_usage_setting->ue_usage_setting << 2; + **ie_ptr |= voice_domain_pref_and_ue_usage_setting->voice_domain_pref; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_voice_domain_pref_and_ue_usage_setting_ie(uint8 **ie_ptr, + LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_STRUCT *voice_domain_pref_and_ue_usage_setting) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + voice_domain_pref_and_ue_usage_setting != NULL) + { + *ie_ptr += 1; + voice_domain_pref_and_ue_usage_setting->ue_usage_setting = (LIBLTE_MME_UE_USAGE_SETTING_ENUM)((**ie_ptr >> 2) & 0x01); + voice_domain_pref_and_ue_usage_setting->voice_domain_pref = (LIBLTE_MME_VOICE_DOMAIN_PREF_ENUM)(**ie_ptr & 0x03); + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: GUTI Type + + Description: Indicates whether the GUTI included in the same + message in an information element of type EPS + mobility identity represents a native GUTI or a + mapped GUTI. + + Document Reference: 24.301 v10.2.0 Section 9.9.3.45 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_type_ie(LIBLTE_MME_GUTI_TYPE_ENUM guti_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= guti_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_GUTI_TYPE_ENUM *guti_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + guti_type != NULL) + { + *guti_type = (LIBLTE_MME_GUTI_TYPE_ENUM)((**ie_ptr >> bit_offset) & 0x01); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Access Point Name + + Description: Identifies the packet data network to which the GPRS + user wishes to connect and notifies the access point + of the packet data network that wishes to connect to + the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.1 + 24.008 v10.2.0 Section 10.5.6.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_access_point_name_ie(LIBLTE_MME_ACCESS_POINT_NAME_STRUCT *apn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + const char *apn_str; + uint32 i; + uint32 len_idx; + uint32 apn_idx; + uint32 label_len; + + if(apn != NULL && + ie_ptr != NULL) + { + apn_str = apn->apn; + (*ie_ptr)[0] = strnlen(apn->apn, LIBLTE_STRING_LEN)+1; + len_idx = 0; + apn_idx = 0; + label_len = 0; + while(strnlen(apn->apn, LIBLTE_STRING_LEN) > apn_idx) + { + (*ie_ptr)[1+apn_idx+1] = (uint8)apn_str[apn_idx]; + apn_idx++; + label_len++; + + if(apn_str[apn_idx] == '.') + { + (*ie_ptr)[1+len_idx] = label_len; + label_len = 0; + len_idx = apn_idx+1; + apn_idx++; + } + } + (*ie_ptr)[1+len_idx] = label_len; + *ie_ptr += strnlen(apn->apn, LIBLTE_STRING_LEN) + 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_access_point_name_ie(uint8 **ie_ptr, + LIBLTE_MME_ACCESS_POINT_NAME_STRUCT *apn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 ie_idx; + uint32 label_len; + uint32 str_cnt; + + if(ie_ptr != NULL && + apn != NULL) + { + ie_idx = 0; + str_cnt = 0; + while(ie_idx < (*ie_ptr)[0] && str_cnt < LIBLTE_STRING_LEN) + { + label_len = (*ie_ptr)[1+ie_idx]; + for(i=0; iapn[str_cnt] = (char)((*ie_ptr)[1+ie_idx+i+1]); + str_cnt++; + } + ie_idx += label_len + 1; + if(ie_idx < (*ie_ptr)[0] && str_cnt < LIBLTE_STRING_LEN) + { + apn->apn[str_cnt] = '.'; + str_cnt++; + } + } + if (str_cnt < LIBLTE_STRING_LEN) { + apn->apn[str_cnt] = '\0'; + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: APN Aggregate Maximum Bit Rate + + Description: Indicates the initial subscribed APN-AMBR when the + UE establishes a PDN connection or indicates the new + APN-AMBR if it is changed by the network. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_apn_aggregate_maximum_bit_rate_ie(LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT *apn_ambr, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(apn_ambr != NULL && + ie_ptr != NULL) + { + if(apn_ambr->ext_present && + apn_ambr->ext2_present) + { + (*ie_ptr)[0] = 6; + }else if(apn_ambr->ext_present){ + (*ie_ptr)[0] = 4; + }else{ + (*ie_ptr)[0] = 2; + } + (*ie_ptr)[1] = apn_ambr->apn_ambr_dl; + (*ie_ptr)[2] = apn_ambr->apn_ambr_ul; + if(apn_ambr->ext_present) + { + (*ie_ptr)[3] = apn_ambr->apn_ambr_dl_ext; + (*ie_ptr)[4] = apn_ambr->apn_ambr_ul_ext; + } + if(apn_ambr->ext2_present) + { + (*ie_ptr)[5] = apn_ambr->apn_ambr_dl_ext2; + (*ie_ptr)[6] = apn_ambr->apn_ambr_ul_ext2; + } + if(apn_ambr->ext_present && + apn_ambr->ext2_present) + { + *ie_ptr += 7; + }else if(apn_ambr->ext_present){ + *ie_ptr += 5; + }else{ + *ie_ptr += 3; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_apn_aggregate_maximum_bit_rate_ie(uint8 **ie_ptr, + LIBLTE_MME_APN_AGGREGATE_MAXIMUM_BIT_RATE_STRUCT *apn_ambr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + apn_ambr != NULL) + { + if(6 == (*ie_ptr)[0]) + { + apn_ambr->ext_present = true; + apn_ambr->ext2_present = true; + }else if(4 == (*ie_ptr)[0]){ + apn_ambr->ext_present = true; + apn_ambr->ext2_present = false; + }else{ + apn_ambr->ext_present = false; + apn_ambr->ext2_present = false; + } + apn_ambr->apn_ambr_dl = (*ie_ptr)[1]; + apn_ambr->apn_ambr_ul = (*ie_ptr)[2]; + if(apn_ambr->ext_present) + { + apn_ambr->apn_ambr_dl_ext = (*ie_ptr)[3]; + apn_ambr->apn_ambr_ul_ext = (*ie_ptr)[4]; + } + if(apn_ambr->ext2_present) + { + apn_ambr->apn_ambr_dl_ext2 = (*ie_ptr)[5]; + apn_ambr->apn_ambr_ul_ext2 = (*ie_ptr)[6]; + } + if(apn_ambr->ext_present && + apn_ambr->ext2_present) + { + *ie_ptr += 7; + }else if(apn_ambr->ext_present){ + *ie_ptr += 5; + }else{ + *ie_ptr += 3; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Connectivity Type + + Description: Specifies the type of connectivity selected by the + network for the PDN connection. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.2A + 24.008 v10.2.0 Section 10.5.6.19 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_connectivity_type_ie(uint8 con_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= con_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_connectivity_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *con_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + con_type != NULL) + { + *con_type = ((*ie_ptr)[0] >> bit_offset) & 0x0F; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: EPS Quality Of Service + + Description: Specifies the QoS parameters for an EPS bearer + context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_eps_quality_of_service_ie(LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT *qos, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(qos != NULL && + ie_ptr != NULL) + { + if(qos->br_present && + qos->br_ext_present) + { + (*ie_ptr)[0] = 9; + }else if(qos->br_present){ + (*ie_ptr)[0] = 5; + }else{ + (*ie_ptr)[0] = 1; + } + (*ie_ptr)[1] = qos->qci; + if(qos->br_present) + { + (*ie_ptr)[2] = qos->mbr_ul; + (*ie_ptr)[3] = qos->mbr_dl; + (*ie_ptr)[4] = qos->gbr_ul; + (*ie_ptr)[5] = qos->gbr_dl; + } + if(qos->br_ext_present) + { + (*ie_ptr)[6] = qos->mbr_ul_ext; + (*ie_ptr)[7] = qos->mbr_dl_ext; + (*ie_ptr)[8] = qos->gbr_ul_ext; + (*ie_ptr)[9] = qos->gbr_dl_ext; + } + if(qos->br_present && + qos->br_ext_present) + { + *ie_ptr += 10; + }else if(qos->br_present){ + *ie_ptr += 6; + }else{ + *ie_ptr += 2; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_eps_quality_of_service_ie(uint8 **ie_ptr, + LIBLTE_MME_EPS_QUALITY_OF_SERVICE_STRUCT *qos) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + qos != NULL) + { + if((*ie_ptr)[0] == 1) + { + qos->br_present = false; + qos->br_ext_present = false; + }else if((*ie_ptr)[0] == 5){ + qos->br_present = true; + qos->br_ext_present = false; + }else{ + qos->br_present = true; + qos->br_ext_present = true; + } + qos->qci = (*ie_ptr)[1]; + if(qos->br_present) + { + qos->mbr_ul = (*ie_ptr)[2]; + qos->mbr_dl = (*ie_ptr)[3]; + qos->gbr_ul = (*ie_ptr)[4]; + qos->gbr_dl = (*ie_ptr)[5]; + } + if(qos->br_ext_present) + { + qos->mbr_ul_ext = (*ie_ptr)[6]; + qos->mbr_dl_ext = (*ie_ptr)[7]; + qos->gbr_ul_ext = (*ie_ptr)[8]; + qos->gbr_dl_ext = (*ie_ptr)[9]; + } + if(qos->br_present && + qos->br_ext_present) + { + *ie_ptr += 10; + }else if(qos->br_present){ + *ie_ptr += 6; + }else{ + *ie_ptr += 2; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ESM Cause + + Description: Indicates the reason why a session management request + is rejected. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_cause_ie(uint8 cause, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = cause; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_cause_ie(uint8 **ie_ptr, + uint8 *cause) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cause != NULL) + { + *cause = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ESM Information Transfer Flag + + Description: Indicates whether ESM information, i.e. protocol + configuration options or APN or both, is to be + transferred security protected. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_info_transfer_flag_ie(LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM esm_info_transfer_flag, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= esm_info_transfer_flag << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_info_transfer_flag_ie(uint8 **ie_ptr, + uint8 bit_offset, + LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM *esm_info_transfer_flag) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + esm_info_transfer_flag != NULL) + { + *esm_info_transfer_flag = (LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_ENUM)((**ie_ptr >> bit_offset) & 0x01); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Linked EPS Bearer Identity + + Description: Identifies the default bearer that is associated + with a dedicated EPS bearer or identifies the EPS + bearer (default or dedicated) with which one or more + packet filters specified in a traffic flow aggregate + are associated. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_linked_eps_bearer_identity_ie(uint8 bearer_id, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= (bearer_id & 0x0F) << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_linked_eps_bearer_identity_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *bearer_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + bearer_id != NULL) + { + *bearer_id = ((*ie_ptr)[0] >> bit_offset) & 0x0F; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: LLC Service Access Point Identifier + + Description: Identifies the service access point that is used for + the GPRS data transfer at LLC layer. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.7 + 24.008 v10.2.0 Section 10.5.6.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_llc_service_access_point_identifier_ie(uint8 llc_sapi, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = llc_sapi; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_llc_service_access_point_identifier_ie(uint8 **ie_ptr, + uint8 *llc_sapi) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + llc_sapi != NULL) + { + *llc_sapi = (*ie_ptr)[0]; + *ie_ptr += 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Notification Indicator + + Description: Informs the UE about an event which is relevant for + the upper layer using an EPS bearer context or + having requested a procedure transaction. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.7A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_notification_indicator_ie(uint8 notification_ind, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = 1; + (*ie_ptr)[1] = notification_ind; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_notification_indicator_ie(uint8 **ie_ptr, + uint8 *notification_ind) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + notification_ind != NULL) + { + *notification_ind = (*ie_ptr)[1]; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Packet Flow Identifier + + Description: Indicates the packet flow identifier for a packet + flow context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.8 + 24.008 v10.2.0 Section 10.5.6.11 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_packet_flow_identifier_ie(uint8 packet_flow_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] = 1; + (*ie_ptr)[1] = packet_flow_id; + *ie_ptr += 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_packet_flow_identifier_ie(uint8 **ie_ptr, + uint8 *packet_flow_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + packet_flow_id != NULL) + { + *packet_flow_id = (*ie_ptr)[1]; + *ie_ptr += (*ie_ptr)[0]; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PDN Address + + Description: Assigns an IPv4 address to the UE associated with a + packet data network and provides the UE with an + interface identifier to be used to build the IPv6 + link local address. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_address_ie(LIBLTE_MME_PDN_ADDRESS_STRUCT *pdn_addr, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(pdn_addr != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[1] = 0x00 | (pdn_addr->pdn_type & 0x07); + if(LIBLTE_MME_PDN_TYPE_IPV4 == pdn_addr->pdn_type) + { + for(i=0; i<4; i++) + { + (*ie_ptr)[2+i] = pdn_addr->addr[i]; + } + (*ie_ptr)[0] = 5; + *ie_ptr += 6; + }else if(LIBLTE_MME_PDN_TYPE_IPV6 == pdn_addr->pdn_type){ + for(i=0; i<8; i++) + { + (*ie_ptr)[2+i] = pdn_addr->addr[i]; + } + (*ie_ptr)[0] = 9; + *ie_ptr += 10; + }else{ + for(i=0; i<12; i++) + { + (*ie_ptr)[2+i] = pdn_addr->addr[i]; + } + (*ie_ptr)[0] = 13; + *ie_ptr += 14; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_address_ie(uint8 **ie_ptr, + LIBLTE_MME_PDN_ADDRESS_STRUCT *pdn_addr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + pdn_addr != NULL) + { + pdn_addr->pdn_type = (*ie_ptr)[1] & 0x07; + if(LIBLTE_MME_PDN_TYPE_IPV4 == pdn_addr->pdn_type) + { + for(i=0; i<4; i++) + { + pdn_addr->addr[i] = (*ie_ptr)[2+i]; + } + }else if(LIBLTE_MME_PDN_TYPE_IPV6 == pdn_addr->pdn_type){ + for(i=0; i<8; i++) + { + pdn_addr->addr[i] = (*ie_ptr)[2+i]; + } + }else{ + for(i=0; i<12; i++) + { + pdn_addr->addr[i] = (*ie_ptr)[2+i]; + } + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PDN Type + + Description: Indicates the IP version capability of the IP stack + associated with the UE. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.10 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_type_ie(uint8 pdn_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= pdn_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *pdn_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pdn_type != NULL) + { + *pdn_type = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Protocol Configuration Options + + Description: Transfers external network protocol options + associated with a PDP context activation and + transfers additional (protocol) data (e.g. + configuration parameters, error codes or messages/ + events) associated with an external protocol or an + application. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.11 + 24.008 v10.2.0 Section 10.5.6.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_protocol_config_options_ie(LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT *protocol_cnfg_opts, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 idx; + uint32 i; + uint32 j; + + if(protocol_cnfg_opts != NULL && + ie_ptr != NULL) + { + (*ie_ptr)[1] = 0x80; + idx = 2; + for(i=0; iN_opts; i++) + { + (*ie_ptr)[idx++] = protocol_cnfg_opts->opt[i].id >> 8; + (*ie_ptr)[idx++] = protocol_cnfg_opts->opt[i].id & 0x00FF; + (*ie_ptr)[idx++] = protocol_cnfg_opts->opt[i].len; + for(j=0; jopt[i].len; j++) + { + (*ie_ptr)[idx++] = protocol_cnfg_opts->opt[i].contents[j]; + } + } + (*ie_ptr)[0] = idx - 1; + *ie_ptr += idx; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_protocol_config_options_ie(uint8 **ie_ptr, + LIBLTE_MME_PROTOCOL_CONFIG_OPTIONS_STRUCT *protocol_cnfg_opts) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 idx; + uint32 i; + + if(ie_ptr != NULL && + protocol_cnfg_opts != NULL) + { + idx = 2; + protocol_cnfg_opts->N_opts = 0; + while(idx < (*ie_ptr)[0]) + { + protocol_cnfg_opts->opt[protocol_cnfg_opts->N_opts].id = (*ie_ptr)[idx++] << 8; + protocol_cnfg_opts->opt[protocol_cnfg_opts->N_opts].id |= (*ie_ptr)[idx++]; + protocol_cnfg_opts->opt[protocol_cnfg_opts->N_opts].len = (*ie_ptr)[idx++]; + for(i=0; iopt[protocol_cnfg_opts->N_opts].len; i++) + { + protocol_cnfg_opts->opt[protocol_cnfg_opts->N_opts].contents[i] = (*ie_ptr)[idx++]; + } + protocol_cnfg_opts->N_opts++; + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Quality Of Service + + Description: Specifies the QoS parameters for a PDP context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.12 + 24.008 v10.2.0 Section 10.5.6.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_quality_of_service_ie(LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT *qos, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(qos != NULL && + ie_ptr != NULL) + { + if(qos->dl_ext_present && + qos->ul_ext_present) + { + (*ie_ptr)[0] = 16; + }else if(qos->dl_ext_present){ + (*ie_ptr)[0] = 14; + }else{ + (*ie_ptr)[0] = 12; + } + (*ie_ptr)[1] = ((qos->delay_class & 0x07) << 3) | (qos->reliability_class & 0x07); + (*ie_ptr)[2] = ((qos->peak_throughput & 0x0F) << 4) | (qos->precedence_class & 0x07); + (*ie_ptr)[3] = qos->mean_throughput & 0x1F; + (*ie_ptr)[4] = ((qos->traffic_class & 0x07) << 5) | ((qos->delivery_order & 0x03) << 3) | (qos->delivery_of_erroneous_sdu & 0x03); + (*ie_ptr)[5] = qos->max_sdu_size; + (*ie_ptr)[6] = qos->mbr_ul; + (*ie_ptr)[7] = qos->mbr_dl; + (*ie_ptr)[8] = ((qos->residual_ber & 0x0F) << 4) | (qos->sdu_error_ratio & 0x0F); + (*ie_ptr)[9] = ((qos->transfer_delay & 0x3F) << 2) | (qos->traffic_handling_prio & 0x03); + (*ie_ptr)[10] = qos->gbr_ul; + (*ie_ptr)[11] = qos->gbr_dl; + (*ie_ptr)[12] = ((qos->signalling_ind & 0x01) << 4) | (qos->source_stats_descriptor & 0x0F); + if(qos->dl_ext_present) + { + (*ie_ptr)[13] = qos->mbr_dl_ext; + (*ie_ptr)[14] = qos->gbr_dl_ext; + } + if(qos->ul_ext_present) + { + (*ie_ptr)[15] = qos->mbr_ul_ext; + (*ie_ptr)[16] = qos->gbr_ul_ext; + } + if(qos->dl_ext_present && + qos->ul_ext_present) + { + *ie_ptr += 17; + }else if(qos->dl_ext_present){ + *ie_ptr += 15; + }else{ + *ie_ptr += 13; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_quality_of_service_ie(uint8 **ie_ptr, + LIBLTE_MME_QUALITY_OF_SERVICE_STRUCT *qos) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + qos != NULL) + { + if(16 == (*ie_ptr)[0]) + { + qos->dl_ext_present = true; + qos->ul_ext_present = true; + }else if(14 == (*ie_ptr)[0]){ + qos->dl_ext_present = true; + qos->ul_ext_present = false; + }else{ + qos->dl_ext_present = false; + qos->ul_ext_present = false; + } + qos->delay_class = ((*ie_ptr)[1] >> 3) & 0x07; + qos->reliability_class = (*ie_ptr)[1] & 0x07; + qos->peak_throughput = (*ie_ptr)[2] >> 4; + qos->precedence_class = (*ie_ptr)[2] & 0x07; + qos->mean_throughput = (*ie_ptr)[3] & 0x1F; + qos->traffic_class = ((*ie_ptr)[4] >> 5) & 0x07; + qos->delivery_order = ((*ie_ptr)[4] >> 3) & 0x03; + qos->delivery_of_erroneous_sdu = (*ie_ptr)[4] & 0x07; + qos->max_sdu_size = (*ie_ptr)[5]; + qos->mbr_ul = (*ie_ptr)[6]; + qos->mbr_dl = (*ie_ptr)[7]; + qos->residual_ber = ((*ie_ptr)[8] >> 4) & 0x0F; + qos->sdu_error_ratio = (*ie_ptr)[8] & 0x0F; + qos->transfer_delay = ((*ie_ptr)[9] >> 2) & 0x3F; + qos->traffic_handling_prio = (*ie_ptr)[9] & 0x03; + qos->gbr_ul = (*ie_ptr)[10]; + qos->gbr_dl = (*ie_ptr)[11]; + qos->signalling_ind = ((*ie_ptr)[12] >> 4) & 0x01; + qos->source_stats_descriptor = (*ie_ptr)[12] & 0x0F; + if(qos->dl_ext_present) + { + qos->mbr_dl_ext = (*ie_ptr)[13]; + qos->gbr_dl_ext = (*ie_ptr)[14]; + } + if(qos->ul_ext_present) + { + qos->mbr_ul_ext = (*ie_ptr)[15]; + qos->gbr_ul_ext = (*ie_ptr)[16]; + } + if(qos->dl_ext_present && + qos->ul_ext_present) + { + *ie_ptr += 17; + }else if(qos->dl_ext_present){ + *ie_ptr += 15; + }else{ + *ie_ptr += 13; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Radio Priority + + Description: Specifies the priority level the UE shall use at the + lower layers for transmission of data related to a + PDP context or for mobile originated SMS + transmission. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.13 + 24.008 v10.2.0 Section 10.5.7.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_radio_priority_ie(uint8 radio_prio, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + (*ie_ptr)[0] |= radio_prio << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_radio_priority_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *radio_prio) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + radio_prio != NULL) + { + *radio_prio |= ((*ie_ptr)[0] >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Request Type + + Description: Indicates whether the UE requests to establish a new + connectivity to a PDN or keep the connection(s) to + which it has connected via non-3GPP access. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.14 + 24.008 v10.2.0 Section 10.5.6.17 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_request_type_ie(uint8 req_type, + uint8 bit_offset, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + **ie_ptr |= req_type << bit_offset; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_request_type_ie(uint8 **ie_ptr, + uint8 bit_offset, + uint8 *req_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + req_type != NULL) + { + *req_type = (**ie_ptr >> bit_offset) & 0x07; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Traffic Flow Aggregate Description + + Description: Specifies the aggregate of one or more packet filters + and their related parameters and operations. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.15 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_traffic_flow_aggregate_description_ie(LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT *tfad, + uint8 **ie_ptr) +{ + return(liblte_mme_pack_traffic_flow_template_ie((LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *)tfad, ie_ptr)); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_traffic_flow_aggregate_description_ie(uint8 **ie_ptr, + LIBLTE_MME_TRAFFIC_FLOW_AGGREGATE_DESCRIPTION_STRUCT *tfad) +{ + return(liblte_mme_unpack_traffic_flow_template_ie(ie_ptr, (LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *)tfad)); +} + +/********************************************************************* + IE Name: Traffic Flow Template + + Description: Specifies the TFT parameters and operations for a + PDP context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.16 + 24.008 v10.2.0 Section 10.5.6.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_traffic_flow_template_ie(LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *tft, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 idx; + uint32 i; + uint32 j; + + if(tft != NULL && + ie_ptr != NULL) + { + idx = 1; + (*ie_ptr)[idx] = (tft->tft_op_code & 0x07) << 5; + if(0 != tft->parameter_list_size) + { + (*ie_ptr)[idx] |= 0x10; + } + (*ie_ptr)[idx] |= tft->packet_filter_list_size & 0x0F; + idx++; + + for(i=0; ipacket_filter_list_size; i++) + { + (*ie_ptr)[idx] = (tft->packet_filter_list[i].dir & 0x0F) << 4; + (*ie_ptr)[idx] |= tft->packet_filter_list[i].id & 0x0F; + idx++; + if(LIBLTE_MME_TFT_OPERATION_CODE_DELETE_PACKET_FILTERS_FROM_EXISTING_TFT != tft->tft_op_code) + { + (*ie_ptr)[idx] = tft->packet_filter_list[i].eval_precedence; + idx++; + (*ie_ptr)[idx] = tft->packet_filter_list[i].filter_size; + idx++; + for(j=0; jpacket_filter_list[i].filter_size; j++) + { + (*ie_ptr)[idx] = tft->packet_filter_list[i].filter[j]; + idx++; + } + } + } + + for(i=0; iparameter_list_size; i++) + { + (*ie_ptr)[idx] = tft->parameter_list[i].id; + idx++; + (*ie_ptr)[idx] = tft->parameter_list[i].parameter_size; + idx++; + for(j=0; jparameter_list[i].parameter_size; j++) + { + (*ie_ptr)[idx] = tft->parameter_list[i].parameter[j]; + idx++; + } + } + (*ie_ptr)[0] = idx - 1; + *ie_ptr += idx; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_traffic_flow_template_ie(uint8 **ie_ptr, + LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT *tft) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 idx; + uint32 i; + uint32 j; + bool param_list_present; + + if(ie_ptr != NULL && + tft != NULL) + { + idx = 1; + tft->tft_op_code = (LIBLTE_MME_TFT_OPERATION_CODE_ENUM)(((*ie_ptr)[idx] >> 5) & 0x07); + param_list_present = ((*ie_ptr)[idx] >> 4) & 0x01; + tft->packet_filter_list_size = (*ie_ptr)[idx] & 0x0F; + idx++; + + for(i=0; ipacket_filter_list_size; i++) + { + tft->packet_filter_list[i].dir = (LIBLTE_MME_TFT_PACKET_FILTER_DIRECTION_ENUM)(((*ie_ptr)[idx] >> 4) & 0x0F); + tft->packet_filter_list[i].id = (*ie_ptr)[idx] & 0x0F; + idx++; + if(LIBLTE_MME_TFT_OPERATION_CODE_DELETE_PACKET_FILTERS_FROM_EXISTING_TFT != tft->tft_op_code) + { + tft->packet_filter_list[i].eval_precedence = (*ie_ptr)[idx]; + idx++; + tft->packet_filter_list[i].filter_size = (*ie_ptr)[idx]; + idx++; + for(j=0; jpacket_filter_list[i].filter_size; j++) + { + tft->packet_filter_list[i].filter[j] = (*ie_ptr)[idx]; + idx++; + } + } + } + + if(param_list_present) + { + tft->parameter_list_size = 0; + while(idx < (*ie_ptr)[0]) + { + tft->parameter_list[tft->parameter_list_size].id = (*ie_ptr)[idx]; + idx++; + tft->parameter_list[tft->parameter_list_size].parameter_size = (*ie_ptr)[idx]; + idx++; + for(i=0; iparameter_list[tft->parameter_list_size].parameter_size; i++) + { + tft->parameter_list[tft->parameter_list_size].parameter[i] = (*ie_ptr)[idx]; + idx++; + } + tft->parameter_list_size++; + } + } + + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Transaction Identifier + + Description: Represents the corresponding PDP context in A/Gb + mode or Iu mode which is mapped from the EPS bearer + context. + + Document Reference: 24.301 v10.2.0 Section 9.9.4.17 + 24.008 v10.2.0 Section 10.5.6.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_transaction_identifier_ie(LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT *trans_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(trans_id != NULL && + ie_ptr != NULL) + { + if(LIBLTE_MME_TI_VALUE_IS_GIVEN_BY_TIE == trans_id->tio) + { + (*ie_ptr)[0] = 2; + }else{ + (*ie_ptr)[0] = 1; + } + (*ie_ptr)[1] = ((trans_id->ti_flag & 0x01) << 7) | ((trans_id->tio & 0x07) << 4); + if(LIBLTE_MME_TI_VALUE_IS_GIVEN_BY_TIE == trans_id->tio) + { + (*ie_ptr)[2] = 0x80 | (trans_id->tie & 0x7F); + *ie_ptr += 3; + }else{ + *ie_ptr += 2; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_transaction_identifier_ie(uint8 **ie_ptr, + LIBLTE_MME_TRANSACTION_IDENTIFIER_STRUCT *trans_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + trans_id != NULL) + { + trans_id->ti_flag = (*ie_ptr)[1] >> 7; + trans_id->tio = ((*ie_ptr)[1] >> 4) & 0x07; + if(LIBLTE_MME_TI_VALUE_IS_GIVEN_BY_TIE == trans_id->tio) + { + trans_id->tie = (*ie_ptr)[2] & 0x7F; + } + *ie_ptr += (*ie_ptr)[0] + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/******************************************************************************* + MESSAGE FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + Message Name: Security Message Header (Plain NAS Message) + + Description: Security header for NAS messages. + + Document Reference: 24.301 v10.2.0 Section 9.1 +*********************************************************************/ + +LIBLTE_ERROR_ENUM liblte_mme_parse_msg_sec_header(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 *pd, + uint8 *sec_hdr_type) +{ + + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && + pd != NULL && + sec_hdr_type != NULL) + { + *sec_hdr_type = (uint8) ((msg->msg[0] & 0xF0) >> 4); + err = LIBLTE_SUCCESS; + } + return(err); +} + + +/********************************************************************* + Message Name: Message Header (Plain NAS Message) + + Description: Message header for plain NAS messages. + + Document Reference: 24.301 v10.2.0 Section 9.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_parse_msg_header(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 *pd, + uint8 *msg_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 sec_hdr_type; + + if(msg != NULL && + pd != NULL && + msg_type != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + + // Protocol Discriminator + *pd = msg->msg[0] & 0x0F; + + if(LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST == sec_hdr_type) + { + *msg_type = LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST; + }else{ + if(LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT == *pd) + { + // Message Type + *msg_type = msg->msg[2]; + }else{ + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + // Message Type + *msg_type = msg->msg[1]; + }else{ + // Protocol Discriminator + *pd = msg->msg[6] & 0x0F; + + if(LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT == *pd) + { + *msg_type = msg->msg[8]; + }else{ + *msg_type = msg->msg[7]; + } + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_pack_security_protected_nas_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *sec_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = sec_msg->msg; + uint32 i; + + if(msg != NULL && + sec_msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // NAS Message + for(i=0; iN_bytes; i++) + { + *msg_ptr = msg->msg[i]; + msg_ptr++; + } + + // Fill in the number of bytes used + sec_msg->N_bytes = msg_ptr - sec_msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Attach Accept + + Description: Sent by the network to the UE to indicate that the + corresponding attach request has been accepted. + + Document Reference: 24.301 v10.2.0 Section 8.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_accept_msg(LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT *attach_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(attach_accept != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT; + msg_ptr++; + + // EPS Attach Result & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_eps_attach_result_ie(attach_accept->eps_attach_result, 0, &msg_ptr); + msg_ptr++; + + // T3412 Value + liblte_mme_pack_gprs_timer_ie(&attach_accept->t3412, &msg_ptr); + + // TAI List + liblte_mme_pack_tracking_area_identity_list_ie(&attach_accept->tai_list, &msg_ptr); + + // ESM Message Container + liblte_mme_pack_esm_message_container_ie(&attach_accept->esm_msg, &msg_ptr); + + // GUTI + if(attach_accept->guti_present) + { + *msg_ptr = LIBLTE_MME_GUTI_IEI; + msg_ptr++; + liblte_mme_pack_eps_mobile_id_ie(&attach_accept->guti, &msg_ptr); + } + + // Location Area Identification + if(attach_accept->lai_present) + { + *msg_ptr = LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI; + msg_ptr++; + liblte_mme_pack_location_area_id_ie(&attach_accept->lai, &msg_ptr); + } + + // MS Identity + if(attach_accept->ms_id_present) + { + *msg_ptr = LIBLTE_MME_MS_IDENTITY_IEI; + msg_ptr++; + liblte_mme_pack_mobile_id_ie(&attach_accept->ms_id, &msg_ptr); + } + + // EMM Cause + if(attach_accept->emm_cause_present) + { + *msg_ptr = LIBLTE_MME_EMM_CAUSE_IEI; + msg_ptr++; + liblte_mme_pack_emm_cause_ie(attach_accept->emm_cause, &msg_ptr); + } + + // T3402 Value + if(attach_accept->t3402_present) + { + *msg_ptr = LIBLTE_MME_T3402_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&attach_accept->t3402, &msg_ptr); + } + + // T3423 Value + if(attach_accept->t3423_present) + { + *msg_ptr = LIBLTE_MME_T3423_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&attach_accept->t3423, &msg_ptr); + } + + // Equivalent PLMNs + if(attach_accept->equivalent_plmns_present) + { + *msg_ptr = LIBLTE_MME_EQUIVALENT_PLMNS_IEI; + msg_ptr++; + liblte_mme_pack_plmn_list_ie(&attach_accept->equivalent_plmns, &msg_ptr); + } + + // Emergency Number List + if(attach_accept->emerg_num_list_present) + { + *msg_ptr = LIBLTE_MME_EMERGENCY_NUMBER_LIST_IEI; + msg_ptr++; + liblte_mme_pack_emergency_number_list_ie(&attach_accept->emerg_num_list, &msg_ptr); + } + + // EPS Network Feature Support + if(attach_accept->eps_network_feature_support_present) + { + *msg_ptr = LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI; + msg_ptr++; + liblte_mme_pack_eps_network_feature_support_ie(&attach_accept->eps_network_feature_support, &msg_ptr); + } + + // Additional Update Result + if(attach_accept->additional_update_result_present) + { + *msg_ptr = LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI << 4; + liblte_mme_pack_additional_update_result_ie(attach_accept->additional_update_result, 0, &msg_ptr); + msg_ptr++; + } + + // T3412 Extended Value + if(attach_accept->t3412_ext_present) + { + *msg_ptr = LIBLTE_MME_T3412_EXTENDED_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_3_ie(&attach_accept->t3412_ext, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT *attach_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + attach_accept != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EPS Attach Result & Spare Half Octet + liblte_mme_unpack_eps_attach_result_ie(&msg_ptr, 0, &attach_accept->eps_attach_result); + msg_ptr++; + + // T3412 Value + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &attach_accept->t3412); + + // TAI List + liblte_mme_unpack_tracking_area_identity_list_ie(&msg_ptr, &attach_accept->tai_list); + + // ESM Message Container + liblte_mme_unpack_esm_message_container_ie(&msg_ptr, &attach_accept->esm_msg); + + // GUTI + if(LIBLTE_MME_GUTI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &attach_accept->guti); + attach_accept->guti_present = true; + }else{ + attach_accept->guti_present = false; + } + + // Location Area Identification + if(LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_location_area_id_ie(&msg_ptr, &attach_accept->lai); + attach_accept->lai_present = true; + }else{ + attach_accept->lai_present = false; + } + + // MS Identity + if(LIBLTE_MME_MS_IDENTITY_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_mobile_id_ie(&msg_ptr, &attach_accept->ms_id); + attach_accept->ms_id_present = true; + }else{ + attach_accept->ms_id_present = false; + } + + // EMM Cause + if(LIBLTE_MME_EMM_CAUSE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &attach_accept->emm_cause); + attach_accept->emm_cause_present = true; + }else{ + attach_accept->emm_cause_present = false; + } + + // T3402 Value + if(LIBLTE_MME_T3402_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &attach_accept->t3402); + attach_accept->t3402_present = true; + }else{ + attach_accept->t3402_present = false; + } + + // T3423 Value + if(LIBLTE_MME_T3423_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &attach_accept->t3423); + attach_accept->t3423_present = true; + }else{ + attach_accept->t3423_present = false; + } + + // Equivalent PLMNs + if(LIBLTE_MME_EQUIVALENT_PLMNS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_plmn_list_ie(&msg_ptr, &attach_accept->equivalent_plmns); + attach_accept->equivalent_plmns_present = true; + }else{ + attach_accept->equivalent_plmns_present = false; + } + + // Emergency Number List + if(LIBLTE_MME_EMERGENCY_NUMBER_LIST_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_emergency_number_list_ie(&msg_ptr, &attach_accept->emerg_num_list); + attach_accept->emerg_num_list_present = true; + }else{ + attach_accept->emerg_num_list_present = false; + } + + // EPS Network Feature Support + if(LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_network_feature_support_ie(&msg_ptr, &attach_accept->eps_network_feature_support); + attach_accept->eps_network_feature_support_present = true; + }else{ + attach_accept->eps_network_feature_support_present = false; + } + + // Additional Update Result + if((LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_additional_update_result_ie(&msg_ptr, 0, &attach_accept->additional_update_result); + msg_ptr++; + attach_accept->additional_update_result_present = true; + }else{ + attach_accept->additional_update_result_present = false; + } + + // T3412 Extended Value + if(LIBLTE_MME_T3412_EXTENDED_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_3_ie(&msg_ptr, &attach_accept->t3412_ext); + attach_accept->t3412_ext_present = true; + }else{ + attach_accept->t3412_ext_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Attach Complete + + Description: Sent by the UE to the network in response to an + ATTACH ACCEPT message. + + Document Reference: 24.301 v10.2.0 Section 8.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_complete_msg(LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT *attach_comp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(attach_comp != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE; + msg_ptr++; + + // ESM Message Container + liblte_mme_pack_esm_message_container_ie(&attach_comp->esm_msg, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT *attach_comp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + attach_comp != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // ESM Message Container + liblte_mme_unpack_esm_message_container_ie(&msg_ptr, &attach_comp->esm_msg); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Attach Reject + + Description: Sent by the network to the UE to indicate that the + corresponding attach request has been rejected. + + Document Reference: 24.301 v10.2.0 Section 8.2.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_reject_msg(LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT *attach_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(attach_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ATTACH_REJECT; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(attach_rej->emm_cause, &msg_ptr); + + // ESM Message Container + if(attach_rej->esm_msg_present) + { + *msg_ptr = LIBLTE_MME_ESM_MSG_CONTAINER_IEI; + msg_ptr++; + liblte_mme_pack_esm_message_container_ie(&attach_rej->esm_msg, &msg_ptr); + } + + // T3446 Value + if(attach_rej->t3446_value_present) + { + *msg_ptr = LIBLTE_MME_T3446_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_2_ie(attach_rej->t3446_value, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT *attach_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + attach_rej != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &attach_rej->emm_cause); + + // ESM Message Container + if(LIBLTE_MME_ESM_MSG_CONTAINER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_esm_message_container_ie(&msg_ptr, &attach_rej->esm_msg); + attach_rej->esm_msg_present = true; + }else{ + attach_rej->esm_msg_present = false; + } + + // T3446 Value + if(LIBLTE_MME_T3446_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_2_ie(&msg_ptr, &attach_rej->t3446_value); + attach_rej->t3446_value_present = true; + }else{ + attach_rej->t3446_value_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Attach Request + + Description: Sent by the UE to the network to perform an attach + procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + return liblte_mme_pack_attach_request_msg(attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS, 0, msg); +} + +LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(attach_req != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST; + msg_ptr++; + + // EPS Attach Type & NAS Key Set Identifier + *msg_ptr = 0; + liblte_mme_pack_eps_attach_type_ie(attach_req->eps_attach_type, 0, &msg_ptr); + liblte_mme_pack_nas_key_set_id_ie(&attach_req->nas_ksi, 4, &msg_ptr); + msg_ptr++; + + // EPS Mobile ID + liblte_mme_pack_eps_mobile_id_ie(&attach_req->eps_mobile_id, &msg_ptr); + + // UE Network Capability + liblte_mme_pack_ue_network_capability_ie(&attach_req->ue_network_cap, &msg_ptr); + + // ESM Message Container + liblte_mme_pack_esm_message_container_ie(&attach_req->esm_msg, &msg_ptr); + + // Old P-TMSI Signature + if(attach_req->old_p_tmsi_signature_present) + { + *msg_ptr = LIBLTE_MME_P_TMSI_SIGNATURE_IEI; + msg_ptr++; + liblte_mme_pack_p_tmsi_signature_ie(attach_req->old_p_tmsi_signature, &msg_ptr); + } + + // Additional GUTI + if(attach_req->additional_guti_present) + { + *msg_ptr = LIBLTE_MME_ADDITIONAL_GUTI_IEI; + msg_ptr++; + liblte_mme_pack_eps_mobile_id_ie(&attach_req->additional_guti, &msg_ptr); + } + + // Last Visited Registered TAI + if(attach_req->last_visited_registered_tai_present) + { + *msg_ptr = LIBLTE_MME_LAST_VISITED_REGISTERED_TAI_IEI; + msg_ptr++; + liblte_mme_pack_tracking_area_id_ie(&attach_req->last_visited_registered_tai, &msg_ptr); + } + + // DRX Parameter + if(attach_req->drx_param_present) + { + *msg_ptr = LIBLTE_MME_DRX_PARAMETER_IEI; + msg_ptr++; + liblte_mme_pack_drx_parameter_ie(&attach_req->drx_param, &msg_ptr); + } + + // MS Network Capability + if(attach_req->ms_network_cap_present) + { + *msg_ptr = LIBLTE_MME_MS_NETWORK_CAPABILITY_IEI; + msg_ptr++; + liblte_mme_pack_ms_network_capability_ie(&attach_req->ms_network_cap, &msg_ptr); + } + + // Old Location Area ID + if(attach_req->old_lai_present) + { + *msg_ptr = LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI; + msg_ptr++; + liblte_mme_pack_location_area_id_ie(&attach_req->old_lai, &msg_ptr); + } + + // TMSI Status + if(attach_req->tmsi_status_present) + { + *msg_ptr = LIBLTE_MME_TMSI_STATUS_IEI << 4; + liblte_mme_pack_tmsi_status_ie(attach_req->tmsi_status, 0, &msg_ptr); + msg_ptr++; + } + + // Mobile Station Classmark 2 + if(attach_req->ms_cm2_present) + { + *msg_ptr = LIBLTE_MME_MS_CLASSMARK_2_IEI; + msg_ptr++; + liblte_mme_pack_mobile_station_classmark_2_ie(&attach_req->ms_cm2, &msg_ptr); + } + + // Mobile Station Classmark 3 + if(attach_req->ms_cm3_present) + { + *msg_ptr = LIBLTE_MME_MS_CLASSMARK_3_IEI; + msg_ptr++; + liblte_mme_pack_mobile_station_classmark_3_ie(&attach_req->ms_cm3, &msg_ptr); + } + + // Supported Codecs + if(attach_req->supported_codecs_present) + { + *msg_ptr = LIBLTE_MME_SUPPORTED_CODEC_LIST_IEI; + msg_ptr++; + liblte_mme_pack_supported_codec_list_ie(&attach_req->supported_codecs, &msg_ptr); + } + + // Additional Update Type + if(attach_req->additional_update_type_present) + { + *msg_ptr = LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_IEI << 4; + liblte_mme_pack_additional_update_type_ie(attach_req->additional_update_type, 0, &msg_ptr); + msg_ptr++; + } + + // Voice Domain Preference and UE's Usage Setting + if(attach_req->voice_domain_pref_and_ue_usage_setting_present) + { + *msg_ptr = LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_IEI; + msg_ptr++; + liblte_mme_pack_voice_domain_pref_and_ue_usage_setting_ie(&attach_req->voice_domain_pref_and_ue_usage_setting, &msg_ptr); + } + + // Device Properties + if(attach_req->device_properties_present) + { + *msg_ptr = LIBLTE_MME_ATTACH_REQUEST_DEVICE_PROPERTIES_IEI << 4; + liblte_mme_pack_device_properties_ie(attach_req->device_properties, 0, &msg_ptr); + msg_ptr++; + } + + // Old GUTI Type + if(attach_req->old_guti_type_present) + { + *msg_ptr = LIBLTE_MME_GUTI_TYPE_IEI << 4; + liblte_mme_pack_guti_type_ie(attach_req->old_guti_type, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + attach_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EPS Attach Type & NAS Key Set Identifier + liblte_mme_unpack_eps_attach_type_ie(&msg_ptr, 0, &attach_req->eps_attach_type); + liblte_mme_unpack_nas_key_set_id_ie(&msg_ptr, 4, &attach_req->nas_ksi); + msg_ptr++; + + // EPS Mobile ID + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &attach_req->eps_mobile_id); + + // UE Network Capability + liblte_mme_unpack_ue_network_capability_ie(&msg_ptr, &attach_req->ue_network_cap); + + // ESM Message Container + liblte_mme_unpack_esm_message_container_ie(&msg_ptr, &attach_req->esm_msg); + + // Old P-TMSI Signature + if(LIBLTE_MME_P_TMSI_SIGNATURE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_p_tmsi_signature_ie(&msg_ptr, &attach_req->old_p_tmsi_signature); + attach_req->old_p_tmsi_signature_present = true; + }else{ + attach_req->old_p_tmsi_signature_present = false; + } + + // Additional GUTI + if(LIBLTE_MME_ADDITIONAL_GUTI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &attach_req->additional_guti); + attach_req->additional_guti_present = true; + }else{ + attach_req->additional_guti_present = false; + } + + // Last Visited Registered TAI + if(LIBLTE_MME_LAST_VISITED_REGISTERED_TAI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_tracking_area_id_ie(&msg_ptr, &attach_req->last_visited_registered_tai); + attach_req->last_visited_registered_tai_present = true; + }else{ + attach_req->last_visited_registered_tai_present = false; + } + + // DRX Parameter + if(LIBLTE_MME_DRX_PARAMETER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_drx_parameter_ie(&msg_ptr, &attach_req->drx_param); + attach_req->drx_param_present = true; + }else{ + attach_req->drx_param_present = false; + } + + // MS Network Capability + if(LIBLTE_MME_MS_NETWORK_CAPABILITY_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_ms_network_capability_ie(&msg_ptr, &attach_req->ms_network_cap); + attach_req->ms_network_cap_present = true; + }else{ + attach_req->ms_network_cap_present = false; + } + + // Old Location Area ID + if(LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_location_area_id_ie(&msg_ptr, &attach_req->old_lai); + attach_req->old_lai_present = true; + }else{ + attach_req->old_lai_present = false; + } + + // TMSI Status + if((LIBLTE_MME_TMSI_STATUS_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_tmsi_status_ie(&msg_ptr, 0, &attach_req->tmsi_status); + msg_ptr++; + attach_req->tmsi_status_present = true; + }else{ + attach_req->tmsi_status_present = false; + } + + // Mobile Station Classmark 2 + if(LIBLTE_MME_MS_CLASSMARK_2_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_mobile_station_classmark_2_ie(&msg_ptr, &attach_req->ms_cm2); + attach_req->ms_cm2_present = true; + }else{ + attach_req->ms_cm2_present = false; + } + + // Mobile Station Classmark 3 + if(LIBLTE_MME_MS_CLASSMARK_3_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_mobile_station_classmark_3_ie(&msg_ptr, &attach_req->ms_cm3); + attach_req->ms_cm3_present = true; + }else{ + attach_req->ms_cm3_present = false; + } + + // Supported Codecs + if(LIBLTE_MME_SUPPORTED_CODEC_LIST_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_supported_codec_list_ie(&msg_ptr, &attach_req->supported_codecs); + attach_req->supported_codecs_present = true; + }else{ + attach_req->supported_codecs_present = false; + } + + // Additional Update Type + if((LIBLTE_MME_ADDITIONAL_UPDATE_TYPE_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_additional_update_type_ie(&msg_ptr, 0, &attach_req->additional_update_type); + msg_ptr++; + attach_req->additional_update_type_present = true; + }else{ + attach_req->additional_update_type_present = false; + } + + // Voice Domain Preference and UE's Usage Setting + if(LIBLTE_MME_VOICE_DOMAIN_PREF_AND_UE_USAGE_SETTING_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_voice_domain_pref_and_ue_usage_setting_ie(&msg_ptr, &attach_req->voice_domain_pref_and_ue_usage_setting); + attach_req->voice_domain_pref_and_ue_usage_setting_present = true; + }else{ + attach_req->voice_domain_pref_and_ue_usage_setting_present = false; + } + + // Device Properties + if((LIBLTE_MME_ATTACH_REQUEST_DEVICE_PROPERTIES_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_device_properties_ie(&msg_ptr, 0, &attach_req->device_properties); + msg_ptr++; + attach_req->device_properties_present = true; + }else{ + attach_req->device_properties_present = false; + } + + // Old GUTI Type + if((LIBLTE_MME_GUTI_TYPE_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_guti_type_ie(&msg_ptr, 0, &attach_req->old_guti_type); + msg_ptr++; + attach_req->old_guti_type_present = true; + }else{ + attach_req->old_guti_type_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Authentication Failure + + Description: Sent by the UE to the network to indicate that + authentication of the network has failed. + + Document Reference: 24.301 v10.2.0 Section 8.2.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_failure_msg(LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT *auth_fail, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(auth_fail != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(auth_fail->emm_cause, &msg_ptr); + + // Authentication Failure Parameter + if(auth_fail->auth_fail_param_present) + { + *msg_ptr = LIBLTE_MME_AUTHENTICATION_FAILURE_PARAMETER_IEI; + msg_ptr++; + liblte_mme_pack_authentication_failure_parameter_ie(auth_fail->auth_fail_param, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_failure_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT *auth_fail) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + auth_fail != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &auth_fail->emm_cause); + + // Authentication Failure Parameter + if(LIBLTE_MME_AUTHENTICATION_FAILURE_PARAMETER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_authentication_failure_parameter_ie(&msg_ptr, auth_fail->auth_fail_param); + auth_fail->auth_fail_param_present = true; + }else{ + auth_fail->auth_fail_param_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Authentication Reject + + Description: Sent by the network to the UE to indicate that the + authentication procedure has failed and that the UE + shall abort all activities. + + Document Reference: 24.301 v10.2.0 Section 8.2.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_reject_msg(LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT *auth_reject, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(auth_reject != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT *auth_reject) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + auth_reject != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Authentication Request + + Description: Sent by the network to the UE to initiate + authentication of the UE identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_request_msg(LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT *auth_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(auth_req != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST; + msg_ptr++; + + // NAS Key Set Identifier & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_nas_key_set_id_ie(&auth_req->nas_ksi, 0, &msg_ptr); + msg_ptr++; + + // Authentication Parameter RAND + liblte_mme_pack_authentication_parameter_rand_ie(auth_req->rand, &msg_ptr); + + // Authentication Parameter AUTN + liblte_mme_pack_authentication_parameter_autn_ie(auth_req->autn, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT *auth_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + auth_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // NAS Key Set Identifier & Spare Half Octet + liblte_mme_unpack_nas_key_set_id_ie(&msg_ptr, 0, &auth_req->nas_ksi); + msg_ptr++; + + // Authentication Parameter RAND + liblte_mme_unpack_authentication_parameter_rand_ie(&msg_ptr, auth_req->rand); + + // Authentication Parameter AUTN + liblte_mme_unpack_authentication_parameter_autn_ie(&msg_ptr, auth_req->autn); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Authentication Response + + Description: Sent by the UE to the network to deliver a calculated + authentication response to the network. + + Document Reference: 24.301 v10.2.0 Section 8.2.8 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_authentication_response_msg(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(auth_resp != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE; + msg_ptr++; + + // Authentication Response Parameter (RES) + liblte_mme_pack_authentication_response_parameter_ie(auth_resp->res, auth_resp->res_len, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_authentication_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + auth_resp != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Authentication Response Parameter (RES) + liblte_mme_unpack_authentication_response_parameter_ie(&msg_ptr, auth_resp->res); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: CS Service Notification + + Description: Sent by the network when a paging request with CS + call indicator was received via SGs for a UE, and a + NAS signalling connection is already established for + the UE. + + Document Reference: 24.301 v10.2.0 Section 8.2.9 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Detach Accept + + Description: Sent by the network to indicate that the detach + procedure has been completed. + + Document Reference: 24.301 v10.2.0 Section 8.2.10 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_accept_msg(LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT *detach_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(detach_accept != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DETACH_ACCEPT; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT *detach_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + detach_accept != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Detach Request + + Description: Sent by the UE to request the release of an EMM + context. + + Document Reference: 24.301 v10.2.0 Section 8.2.11 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_detach_request_msg(LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT *detach_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(detach_req != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DETACH_REQUEST; + msg_ptr++; + + // Detach Type & NAS Key Set Identifier + *msg_ptr = 0; + liblte_mme_pack_detach_type_ie(&detach_req->detach_type, 0, &msg_ptr); + liblte_mme_pack_nas_key_set_id_ie(&detach_req->nas_ksi, 4, &msg_ptr); + msg_ptr++; + + // EPS Mobile ID + liblte_mme_pack_eps_mobile_id_ie(&detach_req->eps_mobile_id, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_detach_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT *detach_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + detach_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Detach Type & NAS Key Set Identifier + liblte_mme_unpack_detach_type_ie(&msg_ptr, 0, &detach_req->detach_type); + liblte_mme_unpack_nas_key_set_id_ie(&msg_ptr, 4, &detach_req->nas_ksi); + msg_ptr++; + + // EPS Mobile ID + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &detach_req->eps_mobile_id); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Downlink NAS Transport + + Description: Sent by the network to the UE in order to carry an + SMS message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_nas_transport_msg(LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT *dl_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(dl_nas_transport != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DOWNLINK_NAS_TRANSPORT; + msg_ptr++; + + // NAS Message Container + liblte_mme_pack_nas_message_container_ie(&dl_nas_transport->nas_msg, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DOWNLINK_NAS_TRANSPORT_MSG_STRUCT *dl_nas_transport) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + dl_nas_transport != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // NAS Message Container + liblte_mme_unpack_nas_message_container_ie(&msg_ptr, &dl_nas_transport->nas_msg); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: EMM Information + + Description: Sent by the network at any time during EMM context is + established to send certain information to the UE. + + Document Reference: 24.301 v10.2.0 Section 8.2.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_information_msg(LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT *emm_info, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(emm_info != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_EMM_INFORMATION; + msg_ptr++; + + // Full Name For Network + if(emm_info->full_net_name_present) + { + *msg_ptr = LIBLTE_MME_FULL_NAME_FOR_NETWORK_IEI; + msg_ptr++; + liblte_mme_pack_network_name_ie(&emm_info->full_net_name, &msg_ptr); + } + + // Short Name For Network + if(emm_info->short_net_name_present) + { + *msg_ptr = LIBLTE_MME_SHORT_NAME_FOR_NETWORK_IEI; + msg_ptr++; + liblte_mme_pack_network_name_ie(&emm_info->short_net_name, &msg_ptr); + } + + // Local Time Zone + if(emm_info->local_time_zone_present) + { + *msg_ptr = LIBLTE_MME_LOCAL_TIME_ZONE_IEI; + msg_ptr++; + liblte_mme_pack_time_zone_ie(emm_info->local_time_zone, &msg_ptr); + } + + // Universal Time And Local Time Zone + if(emm_info->utc_and_local_time_zone_present) + { + *msg_ptr = LIBLTE_MME_UNIVERSAL_TIME_AND_LOCAL_TIME_ZONE_IEI; + msg_ptr++; + liblte_mme_pack_time_zone_and_time_ie(&emm_info->utc_and_local_time_zone, &msg_ptr); + } + + // Network Daylight Saving Time + if(emm_info->net_dst_present) + { + *msg_ptr = LIBLTE_MME_NETWORK_DAYLIGHT_SAVING_TIME_IEI; + msg_ptr++; + liblte_mme_pack_daylight_saving_time_ie(emm_info->net_dst, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_information_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT *emm_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + emm_info != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Full Name For Network + if(LIBLTE_MME_FULL_NAME_FOR_NETWORK_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->full_net_name); + emm_info->full_net_name_present = true; + }else{ + emm_info->full_net_name_present = false; + } + + // Short Name For Network + if(LIBLTE_MME_SHORT_NAME_FOR_NETWORK_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->short_net_name); + emm_info->short_net_name_present = true; + }else{ + emm_info->short_net_name_present = false; + } + + // Local Time Zone + if(LIBLTE_MME_LOCAL_TIME_ZONE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_time_zone_ie(&msg_ptr, &emm_info->local_time_zone); + emm_info->local_time_zone_present = true; + }else{ + emm_info->local_time_zone_present = false; + } + + // Universal Time And Local Time Zone + if(LIBLTE_MME_UNIVERSAL_TIME_AND_LOCAL_TIME_ZONE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_time_zone_and_time_ie(&msg_ptr, &emm_info->utc_and_local_time_zone); + emm_info->utc_and_local_time_zone_present = true; + }else{ + emm_info->utc_and_local_time_zone_present = false; + } + + // Network Daylight Saving Time + if(LIBLTE_MME_NETWORK_DAYLIGHT_SAVING_TIME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_daylight_saving_time_ie(&msg_ptr, &emm_info->net_dst); + emm_info->net_dst_present = true; + }else{ + emm_info->net_dst_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: EMM Status + + Description: Sent by the UE or by the network at any time to + report certain error conditions. + + Document Reference: 24.301 v10.2.0 Section 8.2.14 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_emm_status_msg(LIBLTE_MME_EMM_STATUS_MSG_STRUCT *emm_status, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(emm_status != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_EMM_STATUS; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(emm_status->emm_cause, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_status_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EMM_STATUS_MSG_STRUCT *emm_status) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + emm_status != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &emm_status->emm_cause); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Extended Service Request + + Description: Sent by the UE to the network to initiate a CS + fallback or 1xCS fallback call or respond to a mobile + terminated CS fallback or 1xCS fallback request from + the network or to request the establishment of a NAS + signalling connection and of the radio and S1 bearers + for packet services, if the UE needs to provide + additional information that cannot be provided via a + SERVICE REQUEST message. + + Document Reference: 24.301 v10.2.0 Section 8.2.15 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_extended_service_request_msg(LIBLTE_MME_EXTENDED_SERVICE_REQUEST_MSG_STRUCT *ext_service_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ext_service_req != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_EXTENDED_SERVICE_REQUEST; + msg_ptr++; + + // Service Type & NAS Key Set Identifier + *msg_ptr = 0; + liblte_mme_pack_service_type_ie(ext_service_req->service_type, 0, &msg_ptr); + liblte_mme_pack_nas_key_set_id_ie(&ext_service_req->nas_ksi, 4, &msg_ptr); + msg_ptr++; + + // M-TMSI + liblte_mme_pack_mobile_id_ie(&ext_service_req->m_tmsi, &msg_ptr); + + // CSFB Response + if(ext_service_req->csfb_resp_present) + { + *msg_ptr = LIBLTE_MME_CSFB_RESPONSE_IEI << 4; + liblte_mme_pack_csfb_response_ie(ext_service_req->csfb_resp, 0, &msg_ptr); + msg_ptr++; + } + + // EPS Bearer Context Status + if(ext_service_req->eps_bearer_context_status_present) + { + *msg_ptr = LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_IEI; + msg_ptr++; + liblte_mme_pack_eps_bearer_context_status_ie(&ext_service_req->eps_bearer_context_status, &msg_ptr); + } + + // Device Properties + if(ext_service_req->device_props_present) + { + *msg_ptr = LIBLTE_MME_EXTENDED_SERVICE_REQUEST_DEVICE_PROPERTIES_IEI << 4; + liblte_mme_pack_device_properties_ie(ext_service_req->device_props, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_extended_service_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_EXTENDED_SERVICE_REQUEST_MSG_STRUCT *ext_service_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ext_service_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Service Type & NAS Key Set Identifier + liblte_mme_unpack_service_type_ie(&msg_ptr, 0, &ext_service_req->service_type); + liblte_mme_unpack_nas_key_set_id_ie(&msg_ptr, 4, &ext_service_req->nas_ksi); + msg_ptr++; + + // M-TMSI + liblte_mme_unpack_mobile_id_ie(&msg_ptr, &ext_service_req->m_tmsi); + + // CSFB Response + if((LIBLTE_MME_CSFB_RESPONSE_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_csfb_response_ie(&msg_ptr, 0, &ext_service_req->csfb_resp); + msg_ptr++; + ext_service_req->csfb_resp_present = true; + }else{ + ext_service_req->csfb_resp_present = false; + } + + // EPS Bearer Context Status + if(LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_bearer_context_status_ie(&msg_ptr, &ext_service_req->eps_bearer_context_status); + ext_service_req->eps_bearer_context_status_present = true; + }else{ + ext_service_req->eps_bearer_context_status_present = false; + } + + // Device Properties + if((LIBLTE_MME_EXTENDED_SERVICE_REQUEST_DEVICE_PROPERTIES_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_device_properties_ie(&msg_ptr, 0, &ext_service_req->device_props); + msg_ptr++; + ext_service_req->device_props_present = true; + }else{ + ext_service_req->device_props_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: GUTI Reallocation Command + + Description: Sent by the network to the UE to reallocate a GUTI + and optionally provide a new TAI list. + + Document Reference: 24.301 v10.2.0 Section 8.2.16 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_reallocation_command_msg(LIBLTE_MME_GUTI_REALLOCATION_COMMAND_MSG_STRUCT *guti_realloc_cmd, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(guti_realloc_cmd != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMMAND; + msg_ptr++; + + // GUTI + liblte_mme_pack_eps_mobile_id_ie(&guti_realloc_cmd->guti, &msg_ptr); + + // TAI List + if(guti_realloc_cmd->tai_list_present) + { + *msg_ptr = LIBLTE_MME_TAI_LIST_IEI; + msg_ptr++; + liblte_mme_pack_tracking_area_identity_list_ie(&guti_realloc_cmd->tai_list, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_reallocation_command_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_GUTI_REALLOCATION_COMMAND_MSG_STRUCT *guti_realloc_cmd) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + guti_realloc_cmd != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // GUTI + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &guti_realloc_cmd->guti); + + // TAI List + if(LIBLTE_MME_TAI_LIST_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_tracking_area_identity_list_ie(&msg_ptr, &guti_realloc_cmd->tai_list); + guti_realloc_cmd->tai_list_present = true; + }else{ + guti_realloc_cmd->tai_list_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: GUTI Reallocation Complete + + Description: Sent by the UE to the network to indicate that + reallocation of a GUTI has taken place. + + Document Reference: 24.301 v10.2.0 Section 8.2.17 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_guti_reallocation_complete_msg(LIBLTE_MME_GUTI_REALLOCATION_COMPLETE_MSG_STRUCT *guti_realloc_complete, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(guti_realloc_complete != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMPLETE; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_guti_reallocation_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_GUTI_REALLOCATION_COMPLETE_MSG_STRUCT *guti_realloc_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + guti_realloc_complete != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Identity Request + + Description: Sent by the network to the UE to request the UE to + provide the specified identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.18 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_request_msg(LIBLTE_MME_ID_REQUEST_MSG_STRUCT *id_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(id_req != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST; + msg_ptr++; + + // ID Type & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_identity_type_2_ie(id_req->id_type, 0, &msg_ptr); + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ID_REQUEST_MSG_STRUCT *id_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + id_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // ID Type & Spare Half Offset + liblte_mme_unpack_identity_type_2_ie(&msg_ptr, 0, &id_req->id_type); + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +/********************************************************************* + Message Name: Identity Response + + Description: Sent by the UE to the network in response to an + IDENTITY REQUEST message and provides the requested + identity. + + Document Reference: 24.301 v10.2.0 Section 8.2.19 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_identity_response_msg(LIBLTE_MME_ID_RESPONSE_MSG_STRUCT *id_resp, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(id_resp != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE; + msg_ptr++; + + // Mobile Identity + liblte_mme_pack_mobile_id_ie(&id_resp->mobile_id, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT *id_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + id_resp != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Mobile Identity + liblte_mme_unpack_mobile_id_ie(&msg_ptr, &id_resp->mobile_id); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Command + + Description: Sent by the network to the UE to establish NAS + signalling security. + + Document Reference: 24.301 v10.2.0 Section 8.2.20 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_command_msg(LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT *sec_mode_cmd, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(sec_mode_cmd != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND; + msg_ptr++; + + // Selected NAS Security Algorithms + liblte_mme_pack_nas_security_algorithms_ie(&sec_mode_cmd->selected_nas_sec_algs, &msg_ptr); + + // NAS Key Set Identifier & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_nas_key_set_id_ie(&sec_mode_cmd->nas_ksi, 0, &msg_ptr); + msg_ptr++; + + // Replayed UE Security Capabilities + liblte_mme_pack_ue_security_capabilities_ie(&sec_mode_cmd->ue_security_cap, &msg_ptr); + + // IMEISV Request + if(sec_mode_cmd->imeisv_req_present) + { + *msg_ptr = LIBLTE_MME_IMEISV_REQUEST_IEI << 4; + liblte_mme_pack_imeisv_request_ie(sec_mode_cmd->imeisv_req, 0, &msg_ptr); + msg_ptr++; + } + + // Replayed NONCE_ue + if(sec_mode_cmd->nonce_ue_present) + { + *msg_ptr = LIBLTE_MME_REPLAYED_NONCE_UE_IEI; + msg_ptr++; + liblte_mme_pack_nonce_ie(sec_mode_cmd->nonce_ue, &msg_ptr); + } + + // NONCE_mme + if(sec_mode_cmd->nonce_mme_present) + { + *msg_ptr = LIBLTE_MME_NONCE_MME_IEI; + msg_ptr++; + liblte_mme_pack_nonce_ie(sec_mode_cmd->nonce_mme, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_command_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT *sec_mode_cmd) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + sec_mode_cmd != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Selected NAS Security Algorithms + liblte_mme_unpack_nas_security_algorithms_ie(&msg_ptr, &sec_mode_cmd->selected_nas_sec_algs); + + // NAS Key Set Identifier & Spare Half Octet + liblte_mme_unpack_nas_key_set_id_ie(&msg_ptr, 0, &sec_mode_cmd->nas_ksi); + msg_ptr++; + + // Replayed UE Security Capabilities + liblte_mme_unpack_ue_security_capabilities_ie(&msg_ptr, &sec_mode_cmd->ue_security_cap); + + // IMEISV Request + if((LIBLTE_MME_IMEISV_REQUEST_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_imeisv_request_ie(&msg_ptr, 0, &sec_mode_cmd->imeisv_req); + msg_ptr++; + sec_mode_cmd->imeisv_req_present = true; + }else{ + sec_mode_cmd->imeisv_req_present = false; + } + + // Replayed NONCE_ue + if(LIBLTE_MME_REPLAYED_NONCE_UE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_nonce_ie(&msg_ptr, &sec_mode_cmd->nonce_ue); + sec_mode_cmd->nonce_ue_present = true; + }else{ + sec_mode_cmd->nonce_ue_present = false; + } + + // NONCE_mme + if(LIBLTE_MME_NONCE_MME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_nonce_ie(&msg_ptr, &sec_mode_cmd->nonce_mme); + sec_mode_cmd->nonce_mme_present = true; + }else{ + sec_mode_cmd->nonce_mme_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Complete + + Description: Sent by the UE to the network in response to a + SECURITY MODE COMMAND message. + + Document Reference: 24.301 v10.2.0 Section 8.2.21 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_complete_msg(LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT *sec_mode_comp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(sec_mode_comp != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE; + msg_ptr++; + + // IMEISV + if(sec_mode_comp->imeisv_present) + { + *msg_ptr = LIBLTE_MME_IMEISV_IEI; + msg_ptr++; + liblte_mme_pack_mobile_id_ie(&sec_mode_comp->imeisv, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT *sec_mode_comp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + sec_mode_comp != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // IMEISV + if(LIBLTE_MME_IMEISV_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_mobile_id_ie(&msg_ptr, &sec_mode_comp->imeisv); + sec_mode_comp->imeisv_present = true; + }else{ + sec_mode_comp->imeisv_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Reject + + Description: Sent by the UE to the network to indicate that the + corresponding security mode command has been + rejected. + + Document Reference: 24.301 v10.2.0 Section 8.2.22 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_security_mode_reject_msg(LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT *sec_mode_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(sec_mode_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_SECURITY_MODE_REJECT; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(sec_mode_rej->emm_cause, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_security_mode_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT *sec_mode_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + sec_mode_rej != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &sec_mode_rej->emm_cause); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Service Reject + + Description: Sent by the network to the UE in order to reject the + service request procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.24 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_service_reject_msg(LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT *service_rej, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(service_rej != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_SERVICE_REJECT; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(service_rej->emm_cause, &msg_ptr); + + // T3442 Value + if(service_rej->t3442_present) + { + *msg_ptr = LIBLTE_MME_T3442_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&service_rej->t3442, &msg_ptr); + } + + // T3446 Value + if(service_rej->t3446_present) + { + *msg_ptr = LIBLTE_MME_T3446_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_2_ie(service_rej->t3446, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT *service_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + service_rej != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &service_rej->emm_cause); + + // T3442 Value + if(LIBLTE_MME_T3442_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &service_rej->t3442); + service_rej->t3442_present = true; + }else{ + service_rej->t3442_present = false; + } + + // T3446 Value + if(LIBLTE_MME_T3446_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_2_ie(&msg_ptr, &service_rej->t3446); + service_rej->t3446_present = true; + }else{ + service_rej->t3446_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Service Request + + Description: Sent by the UE to the network to request the + establishment of a NAS signalling connection and of + the radio and S1 bearers. + + Document Reference: 24.301 v10.2.0 Section 8.2.25 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_service_request_msg(LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT *service_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(service_req != NULL && + msg != NULL) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // KSI and Sequence Number + liblte_mme_pack_ksi_and_sequence_number_ie(&service_req->ksi_and_seq_num, &msg_ptr); + + // Short MAC + liblte_mme_pack_short_mac_ie(service_req->short_mac, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_service_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT *service_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + service_req != NULL) + { + // Protocol Discriminator and Security Header Type + msg_ptr++; + + // KSI and Sequence Number + liblte_mme_unpack_ksi_and_sequence_number_ie(&msg_ptr, &service_req->ksi_and_seq_num); + + // Short MAC + liblte_mme_unpack_short_mac_ie(&msg_ptr, &service_req->short_mac); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Tracking Area Update Accept + + Description: Sent by the network to the UE to provide the UE with + EPS mobility management related data in response to + a tracking area update request message. + + Document Reference: 24.301 v10.2.0 Section 8.2.26 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_accept_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT *ta_update_accept, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ta_update_accept != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_ACCEPT; + msg_ptr++; + + // EPS Update Result & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_eps_update_result_ie(ta_update_accept->eps_update_result, 0, &msg_ptr); + msg_ptr++; + + // T3412 Value + if(ta_update_accept->t3412_present) + { + *msg_ptr = LIBLTE_MME_T3412_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&ta_update_accept->t3412, &msg_ptr); + } + + // GUTI + if(ta_update_accept->guti_present) + { + *msg_ptr = LIBLTE_MME_GUTI_IEI; + msg_ptr++; + liblte_mme_pack_eps_mobile_id_ie(&ta_update_accept->guti, &msg_ptr); + } + + // TAI List + if(ta_update_accept->tai_list_present) + { + *msg_ptr = LIBLTE_MME_TAI_LIST_IEI; + msg_ptr++; + liblte_mme_pack_tracking_area_identity_list_ie(&ta_update_accept->tai_list, &msg_ptr); + } + + // EPS Bearer Context Status + if(ta_update_accept->eps_bearer_context_status_present) + { + *msg_ptr = LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_IEI; + msg_ptr++; + liblte_mme_pack_eps_bearer_context_status_ie(&ta_update_accept->eps_bearer_context_status, &msg_ptr); + } + + // Location Area Identification + if(ta_update_accept->lai_present) + { + *msg_ptr = LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI; + msg_ptr++; + liblte_mme_pack_location_area_id_ie(&ta_update_accept->lai, &msg_ptr); + } + + // MS Identity + if(ta_update_accept->ms_id_present) + { + *msg_ptr = LIBLTE_MME_MS_IDENTITY_IEI; + msg_ptr++; + liblte_mme_pack_mobile_id_ie(&ta_update_accept->ms_id, &msg_ptr); + } + + // EMM Cause + if(ta_update_accept->emm_cause_present) + { + *msg_ptr = LIBLTE_MME_EMM_CAUSE_IEI; + msg_ptr++; + liblte_mme_pack_emm_cause_ie(ta_update_accept->emm_cause, &msg_ptr); + } + + // T3402 Value + if(ta_update_accept->t3402_present) + { + *msg_ptr = LIBLTE_MME_T3402_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&ta_update_accept->t3402, &msg_ptr); + } + + // T3423 Value + if(ta_update_accept->t3423_present) + { + *msg_ptr = LIBLTE_MME_T3423_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_ie(&ta_update_accept->t3423, &msg_ptr); + } + + // Equivalent PLMNs + if(ta_update_accept->equivalent_plmns_present) + { + *msg_ptr = LIBLTE_MME_EQUIVALENT_PLMNS_IEI; + msg_ptr++; + liblte_mme_pack_plmn_list_ie(&ta_update_accept->equivalent_plmns, &msg_ptr); + } + + // Emergency Number List + if(ta_update_accept->emerg_num_list_present) + { + *msg_ptr = LIBLTE_MME_EMERGENCY_NUMBER_LIST_IEI; + msg_ptr++; + liblte_mme_pack_emergency_number_list_ie(&ta_update_accept->emerg_num_list, &msg_ptr); + } + + // EPS Network Feature Support + if(ta_update_accept->eps_network_feature_support_present) + { + *msg_ptr = LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI; + msg_ptr++; + liblte_mme_pack_eps_network_feature_support_ie(&ta_update_accept->eps_network_feature_support, &msg_ptr); + } + + // Additional Update Result + if(ta_update_accept->additional_update_result_present) + { + *msg_ptr = LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI << 4; + liblte_mme_pack_additional_update_result_ie(ta_update_accept->additional_update_result, 0, &msg_ptr); + msg_ptr++; + } + + // T3412 Extended Value + if(ta_update_accept->t3412_ext_present) + { + *msg_ptr = LIBLTE_MME_T3412_EXTENDED_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_3_ie(&ta_update_accept->t3412_ext, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT *ta_update_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ta_update_accept != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EPS Update Result & Spare Half Octet + liblte_mme_unpack_eps_update_result_ie(&msg_ptr, 0, &ta_update_accept->eps_update_result); + msg_ptr++; + + // T3412 Value + if(LIBLTE_MME_T3412_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &ta_update_accept->t3412); + ta_update_accept->t3412_present = true; + }else{ + ta_update_accept->t3412_present = false; + } + + // GUTI + if(LIBLTE_MME_GUTI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_mobile_id_ie(&msg_ptr, &ta_update_accept->guti); + ta_update_accept->guti_present = true; + }else{ + ta_update_accept->guti_present = false; + } + + // TAI List + if(LIBLTE_MME_TAI_LIST_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_tracking_area_identity_list_ie(&msg_ptr, &ta_update_accept->tai_list); + ta_update_accept->tai_list_present = true; + }else{ + ta_update_accept->tai_list_present = false; + } + + // EPS Bearer Context Status + if(LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_bearer_context_status_ie(&msg_ptr, &ta_update_accept->eps_bearer_context_status); + ta_update_accept->eps_bearer_context_status_present = true; + }else{ + ta_update_accept->eps_bearer_context_status_present = false; + } + + // Location Area Identification + if(LIBLTE_MME_LOCATION_AREA_IDENTIFICATION_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_location_area_id_ie(&msg_ptr, &ta_update_accept->lai); + ta_update_accept->lai_present = true; + }else{ + ta_update_accept->lai_present = false; + } + + // MS Identity + if(LIBLTE_MME_MS_IDENTITY_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_mobile_id_ie(&msg_ptr, &ta_update_accept->ms_id); + ta_update_accept->ms_id_present = true; + }else{ + ta_update_accept->ms_id_present = false; + } + + // EMM Cause + if(LIBLTE_MME_EMM_CAUSE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &ta_update_accept->emm_cause); + ta_update_accept->emm_cause_present = true; + }else{ + ta_update_accept->emm_cause_present = false; + } + + // T3402 Value + if(LIBLTE_MME_T3402_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &ta_update_accept->t3402); + ta_update_accept->t3402_present = true; + }else{ + ta_update_accept->t3402_present = false; + } + + // T3423 Value + if(LIBLTE_MME_T3423_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_ie(&msg_ptr, &ta_update_accept->t3423); + ta_update_accept->t3423_present = true; + }else{ + ta_update_accept->t3423_present = false; + } + + // Equivalent PLMNs + if(LIBLTE_MME_EQUIVALENT_PLMNS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_plmn_list_ie(&msg_ptr, &ta_update_accept->equivalent_plmns); + ta_update_accept->equivalent_plmns_present = true; + }else{ + ta_update_accept->equivalent_plmns_present = false; + } + + // Emergency Number List + if(LIBLTE_MME_EMERGENCY_NUMBER_LIST_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_emergency_number_list_ie(&msg_ptr, &ta_update_accept->emerg_num_list); + ta_update_accept->emerg_num_list_present = true; + }else{ + ta_update_accept->emerg_num_list_present = false; + } + + // EPS Network Feature Support + if(LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_network_feature_support_ie(&msg_ptr, &ta_update_accept->eps_network_feature_support); + ta_update_accept->eps_network_feature_support_present = true; + }else{ + ta_update_accept->eps_network_feature_support_present = false; + } + + // Additional Update Result + if((LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_additional_update_result_ie(&msg_ptr, 0, &ta_update_accept->additional_update_result); + msg_ptr++; + ta_update_accept->additional_update_result_present = true; + }else{ + ta_update_accept->additional_update_result_present = false; + } + + // T3412 Extended Value + if(LIBLTE_MME_T3412_EXTENDED_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_3_ie(&msg_ptr, &ta_update_accept->t3412_ext); + ta_update_accept->t3412_ext_present = true; + }else{ + ta_update_accept->t3412_ext_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Tracking Area Update Complete + + Description: Sent by the UE to the network in response to a + tracking area update accept message if a GUTI has + been changed or a new TMSI has been assigned. + + Document Reference: 24.301 v10.2.0 Section 8.2.27 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_complete_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_COMPLETE_MSG_STRUCT *ta_update_complete, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ta_update_complete != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_COMPLETE; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_complete_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_COMPLETE_MSG_STRUCT *ta_update_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ta_update_complete != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Tracking Area Update Reject + + Description: Sent by the network to the UE in order to reject the + tracking area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.28 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_tracking_area_update_reject_msg(LIBLTE_MME_TRACKING_AREA_UPDATE_REJECT_MSG_STRUCT *ta_update_rej, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ta_update_rej != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REJECT; + msg_ptr++; + + // EMM Cause + liblte_mme_pack_emm_cause_ie(ta_update_rej->emm_cause, &msg_ptr); + + // T3446 Value + if(ta_update_rej->t3446_present) + { + *msg_ptr = LIBLTE_MME_T3446_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_2_ie(ta_update_rej->t3446, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_tracking_area_update_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_TRACKING_AREA_UPDATE_REJECT_MSG_STRUCT *ta_update_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ta_update_rej != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // EMM Cause + liblte_mme_unpack_emm_cause_ie(&msg_ptr, &ta_update_rej->emm_cause); + + // T3446 Value + if(LIBLTE_MME_T3446_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_2_ie(&msg_ptr, &ta_update_rej->t3446); + ta_update_rej->t3446_present = true; + }else{ + ta_update_rej->t3446_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Tracking Area Update Request + + Description: Sent by the UE to the network to initiate a tracking + area updating procedure. + + Document Reference: 24.301 v10.2.0 Section 8.2.29 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Uplink NAS Transport + + Description: Sent by the UE to the network in order to carry an + SMS message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.30 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_uplink_nas_transport_msg(LIBLTE_MME_UPLINK_NAS_TRANSPORT_MSG_STRUCT *ul_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ul_nas_transport != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_UPLINK_NAS_TRANSPORT; + msg_ptr++; + + // NAS Message Container + liblte_mme_pack_nas_message_container_ie(&ul_nas_transport->nas_msg, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_uplink_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_UPLINK_NAS_TRANSPORT_MSG_STRUCT *ul_nas_transport) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ul_nas_transport != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // NAS Message Container + liblte_mme_unpack_nas_message_container_ie(&msg_ptr, &ul_nas_transport->nas_msg); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Downlink Generic NAS Transport + + Description: Sent by the network to the UE in order to carry an + application message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.31 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_generic_nas_transport_msg(LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *dl_generic_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(dl_generic_nas_transport != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DOWNLINK_GENERIC_NAS_TRANSPORT; + msg_ptr++; + + // Generic Message Container Type + liblte_mme_pack_generic_message_container_type_ie(dl_generic_nas_transport->generic_msg_cont_type, &msg_ptr); + + // Generic Message Container + liblte_mme_pack_generic_message_container_ie(&dl_generic_nas_transport->generic_msg_cont, &msg_ptr); + + // Additional Information + liblte_mme_pack_additional_information_ie(&dl_generic_nas_transport->add_info, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_generic_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *dl_generic_nas_transport) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + dl_generic_nas_transport != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Generic Message Container Type + liblte_mme_unpack_generic_message_container_type_ie(&msg_ptr, &dl_generic_nas_transport->generic_msg_cont_type); + + // Generic Message Container + liblte_mme_unpack_generic_message_container_ie(&msg_ptr, &dl_generic_nas_transport->generic_msg_cont); + + // Additional Information + liblte_mme_unpack_additional_information_ie(&msg_ptr, &dl_generic_nas_transport->add_info); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Uplink Generic NAS Transport + + Description: Sent by the UE to the network in order to carry an + application protocol message in encapsulated format. + + Document Reference: 24.301 v10.2.0 Section 8.2.32 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_uplink_generic_nas_transport_msg(LIBLTE_MME_UPLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *ul_generic_nas_transport, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(ul_generic_nas_transport != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and Security Header Type + *msg_ptr = (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_UPLINK_GENERIC_NAS_TRANSPORT; + msg_ptr++; + + // Generic Message Container Type + liblte_mme_pack_generic_message_container_type_ie(ul_generic_nas_transport->generic_msg_cont_type, &msg_ptr); + + // Generic Message Container + liblte_mme_pack_generic_message_container_ie(&ul_generic_nas_transport->generic_msg_cont, &msg_ptr); + + // Additional Information + liblte_mme_pack_additional_information_ie(&ul_generic_nas_transport->add_info, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_uplink_generic_nas_transport_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_UPLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT *ul_generic_nas_transport) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + ul_generic_nas_transport != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Generic Message Container Type + liblte_mme_unpack_generic_message_container_type_ie(&msg_ptr, &ul_generic_nas_transport->generic_msg_cont_type); + + // Generic Message Container + liblte_mme_unpack_generic_message_container_ie(&msg_ptr, &ul_generic_nas_transport->generic_msg_cont); + + // Additional Information + liblte_mme_unpack_additional_information_ie(&msg_ptr, &ul_generic_nas_transport->add_info); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge + activation of a dedicated EPS bearer context + associated with the same PDN address(es) and APN as + an already active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_accept_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_ded_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_ded_eps_bearer_context_accept != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_ded_eps_bearer_context_accept->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_ded_eps_bearer_context_accept->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT; + msg_ptr++; + + // Protocol Configuration Options + if(act_ded_eps_bearer_context_accept->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_ded_eps_bearer_context_accept->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_ded_eps_bearer_context_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_ded_eps_bearer_context_accept != NULL) + { + // EPS Bearer ID + act_ded_eps_bearer_context_accept->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_ded_eps_bearer_context_accept->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_ded_eps_bearer_context_accept->protocol_cnfg_opts); + act_ded_eps_bearer_context_accept->protocol_cnfg_opts_present = true; + }else{ + act_ded_eps_bearer_context_accept->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Reject + + Description: Sent by the UE to the network to reject activation + of a dedicated EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_reject_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_ded_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_ded_eps_bearer_context_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_ded_eps_bearer_context_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_ded_eps_bearer_context_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(act_ded_eps_bearer_context_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(act_ded_eps_bearer_context_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_ded_eps_bearer_context_rej->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_ded_eps_bearer_context_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_ded_eps_bearer_context_rej != NULL) + { + // EPS Bearer ID + act_ded_eps_bearer_context_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_ded_eps_bearer_context_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &act_ded_eps_bearer_context_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_ded_eps_bearer_context_rej->protocol_cnfg_opts); + act_ded_eps_bearer_context_rej->protocol_cnfg_opts_present = true; + }else{ + act_ded_eps_bearer_context_rej->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Dedicated EPS Bearer Context Request + + Description: Sent by the network to the UE to request activation + of a dedicated EPS bearer context associated with + the same PDN address(es) and APN as an already + active default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_dedicated_eps_bearer_context_request_msg(LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_ded_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_ded_eps_bearer_context_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_ded_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_ded_eps_bearer_context_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST; + msg_ptr++; + + // Linked EPS Bearer Identity & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_linked_eps_bearer_identity_ie(act_ded_eps_bearer_context_req->linked_eps_bearer_id, 0, &msg_ptr); + msg_ptr++; + + // EPS QoS + liblte_mme_pack_eps_quality_of_service_ie(&act_ded_eps_bearer_context_req->eps_qos, &msg_ptr); + + // TFT + liblte_mme_pack_traffic_flow_template_ie(&act_ded_eps_bearer_context_req->tft, &msg_ptr); + + // Transaction Identifier + if(act_ded_eps_bearer_context_req->transaction_id_present) + { + *msg_ptr = LIBLTE_MME_TRANSACTION_IDENTIFIER_IEI; + msg_ptr++; + liblte_mme_pack_transaction_identifier_ie(&act_ded_eps_bearer_context_req->transaction_id, &msg_ptr); + } + + // Negotiated QoS + if(act_ded_eps_bearer_context_req->negotiated_qos_present) + { + *msg_ptr = LIBLTE_MME_QUALITY_OF_SERVICE_IEI; + msg_ptr++; + liblte_mme_pack_quality_of_service_ie(&act_ded_eps_bearer_context_req->negotiated_qos, &msg_ptr); + } + + // Negotiated LLC SAPI + if(act_ded_eps_bearer_context_req->llc_sapi_present) + { + *msg_ptr = LIBLTE_MME_LLC_SAPI_IEI; + msg_ptr++; + liblte_mme_pack_llc_service_access_point_identifier_ie(act_ded_eps_bearer_context_req->llc_sapi, &msg_ptr); + } + + // Radio Priority + if(act_ded_eps_bearer_context_req->radio_prio_present) + { + *msg_ptr = LIBLTE_MME_RADIO_PRIORITY_IEI << 4; + liblte_mme_pack_radio_priority_ie(act_ded_eps_bearer_context_req->radio_prio, 0, &msg_ptr); + msg_ptr++; + } + + // Packet Flow Identifier + if(act_ded_eps_bearer_context_req->packet_flow_id_present) + { + *msg_ptr = LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI; + msg_ptr++; + liblte_mme_pack_packet_flow_identifier_ie(act_ded_eps_bearer_context_req->packet_flow_id, &msg_ptr); + } + + // Protocol Configuration Options + if(act_ded_eps_bearer_context_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_ded_eps_bearer_context_req->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_dedicated_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_ded_eps_bearer_context_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_ded_eps_bearer_context_req != NULL) + { + // EPS Bearer ID + act_ded_eps_bearer_context_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_ded_eps_bearer_context_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Linked Bearer Identity & Spare Half Octet + liblte_mme_unpack_linked_eps_bearer_identity_ie(&msg_ptr, 0, &act_ded_eps_bearer_context_req->linked_eps_bearer_id); + msg_ptr++; + + // EPS QoS + liblte_mme_unpack_eps_quality_of_service_ie(&msg_ptr, &act_ded_eps_bearer_context_req->eps_qos); + + // TFT + liblte_mme_unpack_traffic_flow_template_ie(&msg_ptr, &act_ded_eps_bearer_context_req->tft); + + // Transaction Identifier + if(LIBLTE_MME_TRANSACTION_IDENTIFIER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_transaction_identifier_ie(&msg_ptr, &act_ded_eps_bearer_context_req->transaction_id); + act_ded_eps_bearer_context_req->transaction_id_present = true; + }else{ + act_ded_eps_bearer_context_req->transaction_id_present = false; + } + + // Negotiated QoS + if(LIBLTE_MME_QUALITY_OF_SERVICE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_quality_of_service_ie(&msg_ptr, &act_ded_eps_bearer_context_req->negotiated_qos); + act_ded_eps_bearer_context_req->negotiated_qos_present = true; + }else{ + act_ded_eps_bearer_context_req->negotiated_qos_present = false; + } + + // Negotiated LLC SAPI + if(LIBLTE_MME_LLC_SAPI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_llc_service_access_point_identifier_ie(&msg_ptr, &act_ded_eps_bearer_context_req->llc_sapi); + act_ded_eps_bearer_context_req->llc_sapi_present = true; + }else{ + act_ded_eps_bearer_context_req->llc_sapi_present = false; + } + + // Radio Priority + if((LIBLTE_MME_RADIO_PRIORITY_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_radio_priority_ie(&msg_ptr, 0, &act_ded_eps_bearer_context_req->radio_prio); + msg_ptr++; + act_ded_eps_bearer_context_req->radio_prio_present = true; + }else{ + act_ded_eps_bearer_context_req->radio_prio_present = false; + } + + // Packet Flow Identifier + if(LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_packet_flow_identifier_ie(&msg_ptr, &act_ded_eps_bearer_context_req->packet_flow_id); + act_ded_eps_bearer_context_req->packet_flow_id_present = true; + }else{ + act_ded_eps_bearer_context_req->packet_flow_id_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_ded_eps_bearer_context_req->protocol_cnfg_opts); + act_ded_eps_bearer_context_req->protocol_cnfg_opts_present = true; + }else{ + act_ded_eps_bearer_context_req->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge + activation of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_accept_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_def_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_def_eps_bearer_context_accept != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_def_eps_bearer_context_accept->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_def_eps_bearer_context_accept->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT; + msg_ptr++; + + // Protocol Configuration Options + if(act_def_eps_bearer_context_accept->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_def_eps_bearer_context_accept->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *act_def_eps_bearer_context_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_def_eps_bearer_context_accept != NULL) + { + // EPS Bearer ID + act_def_eps_bearer_context_accept->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_def_eps_bearer_context_accept->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_def_eps_bearer_context_accept->protocol_cnfg_opts); + act_def_eps_bearer_context_accept->protocol_cnfg_opts_present = true; + }else{ + act_def_eps_bearer_context_accept->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Reject + + Description: Sent by the UE to the network to reject activation + of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_reject_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_def_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_def_eps_bearer_context_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_def_eps_bearer_context_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_def_eps_bearer_context_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(act_def_eps_bearer_context_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(act_def_eps_bearer_context_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_def_eps_bearer_context_rej->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *act_def_eps_bearer_context_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_def_eps_bearer_context_rej != NULL) + { + // EPS Bearer ID + act_def_eps_bearer_context_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_def_eps_bearer_context_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &act_def_eps_bearer_context_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_def_eps_bearer_context_rej->protocol_cnfg_opts); + act_def_eps_bearer_context_rej->protocol_cnfg_opts_present = true; + }else{ + act_def_eps_bearer_context_rej->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Activate Default EPS Bearer Context Request + + Description: Sent by the network to the UE to request activation + of a default EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_activate_default_eps_bearer_context_request_msg(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_def_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(act_def_eps_bearer_context_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (act_def_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = act_def_eps_bearer_context_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST; + msg_ptr++; + + // EPS QoS + liblte_mme_pack_eps_quality_of_service_ie(&act_def_eps_bearer_context_req->eps_qos, &msg_ptr); + + // Access Point Name + liblte_mme_pack_access_point_name_ie(&act_def_eps_bearer_context_req->apn, &msg_ptr); + + // PDN Address + liblte_mme_pack_pdn_address_ie(&act_def_eps_bearer_context_req->pdn_addr, &msg_ptr); + + // Transaction Identifier + if(act_def_eps_bearer_context_req->transaction_id_present) + { + *msg_ptr = LIBLTE_MME_TRANSACTION_IDENTIFIER_IEI; + msg_ptr++; + liblte_mme_pack_transaction_identifier_ie(&act_def_eps_bearer_context_req->transaction_id, &msg_ptr); + } + + // Negotiated QoS + if(act_def_eps_bearer_context_req->negotiated_qos_present) + { + *msg_ptr = LIBLTE_MME_QUALITY_OF_SERVICE_IEI; + msg_ptr++; + liblte_mme_pack_quality_of_service_ie(&act_def_eps_bearer_context_req->negotiated_qos, &msg_ptr); + } + + // Negotiated LLC SAPI + if(act_def_eps_bearer_context_req->llc_sapi_present) + { + *msg_ptr = LIBLTE_MME_LLC_SAPI_IEI; + msg_ptr++; + liblte_mme_pack_llc_service_access_point_identifier_ie(act_def_eps_bearer_context_req->llc_sapi, &msg_ptr); + } + + // Radio Priority + if(act_def_eps_bearer_context_req->radio_prio_present) + { + *msg_ptr = LIBLTE_MME_RADIO_PRIORITY_IEI << 4; + liblte_mme_pack_radio_priority_ie(act_def_eps_bearer_context_req->radio_prio, 0, &msg_ptr); + msg_ptr++; + } + + // Packet Flow Identifier + if(act_def_eps_bearer_context_req->packet_flow_id_present) + { + *msg_ptr = LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI; + msg_ptr++; + liblte_mme_pack_packet_flow_identifier_ie(act_def_eps_bearer_context_req->packet_flow_id, &msg_ptr); + } + + // APN-AMBR + if(act_def_eps_bearer_context_req->apn_ambr_present) + { + *msg_ptr = LIBLTE_MME_APN_AMBR_IEI; + msg_ptr++; + liblte_mme_pack_apn_aggregate_maximum_bit_rate_ie(&act_def_eps_bearer_context_req->apn_ambr, &msg_ptr); + } + + // ESM Cause + if(act_def_eps_bearer_context_req->esm_cause_present) + { + *msg_ptr = LIBLTE_MME_ESM_CAUSE_IEI; + msg_ptr++; + liblte_mme_pack_esm_cause_ie(act_def_eps_bearer_context_req->esm_cause, &msg_ptr); + } + + // Protocol Configuration Options + if(act_def_eps_bearer_context_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&act_def_eps_bearer_context_req->protocol_cnfg_opts, &msg_ptr); + } + + // Connectivity Type + if(act_def_eps_bearer_context_req->connectivity_type_present) + { + *msg_ptr = LIBLTE_MME_CONNECTIVITY_TYPE_IEI << 4; + liblte_mme_pack_connectivity_type_ie(act_def_eps_bearer_context_req->connectivity_type, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *act_def_eps_bearer_context_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + act_def_eps_bearer_context_req != NULL) + { + // EPS Bearer ID + act_def_eps_bearer_context_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + act_def_eps_bearer_context_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // EPS QoS + liblte_mme_unpack_eps_quality_of_service_ie(&msg_ptr, &act_def_eps_bearer_context_req->eps_qos); + + // Access Point Name + liblte_mme_unpack_access_point_name_ie(&msg_ptr, &act_def_eps_bearer_context_req->apn); + + // PDN Address + liblte_mme_unpack_pdn_address_ie(&msg_ptr, &act_def_eps_bearer_context_req->pdn_addr); + + // Transaction Identifier + if(LIBLTE_MME_TRANSACTION_IDENTIFIER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_transaction_identifier_ie(&msg_ptr, &act_def_eps_bearer_context_req->transaction_id); + act_def_eps_bearer_context_req->transaction_id_present = true; + }else{ + act_def_eps_bearer_context_req->transaction_id_present = false; + } + + // Negotiated QoS + if(LIBLTE_MME_QUALITY_OF_SERVICE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_quality_of_service_ie(&msg_ptr, &act_def_eps_bearer_context_req->negotiated_qos); + act_def_eps_bearer_context_req->negotiated_qos_present = true; + }else{ + act_def_eps_bearer_context_req->negotiated_qos_present = false; + } + + // Negotiated LLC SAPI + if(LIBLTE_MME_LLC_SAPI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_llc_service_access_point_identifier_ie(&msg_ptr, &act_def_eps_bearer_context_req->llc_sapi); + act_def_eps_bearer_context_req->llc_sapi_present = true; + }else{ + act_def_eps_bearer_context_req->llc_sapi_present = false; + } + + // Radio Priority + if((LIBLTE_MME_RADIO_PRIORITY_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_radio_priority_ie(&msg_ptr, 0, &act_def_eps_bearer_context_req->radio_prio); + msg_ptr++; + act_def_eps_bearer_context_req->radio_prio_present = true; + }else{ + act_def_eps_bearer_context_req->radio_prio_present = false; + } + + // Packet Flow Identifier + if(LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_packet_flow_identifier_ie(&msg_ptr, &act_def_eps_bearer_context_req->packet_flow_id); + act_def_eps_bearer_context_req->packet_flow_id_present = true; + }else{ + act_def_eps_bearer_context_req->packet_flow_id_present = false; + } + + // APN-AMBR + if(LIBLTE_MME_APN_AMBR_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_apn_aggregate_maximum_bit_rate_ie(&msg_ptr, &act_def_eps_bearer_context_req->apn_ambr); + act_def_eps_bearer_context_req->apn_ambr_present = true; + }else{ + act_def_eps_bearer_context_req->apn_ambr_present = false; + } + + // ESM Cause + if(LIBLTE_MME_ESM_CAUSE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &act_def_eps_bearer_context_req->esm_cause); + act_def_eps_bearer_context_req->esm_cause_present = true; + }else{ + act_def_eps_bearer_context_req->esm_cause_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &act_def_eps_bearer_context_req->protocol_cnfg_opts); + act_def_eps_bearer_context_req->protocol_cnfg_opts_present = true; + }else{ + act_def_eps_bearer_context_req->protocol_cnfg_opts_present = false; + } + + // Connectivity Type + if((LIBLTE_MME_CONNECTIVITY_TYPE_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_connectivity_type_ie(&msg_ptr, 0, &act_def_eps_bearer_context_req->connectivity_type); + msg_ptr++; + act_def_eps_bearer_context_req->connectivity_type_present = true; + }else{ + act_def_eps_bearer_context_req->connectivity_type_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Bearer Resource Allocation Reject + + Description: Sent by the network to the UE to reject the + allocation of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_allocation_reject_msg(LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REJECT_MSG_STRUCT *bearer_res_alloc_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(bearer_res_alloc_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (bearer_res_alloc_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = bearer_res_alloc_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(bearer_res_alloc_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(bearer_res_alloc_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&bearer_res_alloc_rej->protocol_cnfg_opts, &msg_ptr); + } + + // T3496 Value + if(bearer_res_alloc_rej->t3496_present) + { + *msg_ptr = LIBLTE_MME_T3496_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_3_ie(&bearer_res_alloc_rej->t3496, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_allocation_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REJECT_MSG_STRUCT *bearer_res_alloc_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + bearer_res_alloc_rej != NULL) + { + // EPS Bearer ID + bearer_res_alloc_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + bearer_res_alloc_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &bearer_res_alloc_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &bearer_res_alloc_rej->protocol_cnfg_opts); + bearer_res_alloc_rej->protocol_cnfg_opts_present = true; + }else{ + bearer_res_alloc_rej->protocol_cnfg_opts_present = false; + } + + // T3496 Value + if(LIBLTE_MME_T3496_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_3_ie(&msg_ptr, &bearer_res_alloc_rej->t3496); + bearer_res_alloc_rej->t3496_present = true; + }else{ + bearer_res_alloc_rej->t3496_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Bearer Resource Allocation Request + + Description: Sent by the UE to the network to request the + allocation of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.8 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_allocation_request_msg(LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_MSG_STRUCT *bearer_res_alloc_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(bearer_res_alloc_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (bearer_res_alloc_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = bearer_res_alloc_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REQUEST; + msg_ptr++; + + // Linked EPS Bearer Identity & Spare Half Octet + liblte_mme_pack_linked_eps_bearer_identity_ie(bearer_res_alloc_req->linked_eps_bearer_id, 0, &msg_ptr); + + // Traffic Flow Aggregate + liblte_mme_pack_traffic_flow_aggregate_description_ie(&bearer_res_alloc_req->tfa, &msg_ptr); + + // Required Traffic Flow QoS + liblte_mme_pack_eps_quality_of_service_ie(&bearer_res_alloc_req->req_tf_qos, &msg_ptr); + + // Protocol Configuration Options + if(bearer_res_alloc_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&bearer_res_alloc_req->protocol_cnfg_opts, &msg_ptr); + } + + // Device Properties + if(bearer_res_alloc_req->device_properties_present) + { + *msg_ptr = LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_DEVICE_PROPERTIES_IEI << 4; + liblte_mme_pack_device_properties_ie(bearer_res_alloc_req->device_properties, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_allocation_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_MSG_STRUCT *bearer_res_alloc_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + bearer_res_alloc_req != NULL) + { + // EPS Bearer ID + bearer_res_alloc_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + bearer_res_alloc_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Linked EPS Bearer Identity & Spare Half Octet + liblte_mme_unpack_linked_eps_bearer_identity_ie(&msg_ptr, 0, &bearer_res_alloc_req->linked_eps_bearer_id); + + // Traffic Flow Aggregate + liblte_mme_unpack_traffic_flow_aggregate_description_ie(&msg_ptr, &bearer_res_alloc_req->tfa); + + // Required Traffic Flow QoS + liblte_mme_unpack_eps_quality_of_service_ie(&msg_ptr, &bearer_res_alloc_req->req_tf_qos); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &bearer_res_alloc_req->protocol_cnfg_opts); + bearer_res_alloc_req->protocol_cnfg_opts_present = true; + }else{ + bearer_res_alloc_req->protocol_cnfg_opts_present = false; + } + + // Device Properties + if((LIBLTE_MME_BEARER_RESOURCE_ALLOCATION_REQUEST_DEVICE_PROPERTIES_IEI << 4) == *msg_ptr) + { + liblte_mme_unpack_device_properties_ie(&msg_ptr, 0, &bearer_res_alloc_req->device_properties); + msg_ptr++; + bearer_res_alloc_req->device_properties_present = true; + }else{ + bearer_res_alloc_req->device_properties_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Bearer Resource Modification Reject + + Description: Sent by the network to the UE to reject the + modification of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.9 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_modification_reject_msg(LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REJECT_MSG_STRUCT *bearer_res_mod_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(bearer_res_mod_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (bearer_res_mod_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = bearer_res_mod_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(bearer_res_mod_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(bearer_res_mod_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&bearer_res_mod_rej->protocol_cnfg_opts, &msg_ptr); + } + + // T3496 Value + if(bearer_res_mod_rej->t3496_present) + { + *msg_ptr = LIBLTE_MME_T3496_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_3_ie(&bearer_res_mod_rej->t3496, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_modification_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REJECT_MSG_STRUCT *bearer_res_mod_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + bearer_res_mod_rej != NULL) + { + // EPS Bearer ID + bearer_res_mod_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + bearer_res_mod_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &bearer_res_mod_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &bearer_res_mod_rej->protocol_cnfg_opts); + bearer_res_mod_rej->protocol_cnfg_opts_present = true; + }else{ + bearer_res_mod_rej->protocol_cnfg_opts_present = false; + } + + // T3496 Value + if(LIBLTE_MME_T3496_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_3_ie(&msg_ptr, &bearer_res_mod_rej->t3496); + bearer_res_mod_rej->t3496_present = true; + }else{ + bearer_res_mod_rej->t3496_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Bearer Resource Modification Request + + Description: Sent by the UE to the network to request the + modification of a dedicated bearer resource. + + Document Reference: 24.301 v10.2.0 Section 8.3.10 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_bearer_resource_modification_request_msg(LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_MSG_STRUCT *bearer_res_mod_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(bearer_res_mod_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (bearer_res_mod_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = bearer_res_mod_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REQUEST; + msg_ptr++; + + // EPS Bearer Identity For Packet Filter & Spare Half Octet + liblte_mme_pack_linked_eps_bearer_identity_ie(bearer_res_mod_req->eps_bearer_id_for_packet_filter, 0, &msg_ptr); + + // Traffic Flow Aggregate + liblte_mme_pack_traffic_flow_aggregate_description_ie(&bearer_res_mod_req->tfa, &msg_ptr); + + // Required Traffic Flow QoS + if(bearer_res_mod_req->req_tf_qos_present) + { + *msg_ptr = LIBLTE_MME_EPS_QUALITY_OF_SERVICE_IEI; + msg_ptr++; + liblte_mme_pack_eps_quality_of_service_ie(&bearer_res_mod_req->req_tf_qos, &msg_ptr); + } + + // ESM Cause + if(bearer_res_mod_req->esm_cause_present) + { + *msg_ptr = LIBLTE_MME_ESM_CAUSE_IEI; + msg_ptr++; + liblte_mme_pack_esm_cause_ie(bearer_res_mod_req->esm_cause, &msg_ptr); + } + + // Protocol Configuration Options + if(bearer_res_mod_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&bearer_res_mod_req->protocol_cnfg_opts, &msg_ptr); + } + + // Device Properties + if(bearer_res_mod_req->device_properties_present) + { + *msg_ptr = LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_DEVICE_PROPERTIES_IEI << 4; + liblte_mme_pack_device_properties_ie(bearer_res_mod_req->device_properties, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_bearer_resource_modification_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_MSG_STRUCT *bearer_res_mod_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + bearer_res_mod_req != NULL) + { + // EPS Bearer ID + bearer_res_mod_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + bearer_res_mod_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // EPS Bearer Identity For Packet Filter & Spare Half Octet + liblte_mme_unpack_linked_eps_bearer_identity_ie(&msg_ptr, 0, &bearer_res_mod_req->eps_bearer_id_for_packet_filter); + + // Traffic Flow Aggregate + liblte_mme_unpack_traffic_flow_aggregate_description_ie(&msg_ptr, &bearer_res_mod_req->tfa); + + // Required Traffic Flow QoS + if(LIBLTE_MME_EPS_QUALITY_OF_SERVICE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_quality_of_service_ie(&msg_ptr, &bearer_res_mod_req->req_tf_qos); + bearer_res_mod_req->req_tf_qos_present = true; + }else{ + bearer_res_mod_req->req_tf_qos_present = false; + } + + // ESM Cause + if(LIBLTE_MME_ESM_CAUSE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &bearer_res_mod_req->esm_cause); + bearer_res_mod_req->esm_cause_present = true; + }else{ + bearer_res_mod_req->esm_cause_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &bearer_res_mod_req->protocol_cnfg_opts); + bearer_res_mod_req->protocol_cnfg_opts_present = true; + }else{ + bearer_res_mod_req->protocol_cnfg_opts_present = false; + } + + // Device Properties + if((LIBLTE_MME_BEARER_RESOURCE_MODIFICATION_REQUEST_DEVICE_PROPERTIES_IEI << 4) == *msg_ptr) + { + liblte_mme_unpack_device_properties_ie(&msg_ptr, 0, &bearer_res_mod_req->device_properties); + msg_ptr++; + bearer_res_mod_req->device_properties_present = true; + }else{ + bearer_res_mod_req->device_properties_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Deactivate EPS Bearer Context Accept + + Description: Sent by the UE to acknowledge deactivation of the + EPS bearer context requested in the corresponding + deactivate EPS bearer context request message. + + Document Reference: 24.301 v10.2.0 Section 8.3.11 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_accept_msg(LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *deact_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(deact_eps_bearer_context_accept != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (deact_eps_bearer_context_accept->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = deact_eps_bearer_context_accept->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT; + msg_ptr++; + + // Protocol Configuration Options + if(deact_eps_bearer_context_accept->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&deact_eps_bearer_context_accept->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *deact_eps_bearer_context_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + deact_eps_bearer_context_accept != NULL) + { + // EPS Bearer ID + deact_eps_bearer_context_accept->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + deact_eps_bearer_context_accept->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &deact_eps_bearer_context_accept->protocol_cnfg_opts); + deact_eps_bearer_context_accept->protocol_cnfg_opts_present = true; + }else{ + deact_eps_bearer_context_accept->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Deactivate EPS Bearer Context Request + + Description: Sent by the network to request deactivation of an + EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.12 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_request_msg(LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *deact_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(deact_eps_bearer_context_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (deact_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = deact_eps_bearer_context_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(deact_eps_bearer_context_req->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(deact_eps_bearer_context_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&deact_eps_bearer_context_req->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *deact_eps_bearer_context_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + deact_eps_bearer_context_req != NULL) + { + // EPS Bearer ID + deact_eps_bearer_context_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + deact_eps_bearer_context_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &deact_eps_bearer_context_req->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &deact_eps_bearer_context_req->protocol_cnfg_opts); + deact_eps_bearer_context_req->protocol_cnfg_opts_present = true; + }else{ + deact_eps_bearer_context_req->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: ESM Information Request + + Description: Sent by the network to the UE to request the UE to + provide ESM information, i.e. protocol configuration + options or APN or both. This function is being added + to support encryption and integrety protection on + ESM information transfer. + + Document Reference: 24.301 v10.2.0 Section 8.3.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM srslte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(esm_info_req != NULL && + msg != NULL) + { + + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (esm_info_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = esm_info_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + Message Name: ESM Information Request + + Description: Sent by the network to the UE to request the UE to + provide ESM information, i.e. protocol configuration + options or APN or both. + + Document Reference: 24.301 v10.2.0 Section 8.3.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(esm_info_req != NULL && + msg != NULL) + { + + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (esm_info_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = esm_info_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + + +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + esm_info_req != NULL) + { + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) { + msg_ptr++; + } else{ + msg_ptr += 6; + } + + // EPS Bearer ID + esm_info_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + esm_info_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: ESM Information Response + + Description: Sent by the UE to the network in response to an ESM + INFORMATION REQUEST message and provides the + requested ESM information. + + Document Reference: 24.301 v10.2.0 Section 8.3.14 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(esm_info_resp != NULL && + msg != NULL) + { + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (esm_info_resp->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = esm_info_resp->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE; + msg_ptr++; + + // Access Point Name + if(esm_info_resp->apn_present) + { + *msg_ptr = LIBLTE_MME_ACCESS_POINT_NAME_IEI; + msg_ptr++; + liblte_mme_pack_access_point_name_ie(&esm_info_resp->apn, &msg_ptr); + } + + // Protocol Configuration Options + if(esm_info_resp->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&esm_info_resp->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + esm_info_resp != NULL) + { + + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 6; + } + // EPS Bearer ID + esm_info_resp->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + esm_info_resp->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Access Point Name + if(LIBLTE_MME_ACCESS_POINT_NAME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_access_point_name_ie(&msg_ptr, &esm_info_resp->apn); + esm_info_resp->apn_present = true; + }else{ + esm_info_resp->apn_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &esm_info_resp->protocol_cnfg_opts); + esm_info_resp->protocol_cnfg_opts_present = true; + }else{ + esm_info_resp->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + esm_info_resp != NULL) + { + // EPS Bearer ID + esm_info_resp->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + esm_info_resp->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Access Point Name + if(LIBLTE_MME_ACCESS_POINT_NAME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_access_point_name_ie(&msg_ptr, &esm_info_resp->apn); + esm_info_resp->apn_present = true; + }else{ + esm_info_resp->apn_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &esm_info_resp->protocol_cnfg_opts); + esm_info_resp->protocol_cnfg_opts_present = true; + }else{ + esm_info_resp->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: ESM Status + + Description: Sent by the network or the UE to pass information on + the status of the indicated EPS bearer context and + report certain error conditions. + + Document Reference: 24.301 v10.2.0 Section 8.3.15 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_status_msg(LIBLTE_MME_ESM_STATUS_MSG_STRUCT *esm_status, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(esm_status != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (esm_status->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = esm_status->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ESM_STATUS; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(esm_status->esm_cause, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_status_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_STATUS_MSG_STRUCT *esm_status) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + esm_status != NULL) + { + // EPS Bearer ID + esm_status->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + esm_status->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &esm_status->esm_cause); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Modify EPS Bearer Context Accept + + Description: Sent by the UE to the network to acknowledge the + modification of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.16 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_accept_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *mod_eps_bearer_context_accept, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(mod_eps_bearer_context_accept != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (mod_eps_bearer_context_accept->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = mod_eps_bearer_context_accept->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_ACCEPT; + msg_ptr++; + + // Protocol Configuration Options + if(mod_eps_bearer_context_accept->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&mod_eps_bearer_context_accept->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_accept_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT *mod_eps_bearer_context_accept) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + mod_eps_bearer_context_accept != NULL) + { + // EPS Bearer ID + mod_eps_bearer_context_accept->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + mod_eps_bearer_context_accept->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &mod_eps_bearer_context_accept->protocol_cnfg_opts); + mod_eps_bearer_context_accept->protocol_cnfg_opts_present = true; + }else{ + mod_eps_bearer_context_accept->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Modify EPS Bearer Context Reject + + Description: Sent by the UE or the network to reject a + modification of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.17 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_reject_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *mod_eps_bearer_context_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(mod_eps_bearer_context_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (mod_eps_bearer_context_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = mod_eps_bearer_context_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(mod_eps_bearer_context_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(mod_eps_bearer_context_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&mod_eps_bearer_context_rej->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REJECT_MSG_STRUCT *mod_eps_bearer_context_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + mod_eps_bearer_context_rej != NULL) + { + // EPS Bearer ID + mod_eps_bearer_context_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + mod_eps_bearer_context_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &mod_eps_bearer_context_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &mod_eps_bearer_context_rej->protocol_cnfg_opts); + mod_eps_bearer_context_rej->protocol_cnfg_opts_present = true; + }else{ + mod_eps_bearer_context_rej->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Modify EPS Bearer Context Request + + Description: Sent by the network to the UE to request modification + of an active EPS bearer context. + + Document Reference: 24.301 v10.2.0 Section 8.3.18 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_modify_eps_bearer_context_request_msg(LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *mod_eps_bearer_context_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(mod_eps_bearer_context_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (mod_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = mod_eps_bearer_context_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REQUEST; + msg_ptr++; + + // New EPS QoS + if(mod_eps_bearer_context_req->new_eps_qos_present) + { + *msg_ptr = LIBLTE_MME_EPS_QUALITY_OF_SERVICE_IEI; + msg_ptr++; + liblte_mme_pack_eps_quality_of_service_ie(&mod_eps_bearer_context_req->new_eps_qos, &msg_ptr); + } + + // TFT + if(mod_eps_bearer_context_req->tft_present) + { + *msg_ptr = LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_IEI; + msg_ptr++; + liblte_mme_pack_traffic_flow_template_ie(&mod_eps_bearer_context_req->tft, &msg_ptr); + } + + // New QoS + if(mod_eps_bearer_context_req->new_qos_present) + { + *msg_ptr = LIBLTE_MME_QUALITY_OF_SERVICE_IEI; + msg_ptr++; + liblte_mme_pack_quality_of_service_ie(&mod_eps_bearer_context_req->new_qos, &msg_ptr); + } + + // Negotiated LLC SAPI + if(mod_eps_bearer_context_req->negotiated_llc_sapi_present) + { + *msg_ptr = LIBLTE_MME_LLC_SAPI_IEI; + msg_ptr++; + liblte_mme_pack_llc_service_access_point_identifier_ie(mod_eps_bearer_context_req->negotiated_llc_sapi, &msg_ptr); + } + + // Radio Priority + if(mod_eps_bearer_context_req->radio_prio_present) + { + *msg_ptr = LIBLTE_MME_RADIO_PRIORITY_IEI << 4; + liblte_mme_pack_radio_priority_ie(mod_eps_bearer_context_req->radio_prio, 0, &msg_ptr); + msg_ptr++; + } + + // Packet Flow Identifier + if(mod_eps_bearer_context_req->packet_flow_id_present) + { + *msg_ptr = LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI; + msg_ptr++; + liblte_mme_pack_packet_flow_identifier_ie(mod_eps_bearer_context_req->packet_flow_id, &msg_ptr); + } + + // APN-AMBR + if(mod_eps_bearer_context_req->apn_ambr_present) + { + *msg_ptr = LIBLTE_MME_APN_AMBR_IEI; + msg_ptr++; + liblte_mme_pack_apn_aggregate_maximum_bit_rate_ie(&mod_eps_bearer_context_req->apn_ambr, &msg_ptr); + } + + // Protocol Configuration Options + if(mod_eps_bearer_context_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&mod_eps_bearer_context_req->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_modify_eps_bearer_context_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_MODIFY_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT *mod_eps_bearer_context_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + mod_eps_bearer_context_req != NULL) + { + // EPS Bearer ID + mod_eps_bearer_context_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + mod_eps_bearer_context_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // New EPS QoS + if(LIBLTE_MME_EPS_QUALITY_OF_SERVICE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_eps_quality_of_service_ie(&msg_ptr, &mod_eps_bearer_context_req->new_eps_qos); + mod_eps_bearer_context_req->new_eps_qos_present = true; + }else{ + mod_eps_bearer_context_req->new_eps_qos_present = false; + } + + // TFT + if(LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_traffic_flow_template_ie(&msg_ptr, &mod_eps_bearer_context_req->tft); + mod_eps_bearer_context_req->tft_present = true; + }else{ + mod_eps_bearer_context_req->tft_present = false; + } + + // New QoS + if(LIBLTE_MME_QUALITY_OF_SERVICE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_quality_of_service_ie(&msg_ptr, &mod_eps_bearer_context_req->new_qos); + mod_eps_bearer_context_req->new_qos_present = true; + }else{ + mod_eps_bearer_context_req->new_qos_present = false; + } + + // Negotiated LLC SAPI + if(LIBLTE_MME_LLC_SAPI_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_llc_service_access_point_identifier_ie(&msg_ptr, &mod_eps_bearer_context_req->negotiated_llc_sapi); + mod_eps_bearer_context_req->negotiated_llc_sapi_present = true; + }else{ + mod_eps_bearer_context_req->negotiated_llc_sapi_present = false; + } + + // Radio Priority + if((LIBLTE_MME_RADIO_PRIORITY_IEI << 4) == *msg_ptr) + { + liblte_mme_unpack_radio_priority_ie(&msg_ptr, 0, &mod_eps_bearer_context_req->radio_prio); + msg_ptr++; + mod_eps_bearer_context_req->radio_prio_present = true; + }else{ + mod_eps_bearer_context_req->radio_prio_present = false; + } + + // Packet Flow Identifier + if(LIBLTE_MME_PACKET_FLOW_IDENTIFIER_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_packet_flow_identifier_ie(&msg_ptr, &mod_eps_bearer_context_req->packet_flow_id); + mod_eps_bearer_context_req->packet_flow_id_present = true; + }else{ + mod_eps_bearer_context_req->packet_flow_id_present = false; + } + + // APN-AMBR + if(LIBLTE_MME_APN_AMBR_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_apn_aggregate_maximum_bit_rate_ie(&msg_ptr, &mod_eps_bearer_context_req->apn_ambr); + mod_eps_bearer_context_req->apn_ambr_present = true; + }else{ + mod_eps_bearer_context_req->apn_ambr_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &mod_eps_bearer_context_req->protocol_cnfg_opts); + mod_eps_bearer_context_req->protocol_cnfg_opts_present = true; + }else{ + mod_eps_bearer_context_req->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Notification + + Description: Sent by the network to inform the UE about events + which are relevant for the upper layer using an EPS + bearer context or having requested a procedure + transaction. + + Document Reference: 24.301 v10.2.0 Section 8.3.18A +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_notification_msg(LIBLTE_MME_NOTIFICATION_MSG_STRUCT *notification, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(notification != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (notification->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = notification->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_NOTIFICATION; + msg_ptr++; + + // Notification Indicator + liblte_mme_pack_notification_indicator_ie(notification->notification_ind, &msg_ptr); + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_notification_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_NOTIFICATION_MSG_STRUCT *notification) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + notification != NULL) + { + // EPS Bearer ID + notification->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + notification->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Notification Indicator + liblte_mme_unpack_notification_indicator_ie(&msg_ptr, ¬ification->notification_ind); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: PDN Connectivity Reject + + Description: Sent by the network to the UE to reject establishment + of a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.19 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_connectivity_reject_msg(LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT *pdn_con_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(pdn_con_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (pdn_con_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = pdn_con_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(pdn_con_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(pdn_con_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&pdn_con_rej->protocol_cnfg_opts, &msg_ptr); + } + + // T3496 Value + if(pdn_con_rej->t3496_present) + { + *msg_ptr = LIBLTE_MME_T3496_VALUE_IEI; + msg_ptr++; + liblte_mme_pack_gprs_timer_3_ie(&pdn_con_rej->t3496, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_connectivity_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT *pdn_con_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + pdn_con_rej != NULL) + { + // EPS Bearer ID + pdn_con_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + pdn_con_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &pdn_con_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &pdn_con_rej->protocol_cnfg_opts); + pdn_con_rej->protocol_cnfg_opts_present = true; + }else{ + pdn_con_rej->protocol_cnfg_opts_present = false; + } + + // T3496 Value + if(LIBLTE_MME_T3496_VALUE_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_gprs_timer_3_ie(&msg_ptr, &pdn_con_rej->t3496); + pdn_con_rej->t3496_present = true; + }else{ + pdn_con_rej->t3496_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: PDN Connectivity Request + + Description: Sent by the UE to the network to initiate + establishment of a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.20 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_connectivity_request_msg(LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(pdn_con_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (pdn_con_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = pdn_con_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REQUEST; + msg_ptr++; + + // Request Type & PDN Type + *msg_ptr = 0; + liblte_mme_pack_request_type_ie(pdn_con_req->request_type, 0, &msg_ptr); + liblte_mme_pack_pdn_type_ie(pdn_con_req->pdn_type, 4, &msg_ptr); + msg_ptr++; + + // ESM Information Transfer Flag + if(pdn_con_req->esm_info_transfer_flag_present) + { + *msg_ptr = LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_IEI << 4; + liblte_mme_pack_esm_info_transfer_flag_ie(pdn_con_req->esm_info_transfer_flag, 0, &msg_ptr); + msg_ptr++; + } + + // Access Point Name + if(pdn_con_req->apn_present) + { + *msg_ptr = LIBLTE_MME_ACCESS_POINT_NAME_IEI; + msg_ptr++; + liblte_mme_pack_access_point_name_ie(&pdn_con_req->apn, &msg_ptr); + } + + // Protocol Configuration Options + if(pdn_con_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&pdn_con_req->protocol_cnfg_opts, &msg_ptr); + } + + // Device Properties + if(pdn_con_req->device_properties_present) + { + *msg_ptr = LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_DEVICE_PROPERTIES_IEI << 4; + liblte_mme_pack_device_properties_ie(pdn_con_req->device_properties, 0, &msg_ptr); + msg_ptr++; + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_connectivity_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + pdn_con_req != NULL) + { + // EPS Bearer ID + pdn_con_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + pdn_con_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Request Type & PDN Type + liblte_mme_unpack_request_type_ie(&msg_ptr, 0, &pdn_con_req->request_type); + liblte_mme_unpack_pdn_type_ie(&msg_ptr, 4, &pdn_con_req->pdn_type); + msg_ptr++; + + // ESM Information Transfer Flag + if((LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_esm_info_transfer_flag_ie(&msg_ptr, 0, &pdn_con_req->esm_info_transfer_flag); + msg_ptr++; + pdn_con_req->esm_info_transfer_flag_present = true; + }else{ + pdn_con_req->esm_info_transfer_flag_present = false; + } + + // Access Point Name + if(LIBLTE_MME_ACCESS_POINT_NAME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_access_point_name_ie(&msg_ptr, &pdn_con_req->apn); + pdn_con_req->apn_present = true; + }else{ + pdn_con_req->apn_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &pdn_con_req->protocol_cnfg_opts); + pdn_con_req->protocol_cnfg_opts_present = true; + }else{ + pdn_con_req->protocol_cnfg_opts_present = false; + } + + // Device Properties + if((LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_DEVICE_PROPERTIES_IEI << 4) == (*msg_ptr & 0xF0)) + { + liblte_mme_unpack_device_properties_ie(&msg_ptr, 0, &pdn_con_req->device_properties); + msg_ptr++; + pdn_con_req->device_properties_present = true; + }else{ + pdn_con_req->device_properties_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: PDN Disconnect Reject + + Description: Sent by the network to the UE to reject release of a + PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.21 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_disconnect_reject_msg(LIBLTE_MME_PDN_DISCONNECT_REJECT_MSG_STRUCT *pdn_discon_rej, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(pdn_discon_rej != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (pdn_discon_rej->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = pdn_discon_rej->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REJECT; + msg_ptr++; + + // ESM Cause + liblte_mme_pack_esm_cause_ie(pdn_discon_rej->esm_cause, &msg_ptr); + + // Protocol Configuration Options + if(pdn_discon_rej->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&pdn_discon_rej->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_disconnect_reject_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_DISCONNECT_REJECT_MSG_STRUCT *pdn_discon_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + pdn_discon_rej != NULL) + { + // EPS Bearer ID + pdn_discon_rej->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + pdn_discon_rej->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // ESM Cause + liblte_mme_unpack_esm_cause_ie(&msg_ptr, &pdn_discon_rej->esm_cause); + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &pdn_discon_rej->protocol_cnfg_opts); + pdn_discon_rej->protocol_cnfg_opts_present = true; + }else{ + pdn_discon_rej->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: PDN Disconnect Request + + Description: Sent by the UE to the network to initiate release of + a PDN connection. + + Document Reference: 24.301 v10.2.0 Section 8.3.22 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_pdn_disconnect_request_msg(LIBLTE_MME_PDN_DISCONNECT_REQUEST_MSG_STRUCT *pdn_discon_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(pdn_discon_req != NULL && + msg != NULL) + { + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (pdn_discon_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = pdn_discon_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REQUEST; + msg_ptr++; + + // Linked EPS Bearer Identity & Spare Half Octet + *msg_ptr = 0; + liblte_mme_pack_linked_eps_bearer_identity_ie(pdn_discon_req->linked_eps_bearer_id, 0, &msg_ptr); + msg_ptr++; + + // Protocol Configuration Options + if(pdn_discon_req->protocol_cnfg_opts_present) + { + *msg_ptr = LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI; + msg_ptr++; + liblte_mme_pack_protocol_config_options_ie(&pdn_discon_req->protocol_cnfg_opts, &msg_ptr); + } + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_disconnect_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_PDN_DISCONNECT_REQUEST_MSG_STRUCT *pdn_discon_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(msg != NULL && + pdn_discon_req != NULL) + { + // EPS Bearer ID + pdn_discon_req->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + pdn_discon_req->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Linked EPS Bearer Identity & Spare Half Octet + liblte_mme_unpack_linked_eps_bearer_identity_ie(&msg_ptr, 0, &pdn_discon_req->linked_eps_bearer_id); + msg_ptr++; + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &pdn_discon_req->protocol_cnfg_opts); + pdn_discon_req->protocol_cnfg_opts_present = true; + }else{ + pdn_discon_req->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} diff --git a/lib/src/asn1/liblte_rrc.cc b/lib/src/asn1/liblte_rrc.cc new file mode 100644 index 0000000..488a23e --- /dev/null +++ b/lib/src/asn1/liblte_rrc.cc @@ -0,0 +1,14399 @@ +/******************************************************************************* + + Copyright 2012-2014 Ben Wojtowicz + Copyright 2014 Andrew Murphy (SIB13 unpack) + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_rrc.cc + + Description: Contains all the implementations for the LTE Radio Resource + Control Layer library. + + Revision History + ---------- ------------- -------------------------------------------- + 03/24/2012 Ben Wojtowicz Created file. + 04/25/2012 Ben Wojtowicz Added SIB1 parameters, IEs, and messages + 06/02/2012 Ben Wojtowicz Added message and IE packing + 06/09/2012 Ben Wojtowicz Added the number of bits used to SIB1 pack + 08/19/2012 Ben Wojtowicz Added functionality to support SIB2, SIB3, + SIB4, and SIB8 packing and unpacking + 10/06/2012 Ben Wojtowicz Added more decoding/encoding. + 11/10/2012 Ben Wojtowicz Filled in the N_bits for SI messages + 03/03/2013 Ben Wojtowicz Added carrier_freqs_geran, SIB5, SIB6, SIB7 + and paging packing and unpacking. + 07/21/2013 Ben Wojtowicz Fixed several bugs and moved to the common + message struct. + 09/16/2013 Ben Wojtowicz Added error checking on sizes passed to + memcpy. + 03/26/2014 Ben Wojtowicz Added support for RRC Connection Request, + RRC Connection Reestablishment Request, + and UL CCCH Messages. + 05/04/2014 Ben Wojtowicz Added support for DL CCCH Messages. + 06/15/2014 Ben Wojtowicz Added support for UL DCCH Messages. + 08/03/2014 Ben Wojtowicz Added more decoding/encoding and using the + common value_2_bits and bits_2_value + functions. + 09/19/2014 Andrew Murphy Added SIB13 unpack. + 11/01/2014 Ben Wojtowicz Added more decoding/encoding. + 11/09/2014 Ben Wojtowicz Added SIB13 pack. + 11/29/2014 Ben Wojtowicz Fixed a bug in RRC connection reestablishment + UE identity. + +*******************************************************************************/ + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/asn1/liblte_rrc.h" + +#include +#include +#include + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + + +/******************************************************************************* + GLOBAL VARIABLES +*******************************************************************************/ + +LIBLTE_BIT_MSG_STRUCT global_msg; + +/******************************************************************************* + HELPERS +*******************************************************************************/ + +/********************************************************************* + Functions for external logging +*********************************************************************/ +static log_handler_t log_handler; +static void *callback_ctx = NULL; + +void liblte_rrc_log_register_handler(void *ctx, log_handler_t handler) { + log_handler = handler; + callback_ctx = ctx; +} + +static void liblte_rrc_log_print(const char *format, ...) { + va_list args; + va_start(args, format); + if (log_handler) { + char *args_msg = NULL; + if(vasprintf(&args_msg, format, args) > 0) { + log_handler(callback_ctx, args_msg); + } + if (args_msg) { + free(args_msg); + } + } else { + vprintf(format, args); + } + va_end(args); +} + +/********************************************************************* + Description: Simply consume non-critical extensions for rel > r8 +*********************************************************************/ +void liblte_rrc_consume_noncrit_extension(bool ext, const char *func_name, uint8 **ie_ptr) +{ + uint32 i=0; + uint32 elem_flags=0; + uint32 elem_len=0; + + if (ext) { + uint8 n_elems = liblte_bits_2_value(ie_ptr, 7) + 1; + for(i=0; i> 1; + } + if (func_name) { + liblte_rrc_log_print("Detected an extension in RRC function: %s\n", func_name); + } + } +} + +void liblte_rrc_warning_not_handled(bool opt, const char *func_name) +{ + if (opt) { + liblte_rrc_log_print("Unhandled feature in RRC function: %s\n\n", func_name?func_name:"Unknown"); + } +} + +/******************************************************************************* + INFORMATION ELEMENT FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + IE Name: MBSFN Notification Config + + Description: Specifies the MBMS notification related configuration + parameters, that are applicable for all MBSFN areas + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_notification_config_ie(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT *mbsfn_notification_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mbsfn_notification_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(mbsfn_notification_cnfg->repetition_coeff, ie_ptr, 1); + liblte_value_2_bits(mbsfn_notification_cnfg->offset, ie_ptr, 4); + liblte_value_2_bits(mbsfn_notification_cnfg->sf_index - 1, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_notification_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT *mbsfn_notification_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mbsfn_notification_cnfg != NULL) + { + mbsfn_notification_cnfg->repetition_coeff = (LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_ENUM)liblte_bits_2_value(ie_ptr, 1); + mbsfn_notification_cnfg->offset = liblte_bits_2_value(ie_ptr, 4); + mbsfn_notification_cnfg->sf_index = liblte_bits_2_value(ie_ptr, 3) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MBSFN Area Info List + + Description: Contains the information required to acquire the MBMS + control information associated with one or more MBSFN + areas + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_info_ie(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *mbsfn_area_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mbsfn_area_info != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(mbsfn_area_info->mbsfn_area_id_r9, ie_ptr, 8); + liblte_value_2_bits(mbsfn_area_info->non_mbsfn_region_length, ie_ptr, 1); + liblte_value_2_bits(mbsfn_area_info->notification_indicator_r9, ie_ptr, 3); + liblte_value_2_bits(mbsfn_area_info->mcch_repetition_period_r9, ie_ptr, 2); + liblte_value_2_bits(mbsfn_area_info->mcch_offset_r9, ie_ptr, 4); + liblte_value_2_bits(mbsfn_area_info->mcch_modification_period_r9, ie_ptr, 1); + liblte_value_2_bits(mbsfn_area_info->sf_alloc_info_r9, ie_ptr, 6); + liblte_value_2_bits(mbsfn_area_info->signalling_mcs_r9, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *mbsfn_area_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext; + + if(ie_ptr != NULL && + mbsfn_area_info != NULL) + { + // Extension indicator + ext = liblte_bits_2_value(ie_ptr, 1); + + mbsfn_area_info->mbsfn_area_id_r9 = liblte_bits_2_value(ie_ptr, 8); + mbsfn_area_info->non_mbsfn_region_length = (LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + mbsfn_area_info->notification_indicator_r9 = liblte_bits_2_value(ie_ptr, 3); + mbsfn_area_info->mcch_repetition_period_r9 = (LIBLTE_RRC_MCCH_REPETITION_PERIOD_ENUM)liblte_bits_2_value(ie_ptr, 2); + mbsfn_area_info->mcch_offset_r9 = liblte_bits_2_value(ie_ptr, 4); + mbsfn_area_info->mcch_modification_period_r9 = (LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_ENUM)liblte_bits_2_value(ie_ptr, 1); + mbsfn_area_info->sf_alloc_info_r9 = liblte_bits_2_value(ie_ptr, 6); + mbsfn_area_info->signalling_mcs_r9 = (LIBLTE_RRC_MCCH_SIGNALLING_MCS_ENUM)liblte_bits_2_value(ie_ptr, 2); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MBSFN Subframe Config + + Description: Defines subframes that are reserved for MBSFN in + downlink + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_subframe_config_ie(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *mbsfn_subfr_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mbsfn_subfr_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(mbsfn_subfr_cnfg->radio_fr_alloc_period, ie_ptr, 3); + liblte_value_2_bits(mbsfn_subfr_cnfg->radio_fr_alloc_offset, ie_ptr, 3); + liblte_value_2_bits(mbsfn_subfr_cnfg->subfr_alloc_num_frames, ie_ptr, 1); + if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == mbsfn_subfr_cnfg->subfr_alloc_num_frames) + { + liblte_value_2_bits(mbsfn_subfr_cnfg->subfr_alloc, ie_ptr, 6); + }else{ + liblte_value_2_bits(mbsfn_subfr_cnfg->subfr_alloc, ie_ptr, 24); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_subframe_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *mbsfn_subfr_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mbsfn_subfr_cnfg != NULL) + { + mbsfn_subfr_cnfg->radio_fr_alloc_period = (LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_ENUM)liblte_bits_2_value(ie_ptr, 3); + mbsfn_subfr_cnfg->radio_fr_alloc_offset = liblte_bits_2_value(ie_ptr, 3); + mbsfn_subfr_cnfg->subfr_alloc_num_frames = (LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == mbsfn_subfr_cnfg->subfr_alloc_num_frames) + { + mbsfn_subfr_cnfg->subfr_alloc = liblte_bits_2_value(ie_ptr, 6); + }else{ + mbsfn_subfr_cnfg->subfr_alloc = liblte_bits_2_value(ie_ptr, 24); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: TMGI + + Description: Temporary Mobile Group Identity (PLMN + MBMS service ID) + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_tmgi_r9_ie(LIBLTE_RRC_TMGI_R9_STRUCT *tmgi, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(tmgi != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(tmgi->plmn_id_explicit?1:0, ie_ptr, 1); + if(tmgi->plmn_id_explicit){ + liblte_rrc_pack_plmn_identity_ie(&tmgi->plmn_id_r9, ie_ptr); + }else{ + liblte_value_2_bits(tmgi->plmn_index_r9-1, ie_ptr, 3); + } + liblte_value_2_bits(tmgi->serviceid_r9, ie_ptr, 24); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tmgi_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_TMGI_R9_STRUCT *tmgi) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tmgi != NULL) + { + tmgi->plmn_id_explicit = liblte_bits_2_value(ie_ptr, 1); + if(tmgi->plmn_id_explicit){ + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &tmgi->plmn_id_r9); + }else{ + tmgi->plmn_index_r9 = liblte_bits_2_value(ie_ptr, 3) + 1; + } + tmgi->serviceid_r9 = liblte_bits_2_value(ie_ptr, 24); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MBMS Session Info + + Description: Information about an individual MBMS session + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbms_session_info_r9_ie(LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mbms_session_info != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(mbms_session_info->sessionid_r9_present?1:0, ie_ptr, 1); + liblte_rrc_pack_tmgi_r9_ie(&mbms_session_info->tmgi_r9, ie_ptr); + if(mbms_session_info->sessionid_r9_present){ + liblte_value_2_bits(mbms_session_info->sessionid_r9, ie_ptr, 8); + } + liblte_value_2_bits(mbms_session_info->logicalchannelid_r9, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbms_session_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *mbms_session_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mbms_session_info != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + mbms_session_info->sessionid_r9_present = liblte_bits_2_value(ie_ptr, 1); + liblte_rrc_unpack_tmgi_r9_ie(ie_ptr, &mbms_session_info->tmgi_r9); + if(mbms_session_info->sessionid_r9_present){ + mbms_session_info->sessionid_r9 = liblte_bits_2_value(ie_ptr, 8); + } + mbms_session_info->logicalchannelid_r9 = liblte_bits_2_value(ie_ptr, 5); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PMCH Config + + Description: Contains configuration parameters of the sessions + carried by a PMCH + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_config_r9_ie(LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pmch_cnfg != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(pmch_cnfg->sf_alloc_end_r9, ie_ptr, 11); + liblte_value_2_bits(pmch_cnfg->datamcs_r9, ie_ptr, 5); + liblte_value_2_bits(pmch_cnfg->mch_schedulingperiod_r9, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_config_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_CONFIG_R9_STRUCT *pmch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pmch_cnfg != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + pmch_cnfg->sf_alloc_end_r9 = liblte_bits_2_value(ie_ptr, 11); + pmch_cnfg->datamcs_r9 = liblte_bits_2_value(ie_ptr, 5); + pmch_cnfg->mch_schedulingperiod_r9 = (LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + IE Name: PMCH Info + + Description: Specifies configuration of PMCH of an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.3.7 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pmch_info_r9_ie(LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(pmch_info != NULL && + ie_ptr != NULL) + { + // ext + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_rrc_pack_pmch_config_r9_ie(&pmch_info->pmch_config_r9, ie_ptr); + liblte_value_2_bits(pmch_info->mbms_sessioninfolist_r9_size, ie_ptr, 5); + for(i=0; imbms_sessioninfolist_r9_size; i++){ + liblte_rrc_pack_mbms_session_info_r9_ie(&pmch_info->mbms_sessioninfolist_r9[i], ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pmch_info_r9_ie(uint8 **ie_ptr, + LIBLTE_RRC_PMCH_INFO_R9_STRUCT *pmch_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + pmch_info != NULL) + { + // ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_pmch_config_r9_ie(ie_ptr, &pmch_info->pmch_config_r9); + pmch_info->mbms_sessioninfolist_r9_size = liblte_bits_2_value(ie_ptr, 5); + for(i=0; imbms_sessioninfolist_r9_size; i++){ + liblte_rrc_unpack_mbms_session_info_r9_ie(ie_ptr, &pmch_info->mbms_sessioninfolist_r9[i]); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: C-RNTI + + Description: Identifies a UE having a RRC connection within a cell + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_c_rnti_ie(uint16 rnti, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(rnti, ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_c_rnti_ie(uint8 **ie_ptr, + uint16 *rnti) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rnti != NULL) + { + *rnti = liblte_bits_2_value(ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Dedicated Info CDMA2000 + + Description: Transfers UE specific CDMA2000 information between + the network and the UE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_dedicated_info_cdma2000_ie(LIBLTE_BYTE_MSG_STRUCT *ded_info_cdma2000, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ded_info_cdma2000 != NULL && + ie_ptr != NULL) + { + if(ded_info_cdma2000->N_bytes < 128) + { + liblte_value_2_bits(0, ie_ptr, 1); + liblte_value_2_bits(ded_info_cdma2000->N_bytes, ie_ptr, 7); + }else if(ded_info_cdma2000->N_bytes < 16383){ + liblte_value_2_bits(1, ie_ptr, 1); + liblte_value_2_bits(0, ie_ptr, 1); + liblte_value_2_bits(ded_info_cdma2000->N_bytes, ie_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + } + + for(i=0; iN_bytes; i++) + { + liblte_value_2_bits(ded_info_cdma2000->msg[i], ie_ptr, 8); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dedicated_info_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *ded_info_cdma2000) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + ded_info_cdma2000 != NULL) + { + if(0 == liblte_bits_2_value(ie_ptr, 1)) + { + ded_info_cdma2000->N_bytes = liblte_bits_2_value(ie_ptr, 7); + }else{ + if(0 == liblte_bits_2_value(ie_ptr, 1)) + { + ded_info_cdma2000->N_bytes = liblte_bits_2_value(ie_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + ded_info_cdma2000->N_bytes = 0; + } + } + + for(i=0; iN_bytes; i++) + { + ded_info_cdma2000->msg[i] = liblte_bits_2_value(ie_ptr, 8); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Dedicated Info NAS + + Description: Transfers UE specific NAS layer information between + the network and the UE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_dedicated_info_nas_ie(LIBLTE_BYTE_MSG_STRUCT *ded_info_nas, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ded_info_nas != NULL && + ie_ptr != NULL) + { + if(ded_info_nas->N_bytes < 128) + { + liblte_value_2_bits(0, ie_ptr, 1); + liblte_value_2_bits(ded_info_nas->N_bytes, ie_ptr, 7); + }else if(ded_info_nas->N_bytes < 16383){ + liblte_value_2_bits(1, ie_ptr, 1); + liblte_value_2_bits(0, ie_ptr, 1); + liblte_value_2_bits(ded_info_nas->N_bytes, ie_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + } + + for(i=0; iN_bytes; i++) + { + liblte_value_2_bits(ded_info_nas->msg[i], ie_ptr, 8); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dedicated_info_nas_ie(uint8 **ie_ptr, + LIBLTE_BYTE_MSG_STRUCT *ded_info_nas) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + ded_info_nas != NULL) + { + if(0 == liblte_bits_2_value(ie_ptr, 1)) + { + ded_info_nas->N_bytes = liblte_bits_2_value(ie_ptr, 7); + }else{ + if(0 == liblte_bits_2_value(ie_ptr, 1)) + { + ded_info_nas->N_bytes = liblte_bits_2_value(ie_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + ded_info_nas->N_bytes = 0; + } + } + + for(i=0; iN_bytes; i++) + { + ded_info_nas->msg[i] = liblte_bits_2_value(ie_ptr, 8); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Filter Coefficient + + Description: Specifies the measurement filtering coefficient + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_filter_coefficient_ie(LIBLTE_RRC_FILTER_COEFFICIENT_ENUM filter_coeff, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(filter_coeff, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_filter_coefficient_ie(uint8 **ie_ptr, + LIBLTE_RRC_FILTER_COEFFICIENT_ENUM *filter_coeff) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + filter_coeff != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + *filter_coeff = (LIBLTE_RRC_FILTER_COEFFICIENT_ENUM)liblte_bits_2_value(ie_ptr, 4); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MMEC + + Description: Identifies an MME within the scope of an MME group + within a PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mmec_ie(uint8 mmec, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(mmec, ie_ptr, 8); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mmec_ie(uint8 **ie_ptr, + uint8 *mmec) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mmec != NULL) + { + *mmec = liblte_bits_2_value(ie_ptr, 8); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Neigh Cell Config + + Description: Provides the information related to MBSFN and TDD + UL/DL configuration of neighbor cells + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_neigh_cell_config_ie(uint8 neigh_cell_config, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(neigh_cell_config, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_neigh_cell_config_ie(uint8 **ie_ptr, + uint8 *neigh_cell_config) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + neigh_cell_config != NULL) + { + *neigh_cell_config = liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Other Config + + Description: Contains configuration related to other configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_other_config_ie(LIBLTE_RRC_OTHER_CONFIG_R9_STRUCT *other_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(other_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(other_cnfg->report_proximity_cnfg_present, ie_ptr, 1); + + if(true == other_cnfg->report_proximity_cnfg_present) + { + // Optional indicators + liblte_value_2_bits(other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra_present, ie_ptr, 1); + liblte_value_2_bits(other_cnfg->report_proximity_cnfg.report_proximity_ind_utra_present, ie_ptr, 1); + + if(true == other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra_present) + { + liblte_value_2_bits(other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra, ie_ptr, 1); + } + + if(true == other_cnfg->report_proximity_cnfg.report_proximity_ind_utra_present) + { + liblte_value_2_bits(other_cnfg->report_proximity_cnfg.report_proximity_ind_utra, ie_ptr, 1); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_other_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_OTHER_CONFIG_R9_STRUCT *other_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + other_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + other_cnfg->report_proximity_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == other_cnfg->report_proximity_cnfg_present) + { + // Optional indicators + other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra_present = liblte_bits_2_value(ie_ptr, 1); + other_cnfg->report_proximity_cnfg.report_proximity_ind_utra_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra_present) + { + other_cnfg->report_proximity_cnfg.report_proximity_ind_eutra = (LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_EUTRA_R9_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + if(true == other_cnfg->report_proximity_cnfg.report_proximity_ind_utra_present) + { + other_cnfg->report_proximity_cnfg.report_proximity_ind_utra = (LIBLTE_RRC_REPORT_PROXIMITY_INDICATION_UTRA_R9_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RAND CDMA2000 (1xRTT) + + Description: Contains a random value, generated by the eNB, to be + passed to the CDMA2000 upper layers + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rand_cdma2000_1xrtt_ie(uint32 rand, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(rand, ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rand_cdma2000_1xrtt_ie(uint8 **ie_ptr, + uint32 *rand) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rand != NULL) + { + *rand = liblte_bits_2_value(ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RAT Type + + Description: Indicates the radio access technology (RAT), + including E-UTRA, of the requested/transferred UE + capabilities + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rat_type_ie(LIBLTE_RRC_RAT_TYPE_ENUM rat_type, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(rat_type, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rat_type_ie(uint8 **ie_ptr, + LIBLTE_RRC_RAT_TYPE_ENUM *rat_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rat_type != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + *rat_type = (LIBLTE_RRC_RAT_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RRC Transaction Identifier + + Description: Identifies an RRC procedure along with the message + type + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_transaction_identifier_ie(uint8 rrc_transaction_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(rrc_transaction_id, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_transaction_identifier_ie(uint8 **ie_ptr, + uint8 *rrc_transaction_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rrc_transaction_id != NULL) + { + *rrc_transaction_id = liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: S-TMSI + + Description: Contains an S-Temporary Mobile Subscriber Identity, + a temporary UE identity provided by the EPC which + uniquely identifies the UE within the tracking area + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_s_tmsi_ie(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(s_tmsi != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_mmec_ie(s_tmsi->mmec, ie_ptr); + liblte_value_2_bits(s_tmsi->m_tmsi, ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_s_tmsi_ie(uint8 **ie_ptr, + LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + s_tmsi != NULL) + { + liblte_rrc_unpack_mmec_ie(ie_ptr, &s_tmsi->mmec); + s_tmsi->m_tmsi = liblte_bits_2_value(ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: UE Capability RAT Container List + + Description: Contains list of containers, one for each RAT for + which UE capabilities are transferred + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: UE EUTRA Capability + + Description: Conveys the E-UTRA UE Radio Access Capability + Parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdcp_params_ie(LIBLTE_RRC_PDCP_PARAMS_STRUCT *pdcp_params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(pdcp_params != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Max ROHC CTXTS default? + liblte_value_2_bits(pdcp_params->max_rohc_ctxts_present, ie_ptr, 1); + + // Supported ROHC Profiles + for(i=0; i<9; i++) + { + liblte_value_2_bits(pdcp_params->supported_rohc_profiles[i], ie_ptr, 1); + } + + if(pdcp_params->max_rohc_ctxts_present) + { + liblte_value_2_bits(pdcp_params->max_rohc_ctxts, ie_ptr, 4); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdcp_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDCP_PARAMS_STRUCT *pdcp_params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + pdcp_params != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Max ROHC CTXTS default? + pdcp_params->max_rohc_ctxts_present = liblte_bits_2_value(ie_ptr, 1); + + // Supported ROHC Profiles + for(i=0; i<9; i++) + { + pdcp_params->supported_rohc_profiles[i] = liblte_bits_2_value(ie_ptr, 1); + } + + if(pdcp_params->max_rohc_ctxts_present) + { + pdcp_params->max_rohc_ctxts = (LIBLTE_RRC_MAX_ROHC_CTXTS_ENUM)liblte_bits_2_value(ie_ptr, 4); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_phy_layer_params_ie(LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT *params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(params != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(params->tx_antenna_selection_supported, ie_ptr, 1); + liblte_value_2_bits(params->specific_ref_sigs_supported, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phy_layer_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHY_LAYER_PARAMS_STRUCT *params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + params != NULL) + { + params->tx_antenna_selection_supported = liblte_bits_2_value(ie_ptr, 1); + params->specific_ref_sigs_supported = liblte_bits_2_value(ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_rf_params_ie(LIBLTE_RRC_RF_PARAMS_STRUCT *params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(params != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(params->N_supported_band_eutras-1, ie_ptr, 6); + for(i=0; iN_supported_band_eutras; i++) + { + liblte_value_2_bits(params->supported_band_eutra[i].band_eutra-1, ie_ptr, 6); + liblte_value_2_bits(params->supported_band_eutra[i].half_duplex, ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rf_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_RF_PARAMS_STRUCT *params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + params != NULL) + { + params->N_supported_band_eutras = liblte_bits_2_value(ie_ptr, 6) + 1; + for(i=0; iN_supported_band_eutras; i++) + { + params->supported_band_eutra[i].band_eutra = liblte_bits_2_value(ie_ptr, 6) + 1; + params->supported_band_eutra[i].half_duplex = liblte_bits_2_value(ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_info_eutra_ie(LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT *info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(info != NULL && + ie_ptr != NULL) + { + // Option indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(info->N_inter_freq_need_for_gaps-1, ie_ptr, 6); + for(i=0; iN_inter_freq_need_for_gaps; i++) + { + liblte_value_2_bits(info->inter_freq_need_for_gaps[i], ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_info_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_INFO_EUTRA_STRUCT *info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + info != NULL) + { + // Option indicator + liblte_bits_2_value(ie_ptr, 1); + + info->N_inter_freq_need_for_gaps = liblte_bits_2_value(ie_ptr, 6) + 1; + for(i=0; iN_inter_freq_need_for_gaps; i++) + { + info->inter_freq_need_for_gaps[i] = liblte_bits_2_value(ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_params_ie(LIBLTE_RRC_MEAS_PARAMS_STRUCT *params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(params != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(params->N_band_list_eutra-1, ie_ptr, 6); + for(i=0; iN_band_list_eutra; i++) + { + liblte_rrc_pack_band_info_eutra_ie(¶ms->band_list_eutra[i], ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_PARAMS_STRUCT *params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + params != NULL) + { + params->N_band_list_eutra = liblte_bits_2_value(ie_ptr, 6) + 1; + for(i=0; iN_band_list_eutra; i++) + { + liblte_rrc_unpack_band_info_eutra_ie(ie_ptr, ¶ms->band_list_eutra[i]); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_inter_rat_params_ie(LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT *params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(params != NULL && + ie_ptr != NULL) + { + // WARNING: Hardcoding all options to not present + liblte_value_2_bits(0, ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_inter_rat_params_ie(uint8 **ie_ptr, + LIBLTE_RRC_INTER_RAT_PARAMS_STRUCT *params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + params != NULL) + { + // WARNING: Hardcoding all options to not present + params->utra_fdd_present = false; + params->utra_tdd128_present = false; + params->utra_tdd384_present = false; + params->utra_tdd768_present = false; + params->geran_present = false; + params->cdma2000_hrpd_present = false; + params->cdma2000_1xrtt_present = false; + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_eutra_capability_ie(LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *ue_eutra_capability, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(ue_eutra_capability != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Option indicator - featureGroupIndicators + liblte_value_2_bits(ue_eutra_capability->feature_group_indicator_present, &msg_ptr, 1); + + // Option indicator - nonCriticalExtension + liblte_value_2_bits(0, &msg_ptr, 1); + + // Option indicator - access stratum release enum + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(ue_eutra_capability->access_stratum_release, &msg_ptr, 3); + + liblte_value_2_bits(ue_eutra_capability->ue_category - 1, &msg_ptr, 3); + liblte_rrc_pack_pdcp_params_ie(&ue_eutra_capability->pdcp_params, &msg_ptr); + liblte_rrc_pack_phy_layer_params_ie(&ue_eutra_capability->phy_params, &msg_ptr); + liblte_rrc_pack_rf_params_ie(&ue_eutra_capability->rf_params, &msg_ptr); + liblte_rrc_pack_meas_params_ie(&ue_eutra_capability->meas_params, &msg_ptr); + if(ue_eutra_capability->feature_group_indicator_present) + liblte_value_2_bits(ue_eutra_capability->feature_group_indicator, &msg_ptr, 32); + liblte_rrc_pack_inter_rat_params_ie(&ue_eutra_capability->inter_rat_params, &msg_ptr); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_eutra_capability_ie(uint8 **ie_ptr, + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *ue_eutra_capability) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ue_eutra_capability != NULL && + ie_ptr != NULL) + { + // Option indicator - featureGroupIndicators + ue_eutra_capability->feature_group_indicator_present = liblte_bits_2_value(ie_ptr, 1); + + // Option indicator - nonCriticalExtension + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Option indicator - access stratum release enum + liblte_bits_2_value(ie_ptr, 1); + ue_eutra_capability->access_stratum_release = liblte_bits_2_value(ie_ptr, 3); + + ue_eutra_capability->ue_category = liblte_bits_2_value(ie_ptr, 3) + 1; + liblte_rrc_unpack_pdcp_params_ie(ie_ptr, &ue_eutra_capability->pdcp_params); + liblte_rrc_unpack_phy_layer_params_ie(ie_ptr, &ue_eutra_capability->phy_params); + liblte_rrc_unpack_rf_params_ie(ie_ptr, &ue_eutra_capability->rf_params); + liblte_rrc_unpack_meas_params_ie(ie_ptr, &ue_eutra_capability->meas_params); + if(ue_eutra_capability->feature_group_indicator_present) + ue_eutra_capability->feature_group_indicator = liblte_bits_2_value(ie_ptr, 32); + liblte_rrc_unpack_inter_rat_params_ie(ie_ptr, &ue_eutra_capability->inter_rat_params); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + + +/********************************************************************* + IE Name: UE Timers and Constants + + Description: Contains timers and constants used by the UE in + either RRC_CONNECTED or RRC_IDLE + + Document Reference: 36.331 v10.0.0 Section 6.3.6 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_timers_and_constants_ie(LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT *ue_timers_and_constants, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ue_timers_and_constants != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(ue_timers_and_constants->t300, ie_ptr, 3); + liblte_value_2_bits(ue_timers_and_constants->t301, ie_ptr, 3); + liblte_value_2_bits(ue_timers_and_constants->t310, ie_ptr, 3); + liblte_value_2_bits(ue_timers_and_constants->n310, ie_ptr, 3); + liblte_value_2_bits(ue_timers_and_constants->t311, ie_ptr, 3); + liblte_value_2_bits(ue_timers_and_constants->n311, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_timers_and_constants_ie(uint8 **ie_ptr, + LIBLTE_RRC_UE_TIMERS_AND_CONSTANTS_STRUCT *ue_timers_and_constants) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ue_timers_and_constants != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + ue_timers_and_constants->t300 = (LIBLTE_RRC_T300_ENUM)liblte_bits_2_value(ie_ptr, 3); + ue_timers_and_constants->t301 = (LIBLTE_RRC_T301_ENUM)liblte_bits_2_value(ie_ptr, 3); + ue_timers_and_constants->t310 = (LIBLTE_RRC_T310_ENUM)liblte_bits_2_value(ie_ptr, 3); + ue_timers_and_constants->n310 = (LIBLTE_RRC_N310_ENUM)liblte_bits_2_value(ie_ptr, 3); + ue_timers_and_constants->t311 = (LIBLTE_RRC_T311_ENUM)liblte_bits_2_value(ie_ptr, 3); + ue_timers_and_constants->n311 = (LIBLTE_RRC_N311_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Allowed Meas Bandwidth + + Description: Indicates the maximum allowed measurement bandwidth + on a carrier frequency as defined by the parameter + Transmission Bandwidth Configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_allowed_meas_bandwidth_ie(LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM allowed_meas_bw, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(allowed_meas_bw, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_allowed_meas_bandwidth_ie(uint8 **ie_ptr, + LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM *allowed_meas_bw) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + allowed_meas_bw != NULL) + { + *allowed_meas_bw = (LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Hysteresis + + Description: Used within the entry and leave condition of an + event triggered reporting condition + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_hysteresis_ie(uint8 hysteresis, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + // FIXME: Convert from actual value + liblte_value_2_bits(hysteresis, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_hysteresis_ie(uint8 **ie_ptr, + uint8 *hysteresis) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + hysteresis != NULL) + { + // FIXME: Convert to actual value + *hysteresis = liblte_bits_2_value(ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Location Info + + Description: Transfers location information available at the UE to + correlate measurements and UE position information + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Meas Config + + Description: Specifies measurements to be performed by the UE, + and covers intra-frequency, inter-frequency and + inter-RAT mobility as well as configuration of + measurement gaps + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_config_ie(LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(meas_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + if(meas_cnfg->N_meas_obj_to_remove) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_cnfg->meas_obj_to_add_mod_list_present, ie_ptr, 1); + if(meas_cnfg->N_rep_cnfg_to_remove) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_cnfg->rep_cnfg_to_add_mod_list_present, ie_ptr, 1); + if(meas_cnfg->N_meas_id_to_remove) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_cnfg->meas_id_to_add_mod_list_present, ie_ptr, 1); + liblte_value_2_bits(meas_cnfg->quantity_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(meas_cnfg->meas_gap_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(meas_cnfg->s_meas_present, ie_ptr, 1); + liblte_value_2_bits(meas_cnfg->pre_reg_info_hrpd_present, ie_ptr, 1); + liblte_value_2_bits(meas_cnfg->speed_state_params_present, ie_ptr, 1); + + // Meas Object To Remove List + if(0 != meas_cnfg->N_meas_obj_to_remove) + { + liblte_value_2_bits(meas_cnfg->N_meas_obj_to_remove - 1, ie_ptr, 5); + } + for(i=0; iN_meas_obj_to_remove; i++) + { + liblte_rrc_pack_meas_object_id_ie(meas_cnfg->meas_obj_to_remove_list[i], ie_ptr); + } + + // Meas Object To Add Mod List + if(meas_cnfg->meas_obj_to_add_mod_list_present) + { + liblte_rrc_pack_meas_object_to_add_mod_list_ie(&meas_cnfg->meas_obj_to_add_mod_list, ie_ptr); + } + + // Report Config To Remove List + if(0 != meas_cnfg->N_rep_cnfg_to_remove) + { + liblte_value_2_bits(meas_cnfg->N_rep_cnfg_to_remove - 1, ie_ptr, 5); + } + for(i=0; iN_rep_cnfg_to_remove; i++) + { + liblte_rrc_pack_report_config_id_ie(meas_cnfg->rep_cnfg_to_remove_list[i], ie_ptr); + } + + // Report Config To Add Mod List + if(meas_cnfg->rep_cnfg_to_add_mod_list_present) + { + liblte_rrc_pack_report_config_to_add_mod_list_ie(&meas_cnfg->rep_cnfg_to_add_mod_list, ie_ptr); + } + + // Meas ID To Remove List + if(0 != meas_cnfg->N_meas_id_to_remove) + { + liblte_value_2_bits(meas_cnfg->N_meas_id_to_remove - 1, ie_ptr, 5); + } + for(i=0; iN_meas_id_to_remove; i++) + { + liblte_rrc_pack_meas_id_ie(meas_cnfg->meas_id_to_remove_list[i], ie_ptr); + } + + // Meas ID To Add Mod List + if(meas_cnfg->meas_id_to_add_mod_list_present) + { + liblte_rrc_pack_meas_id_to_add_mod_list_ie(&meas_cnfg->meas_id_to_add_mod_list, ie_ptr); + } + + // Quantity Config + if(meas_cnfg->quantity_cnfg_present) + { + liblte_rrc_pack_quantity_config_ie(&meas_cnfg->quantity_cnfg, ie_ptr); + } + + // Meas Gap Config + if(meas_cnfg->meas_gap_cnfg_present) + { + liblte_rrc_pack_meas_gap_config_ie(&meas_cnfg->meas_gap_cnfg, ie_ptr); + } + + // S Measure + if(meas_cnfg->s_meas_present) + { + liblte_rrc_pack_rsrp_range_ie(meas_cnfg->s_meas, ie_ptr); + } + + // Pre Registration Info HRPD + if(meas_cnfg->pre_reg_info_hrpd_present) + { + liblte_rrc_pack_pre_registration_info_hrpd_ie(&meas_cnfg->pre_reg_info_hrpd, ie_ptr); + } + + // Speed State Pars + if(meas_cnfg->speed_state_params_present) + { + // Release choice + liblte_value_2_bits(1, ie_ptr, 1); + + // Mobility State Parameters + liblte_rrc_pack_mobility_state_parameters_ie(&meas_cnfg->speed_state_params.mob_state_params, ie_ptr); + + // Time To Trigger SF + liblte_rrc_pack_speed_state_scale_factors_ie(&meas_cnfg->speed_state_params.time_to_trig_sf, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool meas_obj_to_remove_present; + bool rep_cnfg_to_remove_present; + bool meas_id_to_remove_present; + + if(ie_ptr != NULL && + meas_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + meas_obj_to_remove_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->meas_obj_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + rep_cnfg_to_remove_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->rep_cnfg_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_id_to_remove_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->meas_id_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->quantity_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->meas_gap_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->s_meas_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->pre_reg_info_hrpd_present = liblte_bits_2_value(ie_ptr, 1); + meas_cnfg->speed_state_params_present = liblte_bits_2_value(ie_ptr, 1); + + // Meas Object To Remove List + if(meas_obj_to_remove_present) + { + meas_cnfg->N_meas_obj_to_remove = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_meas_obj_to_remove; i++) + { + liblte_rrc_unpack_meas_object_id_ie(ie_ptr, &meas_cnfg->meas_obj_to_remove_list[i]); + } + }else{ + meas_cnfg->N_meas_obj_to_remove = 0; + } + + // Meas Object To Add Mod List + if(meas_cnfg->meas_obj_to_add_mod_list_present) + { + liblte_rrc_unpack_meas_object_to_add_mod_list_ie(ie_ptr, &meas_cnfg->meas_obj_to_add_mod_list); + } + + // Report Config To Remove List + if(rep_cnfg_to_remove_present) + { + meas_cnfg->N_rep_cnfg_to_remove = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_rep_cnfg_to_remove; i++) + { + liblte_rrc_unpack_report_config_id_ie(ie_ptr, &meas_cnfg->rep_cnfg_to_remove_list[i]); + } + }else{ + meas_cnfg->N_rep_cnfg_to_remove = 0; + } + + // Report Config To Add Mod List + if(meas_cnfg->rep_cnfg_to_add_mod_list_present) + { + liblte_rrc_unpack_report_config_to_add_mod_list_ie(ie_ptr, &meas_cnfg->rep_cnfg_to_add_mod_list); + } + + // Meas ID To Remove List + if(meas_id_to_remove_present) + { + meas_cnfg->N_meas_id_to_remove = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_meas_id_to_remove; i++) + { + liblte_rrc_unpack_meas_id_ie(ie_ptr, &meas_cnfg->meas_id_to_remove_list[i]); + } + }else{ + meas_cnfg->N_meas_id_to_remove = 0; + } + + // Meas ID To Add Mod List + if(meas_cnfg->meas_id_to_add_mod_list_present) + { + liblte_rrc_unpack_meas_id_to_add_mod_list_ie(ie_ptr, &meas_cnfg->meas_id_to_add_mod_list); + } + + // Quantity Config + if(meas_cnfg->quantity_cnfg_present) + { + liblte_rrc_unpack_quantity_config_ie(ie_ptr, &meas_cnfg->quantity_cnfg); + } + + // Meas Gap Config + if(meas_cnfg->meas_gap_cnfg_present) + { + liblte_rrc_unpack_meas_gap_config_ie(ie_ptr, &meas_cnfg->meas_gap_cnfg); + } + + // S Measure + if(meas_cnfg->s_meas_present) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &meas_cnfg->s_meas); + } + + // Pre Registration Info HRPD + if(meas_cnfg->pre_reg_info_hrpd_present) + { + liblte_rrc_unpack_pre_registration_info_hrpd_ie(ie_ptr, &meas_cnfg->pre_reg_info_hrpd); + } + + // Speed State Pars + if(meas_cnfg->speed_state_params_present) + { + // Release choice + meas_cnfg->speed_state_params_present = liblte_bits_2_value(ie_ptr, 1); + + if(meas_cnfg->speed_state_params_present) + { + // Mobility State Parameters + liblte_rrc_unpack_mobility_state_parameters_ie(ie_ptr, &meas_cnfg->speed_state_params.mob_state_params); + + // Time To Trigger SF + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &meas_cnfg->speed_state_params.time_to_trig_sf); + } + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Gap Config + + Description: Specifies the measurement gap configuration and + controls setup/release of measurement gaps + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_gap_config_ie(LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT *meas_gap_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(meas_gap_cnfg != NULL && + ie_ptr != NULL) + { + // Release choice + liblte_value_2_bits(meas_gap_cnfg->setup_present, ie_ptr, 1); + + if(meas_gap_cnfg->setup_present) + { + // Gap Offset Type + liblte_value_2_bits(meas_gap_cnfg->gap_offset_type, ie_ptr, 1); + + // Gap Offset + if(LIBLTE_RRC_GAP_OFFSET_TYPE_GP0 == meas_gap_cnfg->gap_offset_type) + { + liblte_value_2_bits(meas_gap_cnfg->gap_offset, ie_ptr, 6); + }else{ + liblte_value_2_bits(meas_gap_cnfg->gap_offset, ie_ptr, 7); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_gap_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_GAP_CONFIG_STRUCT *meas_gap_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_gap_cnfg != NULL) + { + // Release choice + meas_gap_cnfg->setup_present = liblte_bits_2_value(ie_ptr, 1); + + if(meas_gap_cnfg->setup_present) + { + // Gap Offset Type + meas_gap_cnfg->gap_offset_type = (LIBLTE_RRC_GAP_OFFSET_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // Gap Offset + if(LIBLTE_RRC_GAP_OFFSET_TYPE_GP0 == meas_gap_cnfg->gap_offset_type) + { + meas_gap_cnfg->gap_offset = liblte_bits_2_value(ie_ptr, 6); + }else{ + meas_gap_cnfg->gap_offset = liblte_bits_2_value(ie_ptr, 7); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas ID + + Description: Identifies a measurement configuration, i.e. linking + of a measurement object and a reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_id_ie(uint8 meas_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(meas_id - 1, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_id_ie(uint8 **ie_ptr, + uint8 *meas_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_id != NULL) + { + *meas_id = liblte_bits_2_value(ie_ptr, 5) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Id To Add Mod List + + Description: Concerns a list of measurement identities to add or + modify, with for each entry the meas ID, the + associated meas object ID and the associated report + config ID + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_id_to_add_mod_list_ie(LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(list != NULL && + ie_ptr != NULL) + { + // List Size + liblte_value_2_bits(list->N_meas_id - 1, ie_ptr, 5); + + for(i=0; iN_meas_id; i++) + { + // Meas ID + liblte_rrc_pack_meas_id_ie(list->meas_id_list[i].meas_id, ie_ptr); + + // Meas Object ID + liblte_rrc_pack_meas_object_id_ie(list->meas_id_list[i].meas_obj_id, ie_ptr); + + // Report Config ID + liblte_rrc_pack_report_config_id_ie(list->meas_id_list[i].rep_cnfg_id, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_id_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_LIST_STRUCT *list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + list != NULL) + { + // List Size + list->N_meas_id = liblte_bits_2_value(ie_ptr, 5) + 1; + + for(i=0; iN_meas_id; i++) + { + // Meas ID + liblte_rrc_unpack_meas_id_ie(ie_ptr, &list->meas_id_list[i].meas_id); + + // Meas Object ID + liblte_rrc_unpack_meas_object_id_ie(ie_ptr, &list->meas_id_list[i].meas_obj_id); + + // Report Config ID + liblte_rrc_unpack_report_config_id_ie(ie_ptr, &list->meas_id_list[i].rep_cnfg_id); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object CDMA2000 + + Description: Specifies information applicable for inter-RAT + CDMA2000 neighboring cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_cdma2000_ie(LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT *meas_obj_cdma2000, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(meas_obj_cdma2000 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(meas_obj_cdma2000->search_win_size_present, ie_ptr, 1); + liblte_value_2_bits(meas_obj_cdma2000->cells_to_remove_list_present, ie_ptr, 1); + if(0 != meas_obj_cdma2000->N_cells_to_add_mod) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_obj_cdma2000->cell_for_which_to_rep_cgi_present, ie_ptr, 1); + + // CDMA2000 Type + liblte_rrc_pack_cdma2000_type_ie(meas_obj_cdma2000->cdma2000_type, ie_ptr); + + // Carrier Freq + liblte_rrc_pack_carrier_freq_cdma2000_ie(&meas_obj_cdma2000->carrier_freq, ie_ptr); + + // Search Window Size + if(meas_obj_cdma2000->search_win_size_present) + { + liblte_value_2_bits(meas_obj_cdma2000->search_win_size, ie_ptr, 4); + } + + // Offset Freq + liblte_rrc_pack_q_offset_range_inter_rat_ie(meas_obj_cdma2000->offset_freq, ie_ptr); + + // Cells To Remove List + if(meas_obj_cdma2000->cells_to_remove_list_present) + { + liblte_rrc_pack_cell_index_list_ie(&meas_obj_cdma2000->cells_to_remove_list, ie_ptr); + } + + // Cells To Add Mod List + if(0 != meas_obj_cdma2000->N_cells_to_add_mod) + { + liblte_value_2_bits(meas_obj_cdma2000->N_cells_to_add_mod - 1, ie_ptr, 5); + } + for(i=0; iN_cells_to_add_mod; i++) + { + // Cell Index + liblte_value_2_bits(meas_obj_cdma2000->cells_to_add_mod_list[i].cell_idx - 1, ie_ptr, 5); + + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_cdma2000_ie(meas_obj_cdma2000->cells_to_add_mod_list[i].pci, ie_ptr); + } + + // Cell For Which To Report CGI + if(meas_obj_cdma2000->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_pack_phys_cell_id_cdma2000_ie(meas_obj_cdma2000->cell_for_which_to_rep_cgi_present, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_CDMA2000_STRUCT *meas_obj_cdma2000) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool cells_to_add_mod_present; + + if(ie_ptr != NULL && + meas_obj_cdma2000 != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + meas_obj_cdma2000->search_win_size_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_cdma2000->cells_to_remove_list_present = liblte_bits_2_value(ie_ptr, 1); + cells_to_add_mod_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_cdma2000->cell_for_which_to_rep_cgi_present = liblte_bits_2_value(ie_ptr, 1); + + // CDMA2000 Type + liblte_rrc_unpack_cdma2000_type_ie(ie_ptr, &meas_obj_cdma2000->cdma2000_type); + + // Carrier Freq + liblte_rrc_unpack_carrier_freq_cdma2000_ie(ie_ptr, &meas_obj_cdma2000->carrier_freq); + + // Search Window Size + if(meas_obj_cdma2000->search_win_size_present) + { + meas_obj_cdma2000->search_win_size = liblte_bits_2_value(ie_ptr, 4); + } + + // Offset Freq + liblte_rrc_unpack_q_offset_range_inter_rat_ie(ie_ptr, &meas_obj_cdma2000->offset_freq); + + // Cells To Remove List + if(meas_obj_cdma2000->cells_to_remove_list_present) + { + liblte_rrc_unpack_cell_index_list_ie(ie_ptr, &meas_obj_cdma2000->cells_to_remove_list); + } + + // Cells To Add Mod List + if(cells_to_add_mod_present) + { + meas_obj_cdma2000->N_cells_to_add_mod = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_cells_to_add_mod; i++) + { + // Cell Index + meas_obj_cdma2000->cells_to_add_mod_list[i].cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_cdma2000_ie(ie_ptr, &meas_obj_cdma2000->cells_to_add_mod_list[i].pci); + } + }else{ + meas_obj_cdma2000->N_cells_to_add_mod = 0; + } + + // Cell For Which To Report CGI + if(meas_obj_cdma2000->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_unpack_phys_cell_id_cdma2000_ie(ie_ptr, &meas_obj_cdma2000->cell_for_which_to_rep_cgi); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object EUTRA + + Description: Specifies information applicable for intra-frequency + or inter-frequency E-UTRA cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_eutra_ie(LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *meas_obj_eutra, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(meas_obj_eutra != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(meas_obj_eutra->offset_freq_not_default, ie_ptr, 1); + liblte_value_2_bits(meas_obj_eutra->cells_to_remove_list_present, ie_ptr, 1); + if(0 != meas_obj_eutra->N_cells_to_add_mod) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_obj_eutra->black_cells_to_remove_list_present, ie_ptr, 1); + if(0 != meas_obj_eutra->N_black_cells_to_add_mod) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(meas_obj_eutra->cell_for_which_to_rep_cgi_present, ie_ptr, 1); + + // Carrier Freq + liblte_rrc_pack_arfcn_value_eutra_ie(meas_obj_eutra->carrier_freq, ie_ptr); + + // Allowed Meas Bandwidth + liblte_rrc_pack_allowed_meas_bandwidth_ie(meas_obj_eutra->allowed_meas_bw, ie_ptr); + + // Presence Antenna Port 1 + liblte_rrc_pack_presence_antenna_port_1_ie(meas_obj_eutra->presence_ant_port_1, ie_ptr); + + // Neigh Cell Config + liblte_rrc_pack_neigh_cell_config_ie(meas_obj_eutra->neigh_cell_cnfg, ie_ptr); + + // Offset Freq + if(meas_obj_eutra->offset_freq_not_default) + { + liblte_rrc_pack_q_offset_range_ie(meas_obj_eutra->offset_freq, ie_ptr); + } + + // Cells To Remove List + if(meas_obj_eutra->cells_to_remove_list_present) + { + liblte_rrc_pack_cell_index_list_ie(&meas_obj_eutra->cells_to_remove_list, ie_ptr); + } + + // Cells To Add Mod List + if(0 != meas_obj_eutra->N_cells_to_add_mod) + { + liblte_value_2_bits(meas_obj_eutra->N_cells_to_add_mod - 1, ie_ptr, 5); + } + for(i=0; iN_cells_to_add_mod; i++) + { + // Cell Index + liblte_value_2_bits(meas_obj_eutra->cells_to_add_mod_list[i].cell_idx - 1, ie_ptr, 5); + + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_ie(meas_obj_eutra->cells_to_add_mod_list[i].pci, ie_ptr); + + // Cell Individual Offset + liblte_rrc_pack_q_offset_range_ie(meas_obj_eutra->cells_to_add_mod_list[i].cell_offset, ie_ptr); + } + + // Black Cells To Remove List + if(meas_obj_eutra->black_cells_to_remove_list_present) + { + liblte_rrc_pack_cell_index_list_ie(&meas_obj_eutra->black_cells_to_remove_list, ie_ptr); + } + + // Black Cells To Add Mod List + if(0 != meas_obj_eutra->N_black_cells_to_add_mod) + { + liblte_value_2_bits(meas_obj_eutra->N_black_cells_to_add_mod - 1, ie_ptr, 5); + } + for(i=0; iN_black_cells_to_add_mod; i++) + { + // Cell Index + liblte_value_2_bits(meas_obj_eutra->black_cells_to_add_mod_list[i].cell_idx - 1, ie_ptr, 5); + + // Phys Cell ID Range + liblte_rrc_pack_phys_cell_id_range_ie(&meas_obj_eutra->black_cells_to_add_mod_list[i].pci_range, ie_ptr); + } + + // Cell For Which To Report CGI + if(meas_obj_eutra->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_pack_phys_cell_id_ie(meas_obj_eutra->cell_for_which_to_rep_cgi, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *meas_obj_eutra) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool cells_to_add_mod_list_present; + bool black_cells_to_add_mod_list_present; + + if(ie_ptr != NULL && + meas_obj_eutra != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + meas_obj_eutra->offset_freq_not_default = liblte_bits_2_value(ie_ptr, 1); + meas_obj_eutra->cells_to_remove_list_present = liblte_bits_2_value(ie_ptr, 1); + cells_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_eutra->black_cells_to_remove_list_present = liblte_bits_2_value(ie_ptr, 1); + black_cells_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_eutra->cell_for_which_to_rep_cgi_present = liblte_bits_2_value(ie_ptr, 1); + + // Carrier Freq + liblte_rrc_unpack_arfcn_value_eutra_ie(ie_ptr, &meas_obj_eutra->carrier_freq); + + // Allowed Meas Bandwidth + liblte_rrc_unpack_allowed_meas_bandwidth_ie(ie_ptr, &meas_obj_eutra->allowed_meas_bw); + + // Presence Antenna Port 1 + liblte_rrc_unpack_presence_antenna_port_1_ie(ie_ptr, &meas_obj_eutra->presence_ant_port_1); + + // Neigh Cell Config + liblte_rrc_unpack_neigh_cell_config_ie(ie_ptr, &meas_obj_eutra->neigh_cell_cnfg); + + // Offset Freq + if(meas_obj_eutra->offset_freq_not_default) + { + liblte_rrc_unpack_q_offset_range_ie(ie_ptr, &meas_obj_eutra->offset_freq); + } + + // Cells To Remove List + if(meas_obj_eutra->cells_to_remove_list_present) + { + liblte_rrc_unpack_cell_index_list_ie(ie_ptr, &meas_obj_eutra->cells_to_remove_list); + } + + // Cells To Add Mod List + if(cells_to_add_mod_list_present) + { + meas_obj_eutra->N_cells_to_add_mod = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_cells_to_add_mod; i++) + { + // Cell Index + meas_obj_eutra->cells_to_add_mod_list[i].cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &meas_obj_eutra->cells_to_add_mod_list[i].pci); + + // Cell Individual Offset + liblte_rrc_unpack_q_offset_range_ie(ie_ptr, &meas_obj_eutra->cells_to_add_mod_list[i].cell_offset); + } + }else{ + meas_obj_eutra->N_cells_to_add_mod = 0; + } + + // Black Cells To Remove List + if(meas_obj_eutra->black_cells_to_remove_list_present) + { + liblte_rrc_unpack_cell_index_list_ie(ie_ptr, &meas_obj_eutra->black_cells_to_remove_list); + } + + // Black Cells To Add Mod List + if(black_cells_to_add_mod_list_present) + { + meas_obj_eutra->N_black_cells_to_add_mod = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; iN_black_cells_to_add_mod; i++) + { + // Cell Index + meas_obj_eutra->black_cells_to_add_mod_list[i].cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + // Phys Cell ID Range + liblte_rrc_unpack_phys_cell_id_range_ie(ie_ptr, &meas_obj_eutra->black_cells_to_add_mod_list[i].pci_range); + } + }else{ + meas_obj_eutra->N_black_cells_to_add_mod = 0; + } + + // Cell For Which To Report CGI + if(meas_obj_eutra->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &meas_obj_eutra->cell_for_which_to_rep_cgi); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object GERAN + + Description: Specifies information applicable for inter-RAT + GERAN neighboring frequencies + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_geran_ie(LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT *meas_obj_geran, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(meas_obj_geran != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(meas_obj_geran->cell_for_which_to_rep_cgi_present, ie_ptr, 1); + + // Carrier Freqs + liblte_rrc_pack_carrier_freqs_geran_ie(&meas_obj_geran->carrier_freqs, ie_ptr); + + // Offset Freq + liblte_rrc_pack_q_offset_range_inter_rat_ie(meas_obj_geran->offset_freq, ie_ptr); + + // NCC Permitted + liblte_value_2_bits(meas_obj_geran->ncc_permitted, ie_ptr, 8); + + // Cell For Which To Report CGI + if(meas_obj_geran->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_pack_phys_cell_id_geran_ie(&meas_obj_geran->cell_for_which_to_rep_cgi, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_GERAN_STRUCT *meas_obj_geran) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_obj_geran != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + meas_obj_geran->cell_for_which_to_rep_cgi_present = liblte_bits_2_value(ie_ptr, 1); + + // Carrier Freqs + liblte_rrc_unpack_carrier_freqs_geran_ie(ie_ptr, &meas_obj_geran->carrier_freqs); + + // Offset Freq + liblte_rrc_unpack_q_offset_range_inter_rat_ie(ie_ptr, &meas_obj_geran->offset_freq); + + // NCC Permitted + meas_obj_geran->ncc_permitted = liblte_bits_2_value(ie_ptr, 8); + + // Cell For Which To Report CGI + if(meas_obj_geran->cell_for_which_to_rep_cgi_present) + { + liblte_rrc_unpack_phys_cell_id_geran_ie(ie_ptr, &meas_obj_geran->cell_for_which_to_rep_cgi); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object ID + + Description: Identifies a measurement object configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_id_ie(uint8 meas_object_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(meas_object_id - 1, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_id_ie(uint8 **ie_ptr, + uint8 *meas_object_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_object_id != NULL) + { + *meas_object_id = liblte_bits_2_value(ie_ptr, 5) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object To Add Mod List + + Description: Concerns a list of measurement objects to add or + modify + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_to_add_mod_list_ie(LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(list != NULL && + ie_ptr != NULL) + { + // List Size + liblte_value_2_bits(list->N_meas_obj - 1, ie_ptr, 5); + + for(i=0; iN_meas_obj; i++) + { + // Meas Object ID + liblte_rrc_pack_meas_object_id_ie(list->meas_obj_list[i].meas_obj_id, ie_ptr); + + // Meas Object Choice Extension + liblte_value_2_bits(0, ie_ptr, 1); // Choice from before extension marker + + // Meas Object Choice + liblte_value_2_bits(list->meas_obj_list[i].meas_obj_type, ie_ptr, 2); + + // Meas Object + if(LIBLTE_RRC_MEAS_OBJECT_TYPE_EUTRA == list->meas_obj_list[i].meas_obj_type) + { + liblte_rrc_pack_meas_object_eutra_ie(&list->meas_obj_list[i].meas_obj_eutra, ie_ptr); + }else if(LIBLTE_RRC_MEAS_OBJECT_TYPE_UTRA == list->meas_obj_list[i].meas_obj_type){ + liblte_rrc_pack_meas_object_utra_ie(&list->meas_obj_list[i].meas_obj_utra, ie_ptr); + }else if(LIBLTE_RRC_MEAS_OBJECT_TYPE_GERAN == list->meas_obj_list[i].meas_obj_type){ + liblte_rrc_pack_meas_object_geran_ie(&list->meas_obj_list[i].meas_obj_geran, ie_ptr); + }else{ + liblte_rrc_pack_meas_object_cdma2000_ie(&list->meas_obj_list[i].meas_obj_cdma2000, ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_TO_ADD_MOD_LIST_STRUCT *list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + list != NULL) + { + // List Size + list->N_meas_obj = liblte_bits_2_value(ie_ptr, 5) + 1; + + for(i=0; iN_meas_obj; i++) + { + // Meas Object ID + liblte_rrc_unpack_meas_object_id_ie(ie_ptr, &list->meas_obj_list[i].meas_obj_id); + + // Meas Object Choice Extension + liblte_bits_2_value(ie_ptr, 1); + + // Meas Object Choice + list->meas_obj_list[i].meas_obj_type = (LIBLTE_RRC_MEAS_OBJECT_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // Meas Object + if(LIBLTE_RRC_MEAS_OBJECT_TYPE_EUTRA == list->meas_obj_list[i].meas_obj_type) + { + liblte_rrc_unpack_meas_object_eutra_ie(ie_ptr, &list->meas_obj_list[i].meas_obj_eutra); + }else if(LIBLTE_RRC_MEAS_OBJECT_TYPE_UTRA == list->meas_obj_list[i].meas_obj_type){ + liblte_rrc_unpack_meas_object_utra_ie(ie_ptr, &list->meas_obj_list[i].meas_obj_utra); + }else if(LIBLTE_RRC_MEAS_OBJECT_TYPE_GERAN == list->meas_obj_list[i].meas_obj_type){ + liblte_rrc_unpack_meas_object_geran_ie(ie_ptr, &list->meas_obj_list[i].meas_obj_geran); + }else{ + liblte_rrc_unpack_meas_object_cdma2000_ie(ie_ptr, &list->meas_obj_list[i].meas_obj_cdma2000); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Object UTRA + + Description: Specifies information applicable for inter-RAT UTRA + neighboring cells + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_object_utra_ie(LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT *meas_obj_utra, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(meas_obj_utra != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(meas_obj_utra->cells_to_remove_list_present, ie_ptr, 1); + liblte_value_2_bits(meas_obj_utra->cells_to_add_mod_list_present, ie_ptr, 1); + liblte_value_2_bits(meas_obj_utra->cells_for_which_to_rep_cgi_present, ie_ptr, 1); + + // Carrier Freq + liblte_rrc_pack_arfcn_value_utra_ie(meas_obj_utra->carrier_freq, ie_ptr); + + // Offset Freq + liblte_rrc_pack_q_offset_range_inter_rat_ie(meas_obj_utra->offset_freq, ie_ptr); + + // Cells To Remove List + if(meas_obj_utra->cells_to_remove_list_present) + { + liblte_rrc_pack_cell_index_list_ie(&meas_obj_utra->cells_to_remove_list, ie_ptr); + } + + // Cells To Add Mod List + if(meas_obj_utra->cells_to_add_mod_list_present) + { + // UTRA System Type + liblte_value_2_bits(meas_obj_utra->cells_to_add_mod_list.type, ie_ptr, 1); + + liblte_value_2_bits(meas_obj_utra->cells_to_add_mod_list.N_cells - 1, ie_ptr, 5); + for(i=0; icells_to_add_mod_list.N_cells; i++) + { + if(LIBLTE_RRC_UTRA_SYSTEM_TYPE_FDD == meas_obj_utra->cells_to_add_mod_list.type) + { + // Cell Index + liblte_value_2_bits(meas_obj_utra->cells_to_add_mod_list.cells_fdd[i].cell_idx - 1, ie_ptr, 5); + + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_utra_fdd_ie(meas_obj_utra->cells_to_add_mod_list.cells_fdd[i].pci, ie_ptr); + }else{ + // Cell Index + liblte_value_2_bits(meas_obj_utra->cells_to_add_mod_list.cells_tdd[i].cell_idx - 1, ie_ptr, 5); + + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_utra_tdd_ie(meas_obj_utra->cells_to_add_mod_list.cells_tdd[i].pci, ie_ptr); + } + } + } + + // Cells For Which To Report CGI + if(meas_obj_utra->cells_for_which_to_rep_cgi_present) + { + // UTRA System Type + liblte_value_2_bits(meas_obj_utra->cells_for_which_to_rep_cgi.type, ie_ptr, 1); + + if(LIBLTE_RRC_UTRA_SYSTEM_TYPE_FDD == meas_obj_utra->cells_for_which_to_rep_cgi.type) + { + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_utra_fdd_ie(meas_obj_utra->cells_for_which_to_rep_cgi.pci_fdd, ie_ptr); + }else{ + // Phys Cell ID + liblte_rrc_pack_phys_cell_id_utra_tdd_ie(meas_obj_utra->cells_for_which_to_rep_cgi.pci_tdd, ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_object_utra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_OBJECT_UTRA_STRUCT *meas_obj_utra) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + meas_obj_utra != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + meas_obj_utra->cells_to_remove_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_utra->cells_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + meas_obj_utra->cells_for_which_to_rep_cgi_present = liblte_bits_2_value(ie_ptr, 1); + + // Carrier Freq + liblte_rrc_unpack_arfcn_value_utra_ie(ie_ptr, &meas_obj_utra->carrier_freq); + + // Offset Freq + liblte_rrc_unpack_q_offset_range_inter_rat_ie(ie_ptr, &meas_obj_utra->offset_freq); + + // Cells To Remove List + if(meas_obj_utra->cells_to_remove_list_present) + { + liblte_rrc_unpack_cell_index_list_ie(ie_ptr, &meas_obj_utra->cells_to_remove_list); + } + + // Cells To Add Mod List + if(meas_obj_utra->cells_to_add_mod_list_present) + { + // UTRA System Type + meas_obj_utra->cells_to_add_mod_list.type = (LIBLTE_RRC_UTRA_SYSTEM_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + + meas_obj_utra->cells_to_add_mod_list.N_cells = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; icells_to_add_mod_list.N_cells; i++) + { + if(LIBLTE_RRC_UTRA_SYSTEM_TYPE_FDD == meas_obj_utra->cells_to_add_mod_list.type) + { + // Cell Index + meas_obj_utra->cells_to_add_mod_list.cells_fdd[i].cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_utra_fdd_ie(ie_ptr, &meas_obj_utra->cells_to_add_mod_list.cells_fdd[i].pci); + }else{ + // Cell Index + meas_obj_utra->cells_to_add_mod_list.cells_tdd[i].cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_utra_tdd_ie(ie_ptr, &meas_obj_utra->cells_to_add_mod_list.cells_tdd[i].pci); + } + } + } + + // Cells For Which To Report CGI + if(meas_obj_utra->cells_for_which_to_rep_cgi_present) + { + // UTRA System Type + meas_obj_utra->cells_for_which_to_rep_cgi.type = (LIBLTE_RRC_UTRA_SYSTEM_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + + if(LIBLTE_RRC_UTRA_SYSTEM_TYPE_FDD == meas_obj_utra->cells_for_which_to_rep_cgi.type) + { + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_utra_fdd_ie(ie_ptr, &meas_obj_utra->cells_for_which_to_rep_cgi.pci_fdd); + }else{ + // Phys Cell ID + liblte_rrc_unpack_phys_cell_id_utra_tdd_ie(ie_ptr, &meas_obj_utra->cells_for_which_to_rep_cgi.pci_tdd); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Meas Results + + Description: Covers measured results for intra-frequency, + inter-frequency and inter-RAT mobility + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Quantity Config + + Description: Specifies the measurement quantities and layer 3 + filtering coefficients for E-UTRA and inter-RAT + measurements + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_quantity_config_ie(LIBLTE_RRC_QUANTITY_CONFIG_STRUCT *qc, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(qc != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(qc->qc_eutra_present, ie_ptr, 1); + liblte_value_2_bits(qc->qc_utra_present, ie_ptr, 1); + liblte_value_2_bits(qc->qc_geran_present, ie_ptr, 1); + liblte_value_2_bits(qc->qc_cdma2000_present, ie_ptr, 1); + + // Quality Config EUTRA + if(qc->qc_eutra_present) + { + liblte_rrc_pack_filter_coefficient_ie(qc->qc_eutra.fc_rsrp, ie_ptr); + liblte_rrc_pack_filter_coefficient_ie(qc->qc_eutra.fc_rsrq, ie_ptr); + } + + // Quality Config UTRA + if(qc->qc_utra_present) + { + liblte_value_2_bits(qc->qc_utra.mq_fdd, ie_ptr, 1); + liblte_value_2_bits(qc->qc_utra.mq_tdd, ie_ptr, 1); + liblte_rrc_pack_filter_coefficient_ie(qc->qc_utra.fc, ie_ptr); + } + + // Quality Config GERAN + if(qc->qc_geran_present) + { + liblte_value_2_bits(qc->qc_geran.mq, ie_ptr, 1); + liblte_rrc_pack_filter_coefficient_ie(qc->qc_geran.fc, ie_ptr); + } + + // Quality Config CDMA2000 + if(qc->qc_cdma2000_present) + { + liblte_value_2_bits(qc->qc_cdma2000.mq, ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_quantity_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_QUANTITY_CONFIG_STRUCT *qc) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + qc != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + qc->qc_eutra_present = liblte_bits_2_value(ie_ptr, 1); + qc->qc_utra_present = liblte_bits_2_value(ie_ptr, 1); + qc->qc_geran_present = liblte_bits_2_value(ie_ptr, 1); + qc->qc_cdma2000_present = liblte_bits_2_value(ie_ptr, 1); + + // Quantity Config EUTRA + if(qc->qc_eutra_present) + { + qc->qc_eutra.fc_rsrp_not_default = liblte_bits_2_value(ie_ptr, 1); + qc->qc_eutra.fc_rsrq_not_default = liblte_bits_2_value(ie_ptr, 1); + if(qc->qc_eutra.fc_rsrp_not_default) { + liblte_rrc_unpack_filter_coefficient_ie(ie_ptr, &qc->qc_eutra.fc_rsrp); + } + if(qc->qc_eutra.fc_rsrq_not_default) { + liblte_rrc_unpack_filter_coefficient_ie(ie_ptr, &qc->qc_eutra.fc_rsrq); + } + } + + // Quantity Config UTRA + if(qc->qc_utra_present) + { + qc->qc_utra.fc_not_default = liblte_bits_2_value(ie_ptr, 1); + qc->qc_utra.mq_fdd = (LIBLTE_RRC_MEAS_QUANTITY_UTRA_FDD_ENUM)liblte_bits_2_value(ie_ptr, 1); + qc->qc_utra.mq_tdd = LIBLTE_RRC_MEAS_QUANTITY_UTRA_TDD_PCCPCH_RSCP; + if(qc->qc_utra.fc_not_default) { + liblte_rrc_unpack_filter_coefficient_ie(ie_ptr, &qc->qc_utra.fc); + } + } + + // Quantity Config GERAN + if(qc->qc_geran_present) + { + qc->qc_geran.fc_not_default = liblte_bits_2_value(ie_ptr, 1); + qc->qc_geran.mq = LIBLTE_RRC_MEAS_QUANTITY_GERAN_RSSI; + if(qc->qc_geran.fc_not_default) { + liblte_rrc_unpack_filter_coefficient_ie(ie_ptr, &qc->qc_geran.fc); + } + } + + // Quality Config CDMA2000 + if(qc->qc_cdma2000_present) + { + qc->qc_cdma2000.mq = (LIBLTE_RRC_MEAS_QUANTITY_CDMA2000_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Report Config EUTRA + + Description: Specifies criteria for triggering of an E-UTRA + measurement reporting event + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_eutra_ie(LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *rep_cnfg_eutra, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rep_cnfg_eutra != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Trigger Type + liblte_value_2_bits(rep_cnfg_eutra->trigger_type, ie_ptr, 1); + if(LIBLTE_RRC_TRIGGER_TYPE_EUTRA_EVENT == rep_cnfg_eutra->trigger_type) + { + // Event ID choice extension indicator + liblte_value_2_bits(0, ie_ptr, 1); // Choice with extension - unlikely to be >63 choices + + // Event ID + liblte_value_2_bits(rep_cnfg_eutra->event.event_id, ie_ptr, 3); + if(LIBLTE_RRC_EVENT_ID_EUTRA_A1 == rep_cnfg_eutra->event.event_id) + { + // Threshold Type + liblte_value_2_bits(rep_cnfg_eutra->event.event_a1.eutra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a1.eutra.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_eutra->event.event_a1.eutra.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_eutra->event.event_a1.eutra.range, ie_ptr); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A2 == rep_cnfg_eutra->event.event_id){ + // Threshold Type + liblte_value_2_bits(rep_cnfg_eutra->event.event_a2.eutra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a2.eutra.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_eutra->event.event_a2.eutra.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_eutra->event.event_a2.eutra.range, ie_ptr); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A3 == rep_cnfg_eutra->event.event_id){ + // Offset + liblte_value_2_bits(rep_cnfg_eutra->event.event_a3.offset + 30, ie_ptr, 6); + + // Report On Leave + liblte_value_2_bits(rep_cnfg_eutra->event.event_a3.report_on_leave, ie_ptr, 1); + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A4 == rep_cnfg_eutra->event.event_id){ + // Threshold Type + liblte_value_2_bits(rep_cnfg_eutra->event.event_a4.eutra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a4.eutra.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_eutra->event.event_a4.eutra.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_eutra->event.event_a4.eutra.range, ie_ptr); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A5 == rep_cnfg_eutra->event.event_id){ + // Threshold1 Type + liblte_value_2_bits(rep_cnfg_eutra->event.event_a5.eutra1.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a5.eutra1.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_eutra->event.event_a5.eutra1.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_eutra->event.event_a5.eutra1.range, ie_ptr); + } + + // Threshold2 Type + liblte_value_2_bits(rep_cnfg_eutra->event.event_a5.eutra2.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a5.eutra2.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_eutra->event.event_a5.eutra2.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_eutra->event.event_a5.eutra2.range, ie_ptr); + } + }else{ + // Offset + liblte_value_2_bits(rep_cnfg_eutra->event.event_a6.offset + 30, ie_ptr, 6); + + // Report On Leave + liblte_value_2_bits(rep_cnfg_eutra->event.event_a6.report_on_leave, ie_ptr, 1); + } + + // Hysteresis + liblte_rrc_pack_hysteresis_ie(rep_cnfg_eutra->event.hysteresis, ie_ptr); + + // Time To Trigger + liblte_rrc_pack_time_to_trigger_ie(rep_cnfg_eutra->event.time_to_trigger, ie_ptr); + }else{ + // Purpose + liblte_value_2_bits(rep_cnfg_eutra->periodical.purpose, ie_ptr, 1); + } + + // Trigger Quantity + liblte_value_2_bits(rep_cnfg_eutra->trigger_quantity, ie_ptr, 1); + + // Report Quantity + liblte_value_2_bits(rep_cnfg_eutra->report_quantity, ie_ptr, 1); + + // Max Report Cells + liblte_value_2_bits(rep_cnfg_eutra->max_report_cells - 1, ie_ptr, 3); + + // Report Interval + liblte_rrc_pack_report_interval_ie(rep_cnfg_eutra->report_interval, ie_ptr); + + // Report Amount + liblte_value_2_bits(rep_cnfg_eutra->report_amount, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *rep_cnfg_eutra) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rep_cnfg_eutra != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Trigger Type + rep_cnfg_eutra->trigger_type = (LIBLTE_RRC_TRIGGER_TYPE_EUTRA_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_TRIGGER_TYPE_EUTRA_EVENT == rep_cnfg_eutra->trigger_type) + { + // Event ID choice extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Event ID + rep_cnfg_eutra->event.event_id = (LIBLTE_RRC_EVENT_ID_EUTRA_ENUM)liblte_bits_2_value(ie_ptr, 3); + + if(LIBLTE_RRC_EVENT_ID_EUTRA_A1 == rep_cnfg_eutra->event.event_id) + { + // Threshold Type + rep_cnfg_eutra->event.event_a1.eutra.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a1.eutra.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a1.eutra.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a1.eutra.range); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A2 == rep_cnfg_eutra->event.event_id){ + // Threshold Type + rep_cnfg_eutra->event.event_a2.eutra.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a2.eutra.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a2.eutra.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a2.eutra.range); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A3 == rep_cnfg_eutra->event.event_id){ + // Offset + rep_cnfg_eutra->event.event_a3.offset = liblte_bits_2_value(ie_ptr, 6) - 30; + + // Report On Leave + rep_cnfg_eutra->event.event_a3.report_on_leave = liblte_bits_2_value(ie_ptr, 1); + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A4 == rep_cnfg_eutra->event.event_id){ + // Threshold Type + rep_cnfg_eutra->event.event_a4.eutra.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a4.eutra.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a4.eutra.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a4.eutra.range); + } + }else if(LIBLTE_RRC_EVENT_ID_EUTRA_A5 == rep_cnfg_eutra->event.event_id){ + // Threshold1 Type + rep_cnfg_eutra->event.event_a5.eutra1.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a5.eutra1.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a5.eutra1.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a5.eutra1.range); + } + + // Threshold2 Type + rep_cnfg_eutra->event.event_a5.eutra2.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_eutra->event.event_a5.eutra2.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a5.eutra2.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_eutra->event.event_a5.eutra2.range); + } + }else{ + // Offset + rep_cnfg_eutra->event.event_a6.offset = liblte_bits_2_value(ie_ptr, 6) - 30; + + // Report On Leave + rep_cnfg_eutra->event.event_a6.report_on_leave = liblte_bits_2_value(ie_ptr, 1); + } + + // Hysteresis + liblte_rrc_unpack_hysteresis_ie(ie_ptr, &rep_cnfg_eutra->event.hysteresis); + + // Time To Trigger + liblte_rrc_unpack_time_to_trigger_ie(ie_ptr, &rep_cnfg_eutra->event.time_to_trigger); + }else{ + // Purpose + rep_cnfg_eutra->periodical.purpose = (LIBLTE_RRC_PURPOSE_EUTRA_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + // Trigger Quantity + rep_cnfg_eutra->trigger_quantity = (LIBLTE_RRC_TRIGGER_QUANTITY_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // Report Quantity + rep_cnfg_eutra->report_quantity = (LIBLTE_RRC_REPORT_QUANTITY_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // Max Report Cells + rep_cnfg_eutra->max_report_cells = liblte_bits_2_value(ie_ptr, 3) + 1; + + // Report Interval + liblte_rrc_unpack_report_interval_ie(ie_ptr, &rep_cnfg_eutra->report_interval); + + // Report Amount + rep_cnfg_eutra->report_amount = (LIBLTE_RRC_REPORT_AMOUNT_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Report Config ID + + Description: Identifies a measurement reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_id_ie(uint8 report_cnfg_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(report_cnfg_id - 1, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_id_ie(uint8 **ie_ptr, + uint8 *report_cnfg_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + report_cnfg_id != NULL) + { + *report_cnfg_id = liblte_bits_2_value(ie_ptr, 5) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Report Config Inter RAT + + Description: Specifies criteria for triggering of an inter-RAT + measurement reporting event + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_inter_rat_ie(LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT *rep_cnfg_inter_rat, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rep_cnfg_inter_rat != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Trigger Type + liblte_value_2_bits(rep_cnfg_inter_rat->trigger_type, ie_ptr, 1); + if(LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_EVENT == rep_cnfg_inter_rat->trigger_type) + { + // Event ID + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_id, ie_ptr, 1); + if(LIBLTE_RRC_EVENT_ID_INTER_RAT_B1 == rep_cnfg_inter_rat->event.event_id) + { + // Threshold Type + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.type, ie_ptr, 2); + if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_UTRA == rep_cnfg_inter_rat->event.event_b1.type) + { + // Type + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.utra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP == rep_cnfg_inter_rat->event.event_b1.utra.type) + { + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.utra.value + 5, ie_ptr, 7); + }else{ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.utra.value, ie_ptr, 6); + } + }else if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_GERAN == rep_cnfg_inter_rat->event.event_b1.type){ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.geran, ie_ptr, 6); + }else{ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b1.cdma2000, ie_ptr, 6); + } + }else{ + // Threshold1 Type + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.eutra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_inter_rat->event.event_b2.eutra.type) + { + liblte_rrc_pack_rsrp_range_ie(rep_cnfg_inter_rat->event.event_b2.eutra.range, ie_ptr); + }else{ + liblte_rrc_pack_rsrq_range_ie(rep_cnfg_inter_rat->event.event_b2.eutra.range, ie_ptr); + } + + // Threshold2 Type + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.type2, ie_ptr, 2); + if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_UTRA == rep_cnfg_inter_rat->event.event_b2.type2) + { + // Type + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.utra.type, ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP == rep_cnfg_inter_rat->event.event_b2.utra.type) + { + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.utra.value + 5, ie_ptr, 7); + }else{ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.utra.value, ie_ptr, 6); + } + }else if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_GERAN == rep_cnfg_inter_rat->event.event_b2.type2){ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.geran, ie_ptr, 6); + }else{ + liblte_value_2_bits(rep_cnfg_inter_rat->event.event_b2.cdma2000, ie_ptr, 6); + } + } + + // Hysteresis + liblte_rrc_pack_hysteresis_ie(rep_cnfg_inter_rat->event.hysteresis, ie_ptr); + + // Time To Trigger + liblte_rrc_pack_time_to_trigger_ie(rep_cnfg_inter_rat->event.time_to_trigger, ie_ptr); + }else{ + // Purpose + liblte_value_2_bits(rep_cnfg_inter_rat->periodical.purpose, ie_ptr, 2); + } + + // Max Report Cells + liblte_value_2_bits(rep_cnfg_inter_rat->max_report_cells - 1, ie_ptr, 3); + + // Report Interval + liblte_rrc_pack_report_interval_ie(rep_cnfg_inter_rat->report_interval, ie_ptr); + + // Report Amount + liblte_value_2_bits(rep_cnfg_inter_rat->report_amount, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_inter_rat_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_INTER_RAT_STRUCT *rep_cnfg_inter_rat) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rep_cnfg_inter_rat != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Trigger Type + rep_cnfg_inter_rat->trigger_type = (LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_TRIGGER_TYPE_INTER_RAT_EVENT == rep_cnfg_inter_rat->trigger_type) + { + // Event ID + rep_cnfg_inter_rat->event.event_id = (LIBLTE_RRC_EVENT_ID_INTER_RAT_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_EVENT_ID_INTER_RAT_B1 == rep_cnfg_inter_rat->event.event_id) + { + // Threshold Type + rep_cnfg_inter_rat->event.event_b1.type = (LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 2); + if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_UTRA == rep_cnfg_inter_rat->event.event_b1.type) + { + // Type + rep_cnfg_inter_rat->event.event_b1.utra.type = (LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP == rep_cnfg_inter_rat->event.event_b1.utra.type) + { + rep_cnfg_inter_rat->event.event_b1.utra.value = liblte_bits_2_value(ie_ptr, 7) - 5; + }else{ + rep_cnfg_inter_rat->event.event_b1.utra.value = liblte_bits_2_value(ie_ptr, 6); + } + }else if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_GERAN == rep_cnfg_inter_rat->event.event_b1.type){ + rep_cnfg_inter_rat->event.event_b1.geran = liblte_bits_2_value(ie_ptr, 6); + }else{ + rep_cnfg_inter_rat->event.event_b1.cdma2000 = liblte_bits_2_value(ie_ptr, 6); + } + }else{ + // Threshold1 Type + rep_cnfg_inter_rat->event.event_b2.eutra.type = (LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_EUTRA_TYPE_RSRP == rep_cnfg_inter_rat->event.event_b2.eutra.type) + { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &rep_cnfg_inter_rat->event.event_b2.eutra.range); + }else{ + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &rep_cnfg_inter_rat->event.event_b2.eutra.range); + } + + // Threshold2 Type + rep_cnfg_inter_rat->event.event_b2.type2 = (LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 2); + if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_UTRA == rep_cnfg_inter_rat->event.event_b2.type2) + { + // Type + rep_cnfg_inter_rat->event.event_b2.utra.type = (LIBLTE_RRC_THRESHOLD_UTRA_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_THRESHOLD_UTRA_TYPE_RSCP == rep_cnfg_inter_rat->event.event_b2.utra.type) + { + rep_cnfg_inter_rat->event.event_b2.utra.value = liblte_bits_2_value(ie_ptr, 7) - 5; + }else{ + rep_cnfg_inter_rat->event.event_b2.utra.value = liblte_bits_2_value(ie_ptr, 6); + } + }else if(LIBLTE_RRC_THRESHOLD_INTER_RAT_TYPE_GERAN == rep_cnfg_inter_rat->event.event_b2.type2){ + rep_cnfg_inter_rat->event.event_b2.geran = liblte_bits_2_value(ie_ptr, 6); + }else{ + rep_cnfg_inter_rat->event.event_b2.cdma2000 = liblte_bits_2_value(ie_ptr, 6); + } + } + + // Hysteresis + liblte_rrc_unpack_hysteresis_ie(ie_ptr, &rep_cnfg_inter_rat->event.hysteresis); + + // Time To Trigger + liblte_rrc_unpack_time_to_trigger_ie(ie_ptr, &rep_cnfg_inter_rat->event.time_to_trigger); + }else{ + // Purpose + rep_cnfg_inter_rat->periodical.purpose = (LIBLTE_RRC_PURPOSE_INTER_RAT_ENUM)liblte_bits_2_value(ie_ptr, 2); + } + + // Max Report Cells + rep_cnfg_inter_rat->max_report_cells = liblte_bits_2_value(ie_ptr, 3) + 1; + + // Report Interval + liblte_rrc_unpack_report_interval_ie(ie_ptr, &rep_cnfg_inter_rat->report_interval); + + // Report Amount + rep_cnfg_inter_rat->report_amount = (LIBLTE_RRC_REPORT_AMOUNT_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Report Config To Add Mod List + + Description: Concerns a list of reporting configurations to add + or modify + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_config_to_add_mod_list_ie(LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT *list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(list != NULL && + ie_ptr != NULL) + { + // List Size + liblte_value_2_bits(list->N_rep_cnfg - 1, ie_ptr, 5); + + for(i=0; iN_rep_cnfg; i++) + { + // Report Config ID + liblte_rrc_pack_report_config_id_ie(list->rep_cnfg_list[i].rep_cnfg_id, ie_ptr); + + // Report Config Choice + liblte_value_2_bits(list->rep_cnfg_list[i].rep_cnfg_type, ie_ptr, 1); + + if(LIBLTE_RRC_REPORT_CONFIG_TYPE_EUTRA == list->rep_cnfg_list[i].rep_cnfg_type) + { + liblte_rrc_pack_report_config_eutra_ie(&list->rep_cnfg_list[i].rep_cnfg_eutra, ie_ptr); + }else{ + liblte_rrc_pack_report_config_inter_rat_ie(&list->rep_cnfg_list[i].rep_cnfg_inter_rat, ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_config_to_add_mod_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_CONFIG_TO_ADD_MOD_LIST_STRUCT *list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + list != NULL) + { + // List Size + list->N_rep_cnfg = liblte_bits_2_value(ie_ptr, 5) + 1; + + for(i=0; iN_rep_cnfg; i++) + { + // Report Config ID + liblte_rrc_unpack_report_config_id_ie(ie_ptr, &list->rep_cnfg_list[i].rep_cnfg_id); + + // Report Config Choice + list->rep_cnfg_list[i].rep_cnfg_type = (LIBLTE_RRC_REPORT_CONFIG_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + + if(LIBLTE_RRC_REPORT_CONFIG_TYPE_EUTRA == list->rep_cnfg_list[i].rep_cnfg_type) + { + liblte_rrc_unpack_report_config_eutra_ie(ie_ptr, &list->rep_cnfg_list[i].rep_cnfg_eutra); + }else{ + liblte_rrc_unpack_report_config_inter_rat_ie(ie_ptr, &list->rep_cnfg_list[i].rep_cnfg_inter_rat); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Report Interval + + Description: Indicates the interval between periodic reports + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_report_interval_ie(LIBLTE_RRC_REPORT_INTERVAL_ENUM report_int, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(report_int, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_report_interval_ie(uint8 **ie_ptr, + LIBLTE_RRC_REPORT_INTERVAL_ENUM *report_int) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + report_int != NULL) + { + *report_int = (LIBLTE_RRC_REPORT_INTERVAL_ENUM)liblte_bits_2_value(ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RSRP Range + + Description: Specifies the value range used in RSRP measurements + and thresholds + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rsrp_range_ie(uint8 rsrp_range, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(rsrp_range, ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rsrp_range_ie(uint8 **ie_ptr, + uint8 *rsrp_range) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rsrp_range != NULL) + { + *rsrp_range = liblte_bits_2_value(ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RSRQ Range + + Description: Specifies the value range used in RSRQ measurements + and thresholds + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rsrq_range_ie(uint8 rsrq_range, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(rsrq_range, ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rsrq_range_ie(uint8 **ie_ptr, + uint8 *rsrq_range) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rsrq_range != NULL) + { + *rsrq_range = liblte_bits_2_value(ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Time To Trigger + + Description: Specifies the value range used for the time to + trigger parameter, which concerns the time during + which specific criteria for the event needs to be + met in order to trigger a measurement report + + Document Reference: 36.331 v10.0.0 Section 6.3.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_time_to_trigger_ie(LIBLTE_RRC_TIME_TO_TRIGGER_ENUM time_to_trigger, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(time_to_trigger, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_time_to_trigger_ie(uint8 **ie_ptr, + LIBLTE_RRC_TIME_TO_TRIGGER_ENUM *time_to_trigger) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + time_to_trigger != NULL) + { + *time_to_trigger = (LIBLTE_RRC_TIME_TO_TRIGGER_ENUM)liblte_bits_2_value(ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Additional Spectrum Emission + + Description: FIXME + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_additional_spectrum_emission_ie(uint8 add_spect_em, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(add_spect_em - 1, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_additional_spectrum_emission_ie(uint8 **ie_ptr, + uint8 *add_spect_em) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + add_spect_em != NULL) + { + *add_spect_em = liblte_bits_2_value(ie_ptr, 5) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ARFCN value CDMA2000 + + Description: Indicates the CDMA2000 carrier frequency within + a CDMA2000 band + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_cdma2000_ie(uint16 arfcn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(arfcn, ie_ptr, 11); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_cdma2000_ie(uint8 **ie_ptr, + uint16 *arfcn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + arfcn != NULL) + { + *arfcn = liblte_bits_2_value(ie_ptr, 11); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ARFCN value EUTRA + + Description: Indicates the ARFCN applicable for a downlink, + uplink, or bi-directional (TDD) E-UTRA carrier + frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_eutra_ie(uint16 arfcn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(arfcn, ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_eutra_ie(uint8 **ie_ptr, + uint16 *arfcn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + arfcn != NULL) + { + *arfcn = liblte_bits_2_value(ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ARFCN value GERAN + + Description: Specifies the ARFCN value applicable for a GERAN + BCCH carrier frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_geran_ie(uint16 arfcn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(arfcn, ie_ptr, 10); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_geran_ie(uint8 **ie_ptr, + uint16 *arfcn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + arfcn != NULL) + { + *arfcn = liblte_bits_2_value(ie_ptr, 10); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: ARFCN value UTRA + + Description: Indicates the ARFCN applicable for a downlink (Nd, + FDD) or bi-directional (Nt, TDD) UTRA carrier + frequency + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_arfcn_value_utra_ie(uint16 arfcn, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(arfcn, ie_ptr, 14); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_arfcn_value_utra_ie(uint8 **ie_ptr, + uint16 *arfcn) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + arfcn != NULL) + { + *arfcn = liblte_bits_2_value(ie_ptr, 14); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Band Class CDMA2000 + + Description: Defines the CDMA2000 band in which the CDMA2000 + carrier frequency can be found + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_class_cdma2000_ie(LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM bc_cdma2000, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(bc_cdma2000, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_class_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM *bc_cdma2000) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + bc_cdma2000 != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + *bc_cdma2000 = (LIBLTE_RRC_BAND_CLASS_CDMA2000_ENUM)liblte_bits_2_value(ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Band Indicator GERAN + + Description: Indicates how to interpret an associated GERAN + carrier ARFCN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_band_indicator_geran_ie(LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM bi_geran, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(bi_geran, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_band_indicator_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM *bi_geran) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + bi_geran != NULL) + { + *bi_geran = (LIBLTE_RRC_BAND_INDICATOR_GERAN_ENUM)liblte_bits_2_value(ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Carrier Freq CDMA2000 + + Description: Provides the CDMA2000 carrier information + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freq_cdma2000_ie(LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT *carrier_freq, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(carrier_freq != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_band_class_cdma2000_ie(carrier_freq->bandclass, ie_ptr); + liblte_rrc_pack_arfcn_value_cdma2000_ie(carrier_freq->arfcn, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freq_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQ_CDMA2000_STRUCT *carrier_freq) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + carrier_freq != NULL) + { + liblte_rrc_unpack_band_class_cdma2000_ie(ie_ptr, &carrier_freq->bandclass); + liblte_rrc_unpack_arfcn_value_cdma2000_ie(ie_ptr, &carrier_freq->arfcn); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Carrier Freq GERAN + + Description: Provides an unambiguous carrier frequency description + of a GERAN cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freq_geran_ie(LIBLTE_RRC_CARRIER_FREQ_GERAN_STRUCT *carrier_freq, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(carrier_freq != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_arfcn_value_geran_ie(carrier_freq->arfcn, ie_ptr); + liblte_rrc_pack_band_indicator_geran_ie(carrier_freq->band_indicator, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freq_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQ_GERAN_STRUCT *carrier_freq) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + carrier_freq != NULL) + { + liblte_rrc_unpack_arfcn_value_geran_ie(ie_ptr, &carrier_freq->arfcn); + liblte_rrc_unpack_band_indicator_geran_ie(ie_ptr, &carrier_freq->band_indicator); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Carrier Freqs GERAN + + Description: Provides one or more GERAN ARFCN values, which + represent a list of GERAN BCCH carrier frequencies + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_carrier_freqs_geran_ie(LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT *carrier_freqs, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(carrier_freqs != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_arfcn_value_geran_ie(carrier_freqs->starting_arfcn, ie_ptr); + liblte_rrc_pack_band_indicator_geran_ie(carrier_freqs->band_indicator, ie_ptr); + liblte_value_2_bits(carrier_freqs->following_arfcns, ie_ptr, 2); + if(LIBLTE_RRC_FOLLOWING_ARFCNS_EXPLICIT_LIST == carrier_freqs->following_arfcns) + { + liblte_value_2_bits(carrier_freqs->explicit_list_of_arfcns_size, ie_ptr, 5); + for(i=0; iexplicit_list_of_arfcns_size; i++) + { + liblte_rrc_pack_arfcn_value_geran_ie(carrier_freqs->explicit_list_of_arfcns[i], ie_ptr); + } + }else if(LIBLTE_RRC_FOLLOWING_ARFCNS_EQUALLY_SPACED == carrier_freqs->following_arfcns){ + liblte_value_2_bits(carrier_freqs->equally_spaced_arfcns.arfcn_spacing - 1, ie_ptr, 3); + liblte_value_2_bits(carrier_freqs->equally_spaced_arfcns.number_of_arfcns, ie_ptr, 5); + }else{ // LIBLTE_RRC_FOLLOWING_ARFCNS_VARIABLE_BIT_MAP == carrier_freqs->following_arfcns + liblte_value_2_bits(carrier_freqs->variable_bit_map_of_arfcns, ie_ptr, 16); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_carrier_freqs_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CARRIER_FREQS_GERAN_STRUCT *carrier_freqs) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + carrier_freqs != NULL) + { + liblte_rrc_unpack_arfcn_value_geran_ie(ie_ptr, &carrier_freqs->starting_arfcn); + liblte_rrc_unpack_band_indicator_geran_ie(ie_ptr, &carrier_freqs->band_indicator); + carrier_freqs->following_arfcns = (LIBLTE_RRC_FOLLOWING_ARFCNS_ENUM)liblte_bits_2_value(ie_ptr, 2); + if(LIBLTE_RRC_FOLLOWING_ARFCNS_EXPLICIT_LIST == carrier_freqs->following_arfcns) + { + carrier_freqs->explicit_list_of_arfcns_size = liblte_bits_2_value(ie_ptr, 5); + for(i=0; iexplicit_list_of_arfcns_size; i++) + { + liblte_rrc_unpack_arfcn_value_geran_ie(ie_ptr, &carrier_freqs->explicit_list_of_arfcns[i]); + } + }else if(LIBLTE_RRC_FOLLOWING_ARFCNS_EQUALLY_SPACED == carrier_freqs->following_arfcns){ + carrier_freqs->equally_spaced_arfcns.arfcn_spacing = liblte_bits_2_value(ie_ptr, 3) + 1; + carrier_freqs->equally_spaced_arfcns.number_of_arfcns = liblte_bits_2_value(ie_ptr, 5); + }else{ // LIBLTE_RRC_FOLLOWING_ARFCNS_VARIABLE_BIT_MAP == carrier_freqs->following_arfcns + carrier_freqs->variable_bit_map_of_arfcns = liblte_bits_2_value(ie_ptr, 16); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CDMA2000 Type + + Description: Describes the type of CDMA2000 network + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cdma2000_type_ie(LIBLTE_RRC_CDMA2000_TYPE_ENUM cdma2000_type, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(cdma2000_type, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cdma2000_type_ie(uint8 **ie_ptr, + LIBLTE_RRC_CDMA2000_TYPE_ENUM *cdma2000_type) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cdma2000_type != NULL) + { + *cdma2000_type = (LIBLTE_RRC_CDMA2000_TYPE_ENUM)liblte_bits_2_value(ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Identity + + Description: Unambiguously identifies a cell within a PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_identity_ie(uint32 cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(cell_id, ie_ptr, 28); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_identity_ie(uint8 **ie_ptr, + uint32 *cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_id != NULL) + { + *cell_id = liblte_bits_2_value(ie_ptr, 28); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Index List + + Description: Concerns a list of cell indecies, which may be used + for different purposes + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_index_list_ie(LIBLTE_RRC_CELL_INDEX_LIST_STRUCT *cell_idx_list, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(cell_idx_list != NULL && + ie_ptr != NULL) + { + // List Size + liblte_value_2_bits(cell_idx_list->N_cell_idx - 1, ie_ptr, 5); + + for(i=0; iN_cell_idx; i++) + { + // Cell Index + liblte_value_2_bits(cell_idx_list->cell_idx[i] - 1, ie_ptr, 5); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_index_list_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_INDEX_LIST_STRUCT *cell_idx_list) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + cell_idx_list != NULL) + { + // List Size + cell_idx_list->N_cell_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + + for(i=0; iN_cell_idx; i++) + { + // Cell Index + cell_idx_list->cell_idx[i] = liblte_bits_2_value(ie_ptr, 5) + 1; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Reselection Priority + + Description: Contains the absolute priority of the concerned + carrier frequency/set of frequencies (GERAN)/ + bandclass (CDMA2000), as used by the cell + reselection procedure + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_reselection_priority_ie(uint8 cell_resel_prio, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(cell_resel_prio, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_reselection_priority_ie(uint8 **ie_ptr, + uint8 *cell_resel_prio) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_resel_prio != NULL) + { + *cell_resel_prio = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CSFB Registration Param 1xRTT + + Description: Indicates whether or not the UE shall perform a + CDMA2000 1xRTT pre-registration if the UE does not + have a valid/current pre-registration + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_registration_param_1xrtt_ie(LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT *csfb_reg_param, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(csfb_reg_param != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(csfb_reg_param->sid, ie_ptr, 15); + liblte_value_2_bits(csfb_reg_param->nid, ie_ptr, 16); + liblte_value_2_bits(csfb_reg_param->multiple_sid, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->multiple_nid, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->home_reg, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->foreign_sid_reg, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->foreign_nid_reg, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->param_reg, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->power_up_reg, ie_ptr, 1); + liblte_value_2_bits(csfb_reg_param->reg_period, ie_ptr, 7); + liblte_value_2_bits(csfb_reg_param->reg_zone, ie_ptr, 12); + liblte_value_2_bits(csfb_reg_param->total_zone, ie_ptr, 3); + liblte_value_2_bits(csfb_reg_param->zone_timer, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_registration_param_1xrtt_ie(uint8 **ie_ptr, + LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_STRUCT *csfb_reg_param) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + csfb_reg_param != NULL) + { + csfb_reg_param->sid = liblte_bits_2_value(ie_ptr, 15); + csfb_reg_param->nid = liblte_bits_2_value(ie_ptr, 16); + csfb_reg_param->multiple_sid = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->multiple_nid = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->home_reg = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->foreign_sid_reg = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->foreign_nid_reg = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->param_reg = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->power_up_reg = liblte_bits_2_value(ie_ptr, 1); + csfb_reg_param->reg_period = liblte_bits_2_value(ie_ptr, 7); + csfb_reg_param->reg_zone = liblte_bits_2_value(ie_ptr, 12); + csfb_reg_param->total_zone = liblte_bits_2_value(ie_ptr, 3); + csfb_reg_param->zone_timer = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_registration_param_1xrtt_v920_ie(LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_V920_STRUCT *csfb_reg_param, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(csfb_reg_param != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(csfb_reg_param->power_down_reg, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_registration_param_1xrtt_v920_ie(uint8 **ie_ptr, + LIBLTE_RRC_CSFB_REGISTRATION_PARAM_1XRTT_V920_STRUCT *csfb_reg_param) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + csfb_reg_param != NULL) + { + csfb_reg_param->power_down_reg = (LIBLTE_RRC_POWER_DOWN_REG_R9_ENUM)liblte_bits_2_value(ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Global ID EUTRA + + Description: Specifies the Evolved Cell Global Identifier (ECGI), + the globally unique identity of a cell in E-UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_eutra_ie(LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT *cell_global_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cell_global_id != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_plmn_identity_ie(&cell_global_id->plmn_id, ie_ptr); + liblte_rrc_pack_cell_identity_ie(cell_global_id->cell_id, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_EUTRA_STRUCT *cell_global_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_global_id != NULL) + { + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &cell_global_id->plmn_id); + liblte_rrc_unpack_cell_identity_ie(ie_ptr, &cell_global_id->cell_id); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Global ID UTRA + + Description: Specifies the global UTRAN Cell Identifier, the + globally unique identity of a cell in UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_utra_ie(LIBLTE_RRC_CELL_GLOBAL_ID_UTRA_STRUCT *cell_global_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cell_global_id != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_plmn_identity_ie(&cell_global_id->plmn_id, ie_ptr); + liblte_value_2_bits(cell_global_id->cell_id, ie_ptr, 28); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_utra_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_UTRA_STRUCT *cell_global_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_global_id != NULL) + { + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &cell_global_id->plmn_id); + cell_global_id->cell_id = liblte_bits_2_value(ie_ptr, 28); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Global ID GERAN + + Description: Specifies the Cell Global Identity (CGI), the + globally unique identity of a cell in GERAN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_geran_ie(LIBLTE_RRC_CELL_GLOBAL_ID_GERAN_STRUCT *cell_global_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cell_global_id != NULL && + ie_ptr != NULL) + { + liblte_rrc_pack_plmn_identity_ie(&cell_global_id->plmn_id, ie_ptr); + liblte_value_2_bits(cell_global_id->lac, ie_ptr, 16); + liblte_value_2_bits(cell_global_id->cell_id, ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_GERAN_STRUCT *cell_global_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_global_id != NULL) + { + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &cell_global_id->plmn_id); + cell_global_id->lac = liblte_bits_2_value(ie_ptr, 16); + cell_global_id->cell_id = liblte_bits_2_value(ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cell Global ID CDMA2000 + + Description: Specifies the Cell Global Identity (CGI), the + globally unique identity of a cell in CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cell_global_id_cdma2000_ie(LIBLTE_RRC_CELL_GLOBAL_ID_CDMA2000_STRUCT *cell_global_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cell_global_id != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits((uint32)(cell_global_id->onexrtt >> 15), ie_ptr, 32); + liblte_value_2_bits((uint32)(cell_global_id->onexrtt & 0x7FFFULL), ie_ptr, 15); + liblte_value_2_bits(cell_global_id->hrpd[0], ie_ptr, 32); + liblte_value_2_bits(cell_global_id->hrpd[1], ie_ptr, 32); + liblte_value_2_bits(cell_global_id->hrpd[2], ie_ptr, 32); + liblte_value_2_bits(cell_global_id->hrpd[3], ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cell_global_id_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_CELL_GLOBAL_ID_CDMA2000_STRUCT *cell_global_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cell_global_id != NULL) + { + cell_global_id->onexrtt = (uint64)liblte_bits_2_value(ie_ptr, 32) << 15; + cell_global_id->onexrtt |= (uint64)liblte_bits_2_value(ie_ptr, 15); + cell_global_id->hrpd[0] = liblte_bits_2_value(ie_ptr, 32); + cell_global_id->hrpd[1] = liblte_bits_2_value(ie_ptr, 32); + cell_global_id->hrpd[2] = liblte_bits_2_value(ie_ptr, 32); + cell_global_id->hrpd[3] = liblte_bits_2_value(ie_ptr, 32); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CSG Identity + + Description: Identifies a Closed Subscriber Group + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_csg_identity_ie(uint32 csg_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(csg_id, ie_ptr, 27); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csg_identity_ie(uint8 **ie_ptr, + uint32 *csg_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + csg_id != NULL) + { + *csg_id = liblte_bits_2_value(ie_ptr, 27); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Mobility Control Info + + Description: Includes parameters relevant for network controlled + mobility to/within E-UTRA + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mobility_control_info_ie(LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT *mob_ctrl_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mob_ctrl_info != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(mob_ctrl_info->carrier_freq_eutra_present, ie_ptr, 1); + liblte_value_2_bits(mob_ctrl_info->carrier_bw_eutra_present, ie_ptr, 1); + liblte_value_2_bits(mob_ctrl_info->add_spect_em_present, ie_ptr, 1); + liblte_value_2_bits(mob_ctrl_info->rach_cnfg_ded_present, ie_ptr, 1); + + // Target Phys Cell ID + liblte_rrc_pack_phys_cell_id_ie(mob_ctrl_info->target_pci, ie_ptr); + + // Carrier Freq + if(mob_ctrl_info->carrier_freq_eutra_present) + { + // Optional indicator + liblte_value_2_bits(mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq_present, ie_ptr, 1); + + // DL Carrier Freq + liblte_rrc_pack_arfcn_value_eutra_ie(mob_ctrl_info->carrier_freq_eutra.dl_carrier_freq, ie_ptr); + + // UL Carrier Freq + if(mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq_present) + { + liblte_rrc_pack_arfcn_value_eutra_ie(mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq, ie_ptr); + } + } + + // Carrier Bandwidth + if(mob_ctrl_info->carrier_bw_eutra_present) + { + // Optional indicator + liblte_value_2_bits(mob_ctrl_info->carrier_bw_eutra.ul_bw_present, ie_ptr, 1); + + // DL Bandwidth + liblte_value_2_bits(mob_ctrl_info->carrier_bw_eutra.dl_bw, ie_ptr, 4); + + // UL Bandwidth + if(mob_ctrl_info->carrier_bw_eutra.ul_bw_present) + { + liblte_value_2_bits(mob_ctrl_info->carrier_bw_eutra.ul_bw, ie_ptr, 4); + } + } + + // Additional Spectrum Emission + if(mob_ctrl_info->add_spect_em_present) + { + liblte_rrc_pack_additional_spectrum_emission_ie(mob_ctrl_info->add_spect_em, ie_ptr); + } + + // T304 + liblte_value_2_bits(mob_ctrl_info->t304, ie_ptr, 3); + + // New UE Identity + liblte_rrc_pack_c_rnti_ie(mob_ctrl_info->new_ue_id, ie_ptr); + + // Radio Resource Config Common + liblte_rrc_pack_rr_config_common_ie(&mob_ctrl_info->rr_cnfg_common, ie_ptr); + + // RACH Config Dedicated + if(mob_ctrl_info->rach_cnfg_ded_present) + { + liblte_rrc_pack_rach_config_dedicated_ie(&mob_ctrl_info->rach_cnfg_ded, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mobility_control_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_MOBILITY_CONTROL_INFO_STRUCT *mob_ctrl_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mob_ctrl_info != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + mob_ctrl_info->carrier_freq_eutra_present = liblte_bits_2_value(ie_ptr, 1); + mob_ctrl_info->carrier_bw_eutra_present = liblte_bits_2_value(ie_ptr, 1); + mob_ctrl_info->add_spect_em_present = liblte_bits_2_value(ie_ptr, 1); + mob_ctrl_info->rach_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + + // Target Phys Cell ID + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &mob_ctrl_info->target_pci); + + // Carrier Freq + if(mob_ctrl_info->carrier_freq_eutra_present) + { + // Optional indicator + mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq_present = liblte_bits_2_value(ie_ptr, 1); + + // DL Carrier Freq + liblte_rrc_unpack_arfcn_value_eutra_ie(ie_ptr, &mob_ctrl_info->carrier_freq_eutra.dl_carrier_freq); + + // UL Carrier Freq + if(mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq_present) + { + liblte_rrc_unpack_arfcn_value_eutra_ie(ie_ptr, &mob_ctrl_info->carrier_freq_eutra.ul_carrier_freq); + } + } + + // Carrier Bandwidth + if(mob_ctrl_info->carrier_bw_eutra_present) + { + // Optional indicator + mob_ctrl_info->carrier_bw_eutra.ul_bw_present = liblte_bits_2_value(ie_ptr, 1); + + // DL Bandwidth + mob_ctrl_info->carrier_bw_eutra.dl_bw = (LIBLTE_RRC_BANDWIDTH_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // UL Bandwidth + if(mob_ctrl_info->carrier_bw_eutra.ul_bw_present) + { + mob_ctrl_info->carrier_bw_eutra.ul_bw = (LIBLTE_RRC_BANDWIDTH_ENUM)liblte_bits_2_value(ie_ptr, 4); + } + } + + // Additional Spectrum Emission + if(mob_ctrl_info->add_spect_em_present) + { + liblte_rrc_unpack_additional_spectrum_emission_ie(ie_ptr, &mob_ctrl_info->add_spect_em); + } + + // T304 + mob_ctrl_info->t304 = (LIBLTE_RRC_T304_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // New UE Identity + liblte_rrc_unpack_c_rnti_ie(ie_ptr, &mob_ctrl_info->new_ue_id); + + // Radio Resource Config Common + liblte_rrc_unpack_rr_config_common_ie(ie_ptr, &mob_ctrl_info->rr_cnfg_common); + + // RACH Config Dedicated + if(mob_ctrl_info->rach_cnfg_ded_present) + { + liblte_rrc_unpack_rach_config_dedicated_ie(ie_ptr, &mob_ctrl_info->rach_cnfg_ded); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Mobility Parameters CDMA2000 (1xRTT) + + Description: Contains the parameters provided to the UE for + handover and (enhanced) CSFB to 1xRTT support + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Mobility State Parameters + + Description: Contains parameters to determine UE mobility state + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mobility_state_parameters_ie(LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT *mobility_state_params, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(mobility_state_params != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(mobility_state_params->t_eval, ie_ptr, 3); + liblte_value_2_bits(mobility_state_params->t_hyst_normal, ie_ptr, 3); + liblte_value_2_bits(mobility_state_params->n_cell_change_medium - 1, ie_ptr, 4); + liblte_value_2_bits(mobility_state_params->n_cell_change_high - 1, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mobility_state_parameters_ie(uint8 **ie_ptr, + LIBLTE_RRC_MOBILITY_STATE_PARAMETERS_STRUCT *mobility_state_params) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + mobility_state_params != NULL) + { + mobility_state_params->t_eval = (LIBLTE_RRC_T_EVALUATION_ENUM)liblte_bits_2_value(ie_ptr, 3); + mobility_state_params->t_hyst_normal = (LIBLTE_RRC_T_HYST_NORMAL_ENUM)liblte_bits_2_value(ie_ptr, 3); + mobility_state_params->n_cell_change_medium = liblte_bits_2_value(ie_ptr, 4) + 1; + mobility_state_params->n_cell_change_high = liblte_bits_2_value(ie_ptr, 4) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_ie(uint16 phys_cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(phys_cell_id, ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_ie(uint8 **ie_ptr, + uint16 *phys_cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phys_cell_id != NULL) + { + *phys_cell_id = liblte_bits_2_value(ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID Range + + Description: Encodes either a single or a range of physical cell + identities + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_range_ie(LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT *phys_cell_id_range, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool opt; + + if(phys_cell_id_range != NULL && + ie_ptr != NULL) + { + opt = (LIBLTE_RRC_PHYS_CELL_ID_RANGE_N1 != phys_cell_id_range->range); + + liblte_value_2_bits(opt, ie_ptr, 1); + + liblte_rrc_pack_phys_cell_id_ie(phys_cell_id_range->start, ie_ptr); + + if(opt) + { + liblte_value_2_bits(phys_cell_id_range->range, ie_ptr, 4); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_range_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYS_CELL_ID_RANGE_STRUCT *phys_cell_id_range) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool opt; + + if(ie_ptr != NULL && + phys_cell_id_range != NULL) + { + opt = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &phys_cell_id_range->start); + + if(true == opt) + { + phys_cell_id_range->range = (LIBLTE_RRC_PHYS_CELL_ID_RANGE_ENUM)liblte_bits_2_value(ie_ptr, 4); + }else{ + phys_cell_id_range->range = LIBLTE_RRC_PHYS_CELL_ID_RANGE_N1; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID Range UTRA FDD List + + Description: Encodes one or more of Phys Cell ID Range UTRA FDD + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Phys Cell ID CDMA2000 + + Description: Identifies the PN offset that represents the + "Physical cell identity" in CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_cdma2000_ie(uint16 phys_cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(phys_cell_id, ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_cdma2000_ie(uint8 **ie_ptr, + uint16 *phys_cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phys_cell_id != NULL) + { + *phys_cell_id = liblte_bits_2_value(ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID GERAN + + Description: Contains the Base Station Identity Code (BSIC) + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_geran_ie(LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT *phys_cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(phys_cell_id != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(phys_cell_id->ncc, ie_ptr, 3); + liblte_value_2_bits(phys_cell_id->bcc, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_geran_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYS_CELL_ID_GERAN_STRUCT *phys_cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phys_cell_id != NULL) + { + phys_cell_id->ncc = liblte_bits_2_value(ie_ptr, 3); + phys_cell_id->bcc = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID UTRA FDD + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_utra_fdd_ie(uint16 phys_cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(phys_cell_id, ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_utra_fdd_ie(uint8 **ie_ptr, + uint16 *phys_cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phys_cell_id != NULL) + { + *phys_cell_id = liblte_bits_2_value(ie_ptr, 9); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Phys Cell ID UTRA TDD + + Description: Indicates the physical layer identity of the cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phys_cell_id_utra_tdd_ie(uint8 phys_cell_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(phys_cell_id, ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phys_cell_id_utra_tdd_ie(uint8 **ie_ptr, + uint8 *phys_cell_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phys_cell_id != NULL) + { + *phys_cell_id = liblte_bits_2_value(ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PLMN Identity + + Description: Identifies a Public Land Mobile Network + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_plmn_identity_ie(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *plmn_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 mcc_opt = true; + uint8 mnc_size; + + if(plmn_id != NULL && + ie_ptr != NULL) + { + if(0xFFFF == plmn_id->mcc) { + mcc_opt = false; + } + liblte_value_2_bits(mcc_opt, ie_ptr, 1); + + if(true == mcc_opt) + { + liblte_value_2_bits((plmn_id->mcc>>8)&0x0F, ie_ptr, 4); + liblte_value_2_bits((plmn_id->mcc>>4)&0x0F, ie_ptr, 4); + liblte_value_2_bits((plmn_id->mcc)&0x0F, ie_ptr, 4); + } + + if((plmn_id->mnc & 0xFF00) == 0xFF00) + { + mnc_size = 2; + liblte_value_2_bits((mnc_size)-2, ie_ptr, 1); + liblte_value_2_bits((plmn_id->mnc>>4)&0x0F, ie_ptr, 4); + liblte_value_2_bits((plmn_id->mnc)&0x0F, ie_ptr, 4); + }else{ + mnc_size = 3; + liblte_value_2_bits((mnc_size)-2, ie_ptr, 1); + liblte_value_2_bits((plmn_id->mnc>>8)&0x0F, ie_ptr, 4); + liblte_value_2_bits((plmn_id->mnc>>4)&0x0F, ie_ptr, 4); + liblte_value_2_bits((plmn_id->mnc)&0x0F, ie_ptr, 4); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_plmn_identity_ie(uint8 **ie_ptr, + LIBLTE_RRC_PLMN_IDENTITY_STRUCT *plmn_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 mcc_opt; + uint8 mnc_size; + + if(ie_ptr != NULL && + plmn_id != NULL) + { + mcc_opt = liblte_bits_2_value(ie_ptr, 1); + + if(true == mcc_opt) + { + plmn_id->mcc = 0xF000; + plmn_id->mcc |= (liblte_bits_2_value(ie_ptr, 4) << 8); + plmn_id->mcc |= (liblte_bits_2_value(ie_ptr, 4) << 4); + plmn_id->mcc |= liblte_bits_2_value(ie_ptr, 4); + + }else{ + plmn_id->mcc = LIBLTE_RRC_MCC_NOT_PRESENT; + } + + mnc_size = (liblte_bits_2_value(ie_ptr, 1) + 2); + if(2 == mnc_size) + { + plmn_id->mnc = 0xFF00; + plmn_id->mnc |= (liblte_bits_2_value(ie_ptr, 4) << 4); + plmn_id->mnc |= liblte_bits_2_value(ie_ptr, 4); + }else{ + plmn_id->mnc = 0xF000; + plmn_id->mnc |= (liblte_bits_2_value(ie_ptr, 4) << 8); + plmn_id->mnc |= (liblte_bits_2_value(ie_ptr, 4) << 4); + plmn_id->mnc |= liblte_bits_2_value(ie_ptr, 4); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Pre Registration Info HRPD + + Description: FIXME + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pre_registration_info_hrpd_ie(LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT *pre_reg_info_hrpd, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(pre_reg_info_hrpd != NULL && + ie_ptr != NULL) + { + // Optional indicators + liblte_value_2_bits(pre_reg_info_hrpd->pre_reg_zone_id_present, ie_ptr, 1); + if(0 != pre_reg_info_hrpd->secondary_pre_reg_zone_id_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + + liblte_value_2_bits(pre_reg_info_hrpd->pre_reg_allowed, ie_ptr, 1); + + if(true == pre_reg_info_hrpd->pre_reg_zone_id_present) + { + liblte_value_2_bits(pre_reg_info_hrpd->pre_reg_zone_id, ie_ptr, 8); + } + + if(0 != pre_reg_info_hrpd->secondary_pre_reg_zone_id_list_size) + { + liblte_value_2_bits(pre_reg_info_hrpd->secondary_pre_reg_zone_id_list_size - 1, ie_ptr, 1); + for(i=0; isecondary_pre_reg_zone_id_list_size; i++) + { + liblte_value_2_bits(pre_reg_info_hrpd->secondary_pre_reg_zone_id_list[i], ie_ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pre_registration_info_hrpd_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRE_REGISTRATION_INFO_HRPD_STRUCT *pre_reg_info_hrpd) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool secondary_pre_reg_zone_id_opt; + + if(ie_ptr != NULL && + pre_reg_info_hrpd != NULL) + { + // Optional indicators + pre_reg_info_hrpd->pre_reg_zone_id_present = liblte_bits_2_value(ie_ptr, 1); + secondary_pre_reg_zone_id_opt = liblte_bits_2_value(ie_ptr, 1); + + pre_reg_info_hrpd->pre_reg_allowed = liblte_bits_2_value(ie_ptr, 1); + + if(true == pre_reg_info_hrpd->pre_reg_zone_id_present) + { + pre_reg_info_hrpd->pre_reg_zone_id = liblte_bits_2_value(ie_ptr, 8); + } + + if(true == secondary_pre_reg_zone_id_opt) + { + pre_reg_info_hrpd->secondary_pre_reg_zone_id_list_size = liblte_bits_2_value(ie_ptr, 1) + 1; + for(i=0; isecondary_pre_reg_zone_id_list_size; i++) + { + pre_reg_info_hrpd->secondary_pre_reg_zone_id_list[i] = liblte_bits_2_value(ie_ptr, 8); + } + }else{ + pre_reg_info_hrpd->secondary_pre_reg_zone_id_list_size = 0; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Q Qual Min + + Description: Indicates for cell selection/re-selection the + required minimum received RSRQ level in the (E-UTRA) + cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_qual_min_ie(int8 q_qual_min, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(q_qual_min + 34, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_qual_min_ie(uint8 **ie_ptr, + int8 *q_qual_min) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + q_qual_min != NULL) + { + *q_qual_min = (int8)liblte_bits_2_value(ie_ptr, 5) - 34; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Q Rx Lev Min + + Description: Indicates the required minimum received RSRP level in + the (E-UTRA) cell for cell selection/re-selection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_rx_lev_min_ie(int16 q_rx_lev_min, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits((q_rx_lev_min / 2) + 70, ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_rx_lev_min_ie(uint8 **ie_ptr, + int16 *q_rx_lev_min) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + q_rx_lev_min != NULL) + { + *q_rx_lev_min = ((int16)liblte_bits_2_value(ie_ptr, 6) - 70) * 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Q Offset Range + + Description: Indicates a cell or frequency specific offset to be + applied when evaluating candidates for cell + reselection or when evaluating triggering conditions + for measurement reporting + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_offset_range_ie(LIBLTE_RRC_Q_OFFSET_RANGE_ENUM q_offset_range, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(q_offset_range, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_offset_range_ie(uint8 **ie_ptr, + LIBLTE_RRC_Q_OFFSET_RANGE_ENUM *q_offset_range) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + q_offset_range != NULL) + { + *q_offset_range = (LIBLTE_RRC_Q_OFFSET_RANGE_ENUM)liblte_bits_2_value(ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Q Offset Range Inter RAT + + Description: Indicates a frequency specific offset to be applied + when evaluating triggering conditions for + measurement reporting + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_q_offset_range_inter_rat_ie(int8 q_offset_range_inter_rat, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(q_offset_range_inter_rat + 15, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_q_offset_range_inter_rat_ie(uint8 **ie_ptr, + int8 *q_offset_range_inter_rat) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + q_offset_range_inter_rat != NULL) + { + *q_offset_range_inter_rat = (int8)(liblte_bits_2_value(ie_ptr, 5)) - 15; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Reselection Threshold + + Description: Indicates an RX level threshold for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_reselection_threshold_ie(uint8 resel_thresh, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(resel_thresh / 2, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_reselection_threshold_ie(uint8 **ie_ptr, + uint8 *resel_thresh) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + resel_thresh != NULL) + { + *resel_thresh = liblte_bits_2_value(ie_ptr, 5) * 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Reselection Threshold Q + + Description: Indicates a quality level threshold for cell + reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_reselection_threshold_q_ie(uint8 resel_thresh_q, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(resel_thresh_q, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_reselection_threshold_q_ie(uint8 **ie_ptr, + uint8 *resel_thresh_q) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + resel_thresh_q != NULL) + { + *resel_thresh_q = liblte_bits_2_value(ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: S Cell Index + + Description: Contains a short identity, used to identify an + SCell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_s_cell_index_ie(uint8 s_cell_idx, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(s_cell_idx - 1, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_s_cell_index_ie(uint8 **ie_ptr, + uint8 *s_cell_idx) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + s_cell_idx != NULL) + { + *s_cell_idx = liblte_bits_2_value(ie_ptr, 3) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Serv Cell Index + + Description: Contains a short identity, used to identify a + serving cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_serv_cell_index_ie(uint8 serv_cell_idx, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(serv_cell_idx, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_serv_cell_index_ie(uint8 **ie_ptr, + uint8 *serv_cell_idx) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + serv_cell_idx != NULL) + { + *serv_cell_idx = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Speed State Scale Factors + + Description: Contains factors, to be applied when the UE is in + medium or high speed state, used for scaling a + mobility control related parameter + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_speed_state_scale_factors_ie(LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT *speed_state_scale_factors, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(speed_state_scale_factors != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(speed_state_scale_factors->sf_medium, ie_ptr, 2); + liblte_value_2_bits(speed_state_scale_factors->sf_high, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_speed_state_scale_factors_ie(uint8 **ie_ptr, + LIBLTE_RRC_SPEED_STATE_SCALE_FACTORS_STRUCT *speed_state_scale_factors) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + speed_state_scale_factors != NULL) + { + speed_state_scale_factors->sf_medium = (LIBLTE_RRC_SSSF_MEDIUM_ENUM)liblte_bits_2_value(ie_ptr, 2); + speed_state_scale_factors->sf_high = (LIBLTE_RRC_SSSF_HIGH_ENUM)liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Info List GERAN + + Description: Contains system information of a GERAN cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: System Time Info CDMA2000 + + Description: Informs the UE about the absolute time in the current + cell + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_system_time_info_cdma2000_ie(LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT *sys_time_info_cdma2000, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(sys_time_info_cdma2000 != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(sys_time_info_cdma2000->cdma_eutra_sync, ie_ptr, 1); + liblte_value_2_bits(sys_time_info_cdma2000->system_time_async, ie_ptr, 1); + if(true == sys_time_info_cdma2000->system_time_async) + { + liblte_value_2_bits((uint32)(sys_time_info_cdma2000->system_time >> 17), ie_ptr, 32); + liblte_value_2_bits((uint32)(sys_time_info_cdma2000->system_time & 0x1FFFF), ie_ptr, 17); + }else{ + liblte_value_2_bits((uint32)(sys_time_info_cdma2000->system_time >> 7), ie_ptr, 32); + liblte_value_2_bits((uint32)(sys_time_info_cdma2000->system_time & 0x7F), ie_ptr, 7); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_system_time_info_cdma2000_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYSTEM_TIME_INFO_CDMA2000_STRUCT *sys_time_info_cdma2000) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + sys_time_info_cdma2000 != NULL) + { + sys_time_info_cdma2000->cdma_eutra_sync = liblte_bits_2_value(ie_ptr, 1); + sys_time_info_cdma2000->system_time_async = liblte_bits_2_value(ie_ptr, 1); + if(true == sys_time_info_cdma2000->system_time_async) + { + sys_time_info_cdma2000->system_time = (uint64)liblte_bits_2_value(ie_ptr, 32) << 17; + sys_time_info_cdma2000->system_time |= (uint64)liblte_bits_2_value(ie_ptr, 17); + }else{ + sys_time_info_cdma2000->system_time = (uint64)liblte_bits_2_value(ie_ptr, 32) << 7; + sys_time_info_cdma2000->system_time |= (uint64)liblte_bits_2_value(ie_ptr, 7); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Tracking Area Code + + Description: Identifies a tracking area within the scope of a + PLMN + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_tracking_area_code_ie(uint16 tac, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(tac, ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tracking_area_code_ie(uint8 **ie_ptr, + uint16 *tac) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tac != NULL) + { + *tac = liblte_bits_2_value(ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: T Reselection + + Description: Contains the timer T_reselection_rat for E-UTRA, + UTRA, GERAN, or CDMA2000 + + Document Reference: 36.331 v10.0.0 Section 6.3.4 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_t_reselection_ie(uint8 t_resel, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(t_resel, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_t_reselection_ie(uint8 **ie_ptr, + uint8 *t_resel) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + t_resel != NULL) + { + *t_resel = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Next Hop Chaining Count + + Description: Updates the Kenb key and corresponds to parameter + NCC + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_next_hop_chaining_count_ie(uint8 next_hop_chaining_count, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(next_hop_chaining_count, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_next_hop_chaining_count_ie(uint8 **ie_ptr, + uint8 *next_hop_chaining_count) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + next_hop_chaining_count != NULL) + { + *next_hop_chaining_count = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Security Algorithm Config + + Description: Configures AS integrity protection algorithm (SRBs) + and AS ciphering algorithm (SRBs and DRBs) + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_algorithm_config_ie(LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT *sec_alg_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(sec_alg_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(sec_alg_cnfg->cipher_alg, ie_ptr, 3); + + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(sec_alg_cnfg->int_alg, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_algorithm_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SECURITY_ALGORITHM_CONFIG_STRUCT *sec_alg_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + sec_alg_cnfg != NULL) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + sec_alg_cnfg->cipher_alg = (LIBLTE_RRC_CIPHERING_ALGORITHM_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + sec_alg_cnfg->int_alg = (LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Short MAC I + + Description: Identifies and verifies the UE at RRC connection + re-establishment + + Document Reference: 36.331 v10.0.0 Section 6.3.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_short_mac_i_ie(uint16 short_mac_i, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(short_mac_i, ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_short_mac_i_ie(uint8 **ie_ptr, + uint16 *short_mac_i) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + short_mac_i != NULL) + { + *short_mac_i = liblte_bits_2_value(ie_ptr, 16); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Antenna Info + + Description: Specifies the common and the UE specific antenna + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_antenna_info_common_ie(LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM antenna_ports_cnt, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(antenna_ports_cnt, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_antenna_info_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM *antenna_ports_cnt) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + antenna_ports_cnt != NULL) + { + *antenna_ports_cnt = (LIBLTE_RRC_ANTENNA_PORTS_COUNT_ENUM)liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_antenna_info_dedicated_ie(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *antenna_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(antenna_info != NULL && + ie_ptr != NULL) + { + // Optional indicator + liblte_value_2_bits(antenna_info->codebook_subset_restriction_present, ie_ptr, 1); + + // Transmission Mode + liblte_value_2_bits(antenna_info->tx_mode, ie_ptr, 3); + + // Codebook Subset Restriction + if(antenna_info->codebook_subset_restriction_present) + { + liblte_value_2_bits(antenna_info->codebook_subset_restriction_choice, ie_ptr, 3); + switch(antenna_info->codebook_subset_restriction_choice) + { + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM3: + liblte_value_2_bits(antenna_info->codebook_subset_restriction, ie_ptr, 2); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM3: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM5: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM6: + liblte_value_2_bits(antenna_info->codebook_subset_restriction, ie_ptr, 4); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM4: + liblte_value_2_bits(antenna_info->codebook_subset_restriction, ie_ptr, 6); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM4: + liblte_value_2_bits(antenna_info->codebook_subset_restriction >> 32, ie_ptr, 32); + liblte_value_2_bits(antenna_info->codebook_subset_restriction, ie_ptr, 32); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM5: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM6: + liblte_value_2_bits(antenna_info->codebook_subset_restriction, ie_ptr, 16); + break; + } + } + + // UE Transmit Antenna Selection + liblte_value_2_bits(antenna_info->ue_tx_antenna_selection_setup_present, ie_ptr, 1); + if(antenna_info->ue_tx_antenna_selection_setup_present) + { + liblte_value_2_bits(antenna_info->ue_tx_antenna_selection_setup, ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_antenna_info_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *antenna_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + antenna_info != NULL) + { + // Optional indicator + antenna_info->codebook_subset_restriction_present = liblte_bits_2_value(ie_ptr, 1); + + // Transmission Mode + antenna_info->tx_mode = (LIBLTE_RRC_TRANSMISSION_MODE_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // Codebook Subset Restriction + if(antenna_info->codebook_subset_restriction_present) + { + antenna_info->codebook_subset_restriction_choice = (LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_CHOICE_ENUM)liblte_bits_2_value(ie_ptr, 3); + switch(antenna_info->codebook_subset_restriction_choice) + { + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM3: + antenna_info->codebook_subset_restriction = liblte_bits_2_value(ie_ptr, 2); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM3: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM5: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM6: + antenna_info->codebook_subset_restriction = liblte_bits_2_value(ie_ptr, 4); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM4: + antenna_info->codebook_subset_restriction = liblte_bits_2_value(ie_ptr, 6); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM4: + antenna_info->codebook_subset_restriction = (uint64)(liblte_bits_2_value(ie_ptr, 32)) << 32; + antenna_info->codebook_subset_restriction |= liblte_bits_2_value(ie_ptr, 32); + break; + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM5: + case LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N4_TM6: + antenna_info->codebook_subset_restriction = liblte_bits_2_value(ie_ptr, 16); + break; + } + } + + // UE Transmit Antenna Selection + antenna_info->ue_tx_antenna_selection_setup_present = liblte_bits_2_value(ie_ptr, 1); + if(antenna_info->ue_tx_antenna_selection_setup_present) + { + antenna_info->ue_tx_antenna_selection_setup = (LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: CQI Report Config + + Description: Specifies the CQI reporting configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cqi_report_config_ie(LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT *cqi_report_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cqi_report_cnfg != NULL && + ie_ptr != NULL) + { + // Optional indicators + liblte_value_2_bits(cqi_report_cnfg->report_mode_aperiodic_present, ie_ptr, 1); + liblte_value_2_bits(cqi_report_cnfg->report_periodic_present, ie_ptr, 1); + + // CQI Report Mode Aperiodic + if(cqi_report_cnfg->report_mode_aperiodic_present) + { + liblte_value_2_bits(cqi_report_cnfg->report_mode_aperiodic, ie_ptr, 3); + } + + // Nom PDSCH RS EPRE Offset + liblte_value_2_bits(cqi_report_cnfg->nom_pdsch_rs_epre_offset + 1, ie_ptr, 3); + + // CQI Report Periodic + if(cqi_report_cnfg->report_periodic_present) + { + liblte_value_2_bits(cqi_report_cnfg->report_periodic_setup_present, ie_ptr, 1); + if(cqi_report_cnfg->report_periodic_setup_present) + { + // Optional indicator + liblte_value_2_bits(cqi_report_cnfg->report_periodic.ri_cnfg_idx_present, ie_ptr, 1); + + // CQI PUCCH Resource Index + liblte_value_2_bits(cqi_report_cnfg->report_periodic.pucch_resource_idx, ie_ptr, 11); + + // CQI PMI Config Index + liblte_value_2_bits(cqi_report_cnfg->report_periodic.pmi_cnfg_idx, ie_ptr, 10); + + // CQI Format Indicator Periodic + liblte_value_2_bits(cqi_report_cnfg->report_periodic.format_ind_periodic, ie_ptr, 1); + if(LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_SUBBAND_CQI == cqi_report_cnfg->report_periodic.format_ind_periodic) + { + liblte_value_2_bits(cqi_report_cnfg->report_periodic.format_ind_periodic_subband_k - 1, ie_ptr, 2); + } + + // RI Config Index + if(cqi_report_cnfg->report_periodic.ri_cnfg_idx_present) + { + liblte_value_2_bits(cqi_report_cnfg->report_periodic.ri_cnfg_idx, ie_ptr, 10); + } + + // Simultaneous Ack/Nack and CQI + liblte_value_2_bits(cqi_report_cnfg->report_periodic.simult_ack_nack_and_cqi, ie_ptr, 1); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cqi_report_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_CQI_REPORT_CONFIG_STRUCT *cqi_report_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cqi_report_cnfg != NULL) + { + // Optional indicators + cqi_report_cnfg->report_mode_aperiodic_present = liblte_bits_2_value(ie_ptr, 1); + cqi_report_cnfg->report_periodic_present = liblte_bits_2_value(ie_ptr, 1); + + // CQI Report Mode Aperiodic + if(cqi_report_cnfg->report_mode_aperiodic_present) + { + cqi_report_cnfg->report_mode_aperiodic = (LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + // Nom PDSCH RS EPRE Offset + cqi_report_cnfg->nom_pdsch_rs_epre_offset = liblte_bits_2_value(ie_ptr, 3) - 1; + + // CQI Report Periodic + if(cqi_report_cnfg->report_periodic_present) + { + cqi_report_cnfg->report_periodic_setup_present = liblte_bits_2_value(ie_ptr, 1); + if(cqi_report_cnfg->report_periodic_setup_present) + { + // Optional indicator + cqi_report_cnfg->report_periodic.ri_cnfg_idx_present = liblte_bits_2_value(ie_ptr, 1); + + // CQI PUCCH Resource Index + cqi_report_cnfg->report_periodic.pucch_resource_idx = liblte_bits_2_value(ie_ptr, 11); + + // CQI PMI Config Index + cqi_report_cnfg->report_periodic.pmi_cnfg_idx = liblte_bits_2_value(ie_ptr, 10); + + // CQI Format Indicator Periodic + cqi_report_cnfg->report_periodic.format_ind_periodic = (LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_SUBBAND_CQI == cqi_report_cnfg->report_periodic.format_ind_periodic) + { + cqi_report_cnfg->report_periodic.format_ind_periodic_subband_k = liblte_bits_2_value(ie_ptr, 2) + 1; + } + + // RI Config Index + if(cqi_report_cnfg->report_periodic.ri_cnfg_idx_present) + { + cqi_report_cnfg->report_periodic.ri_cnfg_idx = liblte_bits_2_value(ie_ptr, 10); + } + + // Simultaneous Ack/Nack and CQI + cqi_report_cnfg->report_periodic.simult_ack_nack_and_cqi = liblte_bits_2_value(ie_ptr, 1); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Cross Carrier Scheduling Config + + Description: Specifies the configuration when the cross carrier + scheduling is used in a cell + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: CSI RS Config + + Description: Specifies the CSI (Channel State Information) + reference signal configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: DRB Identity + + Description: Identifies a DRB used by a UE + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_drb_identity_ie(uint8 drb_id, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(drb_id - 1, ie_ptr, 5); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_drb_identity_ie(uint8 **ie_ptr, + uint8 *drb_id) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + drb_id != NULL) + { + *drb_id = liblte_bits_2_value(ie_ptr, 5) + 1; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Logical Channel Config + + Description: Configures the logical channel parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_logical_channel_config_ie(LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT *log_chan_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(log_chan_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); // FIXME: Handle extension + + // Optional indicator + liblte_value_2_bits(log_chan_cnfg->ul_specific_params_present, ie_ptr, 1); + + if(true == log_chan_cnfg->ul_specific_params_present) + { + // Optional indicator + liblte_value_2_bits(log_chan_cnfg->ul_specific_params.log_chan_group_present, ie_ptr, 1); + + liblte_value_2_bits(log_chan_cnfg->ul_specific_params.priority - 1, ie_ptr, 4); + liblte_value_2_bits(log_chan_cnfg->ul_specific_params.prioritized_bit_rate, ie_ptr, 4); + liblte_value_2_bits(log_chan_cnfg->ul_specific_params.bucket_size_duration, ie_ptr, 3); + + if(true == log_chan_cnfg->ul_specific_params.log_chan_group_present) + { + liblte_value_2_bits(log_chan_cnfg->ul_specific_params.log_chan_group, ie_ptr, 2); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_logical_channel_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_LOGICAL_CHANNEL_CONFIG_STRUCT *log_chan_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext; + + if(ie_ptr != NULL && + log_chan_cnfg != NULL) + { + // Extension indicator + ext = liblte_bits_2_value(ie_ptr, 1); // FIXME: Handle extension + + // Optional indicator + log_chan_cnfg->ul_specific_params_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == log_chan_cnfg->ul_specific_params_present) + { + // Optional indicator + log_chan_cnfg->ul_specific_params.log_chan_group_present = liblte_bits_2_value(ie_ptr, 1); + + log_chan_cnfg->ul_specific_params.priority = liblte_bits_2_value(ie_ptr, 4) + 1; + log_chan_cnfg->ul_specific_params.prioritized_bit_rate = (LIBLTE_RRC_PRIORITIZED_BIT_RATE_ENUM)liblte_bits_2_value(ie_ptr, 4); + log_chan_cnfg->ul_specific_params.bucket_size_duration = (LIBLTE_RRC_BUCKET_SIZE_DURATION_ENUM)liblte_bits_2_value(ie_ptr, 3); + + if(true == log_chan_cnfg->ul_specific_params.log_chan_group_present) + { + log_chan_cnfg->ul_specific_params.log_chan_group = liblte_bits_2_value(ie_ptr, 2); + } + } + + // Consume non-critical extensions + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: MAC Main Config + + Description: Specifies the MAC main configuration for signalling + and data radio bearers + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mac_main_config_ie(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_main_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext = false; + + if(mac_main_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(ext, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(mac_main_cnfg->drx_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(mac_main_cnfg->phr_cnfg_present, ie_ptr, 1); + + // ULSCH Config + if(mac_main_cnfg->ulsch_cnfg_present) + { + // Optional indicators + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.max_harq_tx_present, ie_ptr, 1); + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer_present, ie_ptr, 1); + + // Max HARQ TX + if(mac_main_cnfg->ulsch_cnfg.max_harq_tx_present) + { + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.max_harq_tx, ie_ptr, 4); + } + + // Periodic BSR Timer + if(mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer_present) + { + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer, ie_ptr, 4); + } + + // Re-TX BSR Timer + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.retx_bsr_timer, ie_ptr, 3); + + // TTI Bundling + liblte_value_2_bits(mac_main_cnfg->ulsch_cnfg.tti_bundling, ie_ptr, 1); + } + + // DRX Config + if(mac_main_cnfg->drx_cnfg_present) + { + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.setup_present, ie_ptr, 1); + if(mac_main_cnfg->drx_cnfg.setup_present) + { + // Optional indicators + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.short_drx_present, ie_ptr, 1); + + // On Duration Timer + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.on_duration_timer, ie_ptr, 4); + + // DRX Inactivity Timer + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.drx_inactivity_timer, ie_ptr, 5); + + // DRX Retransmission Timer + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.drx_retx_timer, ie_ptr, 3); + + // Long DRX Cycle Start Offset + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset_choice, ie_ptr, 4); + switch(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset_choice) + { + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF10: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 4); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF20: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF32: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 5); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF40: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF64: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 6); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF80: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF128: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 7); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF160: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF256: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 8); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF320: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF512: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 9); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF640: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1024: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 10); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1280: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2048: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 11); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2560: + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset, ie_ptr, 12); + break; + } + + // Short DRX + if(mac_main_cnfg->drx_cnfg.short_drx_present) + { + // Short DRX Cycle + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.short_drx_cycle, ie_ptr, 4); + + // DRX Short Cycle Timer + liblte_value_2_bits(mac_main_cnfg->drx_cnfg.short_drx_cycle_timer - 1, ie_ptr, 4); + } + } + } + + // Time Alignment Timer Dedicated + liblte_rrc_pack_time_alignment_timer_ie(mac_main_cnfg->time_alignment_timer, ie_ptr); + + // PHR Config + if(mac_main_cnfg->phr_cnfg_present) + { + liblte_value_2_bits(mac_main_cnfg->phr_cnfg.setup_present, ie_ptr, 1); + if(mac_main_cnfg->phr_cnfg.setup_present) + { + // Periodic PHR Timer + liblte_value_2_bits(mac_main_cnfg->phr_cnfg.periodic_phr_timer, ie_ptr, 3); + + // Prohibit PHR Timer + liblte_value_2_bits(mac_main_cnfg->phr_cnfg.prohibit_phr_timer, ie_ptr, 3); + + // DL Pathloss Change + liblte_value_2_bits(mac_main_cnfg->phr_cnfg.dl_pathloss_change, ie_ptr, 2); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mac_main_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_main_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext; + + if(ie_ptr != NULL && + mac_main_cnfg != NULL) + { + // Extension indicator + ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + mac_main_cnfg->ulsch_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + mac_main_cnfg->drx_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + mac_main_cnfg->phr_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // ULSCH Config + if(mac_main_cnfg->ulsch_cnfg_present) + { + // Optional indicators + mac_main_cnfg->ulsch_cnfg.max_harq_tx_present = liblte_bits_2_value(ie_ptr, 1); + mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer_present = liblte_bits_2_value(ie_ptr, 1); + + // Max HARQ TX + if(mac_main_cnfg->ulsch_cnfg.max_harq_tx_present) + { + mac_main_cnfg->ulsch_cnfg.max_harq_tx = (LIBLTE_RRC_MAX_HARQ_TX_ENUM)liblte_bits_2_value(ie_ptr, 4); + } + + // Periodic BSR Timer + if(mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer_present) + { + mac_main_cnfg->ulsch_cnfg.periodic_bsr_timer = (LIBLTE_RRC_PERIODIC_BSR_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 4); + } + + // Re-TX BSR Timer + mac_main_cnfg->ulsch_cnfg.retx_bsr_timer = (LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // TTI Bundling + mac_main_cnfg->ulsch_cnfg.tti_bundling = liblte_bits_2_value(ie_ptr, 1); + } + + // DRX Config + if(mac_main_cnfg->drx_cnfg_present) + { + mac_main_cnfg->drx_cnfg.setup_present = liblte_bits_2_value(ie_ptr, 1); + if(mac_main_cnfg->drx_cnfg.setup_present) + { + // Optional indicators + mac_main_cnfg->drx_cnfg.short_drx_present = liblte_bits_2_value(ie_ptr, 1); + + // On Duration Timer + mac_main_cnfg->drx_cnfg.on_duration_timer = (LIBLTE_RRC_ON_DURATION_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // DRX Inactivity Timer + mac_main_cnfg->drx_cnfg.drx_inactivity_timer = (LIBLTE_RRC_DRX_INACTIVITY_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 5); + + // DRX Retransmission Timer + mac_main_cnfg->drx_cnfg.drx_retx_timer = (LIBLTE_RRC_DRX_RETRANSMISSION_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // Long DRX Cycle Short Offset + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset_choice = (LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_CHOICE_ENUM)liblte_bits_2_value(ie_ptr, 4); + switch(mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset_choice) + { + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF10: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 4); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF20: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF32: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 5); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF40: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF64: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 6); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF80: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF128: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 7); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF160: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF256: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 8); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF320: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF512: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 9); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF640: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1024: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 10); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF1280: + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2048: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 11); + break; + case LIBLTE_RRC_LONG_DRX_CYCLE_START_OFFSET_SF2560: + mac_main_cnfg->drx_cnfg.long_drx_cycle_start_offset = liblte_bits_2_value(ie_ptr, 12); + break; + } + + // Short DRX + if(mac_main_cnfg->drx_cnfg.short_drx_present) + { + // Short DRX Cycle + mac_main_cnfg->drx_cnfg.short_drx_cycle = (LIBLTE_RRC_SHORT_DRX_CYCLE_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // DRX Short Cycle Timer + mac_main_cnfg->drx_cnfg.short_drx_cycle_timer = liblte_bits_2_value(ie_ptr, 4) + 1; + } + } + } + + // Time Alignment Timer Dedicated + liblte_rrc_unpack_time_alignment_timer_ie(ie_ptr, &mac_main_cnfg->time_alignment_timer); + + // PHR Config + if(mac_main_cnfg->phr_cnfg_present) + { + mac_main_cnfg->phr_cnfg.setup_present = liblte_bits_2_value(ie_ptr, 1); + if(mac_main_cnfg->phr_cnfg.setup_present) + { + // Periodic PHR Timer + mac_main_cnfg->phr_cnfg.periodic_phr_timer = (LIBLTE_RRC_PERIODIC_PHR_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // Prohibit PHR Timer + mac_main_cnfg->phr_cnfg.prohibit_phr_timer = (LIBLTE_RRC_PROHIBIT_PHR_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // DL Pathloss Change + mac_main_cnfg->phr_cnfg.dl_pathloss_change = (LIBLTE_RRC_DL_PATHLOSS_CHANGE_ENUM)liblte_bits_2_value(ie_ptr, 2); + } + } + + // Consume non-critical extensions + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PDCP Config + + Description: Sets the configurable PDCP parameters for data + radio bearers + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdcp_config_ie(LIBLTE_RRC_PDCP_CONFIG_STRUCT *pdcp_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pdcp_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(pdcp_cnfg->discard_timer_present, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->rlc_am_status_report_required_present, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->rlc_um_pdcp_sn_size_present, ie_ptr, 1); + + // Discard Timer + if(pdcp_cnfg->discard_timer_present) + { + liblte_value_2_bits(pdcp_cnfg->discard_timer, ie_ptr, 3); + } + + // RLC AM + if(pdcp_cnfg->rlc_am_status_report_required_present) + { + liblte_value_2_bits(pdcp_cnfg->rlc_am_status_report_required, ie_ptr, 1); + } + + // RLC UM + if(pdcp_cnfg->rlc_um_pdcp_sn_size_present) + { + liblte_value_2_bits(pdcp_cnfg->rlc_um_pdcp_sn_size, ie_ptr, 1); + } + + // Header Compression + liblte_value_2_bits(pdcp_cnfg->hdr_compression_rohc, ie_ptr, 1); + if(pdcp_cnfg->hdr_compression_rohc) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Max CID + liblte_value_2_bits(pdcp_cnfg->hdr_compression_max_cid - 1, ie_ptr, 14); + + // Profiles + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0001, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0002, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0003, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0004, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0006, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0101, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0102, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0103, ie_ptr, 1); + liblte_value_2_bits(pdcp_cnfg->hdr_compression_profile_0104, ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdcp_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDCP_CONFIG_STRUCT *pdcp_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pdcp_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + pdcp_cnfg->discard_timer_present = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->rlc_am_status_report_required_present = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->rlc_um_pdcp_sn_size_present = liblte_bits_2_value(ie_ptr, 1); + + // Discard Timer + if(pdcp_cnfg->discard_timer_present) + { + pdcp_cnfg->discard_timer = (LIBLTE_RRC_DISCARD_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + // RLC AM + if(pdcp_cnfg->rlc_am_status_report_required_present) + { + pdcp_cnfg->rlc_am_status_report_required = liblte_bits_2_value(ie_ptr, 1); + } + + // RLC UM + if(pdcp_cnfg->rlc_um_pdcp_sn_size_present) + { + pdcp_cnfg->rlc_um_pdcp_sn_size = (LIBLTE_RRC_PDCP_SN_SIZE_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + // Header Compression + pdcp_cnfg->hdr_compression_rohc = liblte_bits_2_value(ie_ptr, 1); + if(pdcp_cnfg->hdr_compression_rohc) + { + // Extension indicator + bool ext2 = liblte_bits_2_value(ie_ptr, 1); + + // Max CID + pdcp_cnfg->hdr_compression_max_cid = liblte_bits_2_value(ie_ptr, 14) + 1; + + // Profiles + pdcp_cnfg->hdr_compression_profile_0001 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0002 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0003 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0004 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0006 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0101 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0102 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0103 = liblte_bits_2_value(ie_ptr, 1); + pdcp_cnfg->hdr_compression_profile_0104 = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext2, __func__, ie_ptr); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PDSCH Config + + Description: Specifies the common and the UE specific PDSCH + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdsch_config_common_ie(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT *pdsch_config, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pdsch_config != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(pdsch_config->rs_power + 60, ie_ptr, 7); + liblte_value_2_bits(pdsch_config->p_b, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdsch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT *pdsch_config) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pdsch_config != NULL) + { + pdsch_config->rs_power = liblte_bits_2_value(ie_ptr, 7) - 60; + pdsch_config->p_b = liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_pdsch_config_dedicated_ie(LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM p_a, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(p_a, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pdsch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM *p_a) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(p_a != NULL && + ie_ptr != NULL) + { + *p_a = (LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PHICH Config + + Description: Specifies the PHICH configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_phich_config_ie(LIBLTE_RRC_PHICH_CONFIG_STRUCT *phich_config, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(phich_config != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(phich_config->dur, ie_ptr, 1); + liblte_value_2_bits(phich_config->res, ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_phich_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHICH_CONFIG_STRUCT *phich_config) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + phich_config != NULL) + { + phich_config->dur = (LIBLTE_RRC_PHICH_DURATION_ENUM)liblte_bits_2_value(ie_ptr, 1); + phich_config->res = (LIBLTE_RRC_PHICH_RESOURCE_ENUM)liblte_bits_2_value(ie_ptr, 2); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Physical Config Dedicated + + Description: Specifies the UE specific physical channel + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_physical_config_dedicated_ie(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg_ded, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext = false; + + if(phy_cnfg_ded != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(ext, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(phy_cnfg_ded->pdsch_cnfg_ded_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->pucch_cnfg_ded_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->pusch_cnfg_ded_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->ul_pwr_ctrl_ded_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->tpc_pdcch_cnfg_pucch_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->tpc_pdcch_cnfg_pusch_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->cqi_report_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->srs_ul_cnfg_ded_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->antenna_info_present, ie_ptr, 1); + liblte_value_2_bits(phy_cnfg_ded->sched_request_cnfg_present, ie_ptr, 1); + + // PDSCH Config + if(phy_cnfg_ded->pdsch_cnfg_ded_present) + { + liblte_rrc_pack_pdsch_config_dedicated_ie(phy_cnfg_ded->pdsch_cnfg_ded, ie_ptr); + } + + // PUCCH Config + if(phy_cnfg_ded->pucch_cnfg_ded_present) + { + liblte_rrc_pack_pucch_config_dedicated_ie(&phy_cnfg_ded->pucch_cnfg_ded, ie_ptr); + } + + // PUSCH Config + if(phy_cnfg_ded->pusch_cnfg_ded_present) + { + liblte_rrc_pack_pusch_config_dedicated_ie(&phy_cnfg_ded->pusch_cnfg_ded, ie_ptr); + } + + // Uplink Power Control + if(phy_cnfg_ded->ul_pwr_ctrl_ded_present) + { + liblte_rrc_pack_ul_power_control_dedicated_ie(&phy_cnfg_ded->ul_pwr_ctrl_ded, ie_ptr); + } + + // TPC PDCCH Config PUCCH + if(phy_cnfg_ded->tpc_pdcch_cnfg_pucch_present) + { + liblte_rrc_pack_tpc_pdcch_config_ie(&phy_cnfg_ded->tpc_pdcch_cnfg_pucch, ie_ptr); + } + + // TPC PDCCH Config PUSCH + if(phy_cnfg_ded->tpc_pdcch_cnfg_pusch_present) + { + liblte_rrc_pack_tpc_pdcch_config_ie(&phy_cnfg_ded->tpc_pdcch_cnfg_pusch, ie_ptr); + } + + // CQI Report Config + if(phy_cnfg_ded->cqi_report_cnfg_present) + { + liblte_rrc_pack_cqi_report_config_ie(&phy_cnfg_ded->cqi_report_cnfg, ie_ptr); + } + + // SRS UL Config + if(phy_cnfg_ded->srs_ul_cnfg_ded_present) + { + liblte_rrc_pack_srs_ul_config_dedicated_ie(&phy_cnfg_ded->srs_ul_cnfg_ded, ie_ptr); + } + + // Antenna Info + if(phy_cnfg_ded->antenna_info_present) + { + liblte_value_2_bits(phy_cnfg_ded->antenna_info_default_value, ie_ptr, 1); + if(!phy_cnfg_ded->antenna_info_default_value) + { + liblte_rrc_pack_antenna_info_dedicated_ie(&phy_cnfg_ded->antenna_info_explicit_value, ie_ptr); + } + } + + // Scheduling Request Config + if(phy_cnfg_ded->sched_request_cnfg_present) + { + liblte_rrc_pack_scheduling_request_config_ie(&phy_cnfg_ded->sched_request_cnfg, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_physical_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg_ded) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext; + + if(ie_ptr != NULL && + phy_cnfg_ded != NULL) + { + // Extension indicator + ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + phy_cnfg_ded->pdsch_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->pucch_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->pusch_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->ul_pwr_ctrl_ded_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->tpc_pdcch_cnfg_pucch_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->tpc_pdcch_cnfg_pusch_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->cqi_report_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->srs_ul_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->antenna_info_present = liblte_bits_2_value(ie_ptr, 1); + phy_cnfg_ded->sched_request_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // PDSCH Config + if(phy_cnfg_ded->pdsch_cnfg_ded_present) + { + liblte_rrc_unpack_pdsch_config_dedicated_ie(ie_ptr, &phy_cnfg_ded->pdsch_cnfg_ded); + } + + // PUCCH Config + if(phy_cnfg_ded->pucch_cnfg_ded_present) + { + liblte_rrc_unpack_pucch_config_dedicated_ie(ie_ptr, &phy_cnfg_ded->pucch_cnfg_ded); + } + + // PUSCH Config + if(phy_cnfg_ded->pusch_cnfg_ded_present) + { + liblte_rrc_unpack_pusch_config_dedicated_ie(ie_ptr, &phy_cnfg_ded->pusch_cnfg_ded); + } + + // Uplink Power Control + if(phy_cnfg_ded->ul_pwr_ctrl_ded_present) + { + liblte_rrc_unpack_ul_power_control_dedicated_ie(ie_ptr, &phy_cnfg_ded->ul_pwr_ctrl_ded); + } + + // TPC PDCCH Config PUCCH + if(phy_cnfg_ded->tpc_pdcch_cnfg_pucch_present) + { + liblte_rrc_unpack_tpc_pdcch_config_ie(ie_ptr, &phy_cnfg_ded->tpc_pdcch_cnfg_pucch); + } + + // TPC PDCCH Config PUSCH + if(phy_cnfg_ded->tpc_pdcch_cnfg_pusch_present) + { + liblte_rrc_unpack_tpc_pdcch_config_ie(ie_ptr, &phy_cnfg_ded->tpc_pdcch_cnfg_pusch); + } + + // CQI Report Config + if(phy_cnfg_ded->cqi_report_cnfg_present) + { + liblte_rrc_unpack_cqi_report_config_ie(ie_ptr, &phy_cnfg_ded->cqi_report_cnfg); + } + + // SRS UL Config + if(phy_cnfg_ded->srs_ul_cnfg_ded_present) + { + liblte_rrc_unpack_srs_ul_config_dedicated_ie(ie_ptr, &phy_cnfg_ded->srs_ul_cnfg_ded); + } + + // Antenna Info + if(phy_cnfg_ded->antenna_info_present) + { + phy_cnfg_ded->antenna_info_default_value = liblte_bits_2_value(ie_ptr, 1); + if(!phy_cnfg_ded->antenna_info_default_value) + { + liblte_rrc_unpack_antenna_info_dedicated_ie(ie_ptr, &phy_cnfg_ded->antenna_info_explicit_value); + } + } + + // Scheduling Request Config + if(phy_cnfg_ded->sched_request_cnfg_present) + { + liblte_rrc_unpack_scheduling_request_config_ie(ie_ptr, &phy_cnfg_ded->sched_request_cnfg); + } + + // Consume non-critical extensions + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: P Max + + Description: Limits the UE's uplink transmission power on a + carrier frequency and is used to calculate the + parameter P Compensation + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_p_max_ie(int8 p_max, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(p_max + 30, ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_p_max_ie(uint8 **ie_ptr, + int8 *p_max) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + p_max != NULL) + { + *p_max = (int8)liblte_bits_2_value(ie_ptr, 6) - 30; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PRACH Config + + Description: Specifies the PRACH configuration in the system + information and in the mobility control information + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_sib_ie(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *prach_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(prach_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(prach_cnfg->root_sequence_index, ie_ptr, 10); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.prach_config_index, ie_ptr, 6); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.high_speed_flag, ie_ptr, 1); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.zero_correlation_zone_config, ie_ptr, 4); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.prach_freq_offset, ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_sib_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *prach_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + prach_cnfg != NULL) + { + prach_cnfg->root_sequence_index = liblte_bits_2_value(ie_ptr, 10); + prach_cnfg->prach_cnfg_info.prach_config_index = liblte_bits_2_value(ie_ptr, 6); + prach_cnfg->prach_cnfg_info.high_speed_flag = liblte_bits_2_value(ie_ptr, 1); + prach_cnfg->prach_cnfg_info.zero_correlation_zone_config = liblte_bits_2_value(ie_ptr, 4); + prach_cnfg->prach_cnfg_info.prach_freq_offset = liblte_bits_2_value(ie_ptr, 7); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_ie(LIBLTE_RRC_PRACH_CONFIG_STRUCT *prach_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(prach_cnfg != NULL && + ie_ptr != NULL) + { + // Optional indicator + liblte_value_2_bits(prach_cnfg->prach_cnfg_info_present, ie_ptr, 1); + + liblte_value_2_bits(prach_cnfg->root_sequence_index, ie_ptr, 10); + + if(true == prach_cnfg->prach_cnfg_info_present) + { + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.prach_config_index, ie_ptr, 6); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.high_speed_flag, ie_ptr, 1); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.zero_correlation_zone_config, ie_ptr, 4); + liblte_value_2_bits(prach_cnfg->prach_cnfg_info.prach_freq_offset, ie_ptr, 7); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_PRACH_CONFIG_STRUCT *prach_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + prach_cnfg != NULL) + { + // Optional indicator + prach_cnfg->prach_cnfg_info_present = liblte_bits_2_value(ie_ptr, 1); + + prach_cnfg->root_sequence_index = liblte_bits_2_value(ie_ptr, 10); + + if(true == prach_cnfg->prach_cnfg_info_present) + { + prach_cnfg->prach_cnfg_info.prach_config_index = liblte_bits_2_value(ie_ptr, 6); + prach_cnfg->prach_cnfg_info.high_speed_flag = liblte_bits_2_value(ie_ptr, 1); + prach_cnfg->prach_cnfg_info.zero_correlation_zone_config = liblte_bits_2_value(ie_ptr, 4); + prach_cnfg->prach_cnfg_info.prach_freq_offset = liblte_bits_2_value(ie_ptr, 7); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_prach_config_scell_r10_ie(uint8 prach_cnfg_idx, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(prach_cnfg_idx, ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_prach_config_scell_r10_ie(uint8 **ie_ptr, + uint8 *prach_cnfg_idx) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + prach_cnfg_idx != NULL) + { + *prach_cnfg_idx = liblte_bits_2_value(ie_ptr, 6); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Presence Antenna Port 1 + + Description: Indicates whether all the neighboring cells use + antenna port 1 + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_presence_antenna_port_1_ie(bool presence_ant_port_1, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(presence_ant_port_1, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_presence_antenna_port_1_ie(uint8 **ie_ptr, + bool *presence_ant_port_1) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + presence_ant_port_1 != NULL) + { + *presence_ant_port_1 = liblte_bits_2_value(ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PUCCH Config + + Description: Specifies the common and the UE specific PUCCH + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pucch_config_common_ie(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT *pucch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pucch_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(pucch_cnfg->delta_pucch_shift, ie_ptr, 2); + liblte_value_2_bits(pucch_cnfg->n_rb_cqi, ie_ptr, 7); + liblte_value_2_bits(pucch_cnfg->n_cs_an, ie_ptr, 3); + liblte_value_2_bits(pucch_cnfg->n1_pucch_an, ie_ptr, 11); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pucch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT *pucch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pucch_cnfg != NULL) + { + pucch_cnfg->delta_pucch_shift = (LIBLTE_RRC_DELTA_PUCCH_SHIFT_ENUM)liblte_bits_2_value(ie_ptr, 2); + pucch_cnfg->n_rb_cqi = liblte_bits_2_value(ie_ptr, 7); + pucch_cnfg->n_cs_an = liblte_bits_2_value(ie_ptr, 3); + pucch_cnfg->n1_pucch_an = liblte_bits_2_value(ie_ptr, 11); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_pucch_config_dedicated_ie(LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT *pucch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pucch_cnfg != NULL && + ie_ptr != NULL) + { + // Optional indicator + liblte_value_2_bits(pucch_cnfg->tdd_ack_nack_feedback_mode_present, ie_ptr, 1); + + // Ack/Nack Repetition + liblte_value_2_bits(pucch_cnfg->ack_nack_repetition_setup_present, ie_ptr, 1); + if(pucch_cnfg->ack_nack_repetition_setup_present) + { + // Repetition Factor + liblte_value_2_bits(pucch_cnfg->ack_nack_repetition_factor, ie_ptr, 2); + + // N1 PUCCH AN Repetition + liblte_value_2_bits(pucch_cnfg->ack_nack_repetition_n1_pucch_an, ie_ptr, 11); + } + + // TDD Ack/Nack Feedback Mode + if(pucch_cnfg->tdd_ack_nack_feedback_mode_present) + { + liblte_value_2_bits(pucch_cnfg->tdd_ack_nack_feedback_mode, ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pucch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT *pucch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pucch_cnfg != NULL) + { + // Optional indicator + pucch_cnfg->tdd_ack_nack_feedback_mode_present = liblte_bits_2_value(ie_ptr, 1); + + // Ack/Nack Repetition + pucch_cnfg->ack_nack_repetition_setup_present = liblte_bits_2_value(ie_ptr, 1); + if(pucch_cnfg->ack_nack_repetition_setup_present) + { + // Repetition Factor + pucch_cnfg->ack_nack_repetition_factor = (LIBLTE_RRC_ACK_NACK_REPETITION_FACTOR_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // N1 PUCCH AN Repetition + pucch_cnfg->ack_nack_repetition_n1_pucch_an = liblte_bits_2_value(ie_ptr, 11); + } + + // TDD Ack/Nack Feedback Mode + if(pucch_cnfg->tdd_ack_nack_feedback_mode_present) + { + pucch_cnfg->tdd_ack_nack_feedback_mode = (LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: PUSCH Config + + Description: Specifies the common and the UE specific PUSCH + configuration and the reference signal configuration + for PUSCH and PUCCH + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pusch_config_common_ie(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT *pusch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pusch_cnfg != NULL && + ie_ptr != NULL) + { + // PUSCH Config Basic + liblte_value_2_bits(pusch_cnfg->n_sb - 1, ie_ptr, 2); + liblte_value_2_bits(pusch_cnfg->hopping_mode, ie_ptr, 1); + liblte_value_2_bits(pusch_cnfg->pusch_hopping_offset, ie_ptr, 7); + liblte_value_2_bits(pusch_cnfg->enable_64_qam, ie_ptr, 1); + + // UL Reference Signals PUSCH + liblte_value_2_bits(pusch_cnfg->ul_rs.group_hopping_enabled, ie_ptr, 1); + liblte_value_2_bits(pusch_cnfg->ul_rs.group_assignment_pusch, ie_ptr, 5); + liblte_value_2_bits(pusch_cnfg->ul_rs.sequence_hopping_enabled, ie_ptr, 1); + liblte_value_2_bits(pusch_cnfg->ul_rs.cyclic_shift, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pusch_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT *pusch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pusch_cnfg != NULL) + { + // PUSCH Config Basic + pusch_cnfg->n_sb = liblte_bits_2_value(ie_ptr, 2) + 1; + pusch_cnfg->hopping_mode = (LIBLTE_RRC_HOPPING_MODE_ENUM)liblte_bits_2_value(ie_ptr, 1); + pusch_cnfg->pusch_hopping_offset = liblte_bits_2_value(ie_ptr, 7); + pusch_cnfg->enable_64_qam = liblte_bits_2_value(ie_ptr, 1); + + // UL Reference Signals PUSCH + pusch_cnfg->ul_rs.group_hopping_enabled = liblte_bits_2_value(ie_ptr, 1); + pusch_cnfg->ul_rs.group_assignment_pusch = liblte_bits_2_value(ie_ptr, 5); + pusch_cnfg->ul_rs.sequence_hopping_enabled = liblte_bits_2_value(ie_ptr, 1); + pusch_cnfg->ul_rs.cyclic_shift = liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_pusch_config_dedicated_ie(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT *pusch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(pusch_cnfg != NULL && + ie_ptr != NULL) + { + // Beta Offset ACK Index + liblte_value_2_bits(pusch_cnfg->beta_offset_ack_idx, ie_ptr, 4); + + // Beta Offset RI Index + liblte_value_2_bits(pusch_cnfg->beta_offset_ri_idx, ie_ptr, 4); + + // Beta Offset CQI Index + liblte_value_2_bits(pusch_cnfg->beta_offset_cqi_idx, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pusch_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT *pusch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + pusch_cnfg != NULL) + { + // Beta Offset ACK Index + pusch_cnfg->beta_offset_ack_idx = liblte_bits_2_value(ie_ptr, 4); + + // Beta Offset RI Index + pusch_cnfg->beta_offset_ri_idx = liblte_bits_2_value(ie_ptr, 4); + + // Beta Offset CQI Index + pusch_cnfg->beta_offset_cqi_idx = liblte_bits_2_value(ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RACH Config Common + + Description: Specifies the generic random access parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rach_config_common_ie(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rach_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Preamble Info + liblte_value_2_bits(rach_cnfg->preambles_group_a_cnfg.present, ie_ptr, 1); + liblte_value_2_bits(rach_cnfg->num_ra_preambles, ie_ptr, 4); + if(true == rach_cnfg->preambles_group_a_cnfg.present) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(rach_cnfg->preambles_group_a_cnfg.size_of_ra, ie_ptr, 4); + liblte_value_2_bits(rach_cnfg->preambles_group_a_cnfg.msg_size, ie_ptr, 2); + liblte_value_2_bits(rach_cnfg->preambles_group_a_cnfg.msg_pwr_offset_group_b, ie_ptr, 3); + } + + // Power Ramping Parameters + liblte_value_2_bits(rach_cnfg->pwr_ramping_step, ie_ptr, 2); + liblte_value_2_bits(rach_cnfg->preamble_init_rx_target_pwr, ie_ptr, 4); + + // RA Supervision Info + liblte_value_2_bits(rach_cnfg->preamble_trans_max, ie_ptr, 4); + liblte_value_2_bits(rach_cnfg->ra_resp_win_size, ie_ptr, 3); + liblte_value_2_bits(rach_cnfg->mac_con_res_timer, ie_ptr, 3); + + liblte_value_2_bits(rach_cnfg->max_harq_msg3_tx - 1, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rach_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rach_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Preamble Info + rach_cnfg->preambles_group_a_cnfg.present = liblte_bits_2_value(ie_ptr, 1); + rach_cnfg->num_ra_preambles = (LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_ENUM)liblte_bits_2_value(ie_ptr, 4); + if(true == rach_cnfg->preambles_group_a_cnfg.present) + { + // Extension indicator + bool ext2 = liblte_bits_2_value(ie_ptr, 1); + + rach_cnfg->preambles_group_a_cnfg.size_of_ra = (LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_ENUM)liblte_bits_2_value(ie_ptr, 4); + rach_cnfg->preambles_group_a_cnfg.msg_size = (LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_ENUM)liblte_bits_2_value(ie_ptr, 2); + rach_cnfg->preambles_group_a_cnfg.msg_pwr_offset_group_b = (LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext2, __func__, ie_ptr); + + }else{ + rach_cnfg->preambles_group_a_cnfg.size_of_ra = (LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_ENUM)rach_cnfg->num_ra_preambles; + } + + // Power Ramping Parameters + rach_cnfg->pwr_ramping_step = (LIBLTE_RRC_POWER_RAMPING_STEP_ENUM)liblte_bits_2_value(ie_ptr, 2); + rach_cnfg->preamble_init_rx_target_pwr = (LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // RA Supervision Info + rach_cnfg->preamble_trans_max = (LIBLTE_RRC_PREAMBLE_TRANS_MAX_ENUM)liblte_bits_2_value(ie_ptr, 4); + rach_cnfg->ra_resp_win_size = (LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_ENUM)liblte_bits_2_value(ie_ptr, 3); + rach_cnfg->mac_con_res_timer = (LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + rach_cnfg->max_harq_msg3_tx = liblte_bits_2_value(ie_ptr, 3) + 1; + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RACH Config Dedicated + + Description: Specifies the dedicated random access parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rach_config_dedicated_ie(LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT *rach_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rach_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(rach_cnfg->preamble_index, ie_ptr, 6); + liblte_value_2_bits(rach_cnfg->prach_mask_index, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rach_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_RACH_CONFIG_DEDICATED_STRUCT *rach_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rach_cnfg != NULL) + { + rach_cnfg->preamble_index = liblte_bits_2_value(ie_ptr, 6); + rach_cnfg->prach_mask_index = liblte_bits_2_value(ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Radio Resource Config Common + + Description: Specifies the common radio resource configurations + in the system information and in the mobility control + information, including random access parameters + and static physical layer parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_common_sib_ie(LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT *rr_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rr_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_rrc_pack_rach_config_common_ie(&rr_cnfg->rach_cnfg, ie_ptr); + + // BCCH Config + liblte_value_2_bits(rr_cnfg->bcch_cnfg.modification_period_coeff, ie_ptr, 2); + + // PCCH Config + liblte_value_2_bits(rr_cnfg->pcch_cnfg.default_paging_cycle, ie_ptr, 2); + liblte_value_2_bits(rr_cnfg->pcch_cnfg.nB, ie_ptr, 3); + + liblte_rrc_pack_prach_config_sib_ie(&rr_cnfg->prach_cnfg, ie_ptr); + liblte_rrc_pack_pdsch_config_common_ie(&rr_cnfg->pdsch_cnfg, ie_ptr); + liblte_rrc_pack_pusch_config_common_ie(&rr_cnfg->pusch_cnfg, ie_ptr); + liblte_rrc_pack_pucch_config_common_ie(&rr_cnfg->pucch_cnfg, ie_ptr); + liblte_rrc_pack_srs_ul_config_common_ie(&rr_cnfg->srs_ul_cnfg, ie_ptr); + liblte_rrc_pack_ul_power_control_common_ie(&rr_cnfg->ul_pwr_ctrl, ie_ptr); + + // UL CP Length + liblte_value_2_bits(rr_cnfg->ul_cp_length, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_common_sib_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_COMMON_SIB_STRUCT *rr_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rr_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_rach_config_common_ie(ie_ptr, &rr_cnfg->rach_cnfg); + + // BCCH Config + rr_cnfg->bcch_cnfg.modification_period_coeff = (LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // PCCH Config + rr_cnfg->pcch_cnfg.default_paging_cycle = (LIBLTE_RRC_DEFAULT_PAGING_CYCLE_ENUM)liblte_bits_2_value(ie_ptr, 2); + rr_cnfg->pcch_cnfg.nB = (LIBLTE_RRC_NB_ENUM)liblte_bits_2_value(ie_ptr, 3); + + liblte_rrc_unpack_prach_config_sib_ie(ie_ptr, &rr_cnfg->prach_cnfg); + liblte_rrc_unpack_pdsch_config_common_ie(ie_ptr, &rr_cnfg->pdsch_cnfg); + liblte_rrc_unpack_pusch_config_common_ie(ie_ptr, &rr_cnfg->pusch_cnfg); + liblte_rrc_unpack_pucch_config_common_ie(ie_ptr, &rr_cnfg->pucch_cnfg); + liblte_rrc_unpack_srs_ul_config_common_ie(ie_ptr, &rr_cnfg->srs_ul_cnfg); + liblte_rrc_unpack_ul_power_control_common_ie(ie_ptr, &rr_cnfg->ul_pwr_ctrl); + + // UL CP Length + rr_cnfg->ul_cp_length = (LIBLTE_RRC_UL_CP_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_common_ie(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *rr_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rr_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(rr_cnfg->rach_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->pdsch_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->phich_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->pucch_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->srs_ul_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->ul_pwr_ctrl_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->ant_info_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->p_max_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->tdd_cnfg_present, ie_ptr, 1); + + // RACH Config Common + if(rr_cnfg->rach_cnfg_present) + { + liblte_rrc_pack_rach_config_common_ie(&rr_cnfg->rach_cnfg, ie_ptr); + } + + // PRACH Config + liblte_rrc_pack_prach_config_ie(&rr_cnfg->prach_cnfg, ie_ptr); + + // PDSCH Config Common + if(rr_cnfg->pdsch_cnfg_present) + { + liblte_rrc_pack_pdsch_config_common_ie(&rr_cnfg->pdsch_cnfg, ie_ptr); + } + + // PUSCH Config Common + liblte_rrc_pack_pusch_config_common_ie(&rr_cnfg->pusch_cnfg, ie_ptr); + + // PHICH Config + if(rr_cnfg->phich_cnfg_present) + { + liblte_rrc_pack_phich_config_ie(&rr_cnfg->phich_cnfg, ie_ptr); + } + + // PUCCH Config Common + if(rr_cnfg->pucch_cnfg_present) + { + liblte_rrc_pack_pucch_config_common_ie(&rr_cnfg->pucch_cnfg, ie_ptr); + } + + // Sounding RS UL Config Common + if(rr_cnfg->srs_ul_cnfg_present) + { + liblte_rrc_pack_srs_ul_config_common_ie(&rr_cnfg->srs_ul_cnfg, ie_ptr); + } + + // Antenna Info Common + if(rr_cnfg->ant_info_present) + { + liblte_rrc_pack_antenna_info_common_ie(rr_cnfg->ant_info, ie_ptr); + } + + // P Max + if(rr_cnfg->p_max_present) + { + liblte_rrc_pack_p_max_ie(rr_cnfg->p_max, ie_ptr); + } + + // TDD Config + if(rr_cnfg->tdd_cnfg_present) + { + liblte_rrc_pack_tdd_config_ie(&rr_cnfg->tdd_cnfg, ie_ptr); + } + + // UL CP Length + liblte_value_2_bits(rr_cnfg->ul_cp_length, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *rr_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rr_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + rr_cnfg->rach_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->pdsch_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->phich_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->pucch_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->srs_ul_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->ul_pwr_ctrl_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->ant_info_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->p_max_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->tdd_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // RACH Config Common + if(rr_cnfg->rach_cnfg_present) + { + liblte_rrc_unpack_rach_config_common_ie(ie_ptr, &rr_cnfg->rach_cnfg); + } + + // PRACH Config + liblte_rrc_unpack_prach_config_ie(ie_ptr, &rr_cnfg->prach_cnfg); + + // PDSCH Config Common + if(rr_cnfg->pdsch_cnfg_present) + { + liblte_rrc_unpack_pdsch_config_common_ie(ie_ptr, &rr_cnfg->pdsch_cnfg); + } + + // PUSCH Config Common + liblte_rrc_unpack_pusch_config_common_ie(ie_ptr, &rr_cnfg->pusch_cnfg); + + // PHICH Config + if(rr_cnfg->phich_cnfg_present) + { + liblte_rrc_unpack_phich_config_ie(ie_ptr, &rr_cnfg->phich_cnfg); + } + + // PUCCH Config Common + if(rr_cnfg->pucch_cnfg_present) + { + liblte_rrc_unpack_pucch_config_common_ie(ie_ptr, &rr_cnfg->pucch_cnfg); + } + + // Sounding RS UL Config Common + if(rr_cnfg->srs_ul_cnfg_present) + { + liblte_rrc_unpack_srs_ul_config_common_ie(ie_ptr, &rr_cnfg->srs_ul_cnfg); + } + + // Antenna Info Common + if(rr_cnfg->ant_info_present) + { + liblte_rrc_unpack_antenna_info_common_ie(ie_ptr, &rr_cnfg->ant_info); + } + + // P Max + if(rr_cnfg->p_max_present) + { + liblte_rrc_unpack_p_max_ie(ie_ptr, &rr_cnfg->p_max); + } + + // TDD Config + if(rr_cnfg->tdd_cnfg_present) + { + liblte_rrc_unpack_tdd_config_ie(ie_ptr, &rr_cnfg->tdd_cnfg); + } + + // UL CP Length + rr_cnfg->ul_cp_length = (LIBLTE_RRC_UL_CP_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Radio Resource Config Dedicated + + Description: Sets up/Modifies/Releases RBs, modifies the MAC + main configuration, modifies the SPS configuration + and modifies dedicated physical configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rr_config_dedicated_ie(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *rr_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(rr_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(rr_cnfg->rlf_timers_and_constants_present, ie_ptr, 1); + + // Optional indicators + if(rr_cnfg->srb_to_add_mod_list_size != 0) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + if(rr_cnfg->drb_to_add_mod_list_size != 0) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + if(rr_cnfg->drb_to_release_list_size != 0) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(rr_cnfg->mac_main_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->sps_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->phy_cnfg_ded_present, ie_ptr, 1); + + // SRB To Add Mod List + if(rr_cnfg->srb_to_add_mod_list_size != 0) + { + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list_size - 1, ie_ptr, 1); + } + for(i=0; isrb_to_add_mod_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list[i].rlc_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list[i].lc_cnfg_present, ie_ptr, 1); + + // SRB Identity + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list[i].srb_id - 1, ie_ptr, 1); + + // RLC Config + if(rr_cnfg->srb_to_add_mod_list[i].rlc_cnfg_present) + { + // Choice + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list[i].rlc_default_cnfg_present, ie_ptr, 1); + + // Explicit Config + if(!rr_cnfg->srb_to_add_mod_list[i].rlc_default_cnfg_present) + { + liblte_rrc_pack_rlc_config_ie(&rr_cnfg->srb_to_add_mod_list[i].rlc_explicit_cnfg, ie_ptr); + } + } + + // Logical Channel Config + if(rr_cnfg->srb_to_add_mod_list[i].lc_cnfg_present) + { + // Choice + liblte_value_2_bits(rr_cnfg->srb_to_add_mod_list[i].lc_default_cnfg_present, ie_ptr, 1); + + // Explicit Config + if(!rr_cnfg->srb_to_add_mod_list[i].lc_default_cnfg_present) + { + liblte_rrc_pack_logical_channel_config_ie(&rr_cnfg->srb_to_add_mod_list[i].lc_explicit_cnfg, ie_ptr); + } + } + } + + // DRB To Add Mod List + if(rr_cnfg->drb_to_add_mod_list_size != 0) + { + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list_size - 1, ie_ptr, 4); + } + for(i=0; idrb_to_add_mod_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].lc_id_present, ie_ptr, 1); + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].lc_cnfg_present, ie_ptr, 1); + + // EPS Bearer Identity + if(rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id_present) + { + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id, ie_ptr, 4); + } + + // DRB Identity + liblte_rrc_pack_drb_identity_ie(rr_cnfg->drb_to_add_mod_list[i].drb_id, ie_ptr); + + // PDCP Config + if(rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg_present) + { + liblte_rrc_pack_pdcp_config_ie(&rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg, ie_ptr); + } + + // RLC Config + if(rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg_present) + { + liblte_rrc_pack_rlc_config_ie(&rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg, ie_ptr); + } + + // Logical Channel Identity + if(rr_cnfg->drb_to_add_mod_list[i].lc_id_present) + { + liblte_value_2_bits(rr_cnfg->drb_to_add_mod_list[i].lc_id - 3, ie_ptr, 3); + } + + // Logical Channel Configuration + if(rr_cnfg->drb_to_add_mod_list[i].lc_cnfg_present) + { + liblte_rrc_pack_logical_channel_config_ie(&rr_cnfg->drb_to_add_mod_list[i].lc_cnfg, ie_ptr); + } + } + + // DRB To Release List + if(rr_cnfg->drb_to_release_list_size != 0) + { + liblte_value_2_bits(rr_cnfg->drb_to_release_list_size - 1, ie_ptr, 4); + } + for(i=0; idrb_to_release_list_size; i++) + { + liblte_rrc_pack_drb_identity_ie(rr_cnfg->drb_to_release_list[i], ie_ptr); + } + + // MAC Main Config + if(rr_cnfg->mac_main_cnfg_present) + { + liblte_value_2_bits(rr_cnfg->mac_main_cnfg.default_value, ie_ptr, 1); + if(!rr_cnfg->mac_main_cnfg.default_value) + { + liblte_rrc_pack_mac_main_config_ie(&rr_cnfg->mac_main_cnfg.explicit_value, ie_ptr); + } + } + + // SPS Config + if(rr_cnfg->sps_cnfg_present) + { + liblte_rrc_pack_sps_config_ie(&rr_cnfg->sps_cnfg, ie_ptr); + } + + // Physical Config Dedicated + if(rr_cnfg->phy_cnfg_ded_present) + { + liblte_rrc_pack_physical_config_dedicated_ie(&rr_cnfg->phy_cnfg_ded, ie_ptr); + } + + // Extension + // Optional indicators + liblte_value_2_bits(rr_cnfg->rlf_timers_and_constants_present, ie_ptr, 1); + + // RLF Timers and Constants + if(rr_cnfg->rlf_timers_and_constants_present) + { + liblte_rrc_pack_rlf_timers_and_constants_ie(&rr_cnfg->rlf_timers_and_constants, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rr_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *rr_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool ext; + bool srb_ext; + bool drb_ext; + bool srb_to_add_mod_list_present; + bool drb_to_add_mod_list_present; + bool drb_to_release_list_present; + + if(ie_ptr != NULL && + rr_cnfg != NULL) + { + // Extension indicator + ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + srb_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + drb_to_add_mod_list_present = liblte_bits_2_value(ie_ptr, 1); + drb_to_release_list_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->mac_main_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->sps_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->phy_cnfg_ded_present = liblte_bits_2_value(ie_ptr, 1); + + // SRB To Add Mod List + if(srb_to_add_mod_list_present) + { + rr_cnfg->srb_to_add_mod_list_size = liblte_bits_2_value(ie_ptr, 1) + 1; + for(i=0; isrb_to_add_mod_list_size; i++) + { + // Extension indicator + srb_ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + rr_cnfg->srb_to_add_mod_list[i].rlc_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->srb_to_add_mod_list[i].lc_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // SRB Identity + rr_cnfg->srb_to_add_mod_list[i].srb_id = liblte_bits_2_value(ie_ptr, 1) + 1; + + // RLC Config + if(rr_cnfg->srb_to_add_mod_list[i].rlc_cnfg_present) + { + // Choice + rr_cnfg->srb_to_add_mod_list[i].rlc_default_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // Explicit Config + if(!rr_cnfg->srb_to_add_mod_list[i].rlc_default_cnfg_present) + { + liblte_rrc_unpack_rlc_config_ie(ie_ptr, &rr_cnfg->srb_to_add_mod_list[i].rlc_explicit_cnfg); + } + } + + // Logical Channel Config + if(rr_cnfg->srb_to_add_mod_list[i].lc_cnfg_present) + { + // Choice + rr_cnfg->srb_to_add_mod_list[i].lc_default_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // Explicit Config + if(!rr_cnfg->srb_to_add_mod_list[i].lc_default_cnfg_present) + { + liblte_rrc_unpack_logical_channel_config_ie(ie_ptr, &rr_cnfg->srb_to_add_mod_list[i].lc_explicit_cnfg); + } + } + + // Consume non-critical extensions + liblte_rrc_consume_noncrit_extension(srb_ext, __func__, ie_ptr); + } + } + + // DRB To Add Mod List + if(drb_to_add_mod_list_present) + { + rr_cnfg->drb_to_add_mod_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; idrb_to_add_mod_list_size; i++) + { + // Extension indicator + drb_ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->drb_to_add_mod_list[i].lc_id_present = liblte_bits_2_value(ie_ptr, 1); + rr_cnfg->drb_to_add_mod_list[i].lc_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // EPS Bearer Identity + if(rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id_present) + { + rr_cnfg->drb_to_add_mod_list[i].eps_bearer_id = liblte_bits_2_value(ie_ptr, 4); + } + + // DRB Identity + liblte_rrc_unpack_drb_identity_ie(ie_ptr, &rr_cnfg->drb_to_add_mod_list[i].drb_id); + + // PDCP Config + if(rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg_present) + { + liblte_rrc_unpack_pdcp_config_ie(ie_ptr, &rr_cnfg->drb_to_add_mod_list[i].pdcp_cnfg); + } + + // RLC Config + if(rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg_present) + { + liblte_rrc_unpack_rlc_config_ie(ie_ptr, &rr_cnfg->drb_to_add_mod_list[i].rlc_cnfg); + } + + // Logical Channel Identity + if(rr_cnfg->drb_to_add_mod_list[i].lc_id_present) + { + rr_cnfg->drb_to_add_mod_list[i].lc_id = liblte_bits_2_value(ie_ptr, 3) + 3; + } + + // Logical Channel Configuration + if(rr_cnfg->drb_to_add_mod_list[i].lc_cnfg_present) + { + liblte_rrc_unpack_logical_channel_config_ie(ie_ptr, &rr_cnfg->drb_to_add_mod_list[i].lc_cnfg); + } + + // Consume non-critical extensions + liblte_rrc_consume_noncrit_extension(drb_ext, __func__, ie_ptr); + } + } + + // DRB To Release List + if(drb_to_release_list_present) + { + rr_cnfg->drb_to_release_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; idrb_to_release_list_size; i++) + { + liblte_rrc_unpack_drb_identity_ie(ie_ptr, &rr_cnfg->drb_to_release_list[i]); + } + } + + // MAC Main Config + if(rr_cnfg->mac_main_cnfg_present) + { + rr_cnfg->mac_main_cnfg.default_value = liblte_bits_2_value(ie_ptr, 1); + if(!rr_cnfg->mac_main_cnfg.default_value) + { + liblte_rrc_unpack_mac_main_config_ie(ie_ptr, &rr_cnfg->mac_main_cnfg.explicit_value); + } + } + + // SPS Config + if(rr_cnfg->sps_cnfg_present) + { + liblte_rrc_unpack_sps_config_ie(ie_ptr, &rr_cnfg->sps_cnfg); + } + + // Physical Config Dedicated + if(rr_cnfg->phy_cnfg_ded_present) + { + liblte_rrc_unpack_physical_config_dedicated_ie(ie_ptr, &rr_cnfg->phy_cnfg_ded); + } + + // Extension (FIXME: only handling r9 extensions) +#if 0 + if(ext) + { + // Optional indicators + rr_cnfg->rlf_timers_and_constants_present = liblte_bits_2_value(ie_ptr, 1); + + // RLF Timers and Constants + if(rr_cnfg->rlf_timers_and_constants_present) + { + liblte_rrc_unpack_rlf_timers_and_constants_ie(ie_ptr, &rr_cnfg->rlf_timers_and_constants); + } + } +#endif + // Modified by ismael, just skip extensions + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RLC Config + + Description: Specifies the RLC configuration of SRBs and DRBs + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rlc_config_ie(LIBLTE_RRC_RLC_CONFIG_STRUCT *rlc_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rlc_cnfg != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Mode Choice + liblte_value_2_bits(rlc_cnfg->rlc_mode, ie_ptr, 2); + + if(LIBLTE_RRC_RLC_MODE_AM == rlc_cnfg->rlc_mode) + { + // UL AM RLC + { + // T Poll Retransmit + liblte_value_2_bits(rlc_cnfg->ul_am_rlc.t_poll_retx, ie_ptr, 6); + + // Poll PDU + liblte_value_2_bits(rlc_cnfg->ul_am_rlc.poll_pdu, ie_ptr, 3); + + // Poll Byte + liblte_value_2_bits(rlc_cnfg->ul_am_rlc.poll_byte, ie_ptr, 4); + + // Max Retransmission Threshold + liblte_value_2_bits(rlc_cnfg->ul_am_rlc.max_retx_thresh, ie_ptr, 3); + } + + // DL AM RLC + { + // T Reordering + liblte_value_2_bits(rlc_cnfg->dl_am_rlc.t_reordering, ie_ptr, 5); + + // T Status Prohibit + liblte_value_2_bits(rlc_cnfg->dl_am_rlc.t_status_prohibit, ie_ptr, 6); + } + }else if(LIBLTE_RRC_RLC_MODE_UM_BI == rlc_cnfg->rlc_mode){ + // UL UM RLC + { + // SN Field Length + liblte_value_2_bits(rlc_cnfg->ul_um_bi_rlc.sn_field_len, ie_ptr, 1); + } + + // DL UM RLC + { + // SN Field Length + liblte_value_2_bits(rlc_cnfg->dl_um_bi_rlc.sn_field_len, ie_ptr, 1); + + // T Reordering + liblte_value_2_bits(rlc_cnfg->dl_um_bi_rlc.t_reordering, ie_ptr, 5); + } + }else if(LIBLTE_RRC_RLC_MODE_UM_UNI_UL == rlc_cnfg->rlc_mode){ + // SN Field Length + liblte_value_2_bits(rlc_cnfg->ul_um_uni_rlc.sn_field_len, ie_ptr, 1); + }else{ // LIBLTE_RRC_RLC_MODE_UM_UNI_DL == rlc_cnfg->rlc_mode + // SN Field Length + liblte_value_2_bits(rlc_cnfg->dl_um_uni_rlc.sn_field_len, ie_ptr, 1); + + // T Reordering + liblte_value_2_bits(rlc_cnfg->dl_um_uni_rlc.t_reordering, ie_ptr, 5); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rlc_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_RLC_CONFIG_STRUCT *rlc_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rlc_cnfg != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Mode Choice + rlc_cnfg->rlc_mode = (LIBLTE_RRC_RLC_MODE_ENUM)liblte_bits_2_value(ie_ptr, 2); + + if(LIBLTE_RRC_RLC_MODE_AM == rlc_cnfg->rlc_mode) + { + // UL AM RLC + { + // T Poll Retransmit + rlc_cnfg->ul_am_rlc.t_poll_retx = (LIBLTE_RRC_T_POLL_RETRANSMIT_ENUM)liblte_bits_2_value(ie_ptr, 6); + + // Poll PDU + rlc_cnfg->ul_am_rlc.poll_pdu = (LIBLTE_RRC_POLL_PDU_ENUM)liblte_bits_2_value(ie_ptr, 3); + + // Poll Byte + rlc_cnfg->ul_am_rlc.poll_byte = (LIBLTE_RRC_POLL_BYTE_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // Max Retransmission Threshold + rlc_cnfg->ul_am_rlc.max_retx_thresh = (LIBLTE_RRC_MAX_RETX_THRESHOLD_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + // DL AM RLC + { + // T Reordering + rlc_cnfg->dl_am_rlc.t_reordering = (LIBLTE_RRC_T_REORDERING_ENUM)liblte_bits_2_value(ie_ptr, 5); + + // T Status Prohibit + rlc_cnfg->dl_am_rlc.t_status_prohibit = (LIBLTE_RRC_T_STATUS_PROHIBIT_ENUM)liblte_bits_2_value(ie_ptr, 6); + } + }else if(LIBLTE_RRC_RLC_MODE_UM_BI == rlc_cnfg->rlc_mode){ + // UL UM RLC + { + // SN Field Length + rlc_cnfg->ul_um_bi_rlc.sn_field_len = (LIBLTE_RRC_SN_FIELD_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + // DL UM RLC + { + // SN Field Length + rlc_cnfg->dl_um_bi_rlc.sn_field_len = (LIBLTE_RRC_SN_FIELD_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // T Reordering + rlc_cnfg->dl_um_bi_rlc.t_reordering = (LIBLTE_RRC_T_REORDERING_ENUM)liblte_bits_2_value(ie_ptr, 5); + } + }else if(LIBLTE_RRC_RLC_MODE_UM_UNI_UL == rlc_cnfg->rlc_mode){ + // SN Field Length + rlc_cnfg->ul_um_uni_rlc.sn_field_len = (LIBLTE_RRC_SN_FIELD_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + }else{ // LIBLTE_RRC_RLC_MODE_UM_UNI_DL == rlc_cnfg->rlc_mode + // SN Field Length + rlc_cnfg->dl_um_uni_rlc.sn_field_len = (LIBLTE_RRC_SN_FIELD_LENGTH_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // T Reordering + rlc_cnfg->dl_um_uni_rlc.t_reordering = (LIBLTE_RRC_T_REORDERING_ENUM)liblte_bits_2_value(ie_ptr, 5); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RLF Timers and Constants + + Description: Contains UE specific timers and constants applicable + for UEs in RRC_CONNECTED + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rlf_timers_and_constants_ie(LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT *rlf_timers_and_constants, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(rlf_timers_and_constants != NULL && + ie_ptr != NULL) + { + // Release choice + liblte_value_2_bits(1, ie_ptr, 1); + + // Extension + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(rlf_timers_and_constants->t301, ie_ptr, 3); + liblte_value_2_bits(rlf_timers_and_constants->t310, ie_ptr, 3); + liblte_value_2_bits(rlf_timers_and_constants->n310, ie_ptr, 3); + liblte_value_2_bits(rlf_timers_and_constants->t311, ie_ptr, 3); + liblte_value_2_bits(rlf_timers_and_constants->n311, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rlf_timers_and_constants_ie(uint8 **ie_ptr, + LIBLTE_RRC_RLF_TIMERS_AND_CONSTANTS_STRUCT *rlf_timers_and_constants) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + rlf_timers_and_constants != NULL) + { + // Release choice + liblte_bits_2_value(ie_ptr, 1); + + // Extension + liblte_bits_2_value(ie_ptr, 1); + + rlf_timers_and_constants->t301 = (LIBLTE_RRC_T301_ENUM)liblte_bits_2_value(ie_ptr, 3); + rlf_timers_and_constants->t310 = (LIBLTE_RRC_T310_ENUM)liblte_bits_2_value(ie_ptr, 3); + rlf_timers_and_constants->n310 = (LIBLTE_RRC_N310_ENUM)liblte_bits_2_value(ie_ptr, 3); + rlf_timers_and_constants->t311 = (LIBLTE_RRC_T311_ENUM)liblte_bits_2_value(ie_ptr, 3); + rlf_timers_and_constants->n311 = (LIBLTE_RRC_N311_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: RN Subframe Config + + Description: Specifies the subframe configuration for an RN + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: Scheduling Request Config + + Description: Specifies the scheduling request related parameters + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_scheduling_request_config_ie(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sched_request_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(sched_request_cnfg != NULL && + ie_ptr != NULL) + { + // Setup + liblte_value_2_bits(sched_request_cnfg->setup_present, ie_ptr, 1); + if(sched_request_cnfg->setup_present) + { + // SR PUCCH Resource Index + liblte_value_2_bits(sched_request_cnfg->sr_pucch_resource_idx, ie_ptr, 11); + + // SR Config Index + liblte_value_2_bits(sched_request_cnfg->sr_cnfg_idx, ie_ptr, 8); + + // DRS Trans Max + liblte_value_2_bits(sched_request_cnfg->dsr_trans_max, ie_ptr, 3); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_scheduling_request_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sched_request_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + sched_request_cnfg != NULL) + { + // Setup + sched_request_cnfg->setup_present = liblte_bits_2_value(ie_ptr, 1); + if(sched_request_cnfg->setup_present) + { + // SR PUCCH Resource Index + sched_request_cnfg->sr_pucch_resource_idx = liblte_bits_2_value(ie_ptr, 11); + + // SR Config Index + sched_request_cnfg->sr_cnfg_idx = liblte_bits_2_value(ie_ptr, 8); + + // DRS Trans Max + sched_request_cnfg->dsr_trans_max = (LIBLTE_RRC_DSR_TRANS_MAX_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Sounding RS UL Config + + Description: Specifies the uplink Sounding RS configuration for + periodic and aperiodic sounding + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_srs_ul_config_common_ie(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT *srs_ul_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(srs_ul_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(srs_ul_cnfg->present, ie_ptr, 1); + + if(true == srs_ul_cnfg->present) + { + liblte_value_2_bits(srs_ul_cnfg->max_up_pts_present, ie_ptr, 1); + + liblte_value_2_bits(srs_ul_cnfg->bw_cnfg, ie_ptr, 3); + liblte_value_2_bits(srs_ul_cnfg->subfr_cnfg, ie_ptr, 4); + liblte_value_2_bits(srs_ul_cnfg->ack_nack_simul_tx, ie_ptr, 1); + + if(true == srs_ul_cnfg->max_up_pts_present) + { + liblte_value_2_bits(srs_ul_cnfg->max_up_pts, ie_ptr, 1); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_srs_ul_config_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT *srs_ul_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + srs_ul_cnfg != NULL) + { + srs_ul_cnfg->present = liblte_bits_2_value(ie_ptr, 1); + + if(true == srs_ul_cnfg->present) + { + srs_ul_cnfg->max_up_pts_present = liblte_bits_2_value(ie_ptr, 1); + + srs_ul_cnfg->bw_cnfg = (LIBLTE_RRC_SRS_BW_CONFIG_ENUM)liblte_bits_2_value(ie_ptr, 3); + srs_ul_cnfg->subfr_cnfg = (LIBLTE_RRC_SRS_SUBFR_CONFIG_ENUM)liblte_bits_2_value(ie_ptr, 4); + srs_ul_cnfg->ack_nack_simul_tx = liblte_bits_2_value(ie_ptr, 1); + + if(true == srs_ul_cnfg->max_up_pts_present) + { + srs_ul_cnfg->max_up_pts = liblte_bits_2_value(ie_ptr, 1); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_srs_ul_config_dedicated_ie(LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT *srs_ul_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(srs_ul_cnfg != NULL && + ie_ptr != NULL) + { + // Setup + liblte_value_2_bits(srs_ul_cnfg->setup_present, ie_ptr, 1); + if(srs_ul_cnfg->setup_present) + { + // SRS Bandwidth + liblte_value_2_bits(srs_ul_cnfg->srs_bandwidth, ie_ptr, 2); + + // SRS Hopping Bandwidth + liblte_value_2_bits(srs_ul_cnfg->srs_hopping_bandwidth, ie_ptr, 2); + + // Frequency Domain Position + liblte_value_2_bits(srs_ul_cnfg->freq_domain_pos, ie_ptr, 5); + + // Duration + liblte_value_2_bits(srs_ul_cnfg->duration, ie_ptr, 1); + + // SRS Config Index + liblte_value_2_bits(srs_ul_cnfg->srs_cnfg_idx, ie_ptr, 10); + + // Transmission Comb + liblte_value_2_bits(srs_ul_cnfg->tx_comb, ie_ptr, 1); + + // Cyclic Shift + liblte_value_2_bits(srs_ul_cnfg->cyclic_shift, ie_ptr, 3); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_srs_ul_config_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT *srs_ul_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + srs_ul_cnfg != NULL) + { + // Setup + srs_ul_cnfg->setup_present = liblte_bits_2_value(ie_ptr, 1); + if(srs_ul_cnfg->setup_present) + { + // SRS Bandwidth + srs_ul_cnfg->srs_bandwidth = (LIBLTE_RRC_SRS_BANDWIDTH_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // SRS Hopping Bandwidth + srs_ul_cnfg->srs_hopping_bandwidth = (LIBLTE_RRC_SRS_HOPPING_BANDWIDTH_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // Frequency Domain Position + srs_ul_cnfg->freq_domain_pos = liblte_bits_2_value(ie_ptr, 5); + + // Duration + srs_ul_cnfg->duration = liblte_bits_2_value(ie_ptr, 1); + + // SRS Config Index + srs_ul_cnfg->srs_cnfg_idx = liblte_bits_2_value(ie_ptr, 10); + + // Transmission Comb + srs_ul_cnfg->tx_comb = liblte_bits_2_value(ie_ptr, 1); + + // Cyclic Shift + srs_ul_cnfg->cyclic_shift = (LIBLTE_RRC_CYCLIC_SHIFT_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: SPS Config + + Description: Specifies the semi-persistent scheduling + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sps_config_ie(LIBLTE_RRC_SPS_CONFIG_STRUCT *sps_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sps_cnfg != NULL && + ie_ptr != NULL) + { + // Optional indicators + liblte_value_2_bits(sps_cnfg->sps_c_rnti_present, ie_ptr, 1); + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl_present, ie_ptr, 1); + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul_present, ie_ptr, 1); + + // SPS C-RNTI + if(sps_cnfg->sps_c_rnti_present) + { + liblte_rrc_pack_c_rnti_ie(sps_cnfg->sps_c_rnti, ie_ptr); + } + + // SPS Config DL + if(sps_cnfg->sps_cnfg_dl_present) + { + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl.setup_present, ie_ptr, 1); + if(sps_cnfg->sps_cnfg_dl.setup_present) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // SPS Interval DL + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl.sps_interval_dl, ie_ptr, 4); + + // Number of Configured SPS Processes + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl.N_sps_processes - 1, ie_ptr, 3); + + // N1 PUCCH AN Persistent List + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl.n1_pucch_an_persistent_list_size - 1, ie_ptr, 2); + for(i=0; isps_cnfg_dl.n1_pucch_an_persistent_list_size; i++) + { + liblte_value_2_bits(sps_cnfg->sps_cnfg_dl.n1_pucch_an_persistent_list[i], ie_ptr, 11); + } + } + } + + // SPS Config UL + if(sps_cnfg->sps_cnfg_ul_present) + { + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.setup_present, ie_ptr, 1); + if(sps_cnfg->sps_cnfg_ul.setup_present) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.p0_persistent_present, ie_ptr, 1); + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.two_intervals_cnfg_present, ie_ptr, 1); + + // SPS Interval UL + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.sps_interval_ul, ie_ptr, 4); + + // Implicit Release After + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.implicit_release_after, ie_ptr, 2); + + // P0 Persistent + if(sps_cnfg->sps_cnfg_ul.p0_persistent_present) + { + // P0 Nominal PUSCH Persistent + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.p0_nominal_pusch + 126, ie_ptr, 8); + + // P0 UE PUSCH Persistent + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.p0_ue_pusch + 8, ie_ptr, 4); + } + + // Two Intervals Config + if(sps_cnfg->sps_cnfg_ul.two_intervals_cnfg_present) + { + liblte_value_2_bits(sps_cnfg->sps_cnfg_ul.two_intervals_cnfg, ie_ptr, 1); + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sps_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_SPS_CONFIG_STRUCT *sps_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(ie_ptr != NULL && + sps_cnfg != NULL) + { + // Optional indicators + sps_cnfg->sps_c_rnti_present = liblte_bits_2_value(ie_ptr, 1); + sps_cnfg->sps_cnfg_dl_present = liblte_bits_2_value(ie_ptr, 1); + sps_cnfg->sps_cnfg_ul_present = liblte_bits_2_value(ie_ptr, 1); + + // SPS C-RNTI + if(sps_cnfg->sps_c_rnti_present) + { + liblte_rrc_unpack_c_rnti_ie(ie_ptr, &sps_cnfg->sps_c_rnti); + } + + // SPS Config DL + if(sps_cnfg->sps_cnfg_dl_present) + { + sps_cnfg->sps_cnfg_dl.setup_present = liblte_bits_2_value(ie_ptr, 1); + if(sps_cnfg->sps_cnfg_dl.setup_present) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // SPS Interval DL + sps_cnfg->sps_cnfg_dl.sps_interval_dl = (LIBLTE_RRC_SPS_INTERVAL_DL_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // Number of Configured SPS Processes + sps_cnfg->sps_cnfg_dl.N_sps_processes = liblte_bits_2_value(ie_ptr, 3) + 1; + + // N1 PUCCH AN Persistent List + sps_cnfg->sps_cnfg_dl.n1_pucch_an_persistent_list_size = liblte_bits_2_value(ie_ptr, 2) + 1; + for(i=0; isps_cnfg_dl.n1_pucch_an_persistent_list_size; i++) + { + sps_cnfg->sps_cnfg_dl.n1_pucch_an_persistent_list[i] = liblte_bits_2_value(ie_ptr, 11); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + } + } + + // SPS Config UL + if(sps_cnfg->sps_cnfg_ul_present) + { + sps_cnfg->sps_cnfg_ul.setup_present = liblte_bits_2_value(ie_ptr, 1); + if(sps_cnfg->sps_cnfg_ul.setup_present) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sps_cnfg->sps_cnfg_ul.p0_persistent_present = liblte_bits_2_value(ie_ptr, 1); + sps_cnfg->sps_cnfg_ul.two_intervals_cnfg_present = liblte_bits_2_value(ie_ptr, 1); + + // SPS Interval UL + sps_cnfg->sps_cnfg_ul.sps_interval_ul = (LIBLTE_RRC_SPS_INTERVAL_UL_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // Implicit Release After + sps_cnfg->sps_cnfg_ul.implicit_release_after = (LIBLTE_RRC_IMPLICIT_RELEASE_AFTER_ENUM)liblte_bits_2_value(ie_ptr, 2); + + // P0 Persistent + if(sps_cnfg->sps_cnfg_ul.p0_persistent_present) + { + // P0 Nominal PUSCH Persistent + sps_cnfg->sps_cnfg_ul.p0_nominal_pusch = liblte_bits_2_value(ie_ptr, 8) - 126; + + // P0 UE PUSCH Persistent + sps_cnfg->sps_cnfg_ul.p0_ue_pusch = liblte_bits_2_value(ie_ptr, 4) - 8; + } + + // Two Intervals Config + if(sps_cnfg->sps_cnfg_ul.two_intervals_cnfg_present) + { + sps_cnfg->sps_cnfg_ul.two_intervals_cnfg = (LIBLTE_RRC_TWO_INTERVALS_CONFIG_ENUM)liblte_bits_2_value(ie_ptr, 1); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: TDD Config + + Description: Specifies the TDD specific physical channel + configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_tdd_config_ie(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(tdd_cnfg != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(tdd_cnfg->sf_assignment, ie_ptr, 3); + liblte_value_2_bits(tdd_cnfg->special_sf_patterns, ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tdd_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tdd_cnfg != NULL) + { + tdd_cnfg->sf_assignment = (LIBLTE_RRC_SUBFRAME_ASSIGNMENT_ENUM)liblte_bits_2_value(ie_ptr, 3); + tdd_cnfg->special_sf_patterns = (LIBLTE_RRC_SPECIAL_SUBFRAME_PATTERNS_ENUM)liblte_bits_2_value(ie_ptr, 4); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Time Alignment Timer + + Description: Controls how long the UE is considered uplink time + aligned + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_time_alignment_timer_ie(LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM time_alignment_timer, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL) + { + liblte_value_2_bits(time_alignment_timer, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_time_alignment_timer_ie(uint8 **ie_ptr, + LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM *time_alignment_timer) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + time_alignment_timer != NULL) + { + *time_alignment_timer = (LIBLTE_RRC_TIME_ALIGNMENT_TIMER_ENUM)liblte_bits_2_value(ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: TPC PDCCH Config + + Description: Specifies the RNTIs and indecies for PUCCH and PUSCH + power control + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_tpc_pdcch_config_ie(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT *tpc_pdcch_cnfg, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(tpc_pdcch_cnfg != NULL && + ie_ptr != NULL) + { + // Setup + liblte_value_2_bits(tpc_pdcch_cnfg->setup_present, ie_ptr, 1); + if(tpc_pdcch_cnfg->setup_present) + { + // TPC RNTI + liblte_value_2_bits(tpc_pdcch_cnfg->tpc_rnti, ie_ptr, 16); + + // TPC Index + liblte_value_2_bits(tpc_pdcch_cnfg->tpc_idx_choice, ie_ptr, 1); + if(LIBLTE_RRC_TPC_INDEX_FORMAT_3 == tpc_pdcch_cnfg->tpc_idx_choice) + { + liblte_value_2_bits(tpc_pdcch_cnfg->tpc_idx - 1, ie_ptr, 4); + }else{ + liblte_value_2_bits(tpc_pdcch_cnfg->tpc_idx - 1, ie_ptr, 5); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_tpc_pdcch_config_ie(uint8 **ie_ptr, + LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT *tpc_pdcch_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + tpc_pdcch_cnfg != NULL) + { + // Setup + tpc_pdcch_cnfg->setup_present = liblte_bits_2_value(ie_ptr, 1); + if(tpc_pdcch_cnfg->setup_present) + { + // TPC RNTI + tpc_pdcch_cnfg->tpc_rnti = liblte_bits_2_value(ie_ptr, 16); + + // TPC Index + tpc_pdcch_cnfg->tpc_idx_choice = (LIBLTE_RRC_TPC_INDEX_ENUM)liblte_bits_2_value(ie_ptr, 1); + if(LIBLTE_RRC_TPC_INDEX_FORMAT_3 == tpc_pdcch_cnfg->tpc_idx_choice) + { + tpc_pdcch_cnfg->tpc_idx = liblte_bits_2_value(ie_ptr, 4) + 1; + }else{ + tpc_pdcch_cnfg->tpc_idx = liblte_bits_2_value(ie_ptr, 5) + 1; + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: UL Antenna Info + + Description: Specifies the UL antenna configuration + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_antenna_info_ie(LIBLTE_RRC_UL_ANTENNA_INFO_STRUCT *ul_ant_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ul_ant_info != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(ul_ant_info->ul_tx_mode, ie_ptr, 3); + liblte_value_2_bits(ul_ant_info->four_ant_port_activated, ie_ptr, 1); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_antenna_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_ANTENNA_INFO_STRUCT *ul_ant_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ul_ant_info != NULL) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + ul_ant_info->ul_tx_mode = (LIBLTE_RRC_UL_TRANSMISSION_MODE_R10_ENUM)liblte_bits_2_value(ie_ptr, 3); + ul_ant_info->four_ant_port_activated = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: Uplink Power Control + + Description: Specifies the parameters for uplink power control in + the system information and in the dedicated + signalling + + Document Reference: 36.331 v10.0.0 Section 6.3.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_power_control_common_ie(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT *ul_pwr_ctrl, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ul_pwr_ctrl != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(ul_pwr_ctrl->p0_nominal_pusch + 126, ie_ptr, 8); + liblte_value_2_bits(ul_pwr_ctrl->alpha, ie_ptr, 3); + liblte_value_2_bits(ul_pwr_ctrl->p0_nominal_pucch + 127, ie_ptr, 5); + + // Delta F List + liblte_value_2_bits(ul_pwr_ctrl->delta_flist_pucch.format_1, ie_ptr, 2); + liblte_value_2_bits(ul_pwr_ctrl->delta_flist_pucch.format_1b, ie_ptr, 2); + liblte_value_2_bits(ul_pwr_ctrl->delta_flist_pucch.format_2, ie_ptr, 2); + liblte_value_2_bits(ul_pwr_ctrl->delta_flist_pucch.format_2a, ie_ptr, 2); + liblte_value_2_bits(ul_pwr_ctrl->delta_flist_pucch.format_2b, ie_ptr, 2); + + liblte_value_2_bits((ul_pwr_ctrl->delta_preamble_msg3 / 2) + 1, ie_ptr, 3); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_power_control_common_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT *ul_pwr_ctrl) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ul_pwr_ctrl != NULL) + { + ul_pwr_ctrl->p0_nominal_pusch = liblte_bits_2_value(ie_ptr, 8) - 126; + ul_pwr_ctrl->alpha = (LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_ENUM)liblte_bits_2_value(ie_ptr, 3); + ul_pwr_ctrl->p0_nominal_pucch = liblte_bits_2_value(ie_ptr, 5) - 127; + + // Delta F List + ul_pwr_ctrl->delta_flist_pucch.format_1 = (LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_ENUM)liblte_bits_2_value(ie_ptr, 2); + ul_pwr_ctrl->delta_flist_pucch.format_1b = (LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_ENUM)liblte_bits_2_value(ie_ptr, 2); + ul_pwr_ctrl->delta_flist_pucch.format_2 = (LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_ENUM)liblte_bits_2_value(ie_ptr, 2); + ul_pwr_ctrl->delta_flist_pucch.format_2a = (LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_ENUM)liblte_bits_2_value(ie_ptr, 2); + ul_pwr_ctrl->delta_flist_pucch.format_2b = (LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_ENUM)liblte_bits_2_value(ie_ptr, 2); + + ul_pwr_ctrl->delta_preamble_msg3 = (liblte_bits_2_value(ie_ptr, 3) - 1) * 2; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_power_control_dedicated_ie(LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT *ul_pwr_ctrl, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ul_pwr_ctrl != NULL && + ie_ptr != NULL) + { + // Filter Coefficient default? + liblte_value_2_bits(ul_pwr_ctrl->filter_coeff_present, ie_ptr, 1); + + // P0 UE PUSCH + liblte_value_2_bits(ul_pwr_ctrl->p0_ue_pusch + 8, ie_ptr, 4); + + // Delta MCS Enabled + liblte_value_2_bits(ul_pwr_ctrl->delta_mcs_en, ie_ptr, 1); + + // Accumulation Enabled + liblte_value_2_bits(ul_pwr_ctrl->accumulation_en, ie_ptr, 1); + + // P0 UE PUCCH + liblte_value_2_bits(ul_pwr_ctrl->p0_ue_pucch + 8, ie_ptr, 4); + + // P SRS Offset + liblte_value_2_bits(ul_pwr_ctrl->p_srs_offset, ie_ptr, 4); + + // Filter Coefficient + if(ul_pwr_ctrl->filter_coeff_present) + { + liblte_rrc_pack_filter_coefficient_ie(ul_pwr_ctrl->filter_coeff, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_power_control_dedicated_ie(uint8 **ie_ptr, + LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT *ul_pwr_ctrl) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + ul_pwr_ctrl != NULL) + { + // Filter Coefficient default? + ul_pwr_ctrl->filter_coeff_present = liblte_bits_2_value(ie_ptr, 1); + + // P0 UE PUSCH + ul_pwr_ctrl->p0_ue_pusch = liblte_bits_2_value(ie_ptr, 4) - 8; + + // Delta MCS Enabled + ul_pwr_ctrl->delta_mcs_en = (LIBLTE_RRC_DELTA_MCS_ENABLED_ENUM)liblte_bits_2_value(ie_ptr, 1); + + // Accumulation Enabled + ul_pwr_ctrl->accumulation_en = liblte_bits_2_value(ie_ptr, 1); + + // P0 UE PUCCH + ul_pwr_ctrl->p0_ue_pucch = liblte_bits_2_value(ie_ptr, 4) - 8; + + // P SRS Offset + ul_pwr_ctrl->p_srs_offset = liblte_bits_2_value(ie_ptr, 4); + + // Filter Coefficient + if(ul_pwr_ctrl->filter_coeff_present) + { + liblte_rrc_unpack_filter_coefficient_ie(ie_ptr, &ul_pwr_ctrl->filter_coeff); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 2 + + Description: Contains radio resource configuration that is common + for all UEs + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_2_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool mbsfn_subfr_cnfg_list_opt; + + if(sib2 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sib2->ac_barring_info_present, ie_ptr, 1); + if(0 != sib2->mbsfn_subfr_cnfg_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + mbsfn_subfr_cnfg_list_opt = true; + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + mbsfn_subfr_cnfg_list_opt = false; + } + + // AC Barring + if(true == sib2->ac_barring_info_present) + { + liblte_value_2_bits(sib2->ac_barring_for_mo_signalling.enabled, ie_ptr, 1); + liblte_value_2_bits(sib2->ac_barring_for_mo_data.enabled, ie_ptr, 1); + + // AC Barring for emergency + liblte_value_2_bits(sib2->ac_barring_for_emergency, ie_ptr, 1); + + // AC Barring for MO signalling + if(true == sib2->ac_barring_for_mo_signalling.enabled) + { + liblte_value_2_bits(sib2->ac_barring_for_mo_signalling.factor, ie_ptr, 4); + liblte_value_2_bits(sib2->ac_barring_for_mo_signalling.time, ie_ptr, 3); + liblte_value_2_bits(sib2->ac_barring_for_mo_signalling.for_special_ac, ie_ptr, 5); + } + + // AC Barring for MO data + if(true == sib2->ac_barring_for_mo_data.enabled) + { + liblte_value_2_bits(sib2->ac_barring_for_mo_data.factor, ie_ptr, 4); + liblte_value_2_bits(sib2->ac_barring_for_mo_data.time, ie_ptr, 3); + liblte_value_2_bits(sib2->ac_barring_for_mo_data.for_special_ac, ie_ptr, 5); + } + } + + // Radio Resource Config Common + liblte_rrc_pack_rr_config_common_sib_ie(&sib2->rr_config_common_sib, ie_ptr); + + // UE Timers and Constants + liblte_rrc_pack_ue_timers_and_constants_ie(&sib2->ue_timers_and_constants, ie_ptr); + + // Frequency information + { + // Optional indicators + liblte_value_2_bits(sib2->arfcn_value_eutra.present, ie_ptr, 1); + liblte_value_2_bits(sib2->ul_bw.present, ie_ptr, 1); + + // UL Carrier Frequency + if(true == sib2->arfcn_value_eutra.present) + { + liblte_rrc_pack_arfcn_value_eutra_ie(sib2->arfcn_value_eutra.value, ie_ptr); + } + + // UL Bandwidth + if(true == sib2->ul_bw.present) + { + liblte_value_2_bits(sib2->ul_bw.bw, ie_ptr, 3); + } + + // Additional Spectrum Emission + liblte_rrc_pack_additional_spectrum_emission_ie(sib2->additional_spectrum_emission, + ie_ptr); + } + + // MBSFN Subframe Config List + if(true == mbsfn_subfr_cnfg_list_opt) + { + liblte_value_2_bits(sib2->mbsfn_subfr_cnfg_list_size - 1, ie_ptr, 3); + for(i=0; imbsfn_subfr_cnfg_list_size; i++) + { + liblte_rrc_pack_mbsfn_subframe_config_ie(&sib2->mbsfn_subfr_cnfg_list[i], ie_ptr); + } + } + + // Time Alignment Timer Common + liblte_rrc_pack_time_alignment_timer_ie(sib2->time_alignment_timer, ie_ptr); + + // FIXME: Not handling extensions + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_2_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint8 ext_ind; + bool mbsfn_subfr_cnfg_list_opt; + + if(ie_ptr != NULL && + sib2 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sib2->ac_barring_info_present = liblte_bits_2_value(ie_ptr, 1); + mbsfn_subfr_cnfg_list_opt = liblte_bits_2_value(ie_ptr, 1); + + // AC Barring + if(true == sib2->ac_barring_info_present) + { + // Optional indicators + sib2->ac_barring_for_mo_signalling.enabled = liblte_bits_2_value(ie_ptr, 1); + sib2->ac_barring_for_mo_data.enabled = liblte_bits_2_value(ie_ptr, 1); + + // AC Barring for emergency + sib2->ac_barring_for_emergency = liblte_bits_2_value(ie_ptr, 1); + + // AC Barring for MO signalling + if(true == sib2->ac_barring_for_mo_signalling.enabled) + { + sib2->ac_barring_for_mo_signalling.factor = (LIBLTE_RRC_AC_BARRING_FACTOR_ENUM)liblte_bits_2_value(ie_ptr, 4); + sib2->ac_barring_for_mo_signalling.time = (LIBLTE_RRC_AC_BARRING_TIME_ENUM)liblte_bits_2_value(ie_ptr, 3); + sib2->ac_barring_for_mo_signalling.for_special_ac = liblte_bits_2_value(ie_ptr, 5); + } + + // AC Barring for MO data + if(true == sib2->ac_barring_for_mo_data.enabled) + { + sib2->ac_barring_for_mo_data.factor = (LIBLTE_RRC_AC_BARRING_FACTOR_ENUM)liblte_bits_2_value(ie_ptr, 4); + sib2->ac_barring_for_mo_data.time = (LIBLTE_RRC_AC_BARRING_TIME_ENUM)liblte_bits_2_value(ie_ptr, 3); + sib2->ac_barring_for_mo_data.for_special_ac = liblte_bits_2_value(ie_ptr, 5); + } + }else{ + sib2->ac_barring_for_emergency = false; + sib2->ac_barring_for_mo_signalling.enabled = false; + sib2->ac_barring_for_mo_data.enabled = false; + } + + // Radio Resource Config Common + liblte_rrc_unpack_rr_config_common_sib_ie(ie_ptr, &sib2->rr_config_common_sib); + + // UE Timers and Constants + liblte_rrc_unpack_ue_timers_and_constants_ie(ie_ptr, &sib2->ue_timers_and_constants); + + // Frequency information + { + // Optional indicators + sib2->arfcn_value_eutra.present = liblte_bits_2_value(ie_ptr, 1); + sib2->ul_bw.present = liblte_bits_2_value(ie_ptr, 1); + + // UL Carrier Frequency + if(true == sib2->arfcn_value_eutra.present) + { + liblte_rrc_unpack_arfcn_value_eutra_ie(ie_ptr, &sib2->arfcn_value_eutra.value); + } + + // UL Bandwidth + if(true == sib2->ul_bw.present) + { + sib2->ul_bw.bw = (LIBLTE_RRC_UL_BW_ENUM)liblte_bits_2_value(ie_ptr, 3); + } + + // Additional Spectrum Emission + liblte_rrc_unpack_additional_spectrum_emission_ie(ie_ptr, + &sib2->additional_spectrum_emission); + } + + // MBSFN Subframe Config List + if(true == mbsfn_subfr_cnfg_list_opt) + { + sib2->mbsfn_subfr_cnfg_list_size = liblte_bits_2_value(ie_ptr, 3) + 1; + for(i=0; imbsfn_subfr_cnfg_list_size; i++) + { + liblte_rrc_unpack_mbsfn_subframe_config_ie(ie_ptr, &sib2->mbsfn_subfr_cnfg_list[i]); + } + }else{ + sib2->mbsfn_subfr_cnfg_list_size = 0; + } + + // Time Alignment Timer Common + liblte_rrc_unpack_time_alignment_timer_ie(ie_ptr, &sib2->time_alignment_timer); + + // Extensions + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 3 + + Description: Contains cell reselection information common for + intra-frequency, inter-frequency, and/or inter-RAT + cell re-selection as well as intra-frequency cell + re-selection information other than neighboring + cell related + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_3_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(sib3 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Cell reselection info common + { + // Optional indicator + liblte_value_2_bits(sib3->speed_state_resel_params.present, ie_ptr, 1); + + liblte_value_2_bits(sib3->q_hyst, ie_ptr, 4); + + // Speed state reselection parameters + if(true == sib3->speed_state_resel_params.present) + { + liblte_rrc_pack_mobility_state_parameters_ie(&sib3->speed_state_resel_params.mobility_state_params, ie_ptr); + + liblte_value_2_bits(sib3->speed_state_resel_params.q_hyst_sf.medium, ie_ptr, 2); + liblte_value_2_bits(sib3->speed_state_resel_params.q_hyst_sf.high, ie_ptr, 2); + } + } + + // Cell reselection serving frequency information + { + // Optional indicators + liblte_value_2_bits(sib3->s_non_intra_search_present, ie_ptr, 1); + + if(true == sib3->s_non_intra_search_present) + { + liblte_rrc_pack_reselection_threshold_ie(sib3->s_non_intra_search, ie_ptr); + } + + liblte_rrc_pack_reselection_threshold_ie(sib3->thresh_serving_low, ie_ptr); + + liblte_rrc_pack_cell_reselection_priority_ie(sib3->cell_resel_prio, ie_ptr); + } + + // Intra frequency cell reselection information + { + // Optional indicators + liblte_value_2_bits(sib3->p_max_present, ie_ptr, 1); + liblte_value_2_bits(sib3->s_intra_search_present, ie_ptr, 1); + liblte_value_2_bits(sib3->allowed_meas_bw_present, ie_ptr, 1); + liblte_value_2_bits(sib3->t_resel_eutra_sf_present, ie_ptr, 1); + + liblte_rrc_pack_q_rx_lev_min_ie(sib3->q_rx_lev_min, ie_ptr); + + if(true == sib3->p_max_present) + { + liblte_rrc_pack_p_max_ie(sib3->p_max, ie_ptr); + } + + if(true == sib3->s_intra_search_present) + { + liblte_rrc_pack_reselection_threshold_ie(sib3->s_intra_search, ie_ptr); + } + + if(true == sib3->allowed_meas_bw_present) + { + liblte_rrc_pack_allowed_meas_bandwidth_ie(sib3->allowed_meas_bw, ie_ptr); + } + + liblte_rrc_pack_presence_antenna_port_1_ie(sib3->presence_ant_port_1, ie_ptr); + + liblte_rrc_pack_neigh_cell_config_ie(sib3->neigh_cell_cnfg, ie_ptr); + + liblte_rrc_pack_t_reselection_ie(sib3->t_resel_eutra, ie_ptr); + + if(true == sib3->t_resel_eutra_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib3->t_resel_eutra_sf, ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_3_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext_ind; + + if(ie_ptr != NULL && + sib3 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Cell reselection info common + { + // Optional indicator + sib3->speed_state_resel_params.present = liblte_bits_2_value(ie_ptr, 1); + + sib3->q_hyst = (LIBLTE_RRC_Q_HYST_ENUM)liblte_bits_2_value(ie_ptr, 4); + + // Speed state reselection parameters + if(true == sib3->speed_state_resel_params.present) + { + liblte_rrc_unpack_mobility_state_parameters_ie(ie_ptr, &sib3->speed_state_resel_params.mobility_state_params); + + sib3->speed_state_resel_params.q_hyst_sf.medium = (LIBLTE_RRC_SF_MEDIUM_ENUM)liblte_bits_2_value(ie_ptr, 2); + sib3->speed_state_resel_params.q_hyst_sf.high = (LIBLTE_RRC_SF_HIGH_ENUM)liblte_bits_2_value(ie_ptr, 2); + } + } + + // Cell reselection serving frequency information + { + // Optional indicators + sib3->s_non_intra_search_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == sib3->s_non_intra_search_present) + { + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib3->s_non_intra_search); + } + + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib3->thresh_serving_low); + + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib3->cell_resel_prio); + } + + // Intra frequency cell reselection information + { + // Optional indicators + sib3->p_max_present = liblte_bits_2_value(ie_ptr, 1); + sib3->s_intra_search_present = liblte_bits_2_value(ie_ptr, 1); + sib3->allowed_meas_bw_present = liblte_bits_2_value(ie_ptr, 1); + sib3->t_resel_eutra_sf_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_q_rx_lev_min_ie(ie_ptr, &sib3->q_rx_lev_min); + + if(true == sib3->p_max_present) + { + liblte_rrc_unpack_p_max_ie(ie_ptr, &sib3->p_max); + } + + if(true == sib3->s_intra_search_present) + { + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib3->s_intra_search); + } + + if(true == sib3->allowed_meas_bw_present) + { + liblte_rrc_unpack_allowed_meas_bandwidth_ie(ie_ptr, &sib3->allowed_meas_bw); + } + + liblte_rrc_unpack_presence_antenna_port_1_ie(ie_ptr, &sib3->presence_ant_port_1); + + liblte_rrc_unpack_neigh_cell_config_ie(ie_ptr, &sib3->neigh_cell_cnfg); + + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib3->t_resel_eutra); + + if(true == sib3->t_resel_eutra_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib3->t_resel_eutra_sf); + } + } + + // Extensions + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 4 + + Description: Contains the neighboring cell related information + relevant only for intra-frequency cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_4_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sib4 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + if(0 != sib4->intra_freq_neigh_cell_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + if(0 != sib4->intra_freq_black_cell_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(sib4->csg_phys_cell_id_range_present, ie_ptr, 1); + + if(0 != sib4->intra_freq_neigh_cell_list_size) + { + liblte_value_2_bits(sib4->intra_freq_neigh_cell_list_size - 1, ie_ptr, 4); + for(i=0; iintra_freq_neigh_cell_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_rrc_pack_phys_cell_id_ie(sib4->intra_freq_neigh_cell_list[i].phys_cell_id, ie_ptr); + liblte_rrc_pack_q_offset_range_ie(sib4->intra_freq_neigh_cell_list[i].q_offset_range, ie_ptr); + } + } + + if(0 != sib4->intra_freq_black_cell_list_size) + { + liblte_value_2_bits(sib4->intra_freq_black_cell_list_size - 1, ie_ptr, 4); + for(i=0; iintra_freq_black_cell_list_size; i++) + { + liblte_rrc_pack_phys_cell_id_range_ie(&sib4->intra_freq_black_cell_list[i], ie_ptr); + } + } + + if(true == sib4->csg_phys_cell_id_range_present) + { + liblte_rrc_pack_phys_cell_id_range_ie(&sib4->csg_phys_cell_id_range, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_4_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool ext_ind; + bool intra_freq_neigh_cell_list_opt; + bool intra_freq_black_cell_list_opt; + + if(ie_ptr != NULL && + sib4 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + intra_freq_neigh_cell_list_opt = liblte_bits_2_value(ie_ptr, 1); + intra_freq_black_cell_list_opt = liblte_bits_2_value(ie_ptr, 1); + sib4->csg_phys_cell_id_range_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == intra_freq_neigh_cell_list_opt) + { + sib4->intra_freq_neigh_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; iintra_freq_neigh_cell_list_size; i++) + { + // Extension indicator + liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &sib4->intra_freq_neigh_cell_list[i].phys_cell_id); + liblte_rrc_unpack_q_offset_range_ie(ie_ptr, &sib4->intra_freq_neigh_cell_list[i].q_offset_range); + } + }else{ + sib4->intra_freq_neigh_cell_list_size = 0; + } + + if(true == intra_freq_black_cell_list_opt) + { + sib4->intra_freq_black_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; iintra_freq_black_cell_list_size; i++) + { + liblte_rrc_unpack_phys_cell_id_range_ie(ie_ptr, &sib4->intra_freq_black_cell_list[i]); + } + }else{ + sib4->intra_freq_black_cell_list_size = 0; + } + + if(true == sib4->csg_phys_cell_id_range_present) + { + liblte_rrc_unpack_phys_cell_id_range_ie(ie_ptr, &sib4->csg_phys_cell_id_range); + } + + // Extension + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 5 + + Description: Contains information relevant only for + inter-frequency cell reselection, i.e. information + about other E-UTRA frequencies and inter-frequency + neighboring cells relevant for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_5_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT *sib5, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 j; + + if(sib5 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list_size - 1, ie_ptr, 3); + for(i=0; iinter_freq_carrier_freq_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list[i].p_max_present, ie_ptr, 1); + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present, ie_ptr, 1); + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list[i].cell_resel_prio_present, ie_ptr, 1); + if(0 != sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + if(0 != sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + + liblte_rrc_pack_arfcn_value_eutra_ie(sib5->inter_freq_carrier_freq_list[i].dl_carrier_freq, ie_ptr); + liblte_rrc_pack_q_rx_lev_min_ie(sib5->inter_freq_carrier_freq_list[i].q_rx_lev_min, ie_ptr); + if(true == sib5->inter_freq_carrier_freq_list[i].p_max_present) + { + liblte_rrc_pack_p_max_ie(sib5->inter_freq_carrier_freq_list[i].p_max, ie_ptr); + } + liblte_rrc_pack_t_reselection_ie(sib5->inter_freq_carrier_freq_list[i].t_resel_eutra, ie_ptr); + if(true == sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf, ie_ptr); + } + liblte_rrc_pack_reselection_threshold_ie(sib5->inter_freq_carrier_freq_list[i].threshx_high, ie_ptr); + liblte_rrc_pack_reselection_threshold_ie(sib5->inter_freq_carrier_freq_list[i].threshx_low, ie_ptr); + liblte_rrc_pack_allowed_meas_bandwidth_ie(sib5->inter_freq_carrier_freq_list[i].allowed_meas_bw, ie_ptr); + liblte_rrc_pack_presence_antenna_port_1_ie(sib5->inter_freq_carrier_freq_list[i].presence_ant_port_1, ie_ptr); + if(true == sib5->inter_freq_carrier_freq_list[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib5->inter_freq_carrier_freq_list[i].cell_resel_prio, ie_ptr); + } + liblte_rrc_pack_neigh_cell_config_ie(sib5->inter_freq_carrier_freq_list[i].neigh_cell_cnfg, ie_ptr); + liblte_rrc_pack_q_offset_range_ie(sib5->inter_freq_carrier_freq_list[i].q_offset_freq, ie_ptr); + if(0 != sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size) + { + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size - 1, ie_ptr, 4); + for(j=0; jinter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size; j++) + { + liblte_rrc_pack_phys_cell_id_ie(sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list[j].phys_cell_id, ie_ptr); + liblte_rrc_pack_q_offset_range_ie(sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list[j].q_offset_cell, ie_ptr); + } + } + if(0 != sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size) + { + liblte_value_2_bits(sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size - 1, ie_ptr, 4); + for(j=0; jinter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size; j++) + { + liblte_rrc_pack_phys_cell_id_range_ie(&sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list[j], ie_ptr); + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_5_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT *sib5) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 j; + bool ext_ind; + bool inter_freq_carrier_freq_list_ext_ind; + bool q_offset_freq_opt; + bool inter_freq_neigh_cell_list_opt; + bool inter_freq_black_cell_list_opt; + + if(ie_ptr != NULL && + sib5 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + sib5->inter_freq_carrier_freq_list_size = liblte_bits_2_value(ie_ptr, 3) + 1; + for(i=0; iinter_freq_carrier_freq_list_size; i++) + { + // Extension indicator + inter_freq_carrier_freq_list_ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sib5->inter_freq_carrier_freq_list[i].p_max_present = liblte_bits_2_value(ie_ptr, 1); + sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present = liblte_bits_2_value(ie_ptr, 1); + sib5->inter_freq_carrier_freq_list[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + q_offset_freq_opt = liblte_bits_2_value(ie_ptr, 1); + inter_freq_neigh_cell_list_opt = liblte_bits_2_value(ie_ptr, 1); + inter_freq_black_cell_list_opt = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_arfcn_value_eutra_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].dl_carrier_freq); + liblte_rrc_unpack_q_rx_lev_min_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].q_rx_lev_min); + if(true == sib5->inter_freq_carrier_freq_list[i].p_max_present) + { + liblte_rrc_unpack_p_max_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].p_max); + } + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].t_resel_eutra); + if(true == sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].t_resel_eutra_sf); + } + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].threshx_high); + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].threshx_low); + liblte_rrc_unpack_allowed_meas_bandwidth_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].allowed_meas_bw); + liblte_rrc_unpack_presence_antenna_port_1_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].presence_ant_port_1); + if(true == sib5->inter_freq_carrier_freq_list[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].cell_resel_prio); + } + liblte_rrc_unpack_neigh_cell_config_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].neigh_cell_cnfg); + if(true == q_offset_freq_opt) + { + liblte_rrc_unpack_q_offset_range_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].q_offset_freq); + }else{ + sib5->inter_freq_carrier_freq_list[i].q_offset_freq = LIBLTE_RRC_Q_OFFSET_RANGE_DB_0; + } + if(true == inter_freq_neigh_cell_list_opt) + { + sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(j=0; jinter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size; j++) + { + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list[j].phys_cell_id); + liblte_rrc_unpack_q_offset_range_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list[j].q_offset_cell); + } + }else{ + sib5->inter_freq_carrier_freq_list[i].inter_freq_neigh_cell_list_size = 0; + } + if(true == inter_freq_black_cell_list_opt) + { + sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(j=0; jinter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size; j++) + { + liblte_rrc_unpack_phys_cell_id_range_ie(ie_ptr, &sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list[j]); + } + }else{ + sib5->inter_freq_carrier_freq_list[i].inter_freq_black_cell_list_size = 0; + } + } + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 6 + + Description: Contains information relevant only for inter-RAT + cell reselection, i.e. information about UTRA + frequencies and UTRA neighboring cells relevant for + cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_6_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT *sib6, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sib6 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + if(0 != sib6->carrier_freq_list_utra_fdd_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + if(0 != sib6->carrier_freq_list_utra_tdd_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + liblte_value_2_bits(sib6->t_resel_utra_sf_present, ie_ptr, 1); + + if(0 != sib6->carrier_freq_list_utra_fdd_size) + { + liblte_value_2_bits(sib6->carrier_freq_list_utra_fdd_size - 1, ie_ptr, 4); + for(i=0; icarrier_freq_list_utra_fdd_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio_present, ie_ptr, 1); + + liblte_rrc_pack_arfcn_value_utra_ie(sib6->carrier_freq_list_utra_fdd[i].carrier_freq, ie_ptr); + if(true == sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio, ie_ptr); + } + liblte_rrc_pack_reselection_threshold_ie(sib6->carrier_freq_list_utra_fdd[i].threshx_high, ie_ptr); + liblte_rrc_pack_reselection_threshold_ie(sib6->carrier_freq_list_utra_fdd[i].threshx_low, ie_ptr); + liblte_value_2_bits(((sib6->carrier_freq_list_utra_fdd[i].q_rx_lev_min - 1) / 2) + 60, ie_ptr, 6); + liblte_value_2_bits(sib6->carrier_freq_list_utra_fdd[i].p_max_utra + 50, ie_ptr, 7); + liblte_value_2_bits(sib6->carrier_freq_list_utra_fdd[i].q_qual_min + 24, ie_ptr, 5); + } + } + if(0 != sib6->carrier_freq_list_utra_tdd_size) + { + liblte_value_2_bits(sib6->carrier_freq_list_utra_tdd_size - 1, ie_ptr, 4); + for(i=0; icarrier_freq_list_utra_tdd_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio_present, ie_ptr, 1); + + liblte_rrc_pack_arfcn_value_utra_ie(sib6->carrier_freq_list_utra_tdd[i].carrier_freq, ie_ptr); + if(true == sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio, ie_ptr); + } + liblte_rrc_pack_reselection_threshold_ie(sib6->carrier_freq_list_utra_tdd[i].threshx_high, ie_ptr); + liblte_rrc_pack_reselection_threshold_ie(sib6->carrier_freq_list_utra_tdd[i].threshx_low, ie_ptr); + liblte_value_2_bits(((sib6->carrier_freq_list_utra_tdd[i].q_rx_lev_min - 1) / 2) + 60, ie_ptr, 6); + liblte_value_2_bits(sib6->carrier_freq_list_utra_tdd[i].p_max_utra + 50, ie_ptr, 7); + } + } + liblte_rrc_pack_t_reselection_ie(sib6->t_resel_utra, ie_ptr); + if(true == sib6->t_resel_utra_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib6->t_resel_utra_sf, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_6_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT *sib6) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool ext_ind; + bool carrier_freq_list_utra_fdd_opt; + bool carrier_freq_list_utra_fdd_ext_ind; + bool carrier_freq_list_utra_tdd_opt; + bool carrier_freq_list_utra_tdd_ext_ind; + + if(ie_ptr != NULL && + sib6 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + carrier_freq_list_utra_fdd_opt = liblte_bits_2_value(ie_ptr, 1); + carrier_freq_list_utra_tdd_opt = liblte_bits_2_value(ie_ptr, 1); + sib6->t_resel_utra_sf_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == carrier_freq_list_utra_fdd_opt) + { + sib6->carrier_freq_list_utra_fdd_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; icarrier_freq_list_utra_fdd_size; i++) + { + // Extension indicator + carrier_freq_list_utra_fdd_ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_arfcn_value_utra_ie(ie_ptr, &sib6->carrier_freq_list_utra_fdd[i].carrier_freq); + if(true == sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib6->carrier_freq_list_utra_fdd[i].cell_resel_prio); + } + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib6->carrier_freq_list_utra_fdd[i].threshx_high); + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib6->carrier_freq_list_utra_fdd[i].threshx_low); + sib6->carrier_freq_list_utra_fdd[i].q_rx_lev_min = (int8)((liblte_bits_2_value(ie_ptr, 6) - 60) * 2) + 1; + sib6->carrier_freq_list_utra_fdd[i].p_max_utra = (int8)liblte_bits_2_value(ie_ptr, 7) - 50; + sib6->carrier_freq_list_utra_fdd[i].q_qual_min = (int8)liblte_bits_2_value(ie_ptr, 5) - 24; + } + }else{ + sib6->carrier_freq_list_utra_fdd_size = 0; + } + if(true == carrier_freq_list_utra_tdd_opt) + { + sib6->carrier_freq_list_utra_tdd_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; icarrier_freq_list_utra_tdd_size; i++) + { + // Extension indicator + carrier_freq_list_utra_tdd_ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_arfcn_value_utra_ie(ie_ptr, &sib6->carrier_freq_list_utra_tdd[i].carrier_freq); + if(true == sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib6->carrier_freq_list_utra_tdd[i].cell_resel_prio); + } + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib6->carrier_freq_list_utra_tdd[i].threshx_high); + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib6->carrier_freq_list_utra_tdd[i].threshx_low); + sib6->carrier_freq_list_utra_tdd[i].q_rx_lev_min = (int8)((liblte_bits_2_value(ie_ptr, 6) * 2) + 1) - 60; + sib6->carrier_freq_list_utra_tdd[i].p_max_utra = (int8)liblte_bits_2_value(ie_ptr, 7) - 50; + } + }else{ + sib6->carrier_freq_list_utra_tdd_size = 0; + } + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib6->t_resel_utra); + if(true == sib6->t_resel_utra_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib6->t_resel_utra_sf); + } + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 7 + + Description: Contains information relevant only for inter-RAT + cell reselection, i.e. information about GERAN + frequencies relevant for cell reselection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_7_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT *sib7, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sib7 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sib7->t_resel_geran_sf_present, ie_ptr, 1); + if(0 != sib7->carrier_freqs_info_list_size) + { + liblte_value_2_bits(1, ie_ptr, 1); + }else{ + liblte_value_2_bits(0, ie_ptr, 1); + } + + liblte_rrc_pack_t_reselection_ie(sib7->t_resel_geran, ie_ptr); + if(true == sib7->t_resel_geran_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib7->t_resel_geran_sf, ie_ptr); + } + if(0 != sib7->carrier_freqs_info_list_size) + { + liblte_value_2_bits(sib7->carrier_freqs_info_list_size - 1, ie_ptr, 4); + for(i=0; icarrier_freqs_info_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_rrc_pack_carrier_freqs_geran_ie(&sib7->carrier_freqs_info_list[i].carrier_freqs, ie_ptr); + + // Common Info + { + // Optional indicators + liblte_value_2_bits(sib7->carrier_freqs_info_list[i].cell_resel_prio_present, ie_ptr, 1); + liblte_value_2_bits(sib7->carrier_freqs_info_list[i].p_max_geran_present, ie_ptr, 1); + + if(true == sib7->carrier_freqs_info_list[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib7->carrier_freqs_info_list[i].cell_resel_prio, ie_ptr); + } + liblte_value_2_bits(sib7->carrier_freqs_info_list[i].ncc_permitted, ie_ptr, 8); + liblte_value_2_bits((sib7->carrier_freqs_info_list[i].q_rx_lev_min + 115) / 2, ie_ptr, 6); + if(true == sib7->carrier_freqs_info_list[i].p_max_geran_present) + { + liblte_value_2_bits(sib7->carrier_freqs_info_list[i].p_max_geran, ie_ptr, 6); + } + liblte_rrc_pack_reselection_threshold_ie(sib7->carrier_freqs_info_list[i].threshx_high, ie_ptr); + liblte_rrc_pack_reselection_threshold_ie(sib7->carrier_freqs_info_list[i].threshx_low, ie_ptr); + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_7_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT *sib7) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool ext_ind; + bool carrier_freqs_info_list_opt; + bool carrier_freqs_info_list_ext_ind; + + if(ie_ptr != NULL && + sib7 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sib7->t_resel_geran_sf_present = liblte_bits_2_value(ie_ptr, 1); + carrier_freqs_info_list_opt = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib7->t_resel_geran); + if(true == sib7->t_resel_geran_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib7->t_resel_geran_sf); + } + if(true == carrier_freqs_info_list_opt) + { + sib7->carrier_freqs_info_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; icarrier_freqs_info_list_size; i++) + { + // Extension indicator + carrier_freqs_info_list_ext_ind = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_carrier_freqs_geran_ie(ie_ptr, &sib7->carrier_freqs_info_list[i].carrier_freqs); + + // Common Info + { + // Optional indicators + sib7->carrier_freqs_info_list[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + sib7->carrier_freqs_info_list[i].p_max_geran_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == sib7->carrier_freqs_info_list[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib7->carrier_freqs_info_list[i].cell_resel_prio); + } + sib7->carrier_freqs_info_list[i].ncc_permitted = liblte_bits_2_value(ie_ptr, 8); + sib7->carrier_freqs_info_list[i].q_rx_lev_min = (liblte_bits_2_value(ie_ptr, 6) * 2) - 115; + if(true == sib7->carrier_freqs_info_list[i].p_max_geran_present) + { + sib7->carrier_freqs_info_list[i].p_max_geran = liblte_bits_2_value(ie_ptr, 6); + } + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib7->carrier_freqs_info_list[i].threshx_high); + liblte_rrc_unpack_reselection_threshold_ie(ie_ptr, &sib7->carrier_freqs_info_list[i].threshx_low); + } + } + }else{ + sib7->carrier_freqs_info_list_size = 0; + } + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 8 + + Description: Contains information relevant only for inter-RAT + cell re-selection i.e. information about CDMA2000 + frequencies and CDMA2000 neighboring cells relevant + for cell re-selection + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_8_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *sib8, + uint8 **ie_ptr) +{ + LIBLTE_RRC_NEIGH_CELL_CDMA2000_STRUCT *neigh_cell_list; + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 j; + uint32 k; + + if(sib8 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sib8->sys_time_info_present, ie_ptr, 1); + liblte_value_2_bits(sib8->search_win_size_present, ie_ptr, 1); + liblte_value_2_bits(sib8->params_hrpd_present, ie_ptr, 1); + liblte_value_2_bits(sib8->params_1xrtt_present, ie_ptr, 1); + + if(true == sib8->sys_time_info_present) + { + liblte_rrc_pack_system_time_info_cdma2000_ie(&sib8->sys_time_info_cdma2000, ie_ptr); + } + + if(true == sib8->search_win_size_present) + { + liblte_value_2_bits(sib8->search_win_size, ie_ptr, 4); + } + + if(true == sib8->params_hrpd_present) + { + // Optional indicator + liblte_value_2_bits(sib8->cell_resel_params_hrpd_present, ie_ptr, 1); + + liblte_rrc_pack_pre_registration_info_hrpd_ie(&sib8->pre_reg_info_hrpd, ie_ptr); + + if(true == sib8->cell_resel_params_hrpd_present) + { + // Optional indicator + liblte_value_2_bits(sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf_present, ie_ptr, 1); + + liblte_value_2_bits(sib8->cell_resel_params_hrpd.band_class_list_size - 1, ie_ptr, 5); + for(i=0; icell_resel_params_hrpd.band_class_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio_present, ie_ptr, 1); + + liblte_rrc_pack_band_class_cdma2000_ie(sib8->cell_resel_params_hrpd.band_class_list[i].band_class, ie_ptr); + if(true == sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio, ie_ptr); + } + liblte_value_2_bits(sib8->cell_resel_params_hrpd.band_class_list[i].thresh_x_high, ie_ptr, 6); + liblte_value_2_bits(sib8->cell_resel_params_hrpd.band_class_list[i].thresh_x_low, ie_ptr, 6); + } + + liblte_value_2_bits(sib8->cell_resel_params_hrpd.neigh_cell_list_size - 1, ie_ptr, 4); + for(i=0; icell_resel_params_hrpd.neigh_cell_list_size; i++) + { + neigh_cell_list = &sib8->cell_resel_params_hrpd.neigh_cell_list[i]; + liblte_rrc_pack_band_class_cdma2000_ie(neigh_cell_list->band_class, ie_ptr); + liblte_value_2_bits(neigh_cell_list->neigh_cells_per_freq_list_size - 1, ie_ptr, 4); + for(j=0; jneigh_cells_per_freq_list_size; j++) + { + liblte_rrc_pack_arfcn_value_cdma2000_ie(neigh_cell_list->neigh_cells_per_freq_list[j].arfcn, ie_ptr); + liblte_value_2_bits(neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list_size - 1, ie_ptr, 4); + for(k=0; kneigh_cells_per_freq_list[j].phys_cell_id_list_size; k++) + { + liblte_rrc_pack_phys_cell_id_cdma2000_ie(neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list[k], ie_ptr); + } + } + } + liblte_rrc_pack_t_reselection_ie(sib8->cell_resel_params_hrpd.t_resel_cdma2000, ie_ptr); + + if(true == sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf, ie_ptr); + } + } + } + + if(true == sib8->params_1xrtt_present) + { + // Optional indicators + liblte_value_2_bits(sib8->csfb_reg_param_1xrtt_present, ie_ptr, 1); + liblte_value_2_bits(sib8->long_code_state_1xrtt_present, ie_ptr, 1); + liblte_value_2_bits(sib8->cell_resel_params_1xrtt_present, ie_ptr, 1); + + if(true == sib8->csfb_reg_param_1xrtt_present) + { + liblte_rrc_pack_csfb_registration_param_1xrtt_ie(&sib8->csfb_reg_param_1xrtt, ie_ptr); + } + + if(true == sib8->long_code_state_1xrtt_present) + { + liblte_value_2_bits((uint32)(sib8->long_code_state_1xrtt >> 10), ie_ptr, 32); + liblte_value_2_bits((uint32)(sib8->long_code_state_1xrtt & 0x3FF), ie_ptr, 10); + } + + if(true == sib8->cell_resel_params_1xrtt_present) + { + // Optional indicator + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf_present, ie_ptr, 1); + + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.band_class_list_size - 1, ie_ptr, 5); + for(i=0; icell_resel_params_1xrtt.band_class_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicator + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio_present, ie_ptr, 1); + + liblte_rrc_pack_band_class_cdma2000_ie(sib8->cell_resel_params_1xrtt.band_class_list[i].band_class, ie_ptr); + if(true == sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio_present) + { + liblte_rrc_pack_cell_reselection_priority_ie(sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio, ie_ptr); + } + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.band_class_list[i].thresh_x_high, ie_ptr, 6); + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.band_class_list[i].thresh_x_low, ie_ptr, 6); + } + + liblte_value_2_bits(sib8->cell_resel_params_1xrtt.neigh_cell_list_size - 1, ie_ptr, 4); + for(i=0; icell_resel_params_1xrtt.neigh_cell_list_size; i++) + { + neigh_cell_list = &sib8->cell_resel_params_1xrtt.neigh_cell_list[i]; + liblte_rrc_pack_band_class_cdma2000_ie(neigh_cell_list->band_class, ie_ptr); + liblte_value_2_bits(neigh_cell_list->neigh_cells_per_freq_list_size - 1, ie_ptr, 4); + for(j=0; jneigh_cells_per_freq_list_size; j++) + { + liblte_rrc_pack_arfcn_value_cdma2000_ie(neigh_cell_list->neigh_cells_per_freq_list[j].arfcn, ie_ptr); + liblte_value_2_bits(neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list_size - 1, ie_ptr, 4); + for(k=0; kneigh_cells_per_freq_list[j].phys_cell_id_list_size; k++) + { + liblte_rrc_pack_phys_cell_id_cdma2000_ie(neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list[k], ie_ptr); + } + } + } + liblte_rrc_pack_t_reselection_ie(sib8->cell_resel_params_1xrtt.t_resel_cdma2000, ie_ptr); + + if(true == sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf_present) + { + liblte_rrc_pack_speed_state_scale_factors_ie(&sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf, ie_ptr); + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_8_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *sib8) +{ + LIBLTE_RRC_NEIGH_CELL_CDMA2000_STRUCT *neigh_cell_list; + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint32 j; + uint32 k; + bool ext_ind; + + if(ie_ptr != NULL && + sib8 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sib8->sys_time_info_present = liblte_bits_2_value(ie_ptr, 1); + sib8->search_win_size_present = liblte_bits_2_value(ie_ptr, 1); + sib8->params_hrpd_present = liblte_bits_2_value(ie_ptr, 1); + sib8->params_1xrtt_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == sib8->sys_time_info_present) + { + liblte_rrc_unpack_system_time_info_cdma2000_ie(ie_ptr, &sib8->sys_time_info_cdma2000); + } + + if(true == sib8->search_win_size_present) + { + sib8->search_win_size = liblte_bits_2_value(ie_ptr, 4); + } + + if(true == sib8->params_hrpd_present) + { + // Optional indicator + sib8->cell_resel_params_hrpd_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_pre_registration_info_hrpd_ie(ie_ptr, &sib8->pre_reg_info_hrpd); + + if(true == sib8->cell_resel_params_hrpd_present) + { + // Optional indicator + sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf_present = liblte_bits_2_value(ie_ptr, 1); + + sib8->cell_resel_params_hrpd.band_class_list_size = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; icell_resel_params_hrpd.band_class_list_size; i++) + { + // Extension indicator + bool ext = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_band_class_cdma2000_ie(ie_ptr, &sib8->cell_resel_params_hrpd.band_class_list[i].band_class); + if(true == sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib8->cell_resel_params_hrpd.band_class_list[i].cell_resel_prio); + } + sib8->cell_resel_params_hrpd.band_class_list[i].thresh_x_high = liblte_bits_2_value(ie_ptr, 6); + sib8->cell_resel_params_hrpd.band_class_list[i].thresh_x_low = liblte_bits_2_value(ie_ptr, 6); + + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + } + + sib8->cell_resel_params_hrpd.neigh_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; icell_resel_params_hrpd.neigh_cell_list_size; i++) + { + neigh_cell_list = &sib8->cell_resel_params_hrpd.neigh_cell_list[i]; + liblte_rrc_unpack_band_class_cdma2000_ie(ie_ptr, &neigh_cell_list->band_class); + neigh_cell_list->neigh_cells_per_freq_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(j=0; jneigh_cells_per_freq_list_size; j++) + { + liblte_rrc_unpack_arfcn_value_cdma2000_ie(ie_ptr, &neigh_cell_list->neigh_cells_per_freq_list[j].arfcn); + neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(k=0; kneigh_cells_per_freq_list[j].phys_cell_id_list_size; k++) + { + liblte_rrc_unpack_phys_cell_id_cdma2000_ie(ie_ptr, &neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list[k]); + } + } + } + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib8->cell_resel_params_hrpd.t_resel_cdma2000); + + if(true == sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib8->cell_resel_params_hrpd.t_resel_cdma2000_sf); + } + } + }else{ + sib8->cell_resel_params_hrpd_present = false; + } + + if(true == sib8->params_1xrtt_present) + { + // Optional indicators + sib8->csfb_reg_param_1xrtt_present = liblte_bits_2_value(ie_ptr, 1); + sib8->long_code_state_1xrtt_present = liblte_bits_2_value(ie_ptr, 1); + sib8->cell_resel_params_1xrtt_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == sib8->csfb_reg_param_1xrtt_present) + { + liblte_rrc_unpack_csfb_registration_param_1xrtt_ie(ie_ptr, &sib8->csfb_reg_param_1xrtt); + } + + if(true == sib8->long_code_state_1xrtt_present) + { + sib8->long_code_state_1xrtt = (uint64)liblte_bits_2_value(ie_ptr, 32) << 10; + sib8->long_code_state_1xrtt |= (uint64)liblte_bits_2_value(ie_ptr, 10); + } + + if(true == sib8->cell_resel_params_1xrtt_present) + { + // Optional indicator + sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf_present = liblte_bits_2_value(ie_ptr, 1); + + sib8->cell_resel_params_1xrtt.band_class_list_size = liblte_bits_2_value(ie_ptr, 5) + 1; + for(i=0; icell_resel_params_1xrtt.band_class_list_size; i++) + { + // Extension indicator + bool ext2 = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicator + sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio_present = liblte_bits_2_value(ie_ptr, 1); + + liblte_rrc_unpack_band_class_cdma2000_ie(ie_ptr, &sib8->cell_resel_params_1xrtt.band_class_list[i].band_class); + if(true == sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio_present) + { + liblte_rrc_unpack_cell_reselection_priority_ie(ie_ptr, &sib8->cell_resel_params_1xrtt.band_class_list[i].cell_resel_prio); + } + sib8->cell_resel_params_1xrtt.band_class_list[i].thresh_x_high = liblte_bits_2_value(ie_ptr, 6); + sib8->cell_resel_params_1xrtt.band_class_list[i].thresh_x_low = liblte_bits_2_value(ie_ptr, 6); + + liblte_rrc_consume_noncrit_extension(ext2, __func__, ie_ptr); + } + + sib8->cell_resel_params_1xrtt.neigh_cell_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(i=0; icell_resel_params_1xrtt.neigh_cell_list_size; i++) + { + neigh_cell_list = &sib8->cell_resel_params_1xrtt.neigh_cell_list[i]; + liblte_rrc_unpack_band_class_cdma2000_ie(ie_ptr, &neigh_cell_list->band_class); + neigh_cell_list->neigh_cells_per_freq_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(j=0; jneigh_cells_per_freq_list_size; j++) + { + liblte_rrc_unpack_arfcn_value_cdma2000_ie(ie_ptr, &neigh_cell_list->neigh_cells_per_freq_list[j].arfcn); + neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list_size = liblte_bits_2_value(ie_ptr, 4) + 1; + for(k=0; kneigh_cells_per_freq_list[j].phys_cell_id_list_size; k++) + { + liblte_rrc_unpack_phys_cell_id_cdma2000_ie(ie_ptr, &neigh_cell_list->neigh_cells_per_freq_list[j].phys_cell_id_list[k]); + } + } + } + liblte_rrc_unpack_t_reselection_ie(ie_ptr, &sib8->cell_resel_params_1xrtt.t_resel_cdma2000); + + if(true == sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf_present) + { + liblte_rrc_unpack_speed_state_scale_factors_ie(ie_ptr, &sib8->cell_resel_params_1xrtt.t_resel_cdma2000_sf); + } + } + }else{ + sib8->csfb_reg_param_1xrtt_present = false; + sib8->long_code_state_1xrtt_present = false; + sib8->cell_resel_params_1xrtt_present = false; + } + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 9 + + Description: Contains a home eNB name (HNB name) + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_9_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sib9 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(sib9->hnb_name_present, ie_ptr, 1); + + if(true == sib9->hnb_name_present) { + // Dynamic octet string - hnb_name + liblte_value_2_bits(sib9->hnb_name_size - 1, ie_ptr, 6); + + // Octets + for(i=0;ihnb_name_size;i++) { + liblte_value_2_bits(sib9->hnb_name[i], ie_ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_9_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + bool ext_ind; + uint32 i; + + if(ie_ptr != NULL && + sib9 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + sib9->hnb_name_present = liblte_bits_2_value(ie_ptr, 1); + + if(true == sib9->hnb_name_present) { + // Dynamic octet string - hnb_name + // Length + sib9->hnb_name_size = liblte_bits_2_value(ie_ptr, 6) + 1; + + // Octets + for(i=0;ihnb_name_size;i++) { + sib9->hnb_name[i] = liblte_bits_2_value(ie_ptr, 8); + } + } + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + IE Name: System Information Block Type 10 + + Description: Contains an ETWS primary notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 11 + + Description: Contains an ETWS secondary notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 12 + + Description: Contains a CMAS notification + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +// FIXME + +/********************************************************************* + IE Name: System Information Block Type 13 + + Description: Contains the information required to acquire the + MBMS control information associated with one or more + MBSFN areas + + Document Reference: 36.331 v10.0.0 Section 6.3.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_13_ie(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + + if(sib13 != NULL && + ie_ptr != NULL) + { + // Extension indicator + liblte_value_2_bits(0, ie_ptr, 1); + + // Optional indicators + liblte_value_2_bits(0, ie_ptr, 1); + + liblte_value_2_bits(sib13->mbsfn_area_info_list_r9_size - 1, ie_ptr, 3); + for(i=0; imbsfn_area_info_list_r9_size; i++) + { + liblte_rrc_pack_mbsfn_area_info_ie(&sib13->mbsfn_area_info_list_r9[i], ie_ptr); + } + liblte_rrc_pack_mbsfn_notification_config_ie(&sib13->mbsfn_notification_config, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_13_ie(uint8 **ie_ptr, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + bool ext_ind; + bool non_crit_ext_present; + + if(ie_ptr != NULL && + sib13 != NULL) + { + // Extension indicator + ext_ind = liblte_bits_2_value(ie_ptr, 1); + + // Optional indicators + non_crit_ext_present = liblte_bits_2_value(ie_ptr, 1); + + sib13->mbsfn_area_info_list_r9_size = liblte_bits_2_value(ie_ptr, 3) + 1; + for(i=0; imbsfn_area_info_list_r9_size; i++) + { + liblte_rrc_unpack_mbsfn_area_info_ie(ie_ptr, &sib13->mbsfn_area_info_list_r9[i]); + } + liblte_rrc_unpack_mbsfn_notification_config_ie(ie_ptr, &sib13->mbsfn_notification_config); + + liblte_rrc_consume_noncrit_extension(ext_ind, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/******************************************************************************* + MESSAGE FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + Message Name: UL Information Transfer + + Description: Used for the uplink transfer dedicated NAS + information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_information_transfer_msg(LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *ul_info_transfer, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(ul_info_transfer != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Dedicated info type choice + liblte_value_2_bits(ul_info_transfer->dedicated_info_type, &msg_ptr, 2); + + if(LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS == ul_info_transfer->dedicated_info_type) + { + liblte_rrc_pack_dedicated_info_nas_ie(&ul_info_transfer->dedicated_info, + &msg_ptr); + }else{ + liblte_rrc_pack_dedicated_info_cdma2000_ie(&ul_info_transfer->dedicated_info, + &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_information_transfer_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *ul_info_transfer) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + ul_info_transfer != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Dedicated info type choice + ul_info_transfer->dedicated_info_type = (LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 2); + + if(LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS == ul_info_transfer->dedicated_info_type) + { + liblte_rrc_unpack_dedicated_info_nas_ie(&msg_ptr, + &ul_info_transfer->dedicated_info); + }else{ + liblte_rrc_unpack_dedicated_info_cdma2000_ie(&msg_ptr, + &ul_info_transfer->dedicated_info); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: UL Handover Preparation Transfer (CDMA2000) + + Description: Used for the uplink transfer of handover related + CDMA2000 information when requested by the higher + layers + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: UE Information Response + + Description: Used by the UE to transfer the information requested + by the E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: UE Information Request + + Description: Used by E-UTRAN to retrieve information from the UE + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_information_request_msg(LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *ue_info_req, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(ue_info_req != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(ue_info_req->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // RACH report required + liblte_value_2_bits(ue_info_req->rach_report_req, &msg_ptr, 1); + + // RLF report required + liblte_value_2_bits(ue_info_req->rlf_report_req, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_information_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *ue_info_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + ue_info_req != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &ue_info_req->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // RACH report required + ue_info_req->rach_report_req = liblte_bits_2_value(&msg_ptr, 1); + + // RLF report required + ue_info_req->rlf_report_req = liblte_bits_2_value(&msg_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: UE Capability Information + + Description: Used to transfer UE radio access capabilities + requested by the E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_capability_information_msg(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *ue_capability_info, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(ue_capability_info != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(ue_capability_info->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 3); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + liblte_value_2_bits(ue_capability_info->N_ue_caps, &msg_ptr, 4); + for(i=0; iN_ue_caps; i++) + { + // RAT-Type + liblte_value_2_bits(0, &msg_ptr, 1); //Optional indicator + liblte_value_2_bits(ue_capability_info->ue_capability_rat[i].rat_type, &msg_ptr, 3); + + // Octet string + LIBLTE_BIT_MSG_STRUCT tmp; + liblte_rrc_pack_ue_eutra_capability_ie(&ue_capability_info->ue_capability_rat[i].eutra_capability, &tmp); + + uint32 pad = 8 - tmp.N_bits%8; + uint32 n_bytes = (tmp.N_bits+pad) / 8; + if(n_bytes < 128) + { + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(n_bytes, &msg_ptr, 7); + }else if(n_bytes < 16383){ + liblte_value_2_bits(1, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(n_bytes, &msg_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + } + + for(i=0; iN_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_capability_information_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *ue_capability_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(msg != NULL && + ue_capability_info != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &ue_capability_info->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 3); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + ue_capability_info->N_ue_caps = liblte_bits_2_value(&msg_ptr, 4); + for(i=0; iN_ue_caps; i++) + { + liblte_bits_2_value(&msg_ptr, 1); //Optional indicator + ue_capability_info->ue_capability_rat[i].rat_type = (LIBLTE_RRC_RAT_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + //Octet string + uint32 n_bytes = 0; + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + n_bytes = liblte_bits_2_value(&msg_ptr, 7); + }else{ + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + n_bytes = liblte_bits_2_value(&msg_ptr, 14); + }else{ + // FIXME: Unlikely to have more than 16K of octets + n_bytes = 0; + } + } + + liblte_rrc_unpack_ue_eutra_capability_ie(&msg_ptr, &ue_capability_info->ue_capability_rat[i].eutra_capability); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + Message Name: UE Capability Enquiry + + Description: Used to request the transfer of UE radio access + capabilities for E-UTRA as well as for other RATs + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ue_capability_enquiry_msg(LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *ue_cap_enquiry, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(ue_cap_enquiry != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(ue_cap_enquiry->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // UE-CapabilityRequest + liblte_value_2_bits(ue_cap_enquiry->N_ue_cap_reqs - 1, &msg_ptr, 3); + for(i=0; iue_capability_request[i], &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ue_capability_enquiry_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *ue_cap_enquiry) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(msg != NULL && + ue_cap_enquiry != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &ue_cap_enquiry->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + ue_cap_enquiry->N_ue_cap_reqs = liblte_bits_2_value(&msg_ptr, 3) + 1; + for(i=0; iN_ue_cap_reqs; i++) + { + liblte_rrc_unpack_rat_type_ie(&msg_ptr, &ue_cap_enquiry->ue_capability_request[i]); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + Message Name: System Information Block Type 1 + + Description: Contains information relevant when evaluating if a + UE is allowed to access a cell and defines the + scheduling of other system information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_block_type_1_msg(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + uint32 j; + uint8 non_crit_ext_opt = false; + uint8 csg_id_opt = false; + uint8 q_rx_lev_min_offset_opt = false; + uint8 extension = false; + + if(sib1 != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Optional indicators + liblte_value_2_bits(sib1->p_max_present, &msg_ptr, 1); + liblte_value_2_bits(sib1->tdd, &msg_ptr, 1); + liblte_value_2_bits(non_crit_ext_opt, &msg_ptr, 1); + + // Cell Access Related Info + liblte_value_2_bits(csg_id_opt, &msg_ptr, 1); + liblte_value_2_bits(sib1->N_plmn_ids - 1, &msg_ptr, 3); + for(i=0; iN_plmn_ids; i++) + { + liblte_rrc_pack_plmn_identity_ie(&sib1->plmn_id[i].id, &msg_ptr); + liblte_value_2_bits(sib1->plmn_id[i].resv_for_oper, &msg_ptr, 1); + } + liblte_rrc_pack_tracking_area_code_ie(sib1->tracking_area_code, &msg_ptr); + liblte_rrc_pack_cell_identity_ie(sib1->cell_id, &msg_ptr); + liblte_value_2_bits(sib1->cell_barred, &msg_ptr, 1); + liblte_value_2_bits(sib1->intra_freq_reselection, &msg_ptr, 1); + liblte_value_2_bits(sib1->csg_indication, &msg_ptr, 1); + if(true == csg_id_opt) + { + liblte_rrc_pack_csg_identity_ie(sib1->csg_id, &msg_ptr); + } + + // Cell Selection Info + liblte_value_2_bits(q_rx_lev_min_offset_opt, &msg_ptr, 1); + liblte_rrc_pack_q_rx_lev_min_ie(sib1->q_rx_lev_min, &msg_ptr); + if(true == q_rx_lev_min_offset_opt) + { + liblte_value_2_bits((sib1->q_rx_lev_min_offset / 2) - 1, &msg_ptr, 3); + } + + // P Max + if(true == sib1->p_max_present) + { + liblte_rrc_pack_p_max_ie(sib1->p_max, &msg_ptr); + } + + // Freq Band Indicator + liblte_value_2_bits(sib1->freq_band_indicator - 1, &msg_ptr, 6); + + // Scheduling Info List + liblte_value_2_bits(sib1->N_sched_info - 1, &msg_ptr, 5); + for(i=0; iN_sched_info; i++) + { + liblte_value_2_bits(sib1->sched_info[i].si_periodicity, &msg_ptr, 3); + liblte_value_2_bits(sib1->sched_info[i].N_sib_mapping_info, &msg_ptr, 5); + for(j=0; jsched_info[i].N_sib_mapping_info; j++) + { + liblte_value_2_bits(extension, &msg_ptr, 1); + liblte_value_2_bits(sib1->sched_info[i].sib_mapping_info[j].sib_type, &msg_ptr, 4); + } + } + + // TDD Config + if(true == sib1->tdd) + { + liblte_rrc_pack_tdd_config_ie(&sib1->tdd_cnfg, &msg_ptr); + } + + // SI Window Length + liblte_value_2_bits(sib1->si_window_length, &msg_ptr, 3); + + // System Info Value Tag + liblte_value_2_bits(sib1->system_info_value_tag, &msg_ptr, 5); + + // Non Critical Extension + // FIXME + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_block_type_1_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1, + uint32 *N_bits_used) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + uint32 j; + bool tdd_config_opt; + bool non_crit_ext_opt; + bool csg_id_opt; + bool q_rx_lev_min_offset_opt; + bool extension; + + if(msg != NULL && + sib1 != NULL && + N_bits_used != NULL) + { + msg_ptr = msg->msg; + + // Optional indicators + sib1->p_max_present = liblte_bits_2_value(&msg_ptr, 1); + tdd_config_opt = liblte_bits_2_value(&msg_ptr, 1); + non_crit_ext_opt = liblte_bits_2_value(&msg_ptr, 1); + + // Cell Access Related Info + csg_id_opt = liblte_bits_2_value(&msg_ptr, 1); + sib1->N_plmn_ids = liblte_bits_2_value(&msg_ptr, 3) + 1; + for(i=0; iN_plmn_ids; i++) + { + liblte_rrc_unpack_plmn_identity_ie(&msg_ptr, &sib1->plmn_id[i].id); + if(LIBLTE_RRC_MCC_NOT_PRESENT == sib1->plmn_id[i].id.mcc && + 0 != i) + { + sib1->plmn_id[i].id.mcc = sib1->plmn_id[i-1].id.mcc; + } + sib1->plmn_id[i].resv_for_oper = (LIBLTE_RRC_RESV_FOR_OPER_ENUM)liblte_bits_2_value(&msg_ptr, 1); + } + liblte_rrc_unpack_tracking_area_code_ie(&msg_ptr, &sib1->tracking_area_code); + liblte_rrc_unpack_cell_identity_ie(&msg_ptr, &sib1->cell_id); + sib1->cell_barred = (LIBLTE_RRC_CELL_BARRED_ENUM)liblte_bits_2_value(&msg_ptr, 1); + sib1->intra_freq_reselection = (LIBLTE_RRC_INTRA_FREQ_RESELECTION_ENUM)liblte_bits_2_value(&msg_ptr, 1); + sib1->csg_indication = liblte_bits_2_value(&msg_ptr, 1); + if(true == csg_id_opt) + { + liblte_rrc_unpack_csg_identity_ie(&msg_ptr, &sib1->csg_id); + }else{ + sib1->csg_id = LIBLTE_RRC_CSG_IDENTITY_NOT_PRESENT; + } + + // Cell Selection Info + q_rx_lev_min_offset_opt = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_unpack_q_rx_lev_min_ie(&msg_ptr, &sib1->q_rx_lev_min); + if(true == q_rx_lev_min_offset_opt) + { + sib1->q_rx_lev_min_offset = (liblte_bits_2_value(&msg_ptr, 3) + 1) * 2; + }else{ + sib1->q_rx_lev_min_offset = 0; + } + + // P Max + if(true == sib1->p_max_present) + { + liblte_rrc_unpack_p_max_ie(&msg_ptr, &sib1->p_max); + } + + // Freq Band Indicator + sib1->freq_band_indicator = liblte_bits_2_value(&msg_ptr, 6) + 1; + + // Scheduling Info List + sib1->N_sched_info = liblte_bits_2_value(&msg_ptr, 5) + 1; + for(i=0; iN_sched_info; i++) + { + sib1->sched_info[i].si_periodicity = (LIBLTE_RRC_SI_PERIODICITY_ENUM)liblte_bits_2_value(&msg_ptr, 3); + sib1->sched_info[i].N_sib_mapping_info = liblte_bits_2_value(&msg_ptr, 5); + for(j=0; jsched_info[i].N_sib_mapping_info; j++) + { + extension = liblte_bits_2_value(&msg_ptr, 1); + sib1->sched_info[i].sib_mapping_info[j].sib_type = (LIBLTE_RRC_SIB_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 4); + } + } + + // TDD Config + if(true == tdd_config_opt) + { + sib1->tdd = true; + liblte_rrc_unpack_tdd_config_ie(&msg_ptr, &sib1->tdd_cnfg); + }else{ + sib1->tdd = false; + } + + // SI Window Length + sib1->si_window_length = (LIBLTE_RRC_SI_WINDOW_LENGTH_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + // System Info Value Tag + sib1->system_info_value_tag = liblte_bits_2_value(&msg_ptr, 5); + + // Non Critical Extension + liblte_rrc_consume_noncrit_extension(non_crit_ext_opt, __func__, &msg_ptr); + + // N_bits_used + *N_bits_used = msg_ptr - (msg->msg); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: System Information + + Description: Conveys one or more System Information Blocks + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_sys_info_msg(LIBLTE_RRC_SYS_INFO_MSG_STRUCT *sibs, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 *length_ptr; + uint32 length; + uint32 pad_bits; + uint32 i; + + if(sibs != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Critical extensions choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Number of SIBs present + liblte_value_2_bits(sibs->N_sibs - 1, &msg_ptr, 5); + + for(i=0; iN_sibs; i++) + { + if(sibs->sibs[i].sib_type < LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12) + { + // Extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + liblte_value_2_bits(sibs->sibs[i].sib_type, &msg_ptr, 4); + switch(sibs->sibs[i].sib_type) + { + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2: + err = liblte_rrc_pack_sys_info_block_type_2_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3: + err = liblte_rrc_pack_sys_info_block_type_3_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4: + err = liblte_rrc_pack_sys_info_block_type_4_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8: + err = liblte_rrc_pack_sys_info_block_type_8_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9: + err = liblte_rrc_pack_sys_info_block_type_9_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5: + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6: + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7: + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_10: + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_11: + default: + printf("ERROR: Not handling sib type %u\n", sibs->sibs[i].sib_type); + err = LIBLTE_ERROR_INVALID_INPUTS; + break; + } + }else{ + // Extension indicator + liblte_value_2_bits(1, &msg_ptr, 1); + + liblte_value_2_bits(sibs->sibs[i].sib_type - 10, &msg_ptr, 7); + length_ptr = msg_ptr; + liblte_value_2_bits(0, &msg_ptr, 8); + switch(sibs->sibs[i].sib_type) + { + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13: + err = liblte_rrc_pack_sys_info_block_type_13_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12: + default: + printf("ERROR: Not handling extended sib type %s\n", liblte_rrc_sys_info_block_type_text[sibs->sibs[i].sib_type]); + err = LIBLTE_ERROR_INVALID_INPUTS; + break; + } + length = ((msg_ptr - length_ptr) / 8) - 1; + pad_bits = (msg_ptr - length_ptr) % 8; + if(0 != pad_bits) + { + length++; + } + if(length < 128) + { + liblte_value_2_bits(0, &length_ptr, 1); + liblte_value_2_bits(length, &length_ptr, 7); + }else{ + msg_ptr = length_ptr; + liblte_value_2_bits(0, &msg_ptr, 2); + liblte_value_2_bits(length, &msg_ptr, 14); + switch(sibs->sibs[i].sib_type) + { + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13: + err = liblte_rrc_pack_sys_info_block_type_13_ie((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *)&sibs->sibs[i].sib, + &msg_ptr); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12: + default: + printf("ERROR: Not handling extended sib type %s\n", liblte_rrc_sys_info_block_type_text[sibs->sibs[i].sib_type]); + err = LIBLTE_ERROR_INVALID_INPUTS; + break; + } + } + liblte_value_2_bits(0, &msg_ptr, pad_bits); + } + + if(LIBLTE_SUCCESS != err) + { + break; + } + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_sys_info_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SYS_INFO_MSG_STRUCT *sibs) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 *head_ptr; + uint32 i; + uint32 length_determinant_octets; + uint8 non_crit_ext_opt; + + if(msg != NULL && + sibs != NULL) + { + msg_ptr = msg->msg; + + // Critical extensions choice + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + // Optional indicator + non_crit_ext_opt = liblte_bits_2_value(&msg_ptr, 1); + + // Number of SIBs present + sibs->N_sibs = liblte_bits_2_value(&msg_ptr, 5) + 1; + + for(i=0; iN_sibs; i++) + { + // Extension indicator + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + sibs->sibs[i].sib_type = (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 4); + switch(sibs->sibs[i].sib_type) + { + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2: + err = liblte_rrc_unpack_sys_info_block_type_2_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3: + err = liblte_rrc_unpack_sys_info_block_type_3_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4: + err = liblte_rrc_unpack_sys_info_block_type_4_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5: + err = liblte_rrc_unpack_sys_info_block_type_5_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_5_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6: + err = liblte_rrc_unpack_sys_info_block_type_6_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_6_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7: + err = liblte_rrc_unpack_sys_info_block_type_7_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_7_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8: + err = liblte_rrc_unpack_sys_info_block_type_8_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_8_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9: + err = liblte_rrc_unpack_sys_info_block_type_9_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_10: + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_11: + default: + printf("ERROR: Not handling sib type %u\n", sibs->sibs[i].sib_type); + err = LIBLTE_ERROR_INVALID_INPUTS; + break; + } + }else{ + sibs->sibs[i].sib_type = (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_ENUM)(liblte_bits_2_value(&msg_ptr, 7) + 10); + length_determinant_octets = 0; + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + length_determinant_octets = liblte_bits_2_value(&msg_ptr, 7); + }else{ + if(0 == liblte_bits_2_value(&msg_ptr, 1)) + { + length_determinant_octets = liblte_bits_2_value(&msg_ptr, 14); + }else{ + printf("ERROR: Not handling fragmented length determinants\n"); + } + } + head_ptr = msg_ptr; + switch(sibs->sibs[i].sib_type) + { + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13: + err = liblte_rrc_unpack_sys_info_block_type_13_ie(&msg_ptr, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *)&sibs->sibs[i].sib); + break; + case LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_12: + default: + printf("ERROR: Not handling extended sib type %s\n", liblte_rrc_sys_info_block_type_text[sibs->sibs[i].sib_type]); + err = LIBLTE_ERROR_INVALID_INPUTS; + break; + } + liblte_bits_2_value(&msg_ptr, (msg_ptr - head_ptr) % 8); + } + + if(LIBLTE_SUCCESS != err) + { + break; + } + } + + liblte_rrc_consume_noncrit_extension(non_crit_ext_opt, __func__, &msg_ptr); + + }else{ + printf("ERROR: Not handling critical extensions in system information message\n"); + } + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Failure + + Description: Used to indicate an unsuccessful completion of a + security mode command + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_failure_msg(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *security_mode_failure, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(security_mode_failure != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(security_mode_failure->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_failure_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *security_mode_failure) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + security_mode_failure != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &security_mode_failure->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Complete + + Description: Used to confirm the successful completion of a + security mode command + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_complete_msg(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *security_mode_complete, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(security_mode_complete != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(security_mode_complete->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *security_mode_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + security_mode_complete != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &security_mode_complete->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Security Mode Command + + Description: Used to command the activation of AS security + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_security_mode_command_msg(LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *security_mode_cmd, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(security_mode_cmd != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(security_mode_cmd->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Security Algorithms Config + liblte_rrc_pack_security_algorithm_config_ie(&security_mode_cmd->sec_algs, + &msg_ptr); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_security_mode_command_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *security_mode_cmd) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + security_mode_cmd != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &security_mode_cmd->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Extension indicator + bool ext2 = liblte_bits_2_value(&msg_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext2, __func__, &msg_ptr); + + // Security Algorithms Config + liblte_rrc_unpack_security_algorithm_config_ie(&msg_ptr, + &security_mode_cmd->sec_algs); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Setup Complete + + Description: Used to confirm the successful completion of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_setup_complete_msg(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *con_setup_complete, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_setup_complete != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_setup_complete->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicators + liblte_value_2_bits(con_setup_complete->registered_mme_present, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + + // Selected PLMN identity + liblte_value_2_bits(con_setup_complete->selected_plmn_id - 1, &msg_ptr, 3); + + // Registered MME + if(con_setup_complete->registered_mme_present) + { + // Optional indicator + liblte_value_2_bits(con_setup_complete->registered_mme.plmn_id_present, &msg_ptr, 1); + + // PLMN identity + if(con_setup_complete->registered_mme.plmn_id_present) + { + liblte_rrc_pack_plmn_identity_ie(&con_setup_complete->registered_mme.plmn_id, &msg_ptr); + } + + // MMEGI + liblte_value_2_bits(con_setup_complete->registered_mme.mmegi, &msg_ptr, 16); + + // MMEC + liblte_rrc_pack_mmec_ie(con_setup_complete->registered_mme.mmec, &msg_ptr); + } + + // Dedicated info NAS + liblte_rrc_pack_dedicated_info_nas_ie(&con_setup_complete->dedicated_info_nas, + &msg_ptr); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_setup_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *con_setup_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_setup_complete != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_setup_complete->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicators + con_setup_complete->registered_mme_present = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Selected PLMN identity + con_setup_complete->selected_plmn_id = liblte_bits_2_value(&msg_ptr, 3) + 1; + + // Registered MME + if(con_setup_complete->registered_mme_present) + { + // Optional indicator + con_setup_complete->registered_mme.plmn_id_present = liblte_bits_2_value(&msg_ptr, 1); + + // PLMN identity + if(con_setup_complete->registered_mme.plmn_id_present) + { + liblte_rrc_unpack_plmn_identity_ie(&msg_ptr, &con_setup_complete->registered_mme.plmn_id); + } + + // MMEGI + con_setup_complete->registered_mme.mmegi = liblte_bits_2_value(&msg_ptr, 16); + + // MMEC + liblte_rrc_unpack_mmec_ie(&msg_ptr, &con_setup_complete->registered_mme.mmec); + } + + // Dedicated info NAS + liblte_rrc_unpack_dedicated_info_nas_ie(&msg_ptr, + &con_setup_complete->dedicated_info_nas); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Setup + + Description: Used to establish SRB1 + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_setup_msg(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *con_setup, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_setup != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_setup->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 3); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Radio Resource Config Dedicated + liblte_rrc_pack_rr_config_dedicated_ie(&con_setup->rr_cnfg, &msg_ptr); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_setup_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_SETUP_STRUCT *con_setup) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_setup != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_setup->rrc_transaction_id); + + // Critical extension indicator + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 3); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Radio Resource Config Dedicated + liblte_rrc_unpack_rr_config_dedicated_ie(&msg_ptr, &con_setup->rr_cnfg); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Request + + Description: Used to request the establishment of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_request_msg(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *con_req, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(con_req != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(ext, &msg_ptr, 1); + + // UE Identity Type + liblte_value_2_bits(con_req->ue_id_type, &msg_ptr, 1); + + // UE Identity + if(LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI == con_req->ue_id_type) + { + liblte_rrc_pack_s_tmsi_ie((LIBLTE_RRC_S_TMSI_STRUCT *)&con_req->ue_id, + &msg_ptr); + }else{ // LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE == con_req->ue_id_type + liblte_value_2_bits((uint32)(con_req->ue_id.random >> 32), &msg_ptr, 8); + liblte_value_2_bits((uint32)(con_req->ue_id.random), &msg_ptr, 32); + } + + // Establishment Cause + liblte_value_2_bits(con_req->cause, &msg_ptr, 3); + + // Spare + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *con_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_req != NULL) + { + msg_ptr = msg->msg; + + // Extension Choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // UE Identity Type + con_req->ue_id_type = (LIBLTE_RRC_CON_REQ_UE_ID_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + // UE Identity + if(LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI == con_req->ue_id_type) + { + liblte_rrc_unpack_s_tmsi_ie(&msg_ptr, + (LIBLTE_RRC_S_TMSI_STRUCT *)&con_req->ue_id); + }else{ // LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE == con_req->ue_id_type + con_req->ue_id.random = (uint64)liblte_bits_2_value(&msg_ptr, 8) << 32; + con_req->ue_id.random |= liblte_bits_2_value(&msg_ptr, 32); + } + + // Establishment Cause + con_req->cause = (LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Release + + Description: Used to command the release of an RRC connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_release_msg(LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *con_release, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_release != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_release->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicators + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + + // Release cause + liblte_value_2_bits(con_release->release_cause, &msg_ptr, 2); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_release_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *con_release) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_release != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_release->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicators + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Release cause + con_release->release_cause = (LIBLTE_RRC_RELEASE_CAUSE_ENUM)liblte_bits_2_value(&msg_ptr, 2); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reject + + Description: Used to reject the RRC connection establishment + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reject_msg(LIBLTE_RRC_CONNECTION_REJECT_STRUCT *con_rej, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_rej != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Wait Time + liblte_value_2_bits(con_rej->wait_time - 1, &msg_ptr, 4); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reject_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REJECT_STRUCT *con_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_rej != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Wait Time + con_rej->wait_time = liblte_bits_2_value(&msg_ptr, 4) + 1; + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reestablishment Request + + Description: Used to request the reestablishment of an RRC + connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_request_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *con_reest_req, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(con_reest_req != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(ext, &msg_ptr, 1); + + if(!ext) + { + // UE Identity + liblte_rrc_pack_c_rnti_ie((uint16)con_reest_req->ue_id.c_rnti, &msg_ptr); + liblte_rrc_pack_phys_cell_id_ie((uint16)con_reest_req->ue_id.phys_cell_id, &msg_ptr); + liblte_rrc_pack_short_mac_i_ie((uint16)con_reest_req->ue_id.short_mac_i, &msg_ptr); + + // Reestablishment Cause + liblte_value_2_bits(con_reest_req->cause, &msg_ptr, 2); + + // Spare + liblte_value_2_bits(0, &msg_ptr, 2); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_request_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *con_reest_req) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_reest_req != NULL) + { + msg_ptr = msg->msg; + + // Extension Choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // UE Identity + liblte_rrc_unpack_c_rnti_ie(&msg_ptr, &con_reest_req->ue_id.c_rnti); + liblte_rrc_unpack_phys_cell_id_ie(&msg_ptr, &con_reest_req->ue_id.phys_cell_id); + liblte_rrc_unpack_short_mac_i_ie(&msg_ptr, &con_reest_req->ue_id.short_mac_i); + + // Reestablishment Cause + con_reest_req->cause = (LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM)liblte_bits_2_value(&msg_ptr, 2); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reestablishment Reject + + Description: Used to indicate the rejection of an RRC connection + reestablishment request + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_reject_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *con_reest_rej, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_reest_rej != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_reject_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *con_reest_rej) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_reest_rej != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reestablishment Complete + + Description: Used to confirm the successful completion of an RRC + connection reestablishment + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_complete_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *con_reest_complete, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_reest_complete != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_reest_complete->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *con_reest_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_reest_complete != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_reest_complete->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reestablishment + + Description: Used to resolve contention and to re-establish SRB1 + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reestablishment_msg(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *con_reest, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_reest != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_reest->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 3); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Radio Resource Config Dedicated + liblte_rrc_pack_rr_config_dedicated_ie(&con_reest->rr_cnfg, &msg_ptr); + + // Next Hop Chaining Count + liblte_rrc_pack_next_hop_chaining_count_ie(con_reest->next_hop_chaining_count, &msg_ptr); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reestablishment_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *con_reest) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_reest != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_reest->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 3); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Radio Resource Config Dedicated + liblte_rrc_unpack_rr_config_dedicated_ie(&msg_ptr, &con_reest->rr_cnfg); + + // Next Hop Chaining Count + liblte_rrc_unpack_next_hop_chaining_count_ie(&msg_ptr, &con_reest->next_hop_chaining_count); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reconfiguration Complete + + Description: Used to confirm the successful completion of an RRC + connection reconfiguration + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reconfiguration_complete_msg(LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *con_reconfig_complete, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(con_reconfig_complete != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_reconfig_complete->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reconfiguration_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *con_reconfig_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + con_reconfig_complete != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &con_reconfig_complete->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RRC Connection Reconfiguration + + Description: Modifies an RRC connection + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rrc_connection_reconfiguration_msg(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *con_reconfig, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(con_reconfig != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(con_reconfig->rrc_transaction_id, &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 3); + + // Optional indicators + liblte_value_2_bits(con_reconfig->meas_cnfg_present, &msg_ptr, 1); + liblte_value_2_bits(con_reconfig->mob_ctrl_info_present, &msg_ptr, 1); + if(0 == con_reconfig->N_ded_info_nas) + { + liblte_value_2_bits(0, &msg_ptr, 1); + }else{ + liblte_value_2_bits(1, &msg_ptr, 1); + } + liblte_value_2_bits(con_reconfig->rr_cnfg_ded_present, &msg_ptr, 1); + liblte_value_2_bits(con_reconfig->sec_cnfg_ho_present, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + + // Meas Config + if(con_reconfig->meas_cnfg_present) + { + liblte_rrc_pack_meas_config_ie(&con_reconfig->meas_cnfg, &msg_ptr); + } + + // Mobility Control Info + if(con_reconfig->mob_ctrl_info_present) + { + liblte_rrc_pack_mobility_control_info_ie(&con_reconfig->mob_ctrl_info, &msg_ptr); + } + + // Dedicated Info NAS List + if(0 != con_reconfig->N_ded_info_nas) + { + liblte_value_2_bits(con_reconfig->N_ded_info_nas - 1, &msg_ptr, 4); + } + for(i=0; iN_ded_info_nas; i++) + { + liblte_rrc_pack_dedicated_info_nas_ie(&con_reconfig->ded_info_nas_list[i], &msg_ptr); + } + + // Radio Resource Config Dedicated + if(con_reconfig->rr_cnfg_ded_present) + { + liblte_rrc_pack_rr_config_dedicated_ie(&con_reconfig->rr_cnfg_ded, &msg_ptr); + } + + // Security Config HO + if(con_reconfig->sec_cnfg_ho_present) + { + // Extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Handover Type + liblte_value_2_bits(con_reconfig->sec_cnfg_ho.ho_type, &msg_ptr, 1); + + if(LIBLTE_RRC_HANDOVER_TYPE_INTRA_LTE == con_reconfig->sec_cnfg_ho.ho_type) + { + // Optional indicator + liblte_value_2_bits(con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg_present, &msg_ptr, 1); + + // Security Algorithm Config + if(con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg_present) + { + liblte_rrc_pack_security_algorithm_config_ie(&con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg, &msg_ptr); + } + + // Key Change Indicator + liblte_value_2_bits(con_reconfig->sec_cnfg_ho.intra_lte.key_change_ind, &msg_ptr, 1); + + // Next Hop Chaining Count + liblte_rrc_pack_next_hop_chaining_count_ie(con_reconfig->sec_cnfg_ho.intra_lte.next_hop_chaining_count, &msg_ptr); + }else{ + // Security Algorithm Config + liblte_rrc_pack_security_algorithm_config_ie(&con_reconfig->sec_cnfg_ho.inter_rat.sec_alg_cnfg, &msg_ptr); + + // NAS Security Params To EUTRA + for(i=0; i<6; i++) + { + liblte_value_2_bits(con_reconfig->sec_cnfg_ho.inter_rat.nas_sec_param_to_eutra[i], &msg_ptr, 8); + } + } + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rrc_connection_reconfiguration_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *con_reconfig) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + bool ded_info_nas_list_present; + + if(msg != NULL && + con_reconfig != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, &con_reconfig->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 3); + + // Optional indicators + con_reconfig->meas_cnfg_present = liblte_bits_2_value(&msg_ptr, 1); + con_reconfig->mob_ctrl_info_present = liblte_bits_2_value(&msg_ptr, 1); + ded_info_nas_list_present = liblte_bits_2_value(&msg_ptr, 1); + con_reconfig->rr_cnfg_ded_present = liblte_bits_2_value(&msg_ptr, 1); + con_reconfig->sec_cnfg_ho_present = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Meas Config + if(con_reconfig->meas_cnfg_present) + { + liblte_rrc_unpack_meas_config_ie(&msg_ptr, &con_reconfig->meas_cnfg); + } + + // Mobility Control Info + if(con_reconfig->mob_ctrl_info_present) + { + liblte_rrc_unpack_mobility_control_info_ie(&msg_ptr, &con_reconfig->mob_ctrl_info); + } + + // Dedicated Info NAS List + if(ded_info_nas_list_present) + { + con_reconfig->N_ded_info_nas = liblte_bits_2_value(&msg_ptr, 4) + 1; + for(i=0; iN_ded_info_nas; i++) + { + liblte_rrc_unpack_dedicated_info_nas_ie(&msg_ptr, &con_reconfig->ded_info_nas_list[i]); + } + }else{ + con_reconfig->N_ded_info_nas = 0; + } + + // Radio Resource Config Dedicated + if(con_reconfig->rr_cnfg_ded_present) + { + liblte_rrc_unpack_rr_config_dedicated_ie(&msg_ptr, &con_reconfig->rr_cnfg_ded); + } + + // Security Config HO + if(con_reconfig->sec_cnfg_ho_present) + { + // Extension indicator + bool ext2 = liblte_bits_2_value(&msg_ptr, 1); + + // Handover Type + con_reconfig->sec_cnfg_ho.ho_type = (LIBLTE_RRC_HANDOVER_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + if(LIBLTE_RRC_HANDOVER_TYPE_INTRA_LTE == con_reconfig->sec_cnfg_ho.ho_type) + { + // Optional indicator + con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg_present = liblte_bits_2_value(&msg_ptr, 1); + + // Security Algorithm Config + if(con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg_present) + { + liblte_rrc_unpack_security_algorithm_config_ie(&msg_ptr, &con_reconfig->sec_cnfg_ho.intra_lte.sec_alg_cnfg); + } + + // Key Change Indicator + con_reconfig->sec_cnfg_ho.intra_lte.key_change_ind = liblte_bits_2_value(&msg_ptr, 1); + + // Next Hop Chaining Count + liblte_rrc_unpack_next_hop_chaining_count_ie(&msg_ptr, &con_reconfig->sec_cnfg_ho.intra_lte.next_hop_chaining_count); + }else{ + // Security Algorithm Config + liblte_rrc_unpack_security_algorithm_config_ie(&msg_ptr, &con_reconfig->sec_cnfg_ho.inter_rat.sec_alg_cnfg); + + // NAS Security Params To EUTRA + for(i=0; i<6; i++) + { + con_reconfig->sec_cnfg_ho.inter_rat.nas_sec_param_to_eutra[i] = liblte_bits_2_value(&msg_ptr, 8); + } + } + + liblte_rrc_consume_noncrit_extension(ext2, __func__, &msg_ptr); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RN Reconfiguration Complete + + Description: Used to confirm the successful completion of an RN + reconfiguration + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_rn_reconfiguration_complete_msg(LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *rn_reconfig_complete, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(rn_reconfig_complete != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(rn_reconfig_complete->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicators + liblte_value_2_bits(0, &msg_ptr, 1); + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_rn_reconfiguration_complete_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *rn_reconfig_complete) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + rn_reconfig_complete != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &rn_reconfig_complete->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicators + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: RN Reconfiguration + + Description: Modifies the RRC connection between the RN and the + E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Proximity Indication + + Description: Used to indicate that the UE is entering or leaving + the proximity of one or more cells whose CSG IDs are + in the UEs CSG whitelist + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_proximity_indication_msg(LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *proximity_ind, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(proximity_ind != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Proximity indication type + liblte_value_2_bits(proximity_ind->type, &msg_ptr, 1); + + // Carrier frequency type extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Carrier frequency choice + liblte_value_2_bits(proximity_ind->carrier_freq_type, &msg_ptr, 1); + + // Carrier frequency + if(LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_EUTRA == proximity_ind->carrier_freq_type) + { + liblte_rrc_pack_arfcn_value_eutra_ie(proximity_ind->carrier_freq, + &msg_ptr); + }else{ + liblte_rrc_pack_arfcn_value_utra_ie(proximity_ind->carrier_freq, + &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_proximity_indication_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *proximity_ind) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + proximity_ind != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Proximity indication type + proximity_ind->type = (LIBLTE_RRC_PROXIMITY_INDICATION_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + // Carrier frequency type extension indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Carrier frequency type + proximity_ind->carrier_freq_type = (LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + // Carrier frequency + if(LIBLTE_RRC_PROXIMITY_INDICATION_CARRIER_FREQ_TYPE_EUTRA == proximity_ind->carrier_freq) + { + liblte_rrc_unpack_arfcn_value_eutra_ie(&msg_ptr, + &proximity_ind->carrier_freq); + }else{ + liblte_rrc_unpack_arfcn_value_utra_ie(&msg_ptr, + &proximity_ind->carrier_freq); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Paging + + Description: Used for the notification of one or more UEs + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_paging_msg(LIBLTE_RRC_PAGING_STRUCT *page, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + uint32 j; + + if(page != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Optional indicators + if(page->paging_record_list_size != 0) + { + liblte_value_2_bits(1, &msg_ptr, 1); + }else{ + liblte_value_2_bits(0, &msg_ptr, 1); + } + liblte_value_2_bits(page->system_info_modification_present, &msg_ptr, 1); + liblte_value_2_bits(page->etws_indication_present, &msg_ptr, 1); + liblte_value_2_bits(page->non_crit_ext_present, &msg_ptr, 1); + + if(page->paging_record_list_size != 0) + { + liblte_value_2_bits(page->paging_record_list_size - 1, &msg_ptr, 4); + for(i=0; ipaging_record_list_size; i++) + { + // Extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Paging UE identity + { + // Extension indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + liblte_value_2_bits(page->paging_record_list[i].ue_identity.ue_identity_type, &msg_ptr, 1); + if(LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_S_TMSI == page->paging_record_list[i].ue_identity.ue_identity_type) + { + liblte_rrc_pack_s_tmsi_ie(&page->paging_record_list[i].ue_identity.s_tmsi, + &msg_ptr); + }else{ + liblte_value_2_bits(page->paging_record_list[i].ue_identity.imsi_size - 6, &msg_ptr, 4); + for(j=0; jpaging_record_list[i].ue_identity.imsi_size; j++) + { + liblte_value_2_bits(page->paging_record_list[i].ue_identity.imsi[j], &msg_ptr, 4); + } + } + } + + liblte_value_2_bits(page->paging_record_list[i].cn_domain, &msg_ptr, 1); + } + } + + if(page->system_info_modification_present) + { + liblte_value_2_bits(page->system_info_modification, &msg_ptr, 1); + } + + if(page->etws_indication_present) + { + liblte_value_2_bits(page->etws_indication, &msg_ptr, 1); + } + + if(page->non_crit_ext_present) + { + // Optional indicators + liblte_value_2_bits(page->non_crit_ext.late_non_crit_ext_present, &msg_ptr, 1); + liblte_value_2_bits(page->non_crit_ext.non_crit_ext_present, &msg_ptr, 1); + + if(page->non_crit_ext.late_non_crit_ext_present) + { + // FIXME + } + + if(page->non_crit_ext.non_crit_ext_present) + { + // Optional indicators + liblte_value_2_bits(page->non_crit_ext.non_crit_ext.cmas_ind_present, &msg_ptr, 1); + liblte_value_2_bits(page->non_crit_ext.non_crit_ext.non_crit_ext_present, &msg_ptr, 1); + + if(page->non_crit_ext.non_crit_ext.cmas_ind_present) + { + liblte_value_2_bits(page->non_crit_ext.non_crit_ext.cmas_ind_r9, &msg_ptr, 1); + } + + if(page->non_crit_ext.non_crit_ext.non_crit_ext_present) + { + // FIXME + } + } + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_paging_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PAGING_STRUCT *page) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + uint32 j; + uint8 paging_record_list_present; + + if(msg != NULL && + page != NULL) + { + msg_ptr = msg->msg; + + // Optional indicators + paging_record_list_present = liblte_bits_2_value(&msg_ptr, 1); + page->system_info_modification_present = liblte_bits_2_value(&msg_ptr, 1); + page->etws_indication_present = liblte_bits_2_value(&msg_ptr, 1); + page->non_crit_ext_present = liblte_bits_2_value(&msg_ptr, 1); + + if(paging_record_list_present) + { + page->paging_record_list_size = liblte_bits_2_value(&msg_ptr, 4) + 1; + for(i=0; ipaging_record_list_size; i++) + { + // Extension indicator + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Paging UE identity + { + // Extension indicator + bool ext2 = liblte_bits_2_value(&msg_ptr, 1); + + page->paging_record_list[i].ue_identity.ue_identity_type = (LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + if(LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_S_TMSI == page->paging_record_list[i].ue_identity.ue_identity_type) + { + liblte_rrc_unpack_s_tmsi_ie(&msg_ptr, + &page->paging_record_list[i].ue_identity.s_tmsi); + }else{ + page->paging_record_list[i].ue_identity.imsi_size = liblte_bits_2_value(&msg_ptr, 4) + 6; + for(j=0; jpaging_record_list[i].ue_identity.imsi_size; j++) + { + page->paging_record_list[i].ue_identity.imsi[j] = liblte_bits_2_value(&msg_ptr, 4); + } + } + + liblte_rrc_consume_noncrit_extension(ext2, __func__, &msg_ptr); + } + + page->paging_record_list[i].cn_domain = (LIBLTE_RRC_CN_DOMAIN_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + } + } + + if(page->system_info_modification_present) + { + page->system_info_modification = (LIBLTE_RRC_SYSTEM_INFO_MODIFICATION_ENUM)liblte_bits_2_value(&msg_ptr, 1); + } + + if(page->etws_indication_present) + { + page->etws_indication = (LIBLTE_RRC_ETWS_INDICATION_ENUM)liblte_bits_2_value(&msg_ptr, 1); + } + + if(page->non_crit_ext_present) + { + // Optional indicators + page->non_crit_ext.late_non_crit_ext_present = liblte_bits_2_value(&msg_ptr, 1); + page->non_crit_ext.non_crit_ext_present = liblte_bits_2_value(&msg_ptr, 1); + + if(page->non_crit_ext.late_non_crit_ext_present) + { + // FIXME + printf("Warning late non-crit-extension not handled in paging message\n"); + } + + if(page->non_crit_ext.non_crit_ext_present) + { + // Optional indicators + page->non_crit_ext.non_crit_ext.cmas_ind_present = liblte_bits_2_value(&msg_ptr, 1); + page->non_crit_ext.non_crit_ext.non_crit_ext_present = liblte_bits_2_value(&msg_ptr, 1); + + if(page->non_crit_ext.non_crit_ext.cmas_ind_present) + { + page->non_crit_ext.non_crit_ext.cmas_ind_r9 = (LIBLTE_RRC_CMAS_INDICATION_R9_ENUM)liblte_bits_2_value(&msg_ptr, 1); + } + + if(page->non_crit_ext.non_crit_ext.non_crit_ext_present) + { + // FIXME + printf("Warning non-crit-extension not handled in paging message\n"); + } + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Mobility From EUTRA Command + + Description: Used to command handover or a cell change from E-UTRA + to another RAT, or enhanced CS fallback to CDMA2000 + 1xRTT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Measurement Report + + Description: Used for the indication of measurement results + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_cgi_info_ie(LIBLTE_RRC_CGI_INFO_STRUCT *cgi_info, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(cgi_info != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(cgi_info->have_plmn_identity_list, ie_ptr, 1); + liblte_rrc_pack_cell_global_id_eutra_ie(&cgi_info->cell_global_id, ie_ptr); + liblte_rrc_pack_tracking_area_code_ie(cgi_info->tracking_area_code, ie_ptr); + if(cgi_info->have_plmn_identity_list) { + liblte_value_2_bits(cgi_info->n_plmn_identity_list-1, ie_ptr, 3); + for(uint32 i=0; in_plmn_identity_list; i++) { + liblte_rrc_pack_plmn_identity_ie(&cgi_info->plmn_identity_list[i], ie_ptr); + } + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_cgi_info_ie(uint8 **ie_ptr, + LIBLTE_RRC_CGI_INFO_STRUCT *cgi_info) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + cgi_info != NULL) + { + cgi_info->have_plmn_identity_list = (bool)liblte_bits_2_value(ie_ptr, 1); + liblte_rrc_unpack_cell_global_id_eutra_ie(ie_ptr, &cgi_info->cell_global_id); + liblte_rrc_unpack_tracking_area_code_ie(ie_ptr, &cgi_info->tracking_area_code); + if(cgi_info->have_plmn_identity_list) { + cgi_info->n_plmn_identity_list = liblte_bits_2_value(ie_ptr, 3) + 1; + for(uint32 i=0; in_plmn_identity_list; i++) { + liblte_rrc_unpack_plmn_identity_ie(ie_ptr, &cgi_info->plmn_identity_list[i]); + } + } + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_result_ie(LIBLTE_RRC_MEAS_RESULT_STRUCT *meas_result, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(meas_result != NULL && + ie_ptr != NULL) + { + //ext + liblte_value_2_bits(0, ie_ptr, 1); + + //options + liblte_value_2_bits(meas_result->have_rsrp, ie_ptr, 1); + liblte_value_2_bits(meas_result->have_rsrq, ie_ptr, 1); + + if(meas_result->have_rsrp) { + liblte_rrc_pack_rsrp_range_ie(meas_result->rsrp_result, ie_ptr); + } + if(meas_result->have_rsrq) { + liblte_rrc_pack_rsrq_range_ie(meas_result->rsrq_result, ie_ptr); + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_result_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_RESULT_STRUCT *meas_result) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_result != NULL) + { + //ext + bool ext = liblte_bits_2_value(ie_ptr, 1); + + //options + meas_result->have_rsrp = liblte_bits_2_value(ie_ptr, 1); + meas_result->have_rsrq = liblte_bits_2_value(ie_ptr, 1); + + if(meas_result->have_rsrp) { + liblte_rrc_unpack_rsrp_range_ie(ie_ptr, &meas_result->rsrp_result); + } + if(meas_result->have_rsrq) { + liblte_rrc_unpack_rsrq_range_ie(ie_ptr, &meas_result->rsrq_result); + } + + //skip extensions + liblte_rrc_consume_noncrit_extension(ext, __func__, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_meas_result_eutra_ie(LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *meas_result_eutra, + uint8 **ie_ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(meas_result_eutra != NULL && + ie_ptr != NULL) + { + liblte_value_2_bits(meas_result_eutra->have_cgi_info, ie_ptr, 1); + liblte_rrc_pack_phys_cell_id_ie(meas_result_eutra->phys_cell_id, ie_ptr); + if(meas_result_eutra->have_cgi_info) { + liblte_rrc_pack_cgi_info_ie(&meas_result_eutra->cgi_info, ie_ptr); + } + liblte_rrc_pack_meas_result_ie(&meas_result_eutra->meas_result, ie_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_meas_result_eutra_ie(uint8 **ie_ptr, + LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *meas_result_eutra) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie_ptr != NULL && + meas_result_eutra != NULL) + { + meas_result_eutra->have_cgi_info = liblte_bits_2_value(ie_ptr, 1); + liblte_rrc_unpack_phys_cell_id_ie(ie_ptr, &meas_result_eutra->phys_cell_id); + if(meas_result_eutra->have_cgi_info) { + liblte_rrc_unpack_cgi_info_ie(ie_ptr, &meas_result_eutra->cgi_info); + } + liblte_rrc_unpack_meas_result_ie(ie_ptr, &meas_result_eutra->meas_result); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +LIBLTE_ERROR_ENUM liblte_rrc_pack_measurement_report_msg(LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *meas_report, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(meas_report != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + //MeasurementReport + liblte_value_2_bits(0, &msg_ptr, 1); //critical extensions + liblte_value_2_bits(0, &msg_ptr, 3); //c1 + + //MeasurementReport-r8-IEs + liblte_value_2_bits(0, &msg_ptr, 1); //non-critical extensions + + //MeasResults + liblte_value_2_bits(0, &msg_ptr, 1); //ext + liblte_value_2_bits(meas_report->have_meas_result_neigh_cells, &msg_ptr, 1); + liblte_rrc_pack_meas_id_ie(meas_report->meas_id, &msg_ptr); + liblte_rrc_pack_rsrp_range_ie(meas_report->pcell_rsrp_result, &msg_ptr); + liblte_rrc_pack_rsrq_range_ie(meas_report->pcell_rsrq_result, &msg_ptr); + if(meas_report->have_meas_result_neigh_cells) { + liblte_value_2_bits(0, &msg_ptr, 1); //choice from before extension marker + liblte_value_2_bits(meas_report->meas_result_neigh_cells_choice, &msg_ptr, 2); + if(meas_report->meas_result_neigh_cells_choice != LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA) { + printf("NOT HANDLING %s\n", liblte_rrc_meas_reult_neigh_cells_text[meas_report->meas_result_neigh_cells_choice]); + } else { + //MeasResultListEUTRA + liblte_value_2_bits(meas_report->meas_result_neigh_cells.eutra.n_result-1, &msg_ptr, 3); + for(uint32 i=0; imeas_result_neigh_cells.eutra.n_result; i++) { + liblte_rrc_pack_meas_result_eutra_ie(&meas_report->meas_result_neigh_cells.eutra.result_eutra_list[i], &msg_ptr); + } + } + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_measurement_report_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *meas_report) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + meas_report != NULL) + { + msg_ptr = msg->msg; + + //MeasurementReport + bool crit_ext = liblte_bits_2_value(&msg_ptr, 1); //critical extensions + liblte_bits_2_value(&msg_ptr, 3); //c1 + + //MeasurementReport-r8-IEs + bool non_crit_ext = liblte_bits_2_value(&msg_ptr, 1); //non-critical extensions + + //MeasResults + bool ext = liblte_bits_2_value(&msg_ptr, 1); + meas_report->have_meas_result_neigh_cells = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_unpack_meas_id_ie(&msg_ptr, &meas_report->meas_id); + liblte_rrc_unpack_rsrp_range_ie(&msg_ptr, &meas_report->pcell_rsrp_result); + liblte_rrc_unpack_rsrq_range_ie(&msg_ptr, &meas_report->pcell_rsrq_result); + if(meas_report->have_meas_result_neigh_cells) { + liblte_bits_2_value(&msg_ptr, 1); //choice from before extension marker + meas_report->meas_result_neigh_cells_choice = (LIBLTE_RRC_MEAS_RESULT_NEIGH_CELLS_ENUM) liblte_bits_2_value(&msg_ptr, 2); + if(meas_report->meas_result_neigh_cells_choice != LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA) { + printf("NOT HANDLING %s\n", liblte_rrc_meas_reult_neigh_cells_text[meas_report->meas_result_neigh_cells_choice]); + } else { + //MeasResultListEUTRA + meas_report->meas_result_neigh_cells.eutra.n_result = liblte_bits_2_value(&msg_ptr, 3) + 1; + for(uint32 i=0; imeas_result_neigh_cells.eutra.n_result; i++) { + liblte_rrc_unpack_meas_result_eutra_ie(&msg_ptr, &meas_report->meas_result_neigh_cells.eutra.result_eutra_list[i]); + } + } + } + + //skip extensions + liblte_rrc_consume_noncrit_extension(crit_ext, __func__, &msg_ptr); + liblte_rrc_consume_noncrit_extension(non_crit_ext, __func__, &msg_ptr); + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: MBSFN Area Configuration + + Description: Contains the MBMS control information applicable for + an MBSFN area + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mbsfn_area_configuration_r9_msg(LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + + if(mbsfn_area_cnfg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Non-critical extension + liblte_value_2_bits(0, &msg_ptr, 1); + + // commonsf_allocpatternlist_r9 + liblte_value_2_bits(mbsfn_area_cnfg->commonsf_allocpatternlist_r9_size-1, &msg_ptr, 3); + for(i=0; icommonsf_allocpatternlist_r9_size; i++){ + liblte_rrc_pack_mbsfn_subframe_config_ie(&mbsfn_area_cnfg->commonsf_allocpatternlist_r9[i], &msg_ptr); + } + + // commonsf_allocperiod_r9 + liblte_value_2_bits(mbsfn_area_cnfg->commonsf_allocperiod_r9, &msg_ptr, 3); + + // pmch_infolist_r9 + liblte_value_2_bits(mbsfn_area_cnfg->pmch_infolist_r9_size, &msg_ptr, 4); + for(i=0; ipmch_infolist_r9_size; i++){ + liblte_rrc_pack_pmch_info_r9_ie(&mbsfn_area_cnfg->pmch_infolist_r9[i], &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MBSFN_AREA_CONFIGURATION_R9_STRUCT *mbsfn_area_cnfg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 i; + bool ext; + + if(msg != NULL && + mbsfn_area_cnfg != NULL) + { + msg_ptr = msg->msg; + + // Non-critical extension + ext = liblte_bits_2_value(&msg_ptr, 1); + liblte_rrc_warning_not_handled(ext, __func__); + + // commonsf_allocpatternlist_r9 + mbsfn_area_cnfg->commonsf_allocpatternlist_r9_size = liblte_bits_2_value(&msg_ptr, 3) + 1; + for(i=0; icommonsf_allocpatternlist_r9_size; i++){ + liblte_rrc_unpack_mbsfn_subframe_config_ie(&msg_ptr, &mbsfn_area_cnfg->commonsf_allocpatternlist_r9[i]); + } + + // commonsf_allocperiod_r9 + mbsfn_area_cnfg->commonsf_allocperiod_r9 = (LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + // pmch_infolist_r9 + mbsfn_area_cnfg->pmch_infolist_r9_size = liblte_bits_2_value(&msg_ptr, 4); + for(i=0; ipmch_infolist_r9_size; i++){ + liblte_rrc_unpack_pmch_info_r9_ie(&msg_ptr, &mbsfn_area_cnfg->pmch_infolist_r9[i]); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + +/********************************************************************* + Message Name: Master Information Block + + Description: Includes the system information transmitted on BCH + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// Inlined with BCCH BCH Message + +/********************************************************************* + Message Name: Logged Measurements Configuration + + Description: Used by E-UTRAN to configure the UE to perform + logging of measurement results while in RRC_IDLE + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Handover From EUTRA Preparation Request (CDMA2000) + + Description: Used to trigger the handover preparation procedure + with a CDMA2000 RAT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: DL Information Transfer + + Description: Used for the downlink transfer of dedicated NAS + information + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_information_transfer_msg(LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *dl_info_transfer, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(dl_info_transfer != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_pack_rrc_transaction_identifier_ie(dl_info_transfer->rrc_transaction_id, + &msg_ptr); + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // C1 choice + liblte_value_2_bits(0, &msg_ptr, 2); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Dedicated info type choice + liblte_value_2_bits(dl_info_transfer->dedicated_info_type, &msg_ptr, 2); + + if(LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_NAS == dl_info_transfer->dedicated_info_type) + { + liblte_rrc_pack_dedicated_info_nas_ie(&dl_info_transfer->dedicated_info, + &msg_ptr); + }else{ + liblte_rrc_pack_dedicated_info_cdma2000_ie(&dl_info_transfer->dedicated_info, + &msg_ptr); + } + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_information_transfer_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *dl_info_transfer) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + dl_info_transfer != NULL) + { + msg_ptr = msg->msg; + + // RRC Transaction ID + liblte_rrc_unpack_rrc_transaction_identifier_ie(&msg_ptr, + &dl_info_transfer->rrc_transaction_id); + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // C1 choice + liblte_bits_2_value(&msg_ptr, 2); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + // Dedicated info type choice + dl_info_transfer->dedicated_info_type = (LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 2); + + if(LIBLTE_RRC_DL_INFORMATION_TRANSFER_TYPE_NAS == dl_info_transfer->dedicated_info_type) + { + liblte_rrc_unpack_dedicated_info_nas_ie(&msg_ptr, + &dl_info_transfer->dedicated_info); + }else{ + liblte_rrc_unpack_dedicated_info_cdma2000_ie(&msg_ptr, + &dl_info_transfer->dedicated_info); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: CSFB Parameters Response CDMA2000 + + Description: Used to provide the CDMA2000 1xRTT parameters to the + UE so the UE can register with the CDMA2000 1xRTT + network to support CSFB to CDMA2000 1xRTT + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: CSFB Parameters Request CDMA2000 + + Description: Used by the UE to obtain the CDMA2000 1xRTT + parameters from the network + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_csfb_parameters_request_cdma2000_msg(LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *csfb_params_req_cdma2000, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(csfb_params_req_cdma2000 != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + liblte_value_2_bits(0, &msg_ptr, 1); + + // Optional indicator + liblte_value_2_bits(0, &msg_ptr, 1); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_csfb_parameters_request_cdma2000_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *csfb_params_req_cdma2000) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + csfb_params_req_cdma2000 != NULL) + { + msg_ptr = msg->msg; + + // Extension choice + bool ext = liblte_bits_2_value(&msg_ptr, 1); + + // Optional indicator + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: Counter Check Response + + Description: Used by the UE to respond to a Counter Check message + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: Counter Check + + Description: Used by the E-UTRAN to indicate the current COUNT MSB + values associated to each DRB and to request the UE + to compare these to its COUNT MSB values and to + report the comparison results to E-UTRAN + + Document Reference: 36.331 v10.0.0 Section 6.2.2 +*********************************************************************/ +// FIXME + +/********************************************************************* + Message Name: BCCH BCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE via BCH on the BCCH + logical channel + + Document Reference: 36.331 v10.0.0 Sections 6.2.1 and 6.2.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_bcch_bch_msg(LIBLTE_RRC_MIB_STRUCT *mib, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(mib != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // DL Bandwidth + liblte_value_2_bits(mib->dl_bw, &msg_ptr, 3); + + // PHICH Config + liblte_rrc_pack_phich_config_ie(&mib->phich_config, &msg_ptr); + + // SFN/4 + liblte_value_2_bits(mib->sfn_div_4, &msg_ptr, 8); + + // Spare + liblte_value_2_bits(0, &msg_ptr, 10); + + // Fill in the number of bits used + msg->N_bits = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_bch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MIB_STRUCT *mib) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + + if(msg != NULL && + mib != NULL) + { + msg_ptr = msg->msg; + + // DL Bandwidth + mib->dl_bw = (LIBLTE_RRC_DL_BANDWIDTH_ENUM)liblte_bits_2_value(&msg_ptr, 3); + + // PHICH Config + liblte_rrc_unpack_phich_config_ie(&msg_ptr, &mib->phich_config); + + // SFN/4 + mib->sfn_div_4 = liblte_bits_2_value(&msg_ptr, 8); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Message Name: BCCH DLSCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE via DLSCH on the BCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_bcch_dlsch_msg(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *bcch_dlsch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(bcch_dlsch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + liblte_value_2_bits(ext, &msg_ptr, 1); + + if(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 == bcch_dlsch_msg->sibs[0].sib_type) + { + // SIB1 Choice + liblte_value_2_bits(1, &msg_ptr, 1); + + err = liblte_rrc_pack_sys_info_block_type_1_msg((LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *)&bcch_dlsch_msg->sibs[0].sib, + &global_msg); + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 2)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 2; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + }else{ + // SIB1 Choice + liblte_value_2_bits(0, &msg_ptr, 1); + + err = liblte_rrc_pack_sys_info_msg(bcch_dlsch_msg, + &global_msg); + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 2)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 2; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_bcch_dlsch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *bcch_dlsch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 N_bits_used; + uint8 ext; + + if(msg != NULL && + bcch_dlsch_msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + ext = liblte_bits_2_value(&msg_ptr, 1); + + // SIB1 Choice + if(true == liblte_bits_2_value(&msg_ptr, 1)) + { + bcch_dlsch_msg->N_sibs = 1; + bcch_dlsch_msg->sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1; + if((msg->N_bits-(msg_ptr-msg->msg)) <= (LIBLTE_MAX_MSG_SIZE_BITS - 2)) + { + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + global_msg.N_bits = msg->N_bits-(msg_ptr-msg->msg); + err = liblte_rrc_unpack_sys_info_block_type_1_msg(&global_msg, + (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *)&bcch_dlsch_msg->sibs[0].sib, + &N_bits_used); + msg_ptr += N_bits_used; + } + }else{ + if((msg->N_bits-(msg_ptr-msg->msg)) <= (LIBLTE_MAX_MSG_SIZE_BITS - 2)) + { + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + err = liblte_rrc_unpack_sys_info_msg(&global_msg, + bcch_dlsch_msg); + } + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + } + + return(err); +} + +/********************************************************************* + Message Name: MCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the MCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_mcch_msg(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(mcch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // MCCH choice + liblte_value_2_bits(0, &msg_ptr, 1); + + err = liblte_rrc_pack_mbsfn_area_configuration_r9_msg(mcch_msg, + &global_msg); + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 1; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_mcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 N_bits_used; + + if(msg != NULL && + mcch_msg != NULL) + { + msg_ptr = msg->msg; + + // MCCH choice + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + if((msg->N_bits-(msg_ptr-msg->msg)) <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + err = liblte_rrc_unpack_mbsfn_area_configuration_r9_msg(&global_msg, + mcch_msg); + } + } + + return(err); +} + +/********************************************************************* + Message Name: PCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the PCCH logical + channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_pcch_msg(LIBLTE_RRC_PCCH_MSG_STRUCT *pcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(pcch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Paging choice + liblte_value_2_bits(0, &msg_ptr, 1); + + err = liblte_rrc_pack_paging_msg(pcch_msg, + &global_msg); + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 1; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_pcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_PCCH_MSG_STRUCT *pcch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint32 N_bits_used; + + if(msg != NULL && + pcch_msg != NULL) + { + msg_ptr = msg->msg; + + // Paging choice + liblte_rrc_warning_not_handled(liblte_bits_2_value(&msg_ptr, 1), __func__); + + if((msg->N_bits-(msg_ptr-msg->msg)) <= (LIBLTE_MAX_MSG_SIZE_BITS - 1)) + { + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + err = liblte_rrc_unpack_paging_msg(&global_msg, + pcch_msg); + } + } + + return(err); +} + +/********************************************************************* + Message Name: DL CCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the downlink CCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_ccch_msg(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(dl_ccch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + liblte_value_2_bits(ext, &msg_ptr, 1); + + // Message type choice + liblte_value_2_bits(dl_ccch_msg->msg_type, &msg_ptr, 2); + + if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST == dl_ccch_msg->msg_type) + { + err = liblte_rrc_pack_rrc_connection_reestablishment_msg((LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *)&dl_ccch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ == dl_ccch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_reestablishment_reject_msg((LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *)&dl_ccch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ == dl_ccch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_reject_msg((LIBLTE_RRC_CONNECTION_REJECT_STRUCT *)&dl_ccch_msg->msg, + &global_msg); + }else{ // LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP == dl_ccch_msg->msg_type + err = liblte_rrc_pack_rrc_connection_setup_msg((LIBLTE_RRC_CONNECTION_SETUP_STRUCT *)&dl_ccch_msg->msg, + &global_msg); + } + + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 3)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 3; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_ccch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext; + + if(msg != NULL && + dl_ccch_msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + ext = liblte_bits_2_value(&msg_ptr, 1); + + // Message type choice + dl_ccch_msg->msg_type = (LIBLTE_RRC_DL_CCCH_MSG_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 2); + + // Message + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + global_msg.N_bits = msg->N_bits-(msg_ptr-msg->msg); + if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST == dl_ccch_msg->msg_type) + { + err = liblte_rrc_unpack_rrc_connection_reestablishment_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *)&dl_ccch_msg->msg); + }else if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ == dl_ccch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_reestablishment_reject_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REJECT_STRUCT *)&dl_ccch_msg->msg); + }else if(LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ == dl_ccch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_reject_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REJECT_STRUCT *)&dl_ccch_msg->msg); + }else{ // LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP == dl_ccch_msg->msg_type + err = liblte_rrc_unpack_rrc_connection_setup_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_SETUP_STRUCT *)&dl_ccch_msg->msg); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + } + + return(err); +} + +/********************************************************************* + Message Name: DL DCCH Message + + Description: Contains the set of RRC messages that may be sent + from the E-UTRAN to the UE on the downlink DCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_dl_dcch_msg(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(dl_dcch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + liblte_value_2_bits(ext, &msg_ptr, 1); + + // Message type choice + liblte_value_2_bits(dl_dcch_msg->msg_type, &msg_ptr, 4); + + // Message + if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_CSFB_PARAMS_RESP_CDMA2000 == dl_dcch_msg->msg_type) + { + printf("NOT HANDLING CSFB PARAMETERS RESPONSE CDMA2000\n"); +// err = liblte_rrc_pack_csfb_parameters_response_cdma2000_msg((LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_dl_information_transfer_msg((LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_HANDOVER_FROM_EUTRA_PREP_REQ == dl_dcch_msg->msg_type){ + printf("NOT HANDLING HANDOVER FROM EUTRA PREPARATION REQUEST\n"); +// err = liblte_rrc_pack_handover_from_eutra_preparation_request_msg((LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_MOBILITY_FROM_EUTRA_COMMAND == dl_dcch_msg->msg_type){ + printf("NOT HANDLING MOBILITY FROM EUTRA COMMAND\n"); +// err = liblte_rrc_pack_mobility_from_eutra_command_msg((LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_reconfiguration_msg((LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_release_msg((LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_security_mode_command_msg((LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_ue_capability_enquiry_msg((LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_COUNTER_CHECK == dl_dcch_msg->msg_type){ + printf("NOT HANDLING COUNTER CHECK\n"); +// err = liblte_rrc_pack_counter_check_msg((LIBLTE_RRC_COUNTER_CHECK_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_INFO_REQ == dl_dcch_msg->msg_type){ + err = liblte_rrc_pack_ue_information_request_msg((LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *)&dl_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_LOGGED_MEASUREMENTS_CONFIG == dl_dcch_msg->msg_type){ + printf("NOT HANDLING LOGGED MEASUREMENTS CONFIGURATION\n"); +// err = liblte_rrc_pack_logged_measurements_configuration_msg((LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + }else{ // LIBLTE_RRC_DL_DCCH_MSG_TYPE_RN_RECONFIG == dl_dcch_msg->msg_type + printf("NOT HANDLING RN RECONFIGURATION\n"); +// err = liblte_rrc_pack_rn_reconfiguration_msg((LIBLTE_RRC_RN_RECONFIGURATION_STRUCT *)&dl_dcch_msg->msg, +// &global_msg); + } + + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 5)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 5; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_dl_dcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext; + + if(msg != NULL && + dl_dcch_msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + ext = liblte_bits_2_value(&msg_ptr, 1); + + // Message type choice + dl_dcch_msg->msg_type = (LIBLTE_RRC_DL_DCCH_MSG_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 4); + + // Message + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + global_msg.N_bits = msg->N_bits-(msg_ptr-msg->msg); + if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_CSFB_PARAMS_RESP_CDMA2000 == dl_dcch_msg->msg_type) + { + printf("NOT HANDLING CSFB PARAMETERS RESPONSE CDMA2000\n"); +// err = liblte_rrc_unpack_csfb_parameters_response_cdma2000_msg(&global_msg, +// (LIBLTE_RRC_CSFB_PARAMETERS_RESPONSE_CDMA2000_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_dl_information_transfer_msg(&global_msg, + (LIBLTE_RRC_DL_INFORMATION_TRANSFER_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_HANDOVER_FROM_EUTRA_PREP_REQ == dl_dcch_msg->msg_type){ + printf("NOT HANDLING HANDOVER FROM EUTRA PREPARATION REQUEST\n"); +// err = liblte_rrc_unpack_handover_from_eutra_preparation_request_msg(&global_msg, +// (LIBLTE_RRC_HANDOVER_FROM_EUTRA_PREPARATION_REQUEST_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_MOBILITY_FROM_EUTRA_COMMAND == dl_dcch_msg->msg_type){ + printf("NOT HANDLING MOBILITY FROM EUTRA COMMAND\n"); +// err = liblte_rrc_unpack_mobility_from_eutra_command_msg(&global_msg, +// (LIBLTE_RRC_MOBILITY_FROM_EUTRA_COMMAND_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_reconfiguration_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_release_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_RELEASE_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_security_mode_command_msg(&global_msg, + (LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_ue_capability_enquiry_msg(&global_msg, + (LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_COUNTER_CHECK == dl_dcch_msg->msg_type){ + printf("NOT HANDLING COUNTER CHECK\n"); +// err = liblte_rrc_unpack_counter_check_msg(&global_msg, +// (LIBLTE_RRC_COUNTER_CHECK_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_INFO_REQ == dl_dcch_msg->msg_type){ + err = liblte_rrc_unpack_ue_information_request_msg(&global_msg, + (LIBLTE_RRC_UE_INFORMATION_REQUEST_STRUCT *)&dl_dcch_msg->msg); + }else if(LIBLTE_RRC_DL_DCCH_MSG_TYPE_LOGGED_MEASUREMENTS_CONFIG == dl_dcch_msg->msg_type){ + printf("NOT HANDLING LOGGED MEASUREMENTS CONFIGURATION\n"); +// err = liblte_rrc_unpack_logged_measurements_configuration_msg(&global_msg, +// (LIBLTE_RRC_LOGGED_MEASUREMENTS_CONFIGURATION_STRUCT *)&dl_dcch_msg->msg); + }else{ // LIBLTE_RRC_DL_DCCH_MSG_TYPE_RN_RECONFIG == dl_dcch_msg->msg_type + printf("NOT HANDLING RN RECONFIGURATION\n"); +// err = liblte_rrc_unpack_rn_reconfiguration_msg(&global_msg, +// (LIBLTE_RRC_RN_RECONFIGURATION_STRUCT *)&dl_dcch_msg->msg); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + } + + return(err); +} + +/********************************************************************* + Message Name: UL CCCH Message + + Description: Contains the set of RRC messages that may be sent + from the UE to the E-UTRAN on the uplink CCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_ccch_msg(LIBLTE_RRC_UL_CCCH_MSG_STRUCT *ul_ccch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(ul_ccch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + liblte_value_2_bits(ext, &msg_ptr, 1); + + // Message type choice + liblte_value_2_bits(ul_ccch_msg->msg_type, &msg_ptr, 1); + + if(LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ == ul_ccch_msg->msg_type) + { + err = liblte_rrc_pack_rrc_connection_reestablishment_request_msg((LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *)&ul_ccch_msg->msg, + &global_msg); + }else{ // LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ == ul_ccch_msg->msg_type + err = liblte_rrc_pack_rrc_connection_request_msg((LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *)&ul_ccch_msg->msg, + &global_msg); + } + + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 2)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 2; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_ccch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_CCCH_MSG_STRUCT *ul_ccch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext; + + if(msg != NULL && + ul_ccch_msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + ext = liblte_bits_2_value(&msg_ptr, 1); + + // Message type choice + ul_ccch_msg->msg_type = (LIBLTE_RRC_UL_CCCH_MSG_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 1); + + // Message + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + global_msg.N_bits = msg->N_bits-(msg_ptr-msg->msg); + if(LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ == ul_ccch_msg->msg_type) + { + err = liblte_rrc_unpack_rrc_connection_reestablishment_request_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *)&ul_ccch_msg->msg); + }else{ // LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ == ul_ccch_msg->msg_type + err = liblte_rrc_unpack_rrc_connection_request_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *)&ul_ccch_msg->msg); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + } + + return(err); +} + +/********************************************************************* + Message Name: UL DCCH Message + + Description: Contains the set of RRC messages that may be sent + from the UE to the E-UTRAN on the uplink DCCH + logical channel + + Document Reference: 36.331 v10.0.0 Section 6.2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_rrc_pack_ul_dcch_msg(LIBLTE_RRC_UL_DCCH_MSG_STRUCT *ul_dcch_msg, + LIBLTE_BIT_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext = false; + + if(ul_dcch_msg != NULL && + msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + liblte_value_2_bits(ext, &msg_ptr, 1); + + // Message type choice + liblte_value_2_bits(ul_dcch_msg->msg_type, &msg_ptr, 4); + + // Message + if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_CSFB_PARAMS_REQ_CDMA2000 == ul_dcch_msg->msg_type) + { + err = liblte_rrc_pack_csfb_parameters_request_cdma2000_msg((LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_measurement_report_msg((LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_reconfiguration_complete_msg((LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_reestablishment_complete_msg((LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_rrc_connection_setup_complete_msg((LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_security_mode_complete_msg((LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_FAILURE == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_security_mode_failure_msg((LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_ue_capability_information_msg((LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_HANDOVER_PREP_TRANSFER == ul_dcch_msg->msg_type){ + printf("NOT HANDLING UL HANDOVER PREPARATION TRANSFER\n"); +// err = liblte_rrc_pack_ul_handover_preparation_transfer_msg((LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT *)&ul_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_ul_information_transfer_msg((LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_COUNTER_CHECK_RESP == ul_dcch_msg->msg_type){ + printf("NOT HANDLING COUNTER CHECK RESPONSE\n"); +// err = liblte_rrc_pack_counter_check_response_msg((LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT *)&ul_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_INFO_RESP == ul_dcch_msg->msg_type){ + printf("NOT HANDLING UE INFORMATION RESPONSE\n"); +// err = liblte_rrc_pack_ue_information_response_msg((LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT *)&ul_dcch_msg->msg, +// &global_msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_PROXIMITY_IND == ul_dcch_msg->msg_type){ + err = liblte_rrc_pack_proximity_indication_msg((LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + }else{ // LIBLTE_RRC_UL_DCCH_MSG_TYPE_RN_RECONFIG_COMPLETE == ul_dcch_msg->msg_type + err = liblte_rrc_pack_rn_reconfiguration_complete_msg((LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *)&ul_dcch_msg->msg, + &global_msg); + } + + if(global_msg.N_bits <= (LIBLTE_MAX_MSG_SIZE_BITS - 5)) + { + memcpy(msg_ptr, global_msg.msg, global_msg.N_bits); + msg->N_bits = global_msg.N_bits + 5; + }else{ + msg->N_bits = 0; + err = LIBLTE_ERROR_INVALID_INPUTS; + } + } + + return(err); +} +LIBLTE_ERROR_ENUM liblte_rrc_unpack_ul_dcch_msg(LIBLTE_BIT_MSG_STRUCT *msg, + LIBLTE_RRC_UL_DCCH_MSG_STRUCT *ul_dcch_msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr; + uint8 ext; + + if(msg != NULL && + ul_dcch_msg != NULL) + { + msg_ptr = msg->msg; + + // Extension indicator + ext = liblte_bits_2_value(&msg_ptr, 1); + + // Message type choice + ul_dcch_msg->msg_type = (LIBLTE_RRC_UL_DCCH_MSG_TYPE_ENUM)liblte_bits_2_value(&msg_ptr, 4); + + // Message + memcpy(global_msg.msg, msg_ptr, msg->N_bits-(msg_ptr-msg->msg)); + global_msg.N_bits = msg->N_bits-(msg_ptr-msg->msg); + if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_CSFB_PARAMS_REQ_CDMA2000 == ul_dcch_msg->msg_type) + { + err = liblte_rrc_unpack_csfb_parameters_request_cdma2000_msg(&global_msg, + (LIBLTE_RRC_CSFB_PARAMETERS_REQUEST_CDMA2000_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_measurement_report_msg(&global_msg, + (LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_reconfiguration_complete_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_reestablishment_complete_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_REESTABLISHMENT_COMPLETE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_rrc_connection_setup_complete_msg(&global_msg, + (LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_security_mode_complete_msg(&global_msg, + (LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_FAILURE == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_security_mode_failure_msg(&global_msg, + (LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO == ul_dcch_msg->msg_type){ + printf("NOT HANDLING UE CAPABILITY INFO\n"); +// err = liblte_rrc_unpack_ue_capability_information_msg(&global_msg, +// (LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_HANDOVER_PREP_TRANSFER == ul_dcch_msg->msg_type){ + printf("NOT HANDLING UL HANDOVER PREPARATION TRANSFER\n"); +// err = liblte_rrc_unpack_ul_handover_preparation_transfer_msg(&global_msg, +// (LIBLTE_RRC_UL_HANDOVER_PREPARATION_TRANSFER_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_ul_information_transfer_msg(&global_msg, + (LIBLTE_RRC_UL_INFORMATION_TRANSFER_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_COUNTER_CHECK_RESP == ul_dcch_msg->msg_type){ + printf("NOT HANDLING COUNTER CHECK RESPONSE\n"); +// err = liblte_rrc_unpack_counter_check_response_msg(&global_msg, +// (LIBLTE_RRC_COUNTER_CHECK_RESPONSE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_INFO_RESP == ul_dcch_msg->msg_type){ + printf("NOT HANDLING UE INFORMATION RESPONSE\n"); +// err = liblte_rrc_unpack_ue_information_response_msg(&global_msg, +// (LIBLTE_RRC_UE_INFORMATION_RESPONSE_STRUCT *)&ul_dcch_msg->msg); + }else if(LIBLTE_RRC_UL_DCCH_MSG_TYPE_PROXIMITY_IND == ul_dcch_msg->msg_type){ + err = liblte_rrc_unpack_proximity_indication_msg(&global_msg, + (LIBLTE_RRC_PROXIMITY_INDICATION_STRUCT *)&ul_dcch_msg->msg); + }else{ // LIBLTE_RRC_UL_DCCH_MSG_TYPE_RN_RECONFIG_COMPLETE == ul_dcch_msg->msg_type + err = liblte_rrc_unpack_rn_reconfiguration_complete_msg(&global_msg, + (LIBLTE_RRC_RN_RECONFIGURATION_COMPLETE_STRUCT *)&ul_dcch_msg->msg); + } + + liblte_rrc_consume_noncrit_extension(ext, __func__, &msg_ptr); + + } + + return(err); +} diff --git a/lib/src/asn1/liblte_s1ap.cc b/lib/src/asn1/liblte_s1ap.cc new file mode 100644 index 0000000..ea0b1ec --- /dev/null +++ b/lib/src/asn1/liblte_s1ap.cc @@ -0,0 +1,44058 @@ +/******************************************************************************* +/* +/* Copyright 2016 Software Radio Systems Limited +/* +********************************************************************************/ + +#include "srslte/asn1/liblte_s1ap.h" +# include +# include +# include + +/******************************************************************************* + LOGGING +*******************************************************************************/ + +static log_handler_t log_handler; +static void *callback_ctx = NULL; + +void liblte_log_register_handler(void *ctx, log_handler_t handler) { + log_handler = handler; + callback_ctx = ctx; +} + +static void liblte_log_print(const char *format, ...) { + va_list args; + va_start(args, format); + if (log_handler) { + char *args_msg = NULL; + if(vasprintf(&args_msg, format, args) > 0) { + log_handler(callback_ctx, args_msg); + } + if (args_msg) { + free(args_msg); + } + } else { + vprintf(format, args); + } + va_end(args); +} + +/******************************************************************************* +/* ProtocolIE Criticality ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticality( + LIBLTE_S1AP_CRITICALITY_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticality( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITY_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE local INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_local( + LIBLTE_S1AP_LOCAL_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->local + // lb:0, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->local, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_local( + uint8_t **ptr, + LIBLTE_S1AP_LOCAL_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->local + // lb:0, ub:65535 + liblte_align_up(ptr, 8); + ie->local = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE PrivateIE_ID CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_id( + LIBLTE_S1AP_PRIVATEIE_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_LOCAL) { + if(liblte_s1ap_pack_local(&ie->choice.local, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_GLOBAL) { + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_id( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Choice type + ie->choice_type = (LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_LOCAL) { + if(liblte_s1ap_unpack_local(ptr, &ie->choice.local) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_PRIVATEIE_ID_CHOICE_GLOBAL) { + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensionid( + LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProtocolExtensionID + // lb:0, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->ProtocolExtensionID, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensionid( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProtocolExtensionID + // lb:0, ub:65535 + liblte_align_up(ptr, 8); + ie->ProtocolExtensionID = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TriggeringMessage ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_triggeringmessage( + LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_triggeringmessage( + uint8_t **ptr, + LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM)liblte_bits_2_value(ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Presence ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_presence( + LIBLTE_S1AP_PRESENCE_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_presence( + uint8_t **ptr, + LIBLTE_S1AP_PRESENCE_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_PRESENCE_ENUM)liblte_bits_2_value(ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_id( + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProtocolIE_ID + // lb:0, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->ProtocolIE_ID, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_id( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProtocolIE_ID + // lb:0, ub:65535 + liblte_align_up(ptr, 8); + ie->ProtocolIE_ID = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProcedureCode INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_procedurecode( + LIBLTE_S1AP_PROCEDURECODE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProcedureCode + // lb:0, ub:255 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-8); + liblte_value_2_bits(ie->ProcedureCode, ptr, 8); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_procedurecode( + uint8_t **ptr, + LIBLTE_S1AP_PROCEDURECODE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ProcedureCode + // lb:0, ub:255 + liblte_align_up(ptr, 8); + ie->ProcedureCode = (uint8_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_Field SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_field( + LIBLTE_S1AP_PROTOCOLIE_FIELD_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_pack_protocolie_id(&ie->id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->criticality + liblte_value_2_bits(ie->criticality, ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_field( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_FIELD_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_unpack_protocolie_id(ptr, &ie->id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->criticality + ie->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionField SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensionfield( + LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_pack_protocolextensionid(&ie->id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->criticality + liblte_value_2_bits(ie->criticality, ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensionfield( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONFIELD_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_unpack_protocolextensionid(ptr, &ie->id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->criticality + ie->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_FieldPair SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_fieldpair( + LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_pack_protocolie_id(&ie->id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->firstCriticality + liblte_value_2_bits(ie->firstCriticality, ptr, 2); + + + // Enum - ie->secondCriticality + liblte_value_2_bits(ie->secondCriticality, ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_fieldpair( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_FIELDPAIR_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_unpack_protocolie_id(ptr, &ie->id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->firstCriticality + ie->firstCriticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + + // Enum - ie->secondCriticality + ie->secondCriticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolExtensionContainer DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolextensioncontainer( + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ProtocolExtensionContainer pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_protocolextensionfield(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolextensioncontainer( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLEXTENSIONCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ProtocolExtensionContainer unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolextensionfield(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ContainerPair DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:0, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_containerpair( + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ProtocolIE_ContainerPair pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-0, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_protocolie_fieldpair(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_containerpair( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIR_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 0; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ProtocolIE_ContainerPair unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_fieldpair(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_ContainerPairList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:None, ub:None +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_containerpairlist( + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIRLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ProtocolIE_ContainerPairList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + if(ie->len < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->len, ptr, 7); + } else if(ie->len < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->len, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of bits + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_protocolie_containerpair(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_containerpairlist( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_CONTAINERPAIRLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->len = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->len = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of bits + } + } + if(ie->len > 32) { + liblte_log_print("ProtocolIE_ContainerPairList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_containerpair(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PrivateIE_Field SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_field( + LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_pack_privateie_id(&ie->id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->criticality + liblte_value_2_bits(ie->criticality, ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_field( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_FIELD_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_unpack_privateie_id(ptr, &ie->id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->criticality + ie->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ProtocolIE_SingleContainer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_singlecontainer( + LIBLTE_S1AP_PROTOCOLIE_SINGLECONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_pack_protocolie_id(&ie->id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->criticality + liblte_value_2_bits(ie->criticality, ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_singlecontainer( + uint8_t **ptr, + LIBLTE_S1AP_PROTOCOLIE_SINGLECONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + + if(liblte_s1ap_unpack_protocolie_id(ptr, &ie->id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->criticality + ie->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PrivateIE_Container DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privateie_container( + LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("PrivateIE_Container pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_privateie_field(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privateie_container( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEIE_CONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("PrivateIE_Container unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_privateie_field(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE BitRate INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bitrate( + LIBLTE_S1AP_BITRATE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->BitRate + // lb:0, ub:10000000000 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->BitRate-0)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 3); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->BitRate-0, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bitrate( + uint8_t **ptr, + LIBLTE_S1AP_BITRATE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->BitRate + // lb:0, ub:10000000000 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 3) + 1; + liblte_align_up(ptr, 8); + ie->BitRate = liblte_bits_2_value(ptr, n_octets*8) + 0; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CauseMisc ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causemisc( + LIBLTE_S1AP_CAUSEMISC_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CauseMisc error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causemisc( + uint8_t **ptr, + LIBLTE_S1AP_CAUSEMISC_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CauseMisc error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CAUSEMISC_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CauseRadioNetwork ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causeradionetwork( + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CauseRadioNetwork error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 6); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causeradionetwork( + uint8_t **ptr, + LIBLTE_S1AP_CAUSERADIONETWORK_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CauseRadioNetwork error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CAUSERADIONETWORK_ENUM)liblte_bits_2_value(ptr, 6); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CauseNas ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causenas( + LIBLTE_S1AP_CAUSENAS_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CauseNas error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causenas( + uint8_t **ptr, + LIBLTE_S1AP_CAUSENAS_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CauseNas error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CAUSENAS_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellIdentity STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellidentity( + LIBLTE_S1AP_CELLIDENTITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - CellIdentity + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellidentity( + uint8_t **ptr, + LIBLTE_S1AP_CELLIDENTITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - CellIdentity + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000PDU DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000pdu( + LIBLTE_S1AP_CDMA2000PDU_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000PDU + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000pdu( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000PDU_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000PDU + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000SectorID DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000sectorid( + LIBLTE_S1AP_CDMA2000SECTORID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000SectorID + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000sectorid( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000SECTORID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000SectorID + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000HORequiredIndication ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000horequiredindication( + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000HORequiredIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000horequiredindication( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000HORequiredIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CDMA2000HOREQUIREDINDICATION_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXMSI DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexmsi( + LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXMSI + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexmsi( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXMSI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXMSI + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXRAND DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexrand( + LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXRAND + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexrand( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXRAND_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXRAND + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CNDomain ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cndomain( + LIBLTE_S1AP_CNDOMAIN_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cndomain( + uint8_t **ptr, + LIBLTE_S1AP_CNDOMAIN_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_CNDOMAIN_ENUM)liblte_bits_2_value(ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Correlation_ID STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_correlation_id( + LIBLTE_S1AP_CORRELATION_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - Correlation-ID + if(LIBLTE_S1AP_CORRELATION_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_correlation_id( + uint8_t **ptr, + LIBLTE_S1AP_CORRELATION_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - Correlation-ID + if(LIBLTE_S1AP_CORRELATION_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE AdditionalCSFallbackIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_additionalcsfallbackindicator( + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("AdditionalCSFallbackIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_additionalcsfallbackindicator( + uint8_t **ptr, + LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("AdditionalCSFallbackIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_ADDITIONALCSFALLBACKINDICATOR_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE DL_Forwarding ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_dl_forwarding( + LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("DL_Forwarding error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_dl_forwarding( + uint8_t **ptr, + LIBLTE_S1AP_DL_FORWARDING_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("DL_Forwarding error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_DL_FORWARDING_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Data_Forwarding_Not_Possible ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_data_forwarding_not_possible( + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Data_Forwarding_Not_Possible error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_data_forwarding_not_possible( + uint8_t **ptr, + LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Data_Forwarding_Not_Possible error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_DATA_FORWARDING_NOT_POSSIBLE_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid( + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - EmergencyAreaID + if(LIBLTE_S1AP_EMERGENCYAREAID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - EmergencyAreaID + if(LIBLTE_S1AP_EMERGENCYAREAID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE macroENB_ID STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_macroenb_id( + LIBLTE_S1AP_MACROENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - macroENB-ID + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_macroenb_id( + uint8_t **ptr, + LIBLTE_S1AP_MACROENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - macroENB-ID + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + + + +/******************************************************************************* +/* ProtocolIE homeENB_ID STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_homeenb_id( + LIBLTE_S1AP_HOMEENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - homeENB-ID + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_homeenb_id( + uint8_t **ptr, + LIBLTE_S1AP_HOMEENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - homeENB-ID + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE ENB_ID CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_id( + LIBLTE_S1AP_ENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ENB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID) { + if(liblte_s1ap_pack_macroenb_id(&ie->choice.macroENB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_ENB_ID_CHOICE_HOMEENB_ID) { + if(liblte_s1ap_pack_homeenb_id(&ie->choice.homeENB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_id( + uint8_t **ptr, + LIBLTE_S1AP_ENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ENB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_ENB_ID_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID) { + if(liblte_s1ap_unpack_macroenb_id(ptr, &ie->choice.macroENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_ENB_ID_CHOICE_HOMEENB_ID) { + if(liblte_s1ap_unpack_homeenb_id(ptr, &ie->choice.homeENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENBname PrintableString +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbname( + LIBLTE_S1AP_ENBNAME_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Printable string - ENBname + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ENBname error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Length + liblte_value_2_bits(ie->n_octets-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbname( + uint8_t **ptr, + LIBLTE_S1AP_ENBNAME_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Printable string - ENBname + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ENBname error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Length + ie->n_octets = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EncryptionAlgorithms STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_encryptionalgorithms( + LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - EncryptionAlgorithms + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("EncryptionAlgorithms error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_encryptionalgorithms( + uint8_t **ptr, + LIBLTE_S1AP_ENCRYPTIONALGORITHMS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - EncryptionAlgorithms + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("EncryptionAlgorithms error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EventType ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eventtype( + LIBLTE_S1AP_EVENTTYPE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("EventType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eventtype( + uint8_t **ptr, + LIBLTE_S1AP_EVENTTYPE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("EventType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_EVENTTYPE_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ExtendedRNC_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_extendedrnc_id( + LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ExtendedRNC_ID + // lb:4096, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->ExtendedRNC_ID, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_extendedrnc_id( + uint8_t **ptr, + LIBLTE_S1AP_EXTENDEDRNC_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ExtendedRNC_ID + // lb:4096, ub:65535 + liblte_align_up(ptr, 8); + ie->ExtendedRNC_ID = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenInterRATs ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddeninterrats( + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenInterRATs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddeninterrats( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenInterRATs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_FORBIDDENINTERRATS_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GWContextReleaseIndication ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gwcontextreleaseindication( + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("GWContextReleaseIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gwcontextreleaseindication( + uint8_t **ptr, + LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("GWContextReleaseIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_GWCONTEXTRELEASEINDICATION_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE HFN INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_hfn( + LIBLTE_S1AP_HFN_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->HFN + // lb:0, ub:1048575 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->HFN-0)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 2); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->HFN-0, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_hfn( + uint8_t **ptr, + LIBLTE_S1AP_HFN_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->HFN + // lb:0, ub:1048575 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 2) + 1; + liblte_align_up(ptr, 8); + ie->HFN = liblte_bits_2_value(ptr, n_octets*8) + 0; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE IMSI DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_imsi( + LIBLTE_S1AP_IMSI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // max length of IE buffer is 8, so limit + if (ie->n_octets > 7) { + printf("Length in struct exceeds buffer (%d > 7).\n", ie->n_octets); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Dynamic octet string - IMSI + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_imsi( + uint8_t **ptr, + LIBLTE_S1AP_IMSI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - IMSI + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE InterfacesToTrace STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_interfacestotrace( + LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - InterfacesToTrace + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_interfacestotrace( + uint8_t **ptr, + LIBLTE_S1AP_INTERFACESTOTRACE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - InterfacesToTrace + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LAC STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lac( + LIBLTE_S1AP_LAC_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - LAC + if(LIBLTE_S1AP_LAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lac( + uint8_t **ptr, + LIBLTE_S1AP_LAC_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - LAC + if(LIBLTE_S1AP_LAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LastVisitedUTRANCellInformation DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedutrancellinformation( + LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LastVisitedUTRANCellInformation + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedutrancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDUTRANCELLINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LastVisitedUTRANCellInformation + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE L3_Information DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_l3_information( + LIBLTE_S1AP_L3_INFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - L3-Information + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_l3_information( + uint8_t **ptr, + LIBLTE_S1AP_L3_INFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - L3-Information + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LHN_ID DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lhn_id( + LIBLTE_S1AP_LHN_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LHN-ID + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lhn_id( + uint8_t **ptr, + LIBLTE_S1AP_LHN_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LHN-ID + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LoggingDuration ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggingduration( + LIBLTE_S1AP_LOGGINGDURATION_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggingduration( + uint8_t **ptr, + LIBLTE_S1AP_LOGGINGDURATION_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_LOGGINGDURATION_ENUM)liblte_bits_2_value(ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MDT_Activation ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_activation( + LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MDT_Activation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_activation( + uint8_t **ptr, + LIBLTE_S1AP_MDT_ACTIVATION_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MDT_Activation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_MDT_ACTIVATION_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ManagementBasedMDTAllowed ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_managementbasedmdtallowed( + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ManagementBasedMDTAllowed error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_managementbasedmdtallowed( + uint8_t **ptr, + LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ManagementBasedMDTAllowed error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_MANAGEMENTBASEDMDTALLOWED_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PrivacyIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privacyindicator( + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("PrivacyIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privacyindicator( + uint8_t **ptr, + LIBLTE_S1AP_PRIVACYINDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("PrivacyIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_PRIVACYINDICATOR_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MeasurementsToActivate STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_measurementstoactivate( + LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MeasurementsToActivate + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_measurementstoactivate( + uint8_t **ptr, + LIBLTE_S1AP_MEASUREMENTSTOACTIVATE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MeasurementsToActivate + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MessageIdentifier STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_messageidentifier( + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MessageIdentifier + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_messageidentifier( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGEIDENTIFIER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MessageIdentifier + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MMEname PrintableString +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmename( + LIBLTE_S1AP_MMENAME_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Printable string - MMEname + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MMEname error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Length + liblte_value_2_bits(ie->n_octets-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmename( + uint8_t **ptr, + LIBLTE_S1AP_MMENAME_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Printable string - MMEname + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MMEname error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Length + ie->n_octets = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MME_Group_ID STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_group_id( + LIBLTE_S1AP_MME_GROUP_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - MME-Group-ID + if(LIBLTE_S1AP_MME_GROUP_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_group_id( + uint8_t **ptr, + LIBLTE_S1AP_MME_GROUP_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - MME-Group-ID + if(LIBLTE_S1AP_MME_GROUP_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MME_UE_S1AP_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_ue_s1ap_id( + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->MME_UE_S1AP_ID + // lb:0, ub:4294967295 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->MME_UE_S1AP_ID-0)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 2); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->MME_UE_S1AP_ID-0, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_ue_s1ap_id( + uint8_t **ptr, + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->MME_UE_S1AP_ID + // lb:0, ub:4294967295 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 2) + 1; + liblte_align_up(ptr, 8); + ie->MME_UE_S1AP_ID = liblte_bits_2_value(ptr, n_octets*8) + 0; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MSClassmark2 DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_msclassmark2( + LIBLTE_S1AP_MSCLASSMARK2_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - MSClassmark2 + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_msclassmark2( + uint8_t **ptr, + LIBLTE_S1AP_MSCLASSMARK2_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - MSClassmark2 + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE NAS_PDU DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nas_pdu( + LIBLTE_S1AP_NAS_PDU_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NAS-PDU + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nas_pdu( + uint8_t **ptr, + LIBLTE_S1AP_NAS_PDU_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NAS-PDU + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE NASSecurityParameterstoE_UTRAN DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nassecurityparameterstoe_utran( + LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NASSecurityParameterstoE-UTRAN + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nassecurityparameterstoe_utran( + uint8_t **ptr, + LIBLTE_S1AP_NASSECURITYPARAMETERSTOE_UTRAN_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NASSecurityParameterstoE-UTRAN + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE NumberOfBroadcasts INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_numberofbroadcasts( + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->NumberOfBroadcasts + // lb:0, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->NumberOfBroadcasts, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_numberofbroadcasts( + uint8_t **ptr, + LIBLTE_S1AP_NUMBEROFBROADCASTS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->NumberOfBroadcasts + // lb:0, ub:65535 + liblte_align_up(ptr, 8); + ie->NumberOfBroadcasts = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE OverloadAction ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadaction( + LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("OverloadAction error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadaction( + uint8_t **ptr, + LIBLTE_S1AP_OVERLOADACTION_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("OverloadAction error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_OVERLOADACTION_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PagingDRX ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pagingdrx( + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("PagingDRX error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pagingdrx( + uint8_t **ptr, + LIBLTE_S1AP_PAGINGDRX_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("PagingDRX error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_PAGINGDRX_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PDCP_SN INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pdcp_sn( + LIBLTE_S1AP_PDCP_SN_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PDCP_SN + // lb:0, ub:4095 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-12); + liblte_value_2_bits(ie->PDCP_SN, ptr, 12); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pdcp_sn( + uint8_t **ptr, + LIBLTE_S1AP_PDCP_SN_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PDCP_SN + // lb:0, ub:4095 + liblte_align_up(ptr, 8); + ie->PDCP_SN = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Port_Number STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_port_number( + LIBLTE_S1AP_PORT_NUMBER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - Port-Number + if(LIBLTE_S1AP_PORT_NUMBER_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_port_number( + uint8_t **ptr, + LIBLTE_S1AP_PORT_NUMBER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - Port-Number + if(LIBLTE_S1AP_PORT_NUMBER_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Pre_emptionVulnerability ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pre_emptionvulnerability( + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pre_emptionvulnerability( + uint8_t **ptr, + LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM)liblte_bits_2_value(ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PS_ServiceNotAvailable ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ps_servicenotavailable( + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("PS_ServiceNotAvailable error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ps_servicenotavailable( + uint8_t **ptr, + LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("PS_ServiceNotAvailable error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_PS_SERVICENOTAVAILABLE_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ReceiveStatusofULPDCPSDUs STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_receivestatusofulpdcpsdus( + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - ReceiveStatusofULPDCPSDUs + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_receivestatusofulpdcpsdus( + uint8_t **ptr, + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - ReceiveStatusofULPDCPSDUs + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RelativeMMECapacity INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_relativemmecapacity( + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RelativeMMECapacity + // lb:0, ub:255 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-8); + liblte_value_2_bits(ie->RelativeMMECapacity, ptr, 8); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_relativemmecapacity( + uint8_t **ptr, + LIBLTE_S1AP_RELATIVEMMECAPACITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RelativeMMECapacity + // lb:0, ub:255 + liblte_align_up(ptr, 8); + ie->RelativeMMECapacity = (uint8_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RAC STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rac( + LIBLTE_S1AP_RAC_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - RAC + if(LIBLTE_S1AP_RAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rac( + uint8_t **ptr, + LIBLTE_S1AP_RAC_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - RAC + if(LIBLTE_S1AP_RAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ReportIntervalMDT ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportintervalmdt( + LIBLTE_S1AP_REPORTINTERVALMDT_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportintervalmdt( + uint8_t **ptr, + LIBLTE_S1AP_REPORTINTERVALMDT_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_REPORTINTERVALMDT_ENUM)liblte_bits_2_value(ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ReportArea ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportarea( + LIBLTE_S1AP_REPORTAREA_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ReportArea error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportarea( + uint8_t **ptr, + LIBLTE_S1AP_REPORTAREA_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ReportArea error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_REPORTAREA_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RNC_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rnc_id( + LIBLTE_S1AP_RNC_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RNC_ID + // lb:0, ub:4095 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-12); + liblte_value_2_bits(ie->RNC_ID, ptr, 12); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rnc_id( + uint8_t **ptr, + LIBLTE_S1AP_RNC_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RNC_ID + // lb:0, ub:4095 + liblte_align_up(ptr, 8); + ie->RNC_ID = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RRC_Establishment_Cause ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rrc_establishment_cause( + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("RRC_Establishment_Cause error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rrc_establishment_cause( + uint8_t **ptr, + LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("RRC_Establishment_Cause error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Routing_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_routing_id( + LIBLTE_S1AP_ROUTING_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Routing_ID + // lb:0, ub:255 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-8); + liblte_value_2_bits(ie->Routing_ID, ptr, 8); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_routing_id( + uint8_t **ptr, + LIBLTE_S1AP_ROUTING_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Routing_ID + // lb:0, ub:255 + liblte_align_up(ptr, 8); + ie->Routing_ID = (uint8_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SONInformationRequest ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationrequest( + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformationRequest error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationrequest( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformationRequest error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_SONINFORMATIONREQUEST_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Source_ToTarget_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_source_totarget_transparentcontainer( + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Source-ToTarget-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_source_totarget_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCE_TOTARGET_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Source-ToTarget-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SRVCCHOIndication ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_srvcchoindication( + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SRVCCHOIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_srvcchoindication( + uint8_t **ptr, + LIBLTE_S1AP_SRVCCHOINDICATION_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SRVCCHOIndication error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_SRVCCHOINDICATION_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SourceRNC_ToTargetRNC_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourcernc_totargetrnc_transparentcontainer( + LIBLTE_S1AP_SOURCERNC_TOTARGETRNC_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - SourceRNC-ToTargetRNC-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourcernc_totargetrnc_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCERNC_TOTARGETRNC_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - SourceRNC-ToTargetRNC-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SubscriberProfileIDforRFP INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_subscriberprofileidforrfp( + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->SubscriberProfileIDforRFP + // lb:1, ub:256 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-8); + liblte_value_2_bits(ie->SubscriberProfileIDforRFP, ptr, 8); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_subscriberprofileidforrfp( + uint8_t **ptr, + LIBLTE_S1AP_SUBSCRIBERPROFILEIDFORRFP_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->SubscriberProfileIDforRFP + // lb:1, ub:256 + liblte_align_up(ptr, 8); + ie->SubscriberProfileIDforRFP = (uint8_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SynchronizationStatus ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_synchronizationstatus( + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SynchronizationStatus error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_synchronizationstatus( + uint8_t **ptr, + LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SynchronizationStatus error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_SYNCHRONIZATIONSTATUS_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargetRNC_ToSourceRNC_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_tosourcernc_transparentcontainer( + LIBLTE_S1AP_TARGETRNC_TOSOURCERNC_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - TargetRNC-ToSourceRNC-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_tosourcernc_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETRNC_TOSOURCERNC_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - TargetRNC-ToSourceRNC-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Threshold_RSRQ INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_threshold_rsrq( + LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Threshold_RSRQ + // lb:0, ub:34 + liblte_value_2_bits(ie->Threshold_RSRQ, ptr, 6); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_threshold_rsrq( + uint8_t **ptr, + LIBLTE_S1AP_THRESHOLD_RSRQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Threshold_RSRQ + // lb:0, ub:34 + ie->Threshold_RSRQ = (uint8_t)liblte_bits_2_value(ptr, 6); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Time_UE_StayedInCell INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_time_ue_stayedincell( + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Time_UE_StayedInCell + // lb:0, ub:4095 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-12); + liblte_value_2_bits(ie->Time_UE_StayedInCell, ptr, 12); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_time_ue_stayedincell( + uint8_t **ptr, + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Time_UE_StayedInCell + // lb:0, ub:4095 + liblte_align_up(ptr, 8); + ie->Time_UE_StayedInCell = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TransportLayerAddress DYNAMIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_transportlayeraddress( + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic bit string - TransportLayerAddress + // lb:1, ub:160 + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TransportLayerAddress error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Length + liblte_value_2_bits(ie->n_bits-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Bits + uint32_t i; + for(i=0;in_bits;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_transportlayeraddress( + uint8_t **ptr, + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic bit string - TransportLayerAddress + // lb:1, ub:160 + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TransportLayerAddress error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Length + ie->n_bits = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + + // Bits + uint32_t i; + for(i=0;in_bits;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TraceDepth ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracedepth( + LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TraceDepth error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracedepth( + uint8_t **ptr, + LIBLTE_S1AP_TRACEDEPTH_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TraceDepth error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_TRACEDEPTH_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TrafficLoadReductionIndication INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_trafficloadreductionindication( + LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->TrafficLoadReductionIndication + // lb:1, ub:99 + liblte_value_2_bits(ie->TrafficLoadReductionIndication, ptr, 7); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_trafficloadreductionindication( + uint8_t **ptr, + LIBLTE_S1AP_TRAFFICLOADREDUCTIONINDICATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->TrafficLoadReductionIndication + // lb:1, ub:99 + ie->TrafficLoadReductionIndication = (uint8_t)liblte_bits_2_value(ptr, 7); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UERadioCapability DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapability( + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - UERadioCapability + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapability( + uint8_t **ptr, + LIBLTE_S1AP_UERADIOCAPABILITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - UERadioCapability + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE WarningType STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningtype( + LIBLTE_S1AP_WARNINGTYPE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - WarningType + if(LIBLTE_S1AP_WARNINGTYPE_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningtype( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGTYPE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - WarningType + if(LIBLTE_S1AP_WARNINGTYPE_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE WarningMessageContents DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningmessagecontents( + LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - WarningMessageContents + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningmessagecontents( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGMESSAGECONTENTS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - WarningMessageContents + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CauseProtocol ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causeprotocol( + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CauseProtocol error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causeprotocol( + uint8_t **ptr, + LIBLTE_S1AP_CAUSEPROTOCOL_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CauseProtocol error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CAUSEPROTOCOL_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellAccessMode ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellaccessmode( + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CellAccessMode error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellaccessmode( + uint8_t **ptr, + LIBLTE_S1AP_CELLACCESSMODE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CellAccessMode error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CELLACCESSMODE_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000RATType ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000rattype( + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000RATType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000rattype( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000RATTYPE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000RATType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CDMA2000RATTYPE_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXMEID DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexmeid( + LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXMEID + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexmeid( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXMEID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXMEID + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cell_Size ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cell_size( + LIBLTE_S1AP_CELL_SIZE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cell_Size error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cell_size( + uint8_t **ptr, + LIBLTE_S1AP_CELL_SIZE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cell_Size error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CELL_SIZE_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CI STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ci( + LIBLTE_S1AP_CI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - CI + if(LIBLTE_S1AP_CI_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ci( + uint8_t **ptr, + LIBLTE_S1AP_CI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - CI + if(LIBLTE_S1AP_CI_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CSFallbackIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csfallbackindicator( + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CSFallbackIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csfallbackindicator( + uint8_t **ptr, + LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CSFallbackIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CSFALLBACKINDICATOR_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CSGMembershipStatus ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csgmembershipstatus( + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csgmembershipstatus( + uint8_t **ptr, + LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_CSGMEMBERSHIPSTATUS_ENUM)liblte_bits_2_value(ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE DataCodingScheme STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_datacodingscheme( + LIBLTE_S1AP_DATACODINGSCHEME_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - DataCodingScheme + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_datacodingscheme( + uint8_t **ptr, + LIBLTE_S1AP_DATACODINGSCHEME_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - DataCodingScheme + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaIDList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaidlist( + LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("EmergencyAreaIDList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_emergencyareaid(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaidlist( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAIDLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("EmergencyAreaIDList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_emergencyareaid(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaIDListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaidlistforrestart( + LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("EmergencyAreaIDListForRestart pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_emergencyareaid(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaidlistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAIDLISTFORRESTART_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("EmergencyAreaIDListForRestart unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_emergencyareaid(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENB_UE_S1AP_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_ue_s1ap_id( + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ENB_UE_S1AP_ID + // lb:0, ub:16777215 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->ENB_UE_S1AP_ID-0)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 2); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->ENB_UE_S1AP_ID-0, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_ue_s1ap_id( + uint8_t **ptr, + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ENB_UE_S1AP_ID + // lb:0, ub:16777215 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 2) + 1; + liblte_align_up(ptr, 8); + ie->ENB_UE_S1AP_ID = liblte_bits_2_value(ptr, n_octets*8) + 0; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RAB_ID INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rab_id( + LIBLTE_S1AP_E_RAB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->E_RAB_ID + // lb:0, ub:15 + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ie->E_RAB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_value_2_bits(ie->E_RAB_ID, ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rab_id( + uint8_t **ptr, + LIBLTE_S1AP_E_RAB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->E_RAB_ID + // lb:0, ub:15 + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ie->E_RAB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + ie->E_RAB_ID = (uint8_t)liblte_bits_2_value(ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABInformationListItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlistitem( + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABInformationListItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->dL_Forwarding_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->dL_Forwarding_present) { + if(liblte_s1ap_pack_dl_forwarding(&ie->dL_Forwarding, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlistitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABINFORMATIONLISTITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABInformationListItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->dL_Forwarding_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->dL_Forwarding_present) { + if(liblte_s1ap_unpack_dl_forwarding(ptr, &ie->dL_Forwarding) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EUTRANRoundTripDelayEstimationInfo INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutranroundtripdelayestimationinfo( + LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->EUTRANRoundTripDelayEstimationInfo + // lb:0, ub:2047 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-11); + liblte_value_2_bits(ie->EUTRANRoundTripDelayEstimationInfo, ptr, 11); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutranroundtripdelayestimationinfo( + uint8_t **ptr, + LIBLTE_S1AP_EUTRANROUNDTRIPDELAYESTIMATIONINFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->EUTRANRoundTripDelayEstimationInfo + // lb:0, ub:2047 + liblte_align_up(ptr, 8); + ie->EUTRANRoundTripDelayEstimationInfo = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenLACs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:4096 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlacs( + LIBLTE_S1AP_FORBIDDENLACS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ForbiddenLACs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 12); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_lac(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlacs( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLACS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 12) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ForbiddenLACs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_lac(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GTP_TEID STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gtp_teid( + LIBLTE_S1AP_GTP_TEID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - GTP-TEID + if(LIBLTE_S1AP_GTP_TEID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gtp_teid( + uint8_t **ptr, + LIBLTE_S1AP_GTP_TEID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - GTP-TEID + if(LIBLTE_S1AP_GTP_TEID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GUMMEIType ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummeitype( + LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("GUMMEIType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummeitype( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEITYPE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("GUMMEIType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_GUMMEITYPE_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE HandoverType ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovertype( + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("HandoverType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovertype( + uint8_t **ptr, + LIBLTE_S1AP_HANDOVERTYPE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("HandoverType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_HANDOVERTYPE_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE IntegrityProtectionAlgorithms STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_integrityprotectionalgorithms( + LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - IntegrityProtectionAlgorithms + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("IntegrityProtectionAlgorithms error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_integrityprotectionalgorithms( + uint8_t **ptr, + LIBLTE_S1AP_INTEGRITYPROTECTIONALGORITHMS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - IntegrityProtectionAlgorithms + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("IntegrityProtectionAlgorithms error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LastVisitedGERANCellInformation CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedgerancellinformation( + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedGERANCellInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_UNDEFINED) { + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedgerancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedGERANCellInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_ENUM)liblte_bits_2_value(ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDGERANCELLINFORMATION_CHOICE_UNDEFINED) { + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Links_to_log ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_links_to_log( + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Links_to_log error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_links_to_log( + uint8_t **ptr, + LIBLTE_S1AP_LINKS_TO_LOG_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Links_to_log error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_LINKS_TO_LOG_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LoggingInterval ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_logginginterval( + LIBLTE_S1AP_LOGGINGINTERVAL_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_logginginterval( + uint8_t **ptr, + LIBLTE_S1AP_LOGGINGINTERVAL_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_LOGGINGINTERVAL_ENUM)liblte_bits_2_value(ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M3period ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3period( + LIBLTE_S1AP_M3PERIOD_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M3period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3period( + uint8_t **ptr, + LIBLTE_S1AP_M3PERIOD_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M3period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_M3PERIOD_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M4period ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4period( + LIBLTE_S1AP_M4PERIOD_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M4period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4period( + uint8_t **ptr, + LIBLTE_S1AP_M4PERIOD_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M4period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_M4PERIOD_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M5period ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5period( + LIBLTE_S1AP_M5PERIOD_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M5period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5period( + uint8_t **ptr, + LIBLTE_S1AP_M5PERIOD_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M5period error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_M5PERIOD_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MobilityInformation STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mobilityinformation( + LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MobilityInformation + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mobilityinformation( + uint8_t **ptr, + LIBLTE_S1AP_MOBILITYINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MobilityInformation + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MME_Code STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mme_code( + LIBLTE_S1AP_MME_CODE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - MME-Code + if(LIBLTE_S1AP_MME_CODE_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mme_code( + uint8_t **ptr, + LIBLTE_S1AP_MME_CODE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - MME-Code + if(LIBLTE_S1AP_MME_CODE_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MSClassmark3 DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_msclassmark3( + LIBLTE_S1AP_MSCLASSMARK3_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - MSClassmark3 + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_msclassmark3( + uint8_t **ptr, + LIBLTE_S1AP_MSCLASSMARK3_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - MSClassmark3 + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE NumberofBroadcastRequest INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_numberofbroadcastrequest( + LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->NumberofBroadcastRequest + // lb:0, ub:65535 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->NumberofBroadcastRequest, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_numberofbroadcastrequest( + uint8_t **ptr, + LIBLTE_S1AP_NUMBEROFBROADCASTREQUEST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->NumberofBroadcastRequest + // lb:0, ub:65535 + liblte_align_up(ptr, 8); + ie->NumberofBroadcastRequest = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE OverloadResponse CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadresponse( + LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("OverloadResponse error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_OVERLOADACTION) { + if(liblte_s1ap_pack_overloadaction(&ie->choice.overloadAction, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadresponse( + uint8_t **ptr, + LIBLTE_S1AP_OVERLOADRESPONSE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("OverloadResponse error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_ENUM)liblte_bits_2_value(ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_OVERLOADRESPONSE_CHOICE_OVERLOADACTION) { + if(liblte_s1ap_unpack_overloadaction(ptr, &ie->choice.overloadAction) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PDCP_SNExtended INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pdcp_snextended( + LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PDCP_SNExtended + // lb:0, ub:32767 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-15); + liblte_value_2_bits(ie->PDCP_SNExtended, ptr, 15); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pdcp_snextended( + uint8_t **ptr, + LIBLTE_S1AP_PDCP_SNEXTENDED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PDCP_SNExtended + // lb:0, ub:32767 + liblte_align_up(ptr, 8); + ie->PDCP_SNExtended = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Pre_emptionCapability ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pre_emptioncapability( + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pre_emptioncapability( + uint8_t **ptr, + LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM)liblte_bits_2_value(ptr, 1); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE QCI INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_qci( + LIBLTE_S1AP_QCI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->QCI + // lb:0, ub:255 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-8); + liblte_value_2_bits(ie->QCI, ptr, 8); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_qci( + uint8_t **ptr, + LIBLTE_S1AP_QCI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->QCI + // lb:0, ub:255 + liblte_align_up(ptr, 8); + ie->QCI = (uint8_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RelayNode_Indicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_relaynode_indicator( + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("RelayNode_Indicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_relaynode_indicator( + uint8_t **ptr, + LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("RelayNode_Indicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_RELAYNODE_INDICATOR_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M1ReportingTrigger ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1reportingtrigger( + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M1ReportingTrigger error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 2); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1reportingtrigger( + uint8_t **ptr, + LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M1ReportingTrigger error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_M1REPORTINGTRIGGER_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RIMInformation DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_riminformation( + LIBLTE_S1AP_RIMINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - RIMInformation + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_riminformation( + uint8_t **ptr, + LIBLTE_S1AP_RIMINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - RIMInformation + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RepetitionPeriod INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_repetitionperiod( + LIBLTE_S1AP_REPETITIONPERIOD_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RepetitionPeriod + // lb:0, ub:4095 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (1*8)-12); + liblte_value_2_bits(ie->RepetitionPeriod, ptr, 12); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_repetitionperiod( + uint8_t **ptr, + LIBLTE_S1AP_REPETITIONPERIOD_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->RepetitionPeriod + // lb:0, ub:4095 + liblte_align_up(ptr, 8); + ie->RepetitionPeriod = (uint16_t)liblte_bits_2_value(ptr, 1.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SecurityKey STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitykey( + LIBLTE_S1AP_SECURITYKEY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - SecurityKey + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitykey( + uint8_t **ptr, + LIBLTE_S1AP_SECURITYKEY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - SecurityKey + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SerialNumber STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_serialnumber( + LIBLTE_S1AP_SERIALNUMBER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - SerialNumber + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_serialnumber( + uint8_t **ptr, + LIBLTE_S1AP_SERIALNUMBER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - SerialNumber + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SourceBSS_ToTargetBSS_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourcebss_totargetbss_transparentcontainer( + LIBLTE_S1AP_SOURCEBSS_TOTARGETBSS_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - SourceBSS-ToTargetBSS-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourcebss_totargetbss_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEBSS_TOTARGETBSS_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - SourceBSS-ToTargetBSS-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SRVCCOperationPossible ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_srvccoperationpossible( + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SRVCCOperationPossible error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_srvccoperationpossible( + uint8_t **ptr, + LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SRVCCOperationPossible error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_SRVCCOPERATIONPOSSIBLE_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ServedGroupIDs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgroupids( + LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ServedGroupIDs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_mme_group_id(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgroupids( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ServedGroupIDs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_mme_group_id(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE StratumLevel INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_stratumlevel( + LIBLTE_S1AP_STRATUMLEVEL_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->StratumLevel + // lb:0, ub:3 + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ie->StratumLevel error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_value_2_bits(ie->StratumLevel, ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_stratumlevel( + uint8_t **ptr, + LIBLTE_S1AP_STRATUMLEVEL_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->StratumLevel + // lb:0, ub:3 + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ie->StratumLevel error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + ie->StratumLevel = (uint8_t)liblte_bits_2_value(ptr, 2); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAC STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tac( + LIBLTE_S1AP_TAC_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - TAC + if(LIBLTE_S1AP_TAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tac( + uint8_t **ptr, + LIBLTE_S1AP_TAC_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - TAC + if(LIBLTE_S1AP_TAC_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_talistformdt( + LIBLTE_S1AP_TALISTFORMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAListforMDT pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 3); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tac(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_talistformdt( + uint8_t **ptr, + LIBLTE_S1AP_TALISTFORMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 3) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAListforMDT unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tac(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TBCD_STRING STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tbcd_string( + LIBLTE_S1AP_TBCD_STRING_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - TBCD-STRING + if(LIBLTE_S1AP_TBCD_STRING_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tbcd_string( + uint8_t **ptr, + LIBLTE_S1AP_TBCD_STRING_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - TBCD-STRING + if(LIBLTE_S1AP_TBCD_STRING_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Target_ToSource_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_target_tosource_transparentcontainer( + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Target-ToSource-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_target_tosource_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGET_TOSOURCE_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Target-ToSource-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Threshold_RSRP INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_threshold_rsrp( + LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Threshold_RSRP + // lb:0, ub:97 + liblte_value_2_bits(ie->Threshold_RSRP, ptr, 7); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_threshold_rsrp( + uint8_t **ptr, + LIBLTE_S1AP_THRESHOLD_RSRP_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Threshold_RSRP + // lb:0, ub:97 + ie->Threshold_RSRP = (uint8_t)liblte_bits_2_value(ptr, 7); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Time_UE_StayedInCell_EnhancedGranularity INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_time_ue_stayedincell_enhancedgranularity( + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Time_UE_StayedInCell_EnhancedGranularity + // lb:0, ub:40950 + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (2*8)-16); + liblte_value_2_bits(ie->Time_UE_StayedInCell_EnhancedGranularity, ptr, 16); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_time_ue_stayedincell_enhancedgranularity( + uint8_t **ptr, + LIBLTE_S1AP_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->Time_UE_StayedInCell_EnhancedGranularity + // lb:0, ub:40950 + liblte_align_up(ptr, 8); + ie->Time_UE_StayedInCell_EnhancedGranularity = (uint16_t)liblte_bits_2_value(ptr, 2.0*8); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_UTRAN_Trace_ID STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_utran_trace_id( + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - E-UTRAN-Trace-ID + if(LIBLTE_S1AP_E_UTRAN_TRACE_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_utran_trace_id( + uint8_t **ptr, + LIBLTE_S1AP_E_UTRAN_TRACE_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - E-UTRAN-Trace-ID + if(LIBLTE_S1AP_E_UTRAN_TRACE_ID_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TypeOfError ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_typeoferror( + LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TypeOfError error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_typeoferror( + uint8_t **ptr, + LIBLTE_S1AP_TYPEOFERROR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TypeOfError error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_TYPEOFERROR_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UEAggregateMaximumBitrate SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueaggregatemaximumbitrate( + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UEAggregateMaximumBitrate error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_bitrate(&ie->uEaggregateMaximumBitRateDL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_bitrate(&ie->uEaggregateMaximumBitRateUL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueaggregatemaximumbitrate( + uint8_t **ptr, + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UEAggregateMaximumBitrate error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->uEaggregateMaximumBitRateDL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->uEaggregateMaximumBitRateUL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UE_S1AP_ID_pair SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_id_pair( + LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UE_S1AP_ID_pair error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_mme_ue_s1ap_id(&ie->mME_UE_S1AP_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_enb_ue_s1ap_id(&ie->eNB_UE_S1AP_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_id_pair( + uint8_t **ptr, + LIBLTE_S1AP_UE_S1AP_ID_PAIR_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UE_S1AP_ID_pair error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &ie->mME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &ie->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UEIdentityIndexValue STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueidentityindexvalue( + LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - UEIdentityIndexValue + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueidentityindexvalue( + uint8_t **ptr, + LIBLTE_S1AP_UEIDENTITYINDEXVALUE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - UEIdentityIndexValue + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UESecurityCapabilities SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uesecuritycapabilities( + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UESecurityCapabilities error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_encryptionalgorithms(&ie->encryptionAlgorithms, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_integrityprotectionalgorithms(&ie->integrityProtectionAlgorithms, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uesecuritycapabilities( + uint8_t **ptr, + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UESecurityCapabilities error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_encryptionalgorithms(ptr, &ie->encryptionAlgorithms) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_integrityprotectionalgorithms(ptr, &ie->integrityProtectionAlgorithms) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE VoiceSupportMatchIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_voicesupportmatchindicator( + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("VoiceSupportMatchIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_voicesupportmatchindicator( + uint8_t **ptr, + LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("VoiceSupportMatchIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_VOICESUPPORTMATCHINDICATOR_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE WarningSecurityInfo STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningsecurityinfo( + LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - WarningSecurityInfo + if(LIBLTE_S1AP_WARNINGSECURITYINFO_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningsecurityinfo( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGSECURITYINFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - WarningSecurityInfo + if(LIBLTE_S1AP_WARNINGSECURITYINFO_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENBX2GTPTLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2gtptlas( + LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ENBX2GTPTLAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2gtptlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2GTPTLAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ENBX2GTPTLAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CauseTransport ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_causetransport( + LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CauseTransport error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_causetransport( + uint8_t **ptr, + LIBLTE_S1AP_CAUSETRANSPORT_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CauseTransport error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CAUSETRANSPORT_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000HOStatus ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000hostatus( + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000HOStatus error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 1); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000hostatus( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000HOStatus error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_CDMA2000HOSTATUS_ENUM)liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXPilot DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexpilot( + LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXPilot + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexpilot( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXPILOT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - Cdma2000OneXPilot + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ConcurrentWarningMessageIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_concurrentwarningmessageindicator( + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 0); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_concurrentwarningmessageindicator( + uint8_t **ptr, + LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_CONCURRENTWARNINGMESSAGEINDICATOR_ENUM)liblte_bits_2_value(ptr, 0); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE COUNTvalue SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalue( + LIBLTE_S1AP_COUNTVALUE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("COUNTvalue error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_pdcp_sn(&ie->pDCP_SN, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_hfn(&ie->hFN, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalue( + uint8_t **ptr, + LIBLTE_S1AP_COUNTVALUE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("COUNTvalue error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_pdcp_sn(ptr, &ie->pDCP_SN) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_hfn(ptr, &ie->hFN) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics_IE_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_item( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CriticalityDiagnostics_IE_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + // Enum - ie->iECriticality + liblte_value_2_bits(ie->iECriticality, ptr, 2); + + if(liblte_s1ap_pack_protocolie_id(&ie->iE_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_typeoferror(&ie->typeOfError, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_item( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CriticalityDiagnostics_IE_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + // Enum - ie->iECriticality + ie->iECriticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + + if(liblte_s1ap_unpack_protocolie_id(ptr, &ie->iE_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_typeoferror(ptr, &ie->typeOfError) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENBX2TLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:2 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2tlas( + LIBLTE_S1AP_ENBX2TLAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ENBX2TLAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 0); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2tlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2TLAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 0) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ENBX2TLAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ExtendedRepetitionPeriod INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_extendedrepetitionperiod( + LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ExtendedRepetitionPeriod + // lb:4096, ub:131071 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->ExtendedRepetitionPeriod-4096)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 2); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->ExtendedRepetitionPeriod-4096, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_extendedrepetitionperiod( + uint8_t **ptr, + LIBLTE_S1AP_EXTENDEDREPETITIONPERIOD_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->ExtendedRepetitionPeriod + // lb:4096, ub:131071 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 2) + 1; + liblte_align_up(ptr, 8); + ie->ExtendedRepetitionPeriod = liblte_bits_2_value(ptr, n_octets*8) + 4096; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenTACs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:4096 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentacs( + LIBLTE_S1AP_FORBIDDENTACS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ForbiddenTACs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 12); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tac(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentacs( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTACS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 12) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ForbiddenTACs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tac(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GBR_QosInformation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gbr_qosinformation( + LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("GBR_QosInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_bitrate(&ie->e_RAB_MaximumBitrateDL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_bitrate(&ie->e_RAB_MaximumBitrateUL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_bitrate(&ie->e_RAB_GuaranteedBitrateDL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_bitrate(&ie->e_RAB_GuaranteedBitrateUL, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gbr_qosinformation( + uint8_t **ptr, + LIBLTE_S1AP_GBR_QOSINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("GBR_QosInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->e_RAB_MaximumBitrateDL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->e_RAB_MaximumBitrateUL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->e_RAB_GuaranteedBitrateDL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_bitrate(ptr, &ie->e_RAB_GuaranteedBitrateUL) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE HFNModified INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_hfnmodified( + LIBLTE_S1AP_HFNMODIFIED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->HFNModified + // lb:0, ub:131071 + // Range > 65536 - encoded based on value + { + uint32_t n_bits = floor(log2(ie->HFNModified-0)+1); + uint32_t n_octets = (n_bits+7)/8; + liblte_value_2_bits(n_octets-1, ptr, 2); + liblte_align_up_zero(ptr, 8); + liblte_value_2_bits(0, ptr, (n_octets*8)-n_bits); + liblte_value_2_bits(ie->HFNModified-0, ptr, n_bits); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_hfnmodified( + uint8_t **ptr, + LIBLTE_S1AP_HFNMODIFIED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->HFNModified + // lb:0, ub:131071 + // Range > 65536 - encoded based on value + { + uint32_t n_octets = liblte_bits_2_value(ptr, 2) + 1; + liblte_align_up(ptr, 8); + ie->HFNModified = liblte_bits_2_value(ptr, n_octets*8) + 0; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE KillAllWarningMessages ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killallwarningmessages( + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 0); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killallwarningmessages( + uint8_t **ptr, + LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_KILLALLWARNINGMESSAGES_ENUM)liblte_bits_2_value(ptr, 0); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LPPa_PDU DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lppa_pdu( + LIBLTE_S1AP_LPPA_PDU_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LPPa-PDU + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lppa_pdu( + uint8_t **ptr, + LIBLTE_S1AP_LPPA_PDU_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - LPPa-PDU + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M3Configuration SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3configuration( + LIBLTE_S1AP_M3CONFIGURATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M3Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_m3period(&ie->m3period, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3configuration( + uint8_t **ptr, + LIBLTE_S1AP_M3CONFIGURATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M3Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_m3period(ptr, &ie->m3period) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M5Configuration SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5configuration( + LIBLTE_S1AP_M5CONFIGURATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M5Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_m5period(&ie->m5period, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_links_to_log(&ie->m5_links_to_log, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5configuration( + uint8_t **ptr, + LIBLTE_S1AP_M5CONFIGURATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M5Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_m5period(ptr, &ie->m5period) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_links_to_log(ptr, &ie->m5_links_to_log) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MeasurementThresholdA2 CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_measurementthresholda2( + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MeasurementThresholdA2 error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRP) { + if(liblte_s1ap_pack_threshold_rsrp(&ie->choice.threshold_RSRP, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRQ) { + if(liblte_s1ap_pack_threshold_rsrq(&ie->choice.threshold_RSRQ, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_measurementthresholda2( + uint8_t **ptr, + LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MeasurementThresholdA2 error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRP) { + if(liblte_s1ap_unpack_threshold_rsrp(ptr, &ie->choice.threshold_RSRP) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_MEASUREMENTTHRESHOLDA2_CHOICE_THRESHOLD_RSRQ) { + if(liblte_s1ap_unpack_threshold_rsrq(ptr, &ie->choice.threshold_RSRQ) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M_TMSI STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m_tmsi( + LIBLTE_S1AP_M_TMSI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - M-TMSI + if(LIBLTE_S1AP_M_TMSI_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m_tmsi( + uint8_t **ptr, + LIBLTE_S1AP_M_TMSI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - M-TMSI + if(LIBLTE_S1AP_M_TMSI_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE OldBSS_ToNewBSS_Information DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_oldbss_tonewbss_information( + LIBLTE_S1AP_OLDBSS_TONEWBSS_INFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - OldBSS-ToNewBSS-Information + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_oldbss_tonewbss_information( + uint8_t **ptr, + LIBLTE_S1AP_OLDBSS_TONEWBSS_INFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - OldBSS-ToNewBSS-Information + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PLMNidentity STATIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_plmnidentity( + LIBLTE_S1AP_PLMNIDENTITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - PLMNidentity + if(LIBLTE_S1AP_PLMNIDENTITY_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up_zero(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_plmnidentity( + uint8_t **ptr, + LIBLTE_S1AP_PLMNIDENTITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static octet string - PLMNidentity + if(LIBLTE_S1AP_PLMNIDENTITY_OCTET_STRING_LEN > 2) { // X.691 Sec.16 + liblte_align_up(ptr, 8); + } + // Octets + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ReceiveStatusOfULPDCPSDUsExtended DYNAMIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_receivestatusofulpdcpsdusextended( + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic bit string - ReceiveStatusOfULPDCPSDUsExtended + // lb:1, ub:16384 + // Length + liblte_value_2_bits(ie->n_bits-1, ptr, 14); + liblte_align_up_zero(ptr, 8); + + // Bits + uint32_t i; + for(i=0;in_bits;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_receivestatusofulpdcpsdusextended( + uint8_t **ptr, + LIBLTE_S1AP_RECEIVESTATUSOFULPDCPSDUSEXTENDED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic bit string - ReceiveStatusOfULPDCPSDUsExtended + // lb:1, ub:16384 + // Length + ie->n_bits = liblte_bits_2_value(ptr, 14) + 1; + liblte_align_up(ptr, 8); + + // Bits + uint32_t i; + for(i=0;in_bits;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RequestType SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_requesttype( + LIBLTE_S1AP_REQUESTTYPE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("RequestType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eventtype(&ie->eventType, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_reportarea(&ie->reportArea, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_requesttype( + uint8_t **ptr, + LIBLTE_S1AP_REQUESTTYPE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("RequestType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eventtype(ptr, &ie->eventType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_reportarea(ptr, &ie->reportArea) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RRC_Container DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rrc_container( + LIBLTE_S1AP_RRC_CONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - RRC-Container + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rrc_container( + uint8_t **ptr, + LIBLTE_S1AP_RRC_CONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - RRC-Container + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE nextHopChainingCount INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nexthopchainingcount( + LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->nextHopChainingCount + // lb:0, ub:7 + liblte_value_2_bits(ie->nextHopChainingCount, ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nexthopchainingcount( + uint8_t **ptr, + LIBLTE_S1AP_NEXTHOPCHAININGCOUNT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->nextHopChainingCount + // lb:0, ub:7 + ie->nextHopChainingCount = (uint8_t)liblte_bits_2_value(ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE SecurityContext SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitycontext( + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SecurityContext error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_nexthopchainingcount(&ie->nextHopChainingCount, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_securitykey(&ie->nextHopParameter, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitycontext( + uint8_t **ptr, + LIBLTE_S1AP_SECURITYCONTEXT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SecurityContext error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_nexthopchainingcount(ptr, &ie->nextHopChainingCount) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_securitykey(ptr, &ie->nextHopParameter) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ServedMMECs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedmmecs( + LIBLTE_S1AP_SERVEDMMECS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ServedMMECs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_mme_code(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedmmecs( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDMMECS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ServedMMECs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_mme_code(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TimeSynchronizationInfo SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timesynchronizationinfo( + LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TimeSynchronizationInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_stratumlevel(&ie->stratumLevel, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_synchronizationstatus(&ie->synchronizationStatus, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timesynchronizationinfo( + uint8_t **ptr, + LIBLTE_S1AP_TIMESYNCHRONIZATIONINFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TimeSynchronizationInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_stratumlevel(ptr, &ie->stratumLevel) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_synchronizationstatus(ptr, &ie->synchronizationStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai( + LIBLTE_S1AP_TAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TAI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMNidentity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_tac(&ie->tAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai( + uint8_t **ptr, + LIBLTE_S1AP_TAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TAI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMNidentity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_tac(ptr, &ie->tAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TABasedMDT SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tabasedmdt( + LIBLTE_S1AP_TABASEDMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TABasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_talistformdt(&ie->tAListforMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tabasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_TABASEDMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TABasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_talistformdt(ptr, &ie->tAListforMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargeteNB_ToSourceeNB_TransparentContainer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_tosourceenb_transparentcontainer( + LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TargeteNB_ToSourceeNB_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_rrc_container(&ie->rRC_Container, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_tosourceenb_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TargeteNB_ToSourceeNB_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_rrc_container(ptr, &ie->rRC_Container) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M1ThresholdEventA2 SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1thresholdeventa2( + LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M1ThresholdEventA2 error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_measurementthresholda2(&ie->measurementThreshold, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1thresholdeventa2( + uint8_t **ptr, + LIBLTE_S1AP_M1THRESHOLDEVENTA2_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M1ThresholdEventA2 error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_measurementthresholda2(ptr, &ie->measurementThreshold) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TransportInformation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_transportinformation( + LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TransportInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->uL_GTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_transportinformation( + uint8_t **ptr, + LIBLTE_S1AP_TRANSPORTINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TransportInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->uL_GTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TunnelInformation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tunnelinformation( + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TunnelInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->uDP_Port_Number_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->uDP_Port_Number_present) { + if(liblte_s1ap_pack_port_number(&ie->uDP_Port_Number, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tunnelinformation( + uint8_t **ptr, + LIBLTE_S1AP_TUNNELINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TunnelInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->uDP_Port_Number_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->uDP_Port_Number_present) { + if(liblte_s1ap_unpack_port_number(ptr, &ie->uDP_Port_Number) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UE_S1AP_IDs CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_ids( + LIBLTE_S1AP_UE_S1AP_IDS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UE_S1AP_IDs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) { + if(liblte_s1ap_pack_ue_s1ap_id_pair(&ie->choice.uE_S1AP_ID_pair, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID) { + if(liblte_s1ap_pack_mme_ue_s1ap_id(&ie->choice.mME_UE_S1AP_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_ids( + uint8_t **ptr, + LIBLTE_S1AP_UE_S1AP_IDS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UE_S1AP_IDs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) { + if(liblte_s1ap_unpack_ue_s1ap_id_pair(ptr, &ie->choice.uE_S1AP_ID_pair) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &ie->choice.mME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENBX2ExtTLA SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttla( + LIBLTE_S1AP_ENBX2EXTTLA_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ENBX2ExtTLA error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iPsecTLA_present?1:0, ptr, 1); + liblte_value_2_bits(ie->gTPTLAa_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(ie->iPsecTLA_present) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->iPsecTLA, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->gTPTLAa_present) { + if(liblte_s1ap_pack_enbx2gtptlas(&ie->gTPTLAa, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttla( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2EXTTLA_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ENBX2ExtTLA error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iPsecTLA_present = liblte_bits_2_value(ptr, 1); + ie->gTPTLAa_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(ie->iPsecTLA_present) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->iPsecTLA) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->gTPTLAa_present) { + if(liblte_s1ap_unpack_enbx2gtptlas(ptr, &ie->gTPTLAa) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE BPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:6 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bplmns( + LIBLTE_S1AP_BPLMNS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("BPLMNs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 3); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tbcd_string(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bplmns( + uint8_t **ptr, + LIBLTE_S1AP_BPLMNS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 3) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("BPLMNs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cause CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cause( + LIBLTE_S1AP_CAUSE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cause error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 3); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK) { + if(liblte_s1ap_pack_causeradionetwork(&ie->choice.radioNetwork, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT) { + if(liblte_s1ap_pack_causetransport(&ie->choice.transport, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_NAS) { + if(liblte_s1ap_pack_causenas(&ie->choice.nas, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL) { + if(liblte_s1ap_pack_causeprotocol(&ie->choice.protocol, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_MISC) { + if(liblte_s1ap_pack_causemisc(&ie->choice.misc, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cause( + uint8_t **ptr, + LIBLTE_S1AP_CAUSE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cause error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_CAUSE_CHOICE_ENUM)liblte_bits_2_value(ptr, 3); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK) { + if(liblte_s1ap_unpack_causeradionetwork(ptr, &ie->choice.radioNetwork) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT) { + if(liblte_s1ap_unpack_causetransport(ptr, &ie->choice.transport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_NAS) { + if(liblte_s1ap_unpack_causenas(ptr, &ie->choice.nas) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL) { + if(liblte_s1ap_unpack_causeprotocol(ptr, &ie->choice.protocol) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_CAUSE_CHOICE_MISC) { + if(liblte_s1ap_unpack_causemisc(ptr, &ie->choice.misc) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Cdma2000OneXSRVCCInfo SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexsrvccinfo( + LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000OneXSRVCCInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_cdma2000onexmeid(&ie->cdma2000OneXMEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cdma2000onexmsi(&ie->cdma2000OneXMSI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cdma2000onexpilot(&ie->cdma2000OneXPilot, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexsrvccinfo( + uint8_t **ptr, + LIBLTE_S1AP_CDMA2000ONEXSRVCCINFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Cdma2000OneXSRVCCInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_cdma2000onexmeid(ptr, &ie->cdma2000OneXMEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cdma2000onexmsi(ptr, &ie->cdma2000OneXMSI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cdma2000onexpilot(ptr, &ie->cdma2000OneXPilot) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CGI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cgi( + LIBLTE_S1AP_CGI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CGI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->rAC_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMNidentity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_lac(&ie->lAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_ci(&ie->cI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->rAC_present) { + if(liblte_s1ap_pack_rac(&ie->rAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cgi( + uint8_t **ptr, + LIBLTE_S1AP_CGI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CGI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->rAC_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMNidentity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_lac(ptr, &ie->lAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_ci(ptr, &ie->cI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->rAC_present) { + if(liblte_s1ap_unpack_rac(ptr, &ie->rAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE COUNTValueExtended SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalueextended( + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("COUNTValueExtended error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_pdcp_snextended(&ie->pDCP_SNExtended, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_hfnmodified(&ie->hFNModified, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalueextended( + uint8_t **ptr, + LIBLTE_S1AP_COUNTVALUEEXTENDED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("COUNTValueExtended error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_pdcp_snextended(ptr, &ie->pDCP_SNExtended) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_hfnmodified(ptr, &ie->hFNModified) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics_IE_List DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_list( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CriticalityDiagnostics_IE_List pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_criticalitydiagnostics_ie_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_list( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_IE_LIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CriticalityDiagnostics_IE_List unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_criticalitydiagnostics_ie_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Global_ENB_ID SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_global_enb_id( + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Global_ENB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMNidentity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_enb_id(&ie->eNB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_global_enb_id( + uint8_t **ptr, + LIBLTE_S1AP_GLOBAL_ENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Global_ENB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMNidentity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_enb_id(ptr, &ie->eNB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:15 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eplmns( + LIBLTE_S1AP_EPLMNS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("EPLMNs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tbcd_string(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eplmns( + uint8_t **ptr, + LIBLTE_S1AP_EPLMNS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("EPLMNs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem( + LIBLTE_S1AP_E_RABITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cause(&ie->cause, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cause(ptr, &ie->cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EUTRAN_CGI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutran_cgi( + LIBLTE_S1AP_EUTRAN_CGI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("EUTRAN_CGI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMNidentity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cellidentity(&ie->cell_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutran_cgi( + uint8_t **ptr, + LIBLTE_S1AP_EUTRAN_CGI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("EUTRAN_CGI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMNidentity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cellidentity(ptr, &ie->cell_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenTAs_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas_item( + LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenTAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMN_Identity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_forbiddentacs(&ie->forbiddenTACs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas_item( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTAS_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenTAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMN_Identity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_forbiddentacs(ptr, &ie->forbiddenTACs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenLAs_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas_item( + LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenLAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMN_Identity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_forbiddenlacs(&ie->forbiddenLACs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas_item( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLAS_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ForbiddenLAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMN_Identity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_forbiddenlacs(ptr, &ie->forbiddenLACs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LAI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lai( + LIBLTE_S1AP_LAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("LAI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMNidentity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_lac(&ie->lAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lai( + uint8_t **ptr, + LIBLTE_S1AP_LAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("LAI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMNidentity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_lac(ptr, &ie->lAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M4Configuration SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4configuration( + LIBLTE_S1AP_M4CONFIGURATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M4Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_m4period(&ie->m4period, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_links_to_log(&ie->m4_links_to_log, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4configuration( + uint8_t **ptr, + LIBLTE_S1AP_M4CONFIGURATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M4Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_m4period(ptr, &ie->m4period) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_links_to_log(ptr, &ie->m4_links_to_log) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MDTPLMNList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdtplmnlist( + LIBLTE_S1AP_MDTPLMNLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("MDTPLMNList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tbcd_string(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdtplmnlist( + uint8_t **ptr, + LIBLTE_S1AP_MDTPLMNLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("MDTPLMNList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MMERelaySupportIndicator ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmerelaysupportindicator( + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MMERelaySupportIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmerelaysupportindicator( + uint8_t **ptr, + LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MMERelaySupportIndicator error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_MMERELAYSUPPORTINDICATOR_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PagingPriority ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pagingpriority( + LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("PagingPriority error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pagingpriority( + uint8_t **ptr, + LIBLTE_S1AP_PAGINGPRIORITY_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("PagingPriority error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_PAGINGPRIORITY_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PriorityLevel INTEGER +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_prioritylevel( + LIBLTE_S1AP_PRIORITYLEVEL_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PriorityLevel + // lb:0, ub:15 + liblte_value_2_bits(ie->PriorityLevel, ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_prioritylevel( + uint8_t **ptr, + LIBLTE_S1AP_PRIORITYLEVEL_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Integer - ie->PriorityLevel + // lb:0, ub:15 + ie->PriorityLevel = (uint8_t)liblte_bits_2_value(ptr, 4); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ECGIListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ecgilistforrestart( + LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ECGIListForRestart pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_eutran_cgi(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ecgilistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_ECGILISTFORRESTART_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ECGIListForRestart unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SourceeNB_ID SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_id( + LIBLTE_S1AP_SOURCEENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_global_enb_id(&ie->global_ENB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_tai(&ie->selected_TAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_id( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_global_enb_id(ptr, &ie->global_ENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_tai(ptr, &ie->selected_TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ServedPLMNs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:32 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedplmns( + LIBLTE_S1AP_SERVEDPLMNS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ServedPLMNs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 5); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tbcd_string(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedplmns( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDPLMNS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 5) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ServedPLMNs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SupportedTAs_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas_item( + LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SupportedTAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tac(&ie->tAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_bplmns(&ie->broadcastPLMNs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas_item( + uint8_t **ptr, + LIBLTE_S1AP_SUPPORTEDTAS_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SupportedTAs_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tac(ptr, &ie->tAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_bplmns(ptr, &ie->broadcastPLMNs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAIListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistformdt( + LIBLTE_S1AP_TAILISTFORMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAIListforMDT pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 3); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tai(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistformdt( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 3) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAIListforMDT unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tai(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CompletedCellinTAI_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai_item( + LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CompletedCellinTAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai_item( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINTAI_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CompletedCellinTAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargeteNB_ID SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_id( + LIBLTE_S1AP_TARGETENB_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TargeteNB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_global_enb_id(&ie->global_ENB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_tai(&ie->selected_TAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_id( + uint8_t **ptr, + LIBLTE_S1AP_TARGETENB_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TargeteNB_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_global_enb_id(ptr, &ie->global_ENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_tai(ptr, &ie->selected_TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargetBSS_ToSourceBSS_TransparentContainer DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetbss_tosourcebss_transparentcontainer( + LIBLTE_S1AP_TARGETBSS_TOSOURCEBSS_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - TargetBSS-ToSourceBSS-TransparentContainer + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetbss_tosourcebss_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_TARGETBSS_TOSOURCEBSS_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - TargetBSS-ToSourceBSS-TransparentContainer + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAIListForRestart DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:2048 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistforrestart( + LIBLTE_S1AP_TAILISTFORRESTART_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAIListForRestart pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 11); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tai(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistforrestart( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORRESTART_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 11) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAIListForRestart unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tai(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UserLocationInformation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_userlocationinformation( + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UserLocationInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eutran_cgi, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_tai(&ie->tai, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_userlocationinformation( + uint8_t **ptr, + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UserLocationInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eutran_cgi) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_tai(ptr, &ie->tai) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENBX2ExtTLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttlas( + LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ENBX2ExtTLAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_enbx2exttla(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttlas( + uint8_t **ptr, + LIBLTE_S1AP_ENBX2EXTTLAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ENBX2ExtTLAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_enbx2exttla(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE AllocationAndRetentionPriority SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_allocationandretentionpriority( + LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("AllocationAndRetentionPriority error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_prioritylevel(&ie->priorityLevel, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum - ie->pre_emptionCapability + liblte_value_2_bits(ie->pre_emptionCapability, ptr, 1); + + // Enum - ie->pre_emptionVulnerability + liblte_value_2_bits(ie->pre_emptionVulnerability, ptr, 1); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_allocationandretentionpriority( + uint8_t **ptr, + LIBLTE_S1AP_ALLOCATIONANDRETENTIONPRIORITY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("AllocationAndRetentionPriority error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_prioritylevel(ptr, &ie->priorityLevel) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum - ie->pre_emptionCapability + ie->pre_emptionCapability = (LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_ENUM)liblte_bits_2_value(ptr, 1); + + // Enum - ie->pre_emptionVulnerability + ie->pre_emptionVulnerability = (LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_ENUM)liblte_bits_2_value(ptr, 1); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CancelledCellinEAI_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai_item( + LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CancelledCellinEAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_numberofbroadcasts(&ie->numberOfBroadcasts, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai_item( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINEAI_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CancelledCellinEAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_numberofbroadcasts(ptr, &ie->numberOfBroadcasts) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CancelledCellinTAI_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai_item( + LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CancelledCellinTAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_numberofbroadcasts(&ie->numberOfBroadcasts, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai_item( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINTAI_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CancelledCellinTAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_numberofbroadcasts(ptr, &ie->numberOfBroadcasts) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellID_Broadcast_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast_item( + LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CellID_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_BROADCAST_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CellID_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellID_Cancelled_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled_item( + LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CellID_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_numberofbroadcasts(&ie->numberOfBroadcasts, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_CANCELLED_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CellID_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_numberofbroadcasts(ptr, &ie->numberOfBroadcasts) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellIdListforMDT DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:32 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellidlistformdt( + LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CellIdListforMDT pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 5); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_eutran_cgi(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellidlistformdt( + uint8_t **ptr, + LIBLTE_S1AP_CELLIDLISTFORMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 5) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CellIdListforMDT unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CSG_Id STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_id( + LIBLTE_S1AP_CSG_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - CSG-Id + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_id( + uint8_t **ptr, + LIBLTE_S1AP_CSG_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - CSG-Id + liblte_align_up(ptr, 8); + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CSG_IdList_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist_item( + LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CSG_IdList_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_csg_id(&ie->cSG_Id, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist_item( + uint8_t **ptr, + LIBLTE_S1AP_CSG_IDLIST_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CSG_IdList_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_csg_id(ptr, &ie->cSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Direct_Forwarding_Path_Availability ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_direct_forwarding_path_availability( + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Direct_Forwarding_Path_Availability error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_direct_forwarding_path_availability( + uint8_t **ptr, + LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Direct_Forwarding_Path_Availability error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_DIRECT_FORWARDING_PATH_AVAILABILITY_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CompletedCellinEAI_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai_item( + LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CompletedCellinEAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->eCGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai_item( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINEAI_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CompletedCellinEAI_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->eCGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABInformationList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlist( + LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABInformationList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabinformationlistitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABINFORMATIONLISTITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABINFORMATIONLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABInformationList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABINFORMATIONLISTITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabinformationlistitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenTAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas( + LIBLTE_S1AP_FORBIDDENTAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ForbiddenTAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_forbiddentas_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENTAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ForbiddenTAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_forbiddentas_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GUMMEI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummei( + LIBLTE_S1AP_GUMMEI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("GUMMEI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->pLMN_Identity, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_mme_group_id(&ie->mME_Group_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_mme_code(&ie->mME_Code, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummei( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("GUMMEI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->pLMN_Identity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_mme_group_id(ptr, &ie->mME_Group_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_mme_code(ptr, &ie->mME_Code) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LoggedMDT SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggedmdt( + LIBLTE_S1AP_LOGGEDMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("LoggedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + // Enum - ie->loggingInterval + liblte_value_2_bits(ie->loggingInterval, ptr, 3); + + // Enum - ie->loggingDuration + liblte_value_2_bits(ie->loggingDuration, ptr, 3); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggedmdt( + uint8_t **ptr, + LIBLTE_S1AP_LOGGEDMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("LoggedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + // Enum - ie->loggingInterval + ie->loggingInterval = (LIBLTE_S1AP_LOGGINGINTERVAL_ENUM)liblte_bits_2_value(ptr, 3); + + // Enum - ie->loggingDuration + ie->loggingDuration = (LIBLTE_S1AP_LOGGINGDURATION_ENUM)liblte_bits_2_value(ptr, 3); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE NASSecurityParametersfromE_UTRAN DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nassecurityparametersfrome_utran( + LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NASSecurityParametersfromE-UTRAN + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nassecurityparametersfrome_utran( + uint8_t **ptr, + LIBLTE_S1AP_NASSECURITYPARAMETERSFROME_UTRAN_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - NASSecurityParametersfromE-UTRAN + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ReportAmountMDT ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reportamountmdt( + LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + liblte_value_2_bits(*ie, ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reportamountmdt( + uint8_t **ptr, + LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Enum - *ie + *ie = (LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM)liblte_bits_2_value(ptr, 3); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ServedGUMMEIsItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeisitem( + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ServedGUMMEIsItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_servedplmns(&ie->servedPLMNs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_servedgroupids(&ie->servedGroupIDs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_servedmmecs(&ie->servedMMECs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeisitem( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ServedGUMMEIsItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_servedplmns(ptr, &ie->servedPLMNs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_servedgroupids(ptr, &ie->servedGroupIDs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_servedmmecs(ptr, &ie->servedMMECs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE S_TMSI SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s_tmsi( + LIBLTE_S1AP_S_TMSI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("S_TMSI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_mme_code(&ie->mMEC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_m_tmsi(&ie->m_TMSI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s_tmsi( + uint8_t **ptr, + LIBLTE_S1AP_S_TMSI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("S_TMSI error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_mme_code(ptr, &ie->mMEC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_m_tmsi(ptr, &ie->m_TMSI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAIListforWarning DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailistforwarning( + LIBLTE_S1AP_TAILISTFORWARNING_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAIListforWarning pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tai(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailistforwarning( + uint8_t **ptr, + LIBLTE_S1AP_TAILISTFORWARNING_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAIListforWarning unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tai(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CompletedCellinTAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai( + LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CompletedCellinTAI pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_completedcellintai_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINTAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CompletedCellinTAI unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_completedcellintai_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargetRNC_ID SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_id( + LIBLTE_S1AP_TARGETRNC_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TargetRNC_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->rAC_present?1:0, ptr, 1); + liblte_value_2_bits(ie->extendedRNC_ID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_lai(&ie->lAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->rAC_present) { + if(liblte_s1ap_pack_rac(&ie->rAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(liblte_s1ap_pack_rnc_id(&ie->rNC_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->extendedRNC_ID_present) { + if(liblte_s1ap_pack_extendedrnc_id(&ie->extendedRNC_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_id( + uint8_t **ptr, + LIBLTE_S1AP_TARGETRNC_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TargetRNC_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->rAC_present = liblte_bits_2_value(ptr, 1); + ie->extendedRNC_ID_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_lai(ptr, &ie->lAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->rAC_present) { + if(liblte_s1ap_unpack_rac(ptr, &ie->rAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(liblte_s1ap_unpack_rnc_id(ptr, &ie->rNC_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->extendedRNC_ID_present) { + if(liblte_s1ap_unpack_extendedrnc_id(ptr, &ie->extendedRNC_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UE_associatedLogicalS1_ConnectionItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitem( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UE_associatedLogicalS1_ConnectionItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->mME_UE_S1AP_ID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->eNB_UE_S1AP_ID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(ie->mME_UE_S1AP_ID_present) { + if(liblte_s1ap_pack_mme_ue_s1ap_id(&ie->mME_UE_S1AP_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->eNB_UE_S1AP_ID_present) { + if(liblte_s1ap_pack_enb_ue_s1ap_id(&ie->eNB_UE_S1AP_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UE_associatedLogicalS1_ConnectionItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->mME_UE_S1AP_ID_present = liblte_bits_2_value(ptr, 1); + ie->eNB_UE_S1AP_ID_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(ie->mME_UE_S1AP_ID_present) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &ie->mME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->eNB_UE_S1AP_ID_present) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &ie->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UEPagingID CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uepagingid( + LIBLTE_S1AP_UEPAGINGID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("UEPagingID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_S_TMSI) { + if(liblte_s1ap_pack_s_tmsi(&ie->choice.s_TMSI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_IMSI) { + if(liblte_s1ap_pack_imsi(&ie->choice.iMSI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uepagingid( + uint8_t **ptr, + LIBLTE_S1AP_UEPAGINGID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("UEPagingID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_UEPAGINGID_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_S_TMSI) { + if(liblte_s1ap_unpack_s_tmsi(ptr, &ie->choice.s_TMSI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_IMSI) { + if(liblte_s1ap_unpack_imsi(ptr, &ie->choice.iMSI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Bearers_SubjectToStatusTransfer_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_item( + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Bearers_SubjectToStatusTransfer_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->receiveStatusofULPDCPSDUs_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_countvalue(&ie->uL_COUNTvalue, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_countvalue(&ie->dL_COUNTvalue, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->receiveStatusofULPDCPSDUs_present) { + if(liblte_s1ap_pack_receivestatusofulpdcpsdus(&ie->receiveStatusofULPDCPSDUs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_item( + uint8_t **ptr, + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Bearers_SubjectToStatusTransfer_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->receiveStatusofULPDCPSDUs_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_countvalue(ptr, &ie->uL_COUNTvalue) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_countvalue(ptr, &ie->dL_COUNTvalue) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->receiveStatusofULPDCPSDUs_present) { + if(liblte_s1ap_unpack_receivestatusofulpdcpsdus(ptr, &ie->receiveStatusofULPDCPSDUs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CancelledCellinEAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai( + LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CancelledCellinEAI pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_cancelledcellineai_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINEAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CancelledCellinEAI unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_cancelledcellineai_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellID_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast( + LIBLTE_S1AP_CELLID_BROADCAST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CellID_Broadcast pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_cellid_broadcast_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_BROADCAST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CellID_Broadcast unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_cellid_broadcast_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellBasedMDT SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellbasedmdt( + LIBLTE_S1AP_CELLBASEDMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CellBasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_cellidlistformdt(&ie->cellIdListforMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellbasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_CELLBASEDMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CellBasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_cellidlistformdt(ptr, &ie->cellIdListforMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CSG_IdList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist( + LIBLTE_S1AP_CSG_IDLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CSG_IdList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_csg_idlist_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist( + uint8_t **ptr, + LIBLTE_S1AP_CSG_IDLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CSG_IdList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_csg_idlist_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ECGIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ecgilist( + LIBLTE_S1AP_ECGILIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ECGIList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_eutran_cgi(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ecgilist( + uint8_t **ptr, + LIBLTE_S1AP_ECGILIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ECGIList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Cancelled_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled_item( + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("EmergencyAreaID_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_emergencyareaid(&ie->emergencyAreaID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cancelledcellineai(&ie->cancelledCellinEAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("EmergencyAreaID_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_emergencyareaid(ptr, &ie->emergencyAreaID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cancelledcellineai(ptr, &ie->cancelledCellinEAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GERAN_Cell_ID SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_geran_cell_id( + LIBLTE_S1AP_GERAN_CELL_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("GERAN_Cell_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_lai(&ie->lAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_rac(&ie->rAC, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_ci(&ie->cI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_geran_cell_id( + uint8_t **ptr, + LIBLTE_S1AP_GERAN_CELL_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("GERAN_Cell_ID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_lai(ptr, &ie->lAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_rac(ptr, &ie->rAC) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_ci(ptr, &ie->cI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rablist( + LIBLTE_S1AP_E_RABLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rablist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ForbiddenLAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas( + LIBLTE_S1AP_FORBIDDENLAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ForbiddenLAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_forbiddenlas_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas( + uint8_t **ptr, + LIBLTE_S1AP_FORBIDDENLAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ForbiddenLAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_forbiddenlas_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MDT_Location_Info STATIC BIT STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_location_info( + LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MDT-Location-Info + uint32_t i; + for(i=0;ibuffer[i], ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_location_info( + uint8_t **ptr, + LIBLTE_S1AP_MDT_LOCATION_INFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Static bit string - MDT-Location-Info + uint32_t i; + for(i=0;ibuffer[i] = liblte_bits_2_value(ptr, 1); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE M1PeriodicReporting SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1periodicreporting( + LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("M1PeriodicReporting error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + // Enum - ie->reportInterval + liblte_value_2_bits(ie->reportInterval, ptr, 4); + + // Enum - ie->reportAmount + liblte_value_2_bits(ie->reportAmount, ptr, 3); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1periodicreporting( + uint8_t **ptr, + LIBLTE_S1AP_M1PERIODICREPORTING_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("M1PeriodicReporting error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + // Enum - ie->reportInterval + ie->reportInterval = (LIBLTE_S1AP_REPORTINTERVALMDT_ENUM)liblte_bits_2_value(ptr, 4); + + // Enum - ie->reportAmount + ie->reportAmount = (LIBLTE_S1AP_REPORTAMOUNTMDT_ENUM)liblte_bits_2_value(ptr, 3); + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE eHRPD_Sector_ID DYNAMIC OCTET STRING +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ehrpd_sector_id( + LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - eHRPD-Sector-ID + // Length + if(ie->n_octets < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 7); + } else if(ie->n_octets < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(ie->n_octets, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + liblte_value_2_bits(ie->buffer[i], ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ehrpd_sector_id( + uint8_t **ptr, + LIBLTE_S1AP_EHRPD_SECTOR_ID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Dynamic octet string - eHRPD-Sector-ID + // Length + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + ie->n_octets = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Octets + uint32_t i; + for(i=0;in_octets;i++) { + ie->buffer[i] = liblte_bits_2_value(ptr, 8); + } + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* ProtocolIE RIMRoutingAddress CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimroutingaddress( + LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("RIMRoutingAddress error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_GERAN_CELL_ID) { + if(liblte_s1ap_pack_geran_cell_id(&ie->choice.gERAN_Cell_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_TARGETRNC_ID) { + if(liblte_s1ap_pack_targetrnc_id(&ie->choice.targetRNC_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_EHRPD_SECTOR_ID) { + if(liblte_s1ap_pack_ehrpd_sector_id(&ie->choice.eHRPD_Sector_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimroutingaddress( + uint8_t **ptr, + LIBLTE_S1AP_RIMROUTINGADDRESS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("RIMRoutingAddress error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_GERAN_CELL_ID) { + if(liblte_s1ap_unpack_geran_cell_id(ptr, &ie->choice.gERAN_Cell_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_TARGETRNC_ID) { + if(liblte_s1ap_unpack_targetrnc_id(ptr, &ie->choice.targetRNC_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RIMROUTINGADDRESS_CHOICE_EHRPD_SECTOR_ID) { + if(liblte_s1ap_unpack_ehrpd_sector_id(ptr, &ie->choice.eHRPD_Sector_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ServedGUMMEIs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:8 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeis( + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("ServedGUMMEIs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 3); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_servedgummeisitem(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeis( + uint8_t **ptr, + LIBLTE_S1AP_SERVEDGUMMEIS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 3) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("ServedGUMMEIs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_servedgummeisitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAIBasedMDT SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taibasedmdt( + LIBLTE_S1AP_TAIBASEDMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TAIBasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tailistformdt(&ie->tAIListforMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taibasedmdt( + uint8_t **ptr, + LIBLTE_S1AP_TAIBASEDMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TAIBasedMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tailistformdt(ptr, &ie->tAIListforMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAI_Broadcast_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast_item( + LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TAI_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tai(&ie->tAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_completedcellintai(&ie->completedCellinTAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_TAI_BROADCAST_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TAI_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tai(ptr, &ie->tAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_completedcellintai(ptr, &ie->completedCellinTAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TargetID CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetid( + LIBLTE_S1AP_TARGETID_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TargetID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID) { + if(liblte_s1ap_pack_targetenb_id(&ie->choice.targeteNB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_TARGETRNC_ID) { + if(liblte_s1ap_pack_targetrnc_id(&ie->choice.targetRNC_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_CGI) { + if(liblte_s1ap_pack_cgi(&ie->choice.cGI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetid( + uint8_t **ptr, + LIBLTE_S1AP_TARGETID_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TargetID error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_TARGETID_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_TARGETENB_ID) { + if(liblte_s1ap_unpack_targetenb_id(ptr, &ie->choice.targeteNB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_TARGETRNC_ID) { + if(liblte_s1ap_unpack_targetrnc_id(ptr, &ie->choice.targetRNC_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_TARGETID_CHOICE_CGI) { + if(liblte_s1ap_unpack_cgi(ptr, &ie->choice.cGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE WarningAreaList CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_warningarealist( + LIBLTE_S1AP_WARNINGAREALIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("WarningAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_CELLIDLIST) { + if(liblte_s1ap_pack_ecgilist(&ie->choice.cellIDList, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_TRACKINGAREALISTFORWARNING) { + if(liblte_s1ap_pack_tailistforwarning(&ie->choice.trackingAreaListforWarning, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_EMERGENCYAREAIDLIST) { + if(liblte_s1ap_pack_emergencyareaidlist(&ie->choice.emergencyAreaIDList, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_warningarealist( + uint8_t **ptr, + LIBLTE_S1AP_WARNINGAREALIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("WarningAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_WARNINGAREALIST_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_CELLIDLIST) { + if(liblte_s1ap_unpack_ecgilist(ptr, &ie->choice.cellIDList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_TRACKINGAREALISTFORWARNING) { + if(liblte_s1ap_unpack_tailistforwarning(ptr, &ie->choice.trackingAreaListforWarning) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_WARNINGAREALIST_CHOICE_EMERGENCYAREAIDLIST) { + if(liblte_s1ap_unpack_emergencyareaidlist(ptr, &ie->choice.emergencyAreaIDList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE AreaScopeOfMDT CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_areascopeofmdt( + LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("AreaScopeOfMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_CELLBASED) { + if(liblte_s1ap_pack_cellbasedmdt(&ie->choice.cellBased, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TABASED) { + if(liblte_s1ap_pack_tabasedmdt(&ie->choice.tABased, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_PLMNWIDE) { + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TAIBASED) { + if(liblte_s1ap_pack_taibasedmdt(&ie->choice.tAIBased, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_areascopeofmdt( + uint8_t **ptr, + LIBLTE_S1AP_AREASCOPEOFMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("AreaScopeOfMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_CELLBASED) { + if(liblte_s1ap_unpack_cellbasedmdt(ptr, &ie->choice.cellBased) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TABASED) { + if(liblte_s1ap_unpack_tabasedmdt(ptr, &ie->choice.tABased) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_PLMNWIDE) { + } else if(ie->choice_type == LIBLTE_S1AP_AREASCOPEOFMDT_CHOICE_TAIBASED) { + if(liblte_s1ap_unpack_taibasedmdt(ptr, &ie->choice.tAIBased) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CancelledCellinTAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai( + LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CancelledCellinTAI pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_cancelledcellintai_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai( + uint8_t **ptr, + LIBLTE_S1AP_CANCELLEDCELLINTAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CancelledCellinTAI unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_cancelledcellintai_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellType SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltype( + LIBLTE_S1AP_CELLTYPE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CellType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_cell_size(&ie->cell_Size, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltype( + uint8_t **ptr, + LIBLTE_S1AP_CELLTYPE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CellType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_cell_size(ptr, &ie->cell_Size) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled( + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("EmergencyAreaID_Cancelled pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_emergencyareaid_cancelled_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_CANCELLED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("EmergencyAreaID_Cancelled unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_emergencyareaid_cancelled_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE GUMMEIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummeilist( + LIBLTE_S1AP_GUMMEILIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("GUMMEIList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_gummei(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummeilist( + uint8_t **ptr, + LIBLTE_S1AP_GUMMEILIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("GUMMEIList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_gummei(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABLevelQoSParameters SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rablevelqosparameters( + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABLevelQoSParameters error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->gbrQosInformation_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_qci(&ie->qCI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_allocationandretentionpriority(&ie->allocationRetentionPriority, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->gbrQosInformation_present) { + if(liblte_s1ap_pack_gbr_qosinformation(&ie->gbrQosInformation, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rablevelqosparameters( + uint8_t **ptr, + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABLevelQoSParameters error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->gbrQosInformation_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_qci(ptr, &ie->qCI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_allocationandretentionpriority(ptr, &ie->allocationRetentionPriority) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->gbrQosInformation_present) { + if(liblte_s1ap_unpack_gbr_qosinformation(ptr, &ie->gbrQosInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LastVisitedEUTRANCellInformation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedeutrancellinformation( + LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedEUTRANCellInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_eutran_cgi(&ie->global_Cell_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_celltype(&ie->cellType, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_time_ue_stayedincell(&ie->time_UE_StayedInCell, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedeutrancellinformation( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDEUTRANCELLINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedEUTRANCellInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->global_Cell_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_celltype(ptr, &ie->cellType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_time_ue_stayedincell(ptr, &ie->time_UE_StayedInCell) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE RIMTransfer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimtransfer( + LIBLTE_S1AP_RIMTRANSFER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("RIMTransfer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->rIMRoutingAddress_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_riminformation(&ie->rIMInformation, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->rIMRoutingAddress_present) { + if(liblte_s1ap_pack_rimroutingaddress(&ie->rIMRoutingAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimtransfer( + uint8_t **ptr, + LIBLTE_S1AP_RIMTRANSFER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("RIMTransfer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->rIMRoutingAddress_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_riminformation(ptr, &ie->rIMInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->rIMRoutingAddress_present) { + if(liblte_s1ap_unpack_rimroutingaddress(ptr, &ie->rIMRoutingAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SupportedTAs DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas( + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("SupportedTAs pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_supportedtas_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas( + uint8_t **ptr, + LIBLTE_S1AP_SUPPORTEDTAS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("SupportedTAs unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_supportedtas_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAI_Cancelled_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled_item( + LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TAI_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tai(&ie->tAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cancelledcellintai(&ie->cancelledCellinTAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled_item( + uint8_t **ptr, + LIBLTE_S1AP_TAI_CANCELLED_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TAI_Cancelled_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tai(ptr, &ie->tAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cancelledcellintai(ptr, &ie->cancelledCellinTAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE X2TNLConfigurationInfo SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_x2tnlconfigurationinfo( + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("X2TNLConfigurationInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_enbx2tlas(&ie->eNBX2TransportLayerAddresses, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_x2tnlconfigurationinfo( + uint8_t **ptr, + LIBLTE_S1AP_X2TNLCONFIGURATIONINFO_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("X2TNLConfigurationInfo error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_enbx2tlas(ptr, &ie->eNBX2TransportLayerAddresses) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List Bearers_SubjectToStatusTransferList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransferlist( + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("Bearers_SubjectToStatusTransferList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_bearers_subjecttostatustransfer_item(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransferlist( + uint8_t **ptr, + LIBLTE_S1AP_BEARERS_SUBJECTTOSTATUSTRANSFERLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("Bearers_SubjectToStatusTransferList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_bearers_subjecttostatustransfer_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CellID_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled( + LIBLTE_S1AP_CELLID_CANCELLED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CellID_Cancelled pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_cellid_cancelled_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_CELLID_CANCELLED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CellID_Cancelled unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_cellid_cancelled_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CompletedCellinEAI DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai( + LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("CompletedCellinEAI pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_completedcellineai_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai( + uint8_t **ptr, + LIBLTE_S1AP_COMPLETEDCELLINEAI_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("CompletedCellinEAI unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_completedcellineai_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE HandoverRestrictionList SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrestrictionlist( + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("HandoverRestrictionList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->equivalentPLMNs_present?1:0, ptr, 1); + liblte_value_2_bits(ie->forbiddenTAs_present?1:0, ptr, 1); + liblte_value_2_bits(ie->forbiddenLAs_present?1:0, ptr, 1); + liblte_value_2_bits(ie->forbiddenInterRATs_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tbcd_string(&ie->servingPLMN, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->equivalentPLMNs_present) { + if(liblte_s1ap_pack_eplmns(&ie->equivalentPLMNs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->forbiddenTAs_present) { + if(liblte_s1ap_pack_forbiddentas(&ie->forbiddenTAs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->forbiddenLAs_present) { + if(liblte_s1ap_pack_forbiddenlas(&ie->forbiddenLAs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->forbiddenInterRATs_present) { + if(liblte_s1ap_pack_forbiddeninterrats(&ie->forbiddenInterRATs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrestrictionlist( + uint8_t **ptr, + LIBLTE_S1AP_HANDOVERRESTRICTIONLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("HandoverRestrictionList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->equivalentPLMNs_present = liblte_bits_2_value(ptr, 1); + ie->forbiddenTAs_present = liblte_bits_2_value(ptr, 1); + ie->forbiddenLAs_present = liblte_bits_2_value(ptr, 1); + ie->forbiddenInterRATs_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tbcd_string(ptr, &ie->servingPLMN) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->equivalentPLMNs_present) { + if(liblte_s1ap_unpack_eplmns(ptr, &ie->equivalentPLMNs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->forbiddenTAs_present) { + if(liblte_s1ap_unpack_forbiddentas(ptr, &ie->forbiddenTAs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->forbiddenLAs_present) { + if(liblte_s1ap_unpack_forbiddenlas(ptr, &ie->forbiddenLAs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->forbiddenInterRATs_present) { + if(liblte_s1ap_unpack_forbiddeninterrats(ptr, &ie->forbiddenInterRATs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE LastVisitedCell_Item CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedcell_item( + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedCell_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL) { + if(liblte_s1ap_pack_lastvisitedeutrancellinformation(&ie->choice.e_UTRAN_Cell, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_UTRAN_CELL) { + if(liblte_s1ap_pack_lastvisitedutrancellinformation(&ie->choice.uTRAN_Cell, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_GERAN_CELL) { + if(liblte_s1ap_pack_lastvisitedgerancellinformation(&ie->choice.gERAN_Cell, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedcell_item( + uint8_t **ptr, + LIBLTE_S1AP_LASTVISITEDCELL_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("LastVisitedCell_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_E_UTRAN_CELL) { + if(liblte_s1ap_unpack_lastvisitedeutrancellinformation(ptr, &ie->choice.e_UTRAN_Cell) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_UTRAN_CELL) { + if(liblte_s1ap_unpack_lastvisitedutrancellinformation(ptr, &ie->choice.uTRAN_Cell) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_LASTVISITEDCELL_ITEM_CHOICE_GERAN_CELL) { + if(liblte_s1ap_unpack_lastvisitedgerancellinformation(ptr, &ie->choice.gERAN_Cell) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SONInformationReply SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationreply( + LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformationReply error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->x2TNLConfigurationInfo_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(ie->x2TNLConfigurationInfo_present) { + if(liblte_s1ap_pack_x2tnlconfigurationinfo(&ie->x2TNLConfigurationInfo, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationreply( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATIONREPLY_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformationReply error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->x2TNLConfigurationInfo_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(ie->x2TNLConfigurationInfo_present) { + if(liblte_s1ap_unpack_x2tnlconfigurationinfo(ptr, &ie->x2TNLConfigurationInfo) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAI_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast( + LIBLTE_S1AP_TAI_BROADCAST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAI_Broadcast pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tai_broadcast_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_TAI_BROADCAST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAI_Broadcast unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tai_broadcast_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TimeToWait ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timetowait( + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TimeToWait error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 3); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timetowait( + uint8_t **ptr, + LIBLTE_S1AP_TIMETOWAIT_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TimeToWait error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_TIMETOWAIT_ENUM)liblte_bits_2_value(ptr, 3); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE UE_HistoryInformation DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:16 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_historyinformation( + LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("UE_HistoryInformation pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 4); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_lastvisitedcell_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_historyinformation( + uint8_t **ptr, + LIBLTE_S1AP_UE_HISTORYINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 4) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("UE_HistoryInformation unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_lastvisitedcell_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE CriticalityDiagnostics SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics( + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("CriticalityDiagnostics error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->procedureCode_present?1:0, ptr, 1); + liblte_value_2_bits(ie->triggeringMessage_present?1:0, ptr, 1); + liblte_value_2_bits(ie->procedureCriticality_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iEsCriticalityDiagnostics_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(ie->procedureCode_present) { + if(liblte_s1ap_pack_procedurecode(&ie->procedureCode, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->triggeringMessage_present) { + // Enum - ie->triggeringMessage + liblte_value_2_bits(ie->triggeringMessage, ptr, 2); + } + + if(ie->procedureCriticality_present) { + // Enum - ie->procedureCriticality + liblte_value_2_bits(ie->procedureCriticality, ptr, 2); + } + + if(ie->iEsCriticalityDiagnostics_present) { + if(liblte_s1ap_pack_criticalitydiagnostics_ie_list(&ie->iEsCriticalityDiagnostics, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics( + uint8_t **ptr, + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("CriticalityDiagnostics error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->procedureCode_present = liblte_bits_2_value(ptr, 1); + ie->triggeringMessage_present = liblte_bits_2_value(ptr, 1); + ie->procedureCriticality_present = liblte_bits_2_value(ptr, 1); + ie->iEsCriticalityDiagnostics_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(ie->procedureCode_present) { + if(liblte_s1ap_unpack_procedurecode(ptr, &ie->procedureCode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->triggeringMessage_present) { + // Enum - ie->triggeringMessage + ie->triggeringMessage = (LIBLTE_S1AP_TRIGGERINGMESSAGE_ENUM)liblte_bits_2_value(ptr, 2); + } + + if(ie->procedureCriticality_present) { + // Enum - ie->procedureCriticality + ie->procedureCriticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + } + + if(ie->iEsCriticalityDiagnostics_present) { + if(liblte_s1ap_unpack_criticalitydiagnostics_ie_list(ptr, &ie->iEsCriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Broadcast_Item SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast_item( + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("EmergencyAreaID_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_emergencyareaid(&ie->emergencyAreaID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_completedcellineai(&ie->completedCellinEAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast_item( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_ITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("EmergencyAreaID_Broadcast_Item error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_emergencyareaid(ptr, &ie->emergencyAreaID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_completedcellineai(ptr, &ie->completedCellinEAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ImmediateMDT SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_immediatemdt( + LIBLTE_S1AP_IMMEDIATEMDT_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ImmediateMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->m1thresholdeventA2_present?1:0, ptr, 1); + liblte_value_2_bits(ie->m1periodicReporting_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_measurementstoactivate(&ie->measurementsToActivate, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_m1reportingtrigger(&ie->m1reportingTrigger, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->m1thresholdeventA2_present) { + if(liblte_s1ap_pack_m1thresholdeventa2(&ie->m1thresholdeventA2, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->m1periodicReporting_present) { + if(liblte_s1ap_pack_m1periodicreporting(&ie->m1periodicReporting, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_immediatemdt( + uint8_t **ptr, + LIBLTE_S1AP_IMMEDIATEMDT_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ImmediateMDT error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->m1thresholdeventA2_present = liblte_bits_2_value(ptr, 1); + ie->m1periodicReporting_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_measurementstoactivate(ptr, &ie->measurementsToActivate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_m1reportingtrigger(ptr, &ie->m1reportingTrigger) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->m1thresholdeventA2_present) { + if(liblte_s1ap_unpack_m1thresholdeventa2(ptr, &ie->m1thresholdeventA2) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->m1periodicReporting_present) { + if(liblte_s1ap_unpack_m1periodicreporting(ptr, &ie->m1periodicReporting) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MDTMode CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdtmode( + LIBLTE_S1AP_MDTMODE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MDTMode error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_MDTMODE_CHOICE_IMMEDIATEMDT) { + if(liblte_s1ap_pack_immediatemdt(&ie->choice.immediateMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_MDTMODE_CHOICE_LOGGEDMDT) { + if(liblte_s1ap_pack_loggedmdt(&ie->choice.loggedMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdtmode( + uint8_t **ptr, + LIBLTE_S1AP_MDTMODE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MDTMode error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_MDTMODE_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_MDTMODE_CHOICE_IMMEDIATEMDT) { + if(liblte_s1ap_unpack_immediatemdt(ptr, &ie->choice.immediateMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_MDTMODE_CHOICE_LOGGEDMDT) { + if(liblte_s1ap_unpack_loggedmdt(ptr, &ie->choice.loggedMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SourceeNB_ToTargeteNB_TransparentContainer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer( + LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SourceeNB_ToTargeteNB_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->e_RABInformationList_present?1:0, ptr, 1); + liblte_value_2_bits(ie->subscriberProfileIDforRFP_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_rrc_container(&ie->rRC_Container, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->e_RABInformationList_present) { + if(liblte_s1ap_pack_e_rabinformationlist(&ie->e_RABInformationList, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(liblte_s1ap_pack_eutran_cgi(&ie->targetCell_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->subscriberProfileIDforRFP_present) { + if(liblte_s1ap_pack_subscriberprofileidforrfp(&ie->subscriberProfileIDforRFP, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(liblte_s1ap_pack_ue_historyinformation(&ie->uE_HistoryInformation, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_totargetenb_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SourceeNB_ToTargeteNB_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->e_RABInformationList_present = liblte_bits_2_value(ptr, 1); + ie->subscriberProfileIDforRFP_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_rrc_container(ptr, &ie->rRC_Container) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->e_RABInformationList_present) { + if(liblte_s1ap_unpack_e_rabinformationlist(ptr, &ie->e_RABInformationList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(liblte_s1ap_unpack_eutran_cgi(ptr, &ie->targetCell_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->subscriberProfileIDforRFP_present) { + if(liblte_s1ap_unpack_subscriberprofileidforrfp(ptr, &ie->subscriberProfileIDforRFP) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(liblte_s1ap_unpack_ue_historyinformation(ptr, &ie->uE_HistoryInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE EmergencyAreaID_Broadcast DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast( + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("EmergencyAreaID_Broadcast pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_emergencyareaid_broadcast_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast( + uint8_t **ptr, + LIBLTE_S1AP_EMERGENCYAREAID_BROADCAST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("EmergencyAreaID_Broadcast unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_emergencyareaid_broadcast_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE MDT_Configuration SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_configuration( + LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("MDT_Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_mdt_activation(&ie->mdt_Activation, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_areascopeofmdt(&ie->areaScopeOfMDT, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_mdtmode(&ie->mDTMode, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_configuration( + uint8_t **ptr, + LIBLTE_S1AP_MDT_CONFIGURATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("MDT_Configuration error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_mdt_activation(ptr, &ie->mdt_Activation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_areascopeofmdt(ptr, &ie->areaScopeOfMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_mdtmode(ptr, &ie->mDTMode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAI_Cancelled DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:65535 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled( + LIBLTE_S1AP_TAI_CANCELLED_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAI_Cancelled pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 16); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_tai_cancelled_item(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled( + uint8_t **ptr, + LIBLTE_S1AP_TAI_CANCELLED_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 16) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAI_Cancelled unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_tai_cancelled_item(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE BroadcastCancelledAreaList CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_broadcastcancelledarealist( + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("BroadcastCancelledAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_CELLID_CANCELLED) { + if(liblte_s1ap_pack_cellid_cancelled(&ie->choice.cellID_Cancelled, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_TAI_CANCELLED) { + if(liblte_s1ap_pack_tai_cancelled(&ie->choice.tAI_Cancelled, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_EMERGENCYAREAID_CANCELLED) { + if(liblte_s1ap_pack_emergencyareaid_cancelled(&ie->choice.emergencyAreaID_Cancelled, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_broadcastcancelledarealist( + uint8_t **ptr, + LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("BroadcastCancelledAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_CELLID_CANCELLED) { + if(liblte_s1ap_unpack_cellid_cancelled(ptr, &ie->choice.cellID_Cancelled) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_TAI_CANCELLED) { + if(liblte_s1ap_unpack_tai_cancelled(ptr, &ie->choice.tAI_Cancelled) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCANCELLEDAREALIST_CHOICE_EMERGENCYAREAID_CANCELLED) { + if(liblte_s1ap_unpack_emergencyareaid_cancelled(ptr, &ie->choice.emergencyAreaID_Cancelled) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ENB_StatusTransfer_TransparentContainer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_statustransfer_transparentcontainer( + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ENB_StatusTransfer_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_bearers_subjecttostatustransferlist(&ie->bearers_SubjectToStatusTransferList, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_statustransfer_transparentcontainer( + uint8_t **ptr, + LIBLTE_S1AP_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ENB_StatusTransfer_TransparentContainer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_bearers_subjecttostatustransferlist(ptr, &ie->bearers_SubjectToStatusTransferList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TraceActivation SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_traceactivation( + LIBLTE_S1AP_TRACEACTIVATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TraceActivation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_utran_trace_id(&ie->e_UTRAN_Trace_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_interfacestotrace(&ie->interfacesToTrace, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_tracedepth(&ie->traceDepth, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->traceCollectionEntityIPAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_traceactivation( + uint8_t **ptr, + LIBLTE_S1AP_TRACEACTIVATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TraceActivation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_utran_trace_id(ptr, &ie->e_UTRAN_Trace_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_interfacestotrace(ptr, &ie->interfacesToTrace) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_tracedepth(ptr, &ie->traceDepth) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->traceCollectionEntityIPAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE BroadcastCompletedAreaList CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_broadcastcompletedarealist( + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("BroadcastCompletedAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_CELLID_BROADCAST) { + if(liblte_s1ap_pack_cellid_broadcast(&ie->choice.cellID_Broadcast, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_TAI_BROADCAST) { + if(liblte_s1ap_pack_tai_broadcast(&ie->choice.tAI_Broadcast, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_EMERGENCYAREAID_BROADCAST) { + if(liblte_s1ap_pack_emergencyareaid_broadcast(&ie->choice.emergencyAreaID_Broadcast, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_broadcastcompletedarealist( + uint8_t **ptr, + LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("BroadcastCompletedAreaList error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_CELLID_BROADCAST) { + if(liblte_s1ap_unpack_cellid_broadcast(ptr, &ie->choice.cellID_Broadcast) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_TAI_BROADCAST) { + if(liblte_s1ap_unpack_tai_broadcast(ptr, &ie->choice.tAI_Broadcast) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_BROADCASTCOMPLETEDAREALIST_CHOICE_EMERGENCYAREAID_BROADCAST) { + if(liblte_s1ap_unpack_emergencyareaid_broadcast(ptr, &ie->choice.emergencyAreaID_Broadcast) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SONInformation CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformation( + LIBLTE_S1AP_SONINFORMATION_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREQUEST) { + if(liblte_s1ap_pack_soninformationrequest(&ie->choice.sONInformationRequest, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREPLY) { + if(liblte_s1ap_pack_soninformationreply(&ie->choice.sONInformationReply, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformation( + uint8_t **ptr, + LIBLTE_S1AP_SONINFORMATION_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SONInformation error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_SONINFORMATION_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREQUEST) { + if(liblte_s1ap_unpack_soninformationrequest(ptr, &ie->choice.sONInformationRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_SONINFORMATION_CHOICE_SONINFORMATIONREPLY) { + if(liblte_s1ap_unpack_soninformationreply(ptr, &ie->choice.sONInformationReply) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE SONConfigurationTransfer SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sonconfigurationtransfer( + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("SONConfigurationTransfer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_targetenb_id(&ie->targeteNB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_sourceenb_id(&ie->sourceeNB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_soninformation(&ie->sONInformation, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sonconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_SONCONFIGURATIONTRANSFER_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("SONConfigurationTransfer error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_targetenb_id(ptr, &ie->targeteNB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_sourceenb_id(ptr, &ie->sourceeNB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_soninformation(ptr, &ie->sONInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ResetAll ENUMERATED +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resetall( + LIBLTE_S1AP_RESETALL_ENUM_EXT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ResetAll error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Enum + liblte_value_2_bits(ie->e, ptr, 0); + liblte_align_up_zero(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resetall( + uint8_t **ptr, + LIBLTE_S1AP_RESETALL_ENUM_EXT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ResetAll error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Enum + ie->e = (LIBLTE_S1AP_RESETALL_ENUM)liblte_bits_2_value(ptr, 0); + liblte_align_up(ptr, 8); + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE Inter_SystemInformationTransferType CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_inter_systeminformationtransfertype( + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("Inter_SystemInformationTransferType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_RIMTRANSFER) { + if(liblte_s1ap_pack_rimtransfer(&ie->choice.rIMTransfer, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_inter_systeminformationtransfertype( + uint8_t **ptr, + LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("Inter_SystemInformationTransferType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_ENUM)liblte_bits_2_value(ptr, 0); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_INTER_SYSTEMINFORMATIONTRANSFERTYPE_CHOICE_RIMTRANSFER) { + if(liblte_s1ap_unpack_rimtransfer(ptr, &ie->choice.rIMTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RAB_IE_ContainerPairList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rab_ie_containerpairlist( + LIBLTE_S1AP_E_RAB_IE_CONTAINERPAIRLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RAB_IE_ContainerPairList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_pack_protocolie_containerpair(&ie->buffer[i], ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rab_ie_containerpairlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RAB_IE_CONTAINERPAIRLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RAB_IE_ContainerPairList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_containerpair(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABDataForwardingItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem( + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABDataForwardingItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->dL_transportLayerAddress_present?1:0, ptr, 1); + liblte_value_2_bits(ie->dL_gTP_TEID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->uL_TransportLayerAddress_present?1:0, ptr, 1); + liblte_value_2_bits(ie->uL_GTP_TEID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->dL_transportLayerAddress_present) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->dL_transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->dL_gTP_TEID_present) { + if(liblte_s1ap_pack_gtp_teid(&ie->dL_gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->uL_TransportLayerAddress_present) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->uL_TransportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->uL_GTP_TEID_present) { + if(liblte_s1ap_pack_gtp_teid(&ie->uL_GTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABDATAFORWARDINGITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABDataForwardingItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->dL_transportLayerAddress_present = liblte_bits_2_value(ptr, 1); + ie->dL_gTP_TEID_present = liblte_bits_2_value(ptr, 1); + ie->uL_TransportLayerAddress_present = liblte_bits_2_value(ptr, 1); + ie->uL_GTP_TEID_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->dL_transportLayerAddress_present) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->dL_transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->dL_gTP_TEID_present) { + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->dL_gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->uL_TransportLayerAddress_present) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->uL_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->uL_GTP_TEID_present) { + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->uL_GTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemHOReq SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq( + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemHOReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_e_rablevelqosparameters(&ie->e_RABlevelQosParameters, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMHOREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemHOReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_e_rablevelqosparameters(ptr, &ie->e_RABlevelQosParameters) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABAdmittedItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem( + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABAdmittedItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->dL_transportLayerAddress_present?1:0, ptr, 1); + liblte_value_2_bits(ie->dL_gTP_TEID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->uL_TransportLayerAddress_present?1:0, ptr, 1); + liblte_value_2_bits(ie->uL_GTP_TEID_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->dL_transportLayerAddress_present) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->dL_transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->dL_gTP_TEID_present) { + if(liblte_s1ap_pack_gtp_teid(&ie->dL_gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->uL_TransportLayerAddress_present) { + if(liblte_s1ap_pack_transportlayeraddress(&ie->uL_TransportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->uL_GTP_TEID_present) { + if(liblte_s1ap_pack_gtp_teid(&ie->uL_GTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABADMITTEDITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABAdmittedItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->dL_transportLayerAddress_present = liblte_bits_2_value(ptr, 1); + ie->dL_gTP_TEID_present = liblte_bits_2_value(ptr, 1); + ie->uL_TransportLayerAddress_present = liblte_bits_2_value(ptr, 1); + ie->uL_GTP_TEID_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->dL_transportLayerAddress_present) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->dL_transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->dL_gTP_TEID_present) { + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->dL_gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->uL_TransportLayerAddress_present) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->uL_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->uL_GTP_TEID_present) { + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->uL_GTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABFailedToSetupItemHOReqAck SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack( + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABFailedToSetupItemHOReqAck error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_cause(&ie->cause, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqack( + uint8_t **ptr, + LIBLTE_S1AP_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABFailedToSetupItemHOReqAck error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_cause(ptr, &ie->cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeSwitchedDLItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem( + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSwitchedDLItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDDLITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSwitchedDLItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeSwitchedULItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem( + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSwitchedULItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDULITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSwitchedULItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemBearerSUReq SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureq( + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemBearerSUReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_e_rablevelqosparameters(&ie->e_RABlevelQoSParameters, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_nas_pdu(&ie->nAS_PDU, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemBearerSUReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_e_rablevelqosparameters(ptr, &ie->e_RABlevelQoSParameters) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_nas_pdu(ptr, &ie->nAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABSetupItemBearerSURes SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersures( + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABSetupItemBearerSURes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPITEMBEARERSURES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABSetupItemBearerSURes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeModifiedItemBearerModReq SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifieditembearermodreq( + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeModifiedItemBearerModReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_e_rablevelqosparameters(&ie->e_RABLevelQoSParameters, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_nas_pdu(&ie->nAS_PDU, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifieditembearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeModifiedItemBearerModReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_e_rablevelqosparameters(ptr, &ie->e_RABLevelQoSParameters) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_nas_pdu(ptr, &ie->nAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABModifyItemBearerModRes SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodres( + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABModifyItemBearerModRes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodres( + uint8_t **ptr, + LIBLTE_S1AP_E_RABMODIFYITEMBEARERMODRES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABModifyItemBearerModRes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABReleaseItemBearerRelComp SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcomp( + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABReleaseItemBearerRelComp error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABReleaseItemBearerRelComp error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABToBeSetupItemCtxtSUReq SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureq( + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemCtxtSUReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->nAS_PDU_present?1:0, ptr, 1); + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_e_rablevelqosparameters(&ie->e_RABlevelQoSParameters, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->nAS_PDU_present) { + if(liblte_s1ap_pack_nas_pdu(&ie->nAS_PDU, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABToBeSetupItemCtxtSUReq error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->nAS_PDU_present = liblte_bits_2_value(ptr, 1); + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_e_rablevelqosparameters(ptr, &ie->e_RABlevelQoSParameters) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->nAS_PDU_present) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &ie->nAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE E_RABSetupItemCtxtSURes SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsures( + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABSetupItemCtxtSURes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_e_rab_id(&ie->e_RAB_ID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_transportlayeraddress(&ie->transportLayerAddress, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(liblte_s1ap_pack_gtp_teid(&ie->gTP_TEID, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPITEMCTXTSURES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("E_RABSetupItemCtxtSURes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_e_rab_id(ptr, &ie->e_RAB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &ie->transportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(liblte_s1ap_unpack_gtp_teid(ptr, &ie->gTP_TEID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE TAIItem SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitem( + LIBLTE_S1AP_TAIITEM_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("TAIItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + liblte_value_2_bits(ie->iE_Extensions_present?1:0, ptr, 1); + + if(liblte_s1ap_pack_tai(&ie->tAI, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_pack_protocolextensioncontainer(&ie->iE_Extensions, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitem( + uint8_t **ptr, + LIBLTE_S1AP_TAIITEM_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("TAIItem error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + ie->iE_Extensions_present = liblte_bits_2_value(ptr, 1); + + if(liblte_s1ap_unpack_tai(ptr, &ie->tAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + + if(ie->iE_Extensions_present) { + if(liblte_s1ap_unpack_protocolextensioncontainer(ptr, &ie->iE_Extensions) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List UE_associatedLogicalS1_ConnectionListRes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionlistres( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("UE_associatedLogicalS1_ConnectionListRes pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionlistres( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("UE_associatedLogicalS1_ConnectionListRes unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List UE_associatedLogicalS1_ConnectionListResAck DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionlistresack( + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("UE_associatedLogicalS1_ConnectionListResAck pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionlistresack( + uint8_t **ptr, + LIBLTE_S1AP_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("UE_associatedLogicalS1_ConnectionListResAck unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE PrivateMessage SEQUENCE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privatemessage( + LIBLTE_S1AP_PRIVATEMESSAGE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("PrivateMessage error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + + if(liblte_s1ap_pack_privateie_container(&ie->privateIEs, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privatemessage( + uint8_t **ptr, + LIBLTE_S1AP_PRIVATEMESSAGE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("PrivateMessage error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + + if(liblte_s1ap_unpack_privateie_container(ptr, &ie->privateIEs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE ResetType CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resettype( + LIBLTE_S1AP_RESETTYPE_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + liblte_value_2_bits(ie->ext?1:0, ptr, 1); + if(ie->ext) { + liblte_log_print("ResetType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // Choice type + liblte_value_2_bits(ie->choice_type, ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_RESETTYPE_CHOICE_S1_INTERFACE) { + if(liblte_s1ap_pack_resetall(&ie->choice.s1_Interface, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RESETTYPE_CHOICE_PARTOFS1_INTERFACE) { + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionlistres(&ie->choice.partOfS1_Interface, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resettype( + uint8_t **ptr, + LIBLTE_S1AP_RESETTYPE_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + // Extension + ie->ext = liblte_bits_2_value(ptr, 1); + if(ie->ext) { + liblte_log_print("ResetType error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // Choice type + ie->choice_type = (LIBLTE_S1AP_RESETTYPE_CHOICE_ENUM)liblte_bits_2_value(ptr, 1); + + // Choice + if(ie->choice_type == LIBLTE_S1AP_RESETTYPE_CHOICE_S1_INTERFACE) { + if(liblte_s1ap_unpack_resetall(ptr, &ie->choice.s1_Interface) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(ie->choice_type == LIBLTE_S1AP_RESETTYPE_CHOICE_PARTOFS1_INTERFACE) { + if(liblte_s1ap_unpack_ue_associatedlogicals1_connectionlistres(ptr, &ie->choice.partOfS1_Interface) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABSubjecttoDataForwardingList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsubjecttodataforwardinglist( + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABSubjecttoDataForwardingList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabdataforwardingitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABDATAFORWARDINGITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsubjecttodataforwardinglist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSUBJECTTODATAFORWARDINGLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABSubjecttoDataForwardingList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABDATAFORWARDINGITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabdataforwardingitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListHOReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplisthoreq( + LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListHOReq pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitemhoreq(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMHOREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplisthoreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTHOREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListHOReq unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMHOREQ != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobesetupitemhoreq(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABAdmittedList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmittedlist( + LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABAdmittedList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabadmitteditem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABADMITTEDITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmittedlist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABADMITTEDLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABAdmittedList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABADMITTEDITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabadmitteditem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeSwitchedDLList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddllist( + LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeSwitchedDLList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitcheddlitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLITEM, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddllist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDDLLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeSwitchedDLList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobeswitcheddlitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeSwitchedULList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedullist( + LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeSwitchedULList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitchedulitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedullist( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESWITCHEDULLIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeSwitchedULList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobeswitchedulitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListBearerSUReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplistbearersureq( + LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListBearerSUReq pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitembearersureq(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMBEARERSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplistbearersureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListBearerSUReq unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMBEARERSUREQ != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobesetupitembearersureq(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABSetupListBearerSURes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuplistbearersures( + LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABSetupListBearerSURes pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetupitembearersures(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMBEARERSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuplistbearersures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPLISTBEARERSURES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABSetupListBearerSURes unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABSETUPITEMBEARERSURES != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabsetupitembearersures(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeModifiedListBearerModReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifiedlistbearermodreq( + LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeModifiedListBearerModReq pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobemodifieditembearermodreq(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDITEMBEARERMODREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifiedlistbearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBEMODIFIEDLISTBEARERMODREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeModifiedListBearerModReq unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDITEMBEARERMODREQ != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobemodifieditembearermodreq(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABModifyListBearerModRes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifylistbearermodres( + LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABModifyListBearerModRes pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabmodifyitembearermodres(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABMODIFYITEMBEARERMODRES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifylistbearermodres( + uint8_t **ptr, + LIBLTE_S1AP_E_RABMODIFYLISTBEARERMODRES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABModifyListBearerModRes unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABMODIFYITEMBEARERMODRES != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabmodifyitembearermodres(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABReleaseListBearerRelComp DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaselistbearerrelcomp( + LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABReleaseListBearerRelComp pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabreleaseitembearerrelcomp(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABRELEASEITEMBEARERRELCOMP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaselistbearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_E_RABRELEASELISTBEARERRELCOMP_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABReleaseListBearerRelComp unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABRELEASEITEMBEARERRELCOMP != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabreleaseitembearerrelcomp(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABToBeSetupListCtxtSUReq DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetuplistctxtsureq( + LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListCtxtSUReq pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitemctxtsureq(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMCTXTSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetuplistctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABToBeSetupListCtxtSUReq unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMCTXTSUREQ != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabtobesetupitemctxtsureq(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABSetupListCtxtSURes DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuplistctxtsures( + LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABSetupListCtxtSURes pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetupitemctxtsures(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMCTXTSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuplistctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_E_RABSETUPLISTCTXTSURES_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABSetupListCtxtSURes unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABSETUPITEMCTXTSURES != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabsetupitemctxtsures(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List TAIList DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tailist( + LIBLTE_S1AP_TAILIST_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("TAIList pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_taiitem(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAIITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tailist( + uint8_t **ptr, + LIBLTE_S1AP_TAILIST_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("TAIList unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_TAIITEM != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_taiitem(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Container List E_RABFailedtoSetupListHOReqAck DYNAMIC SEQUENCE OF +********************************************************************************/ +// lb:1, ub:256 +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetuplisthoreqack( + LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT *ie, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + if(ie->len > 32) { + liblte_log_print("E_RABFailedtoSetupListHOReqAck pack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_ENCODE_FAIL; + } + // Length + liblte_value_2_bits(ie->len-1, ptr, 8); + liblte_align_up_zero(ptr, 8); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + uint32_t i; + for(i=0;ilen;i++) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack(&ie->buffer[i], &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPITEMHOREQACK, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetuplisthoreqack( + uint8_t **ptr, + LIBLTE_S1AP_E_RABFAILEDTOSETUPLISTHOREQACK_STRUCT *ie) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(ie != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + + // Length + ie->len = liblte_bits_2_value(ptr, 8) + 1; + liblte_align_up(ptr, 8); + if(ie->len > 32) { + liblte_log_print("E_RABFailedtoSetupListHOReqAck unpack error - max supported dynamic sequence length = 32, ie->len = %d\n", ie->len); + return LIBLTE_ERROR_DECODE_FAIL; + } + + uint32_t i; + for(i=0;ilen;i++) { + if(liblte_s1ap_unpack_protocolie_header(ptr, &ie_id, &crit, &len) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPITEMHOREQACK != ie_id) { + return LIBLTE_ERROR_DECODE_FAIL; + } + if(liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqack(ptr, &ie->buffer[i]) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message AllocationAndRetentionPriority_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_allocationandretentionpriority_ext( + LIBLTE_S1AP_MESSAGE_ALLOCATIONANDRETENTIONPRIORITY_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("AllocationAndRetentionPriority-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_allocationandretentionpriority_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ALLOCATIONANDRETENTIONPRIORITY_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("AllocationAndRetentionPriority-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CancelledCellinEAI_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellineai_item_ext( + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINEAI_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CancelledCellinEAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellineai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINEAI_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CancelledCellinEAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CancelledCellinTAI_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cancelledcellintai_item_ext( + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINTAI_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CancelledCellinTAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cancelledcellintai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CANCELLEDCELLINTAI_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CancelledCellinTAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CellID_Broadcast_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_CELLID_BROADCAST_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CellID-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLID_BROADCAST_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CellID-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CellID_Cancelled_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellid_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_CELLID_CANCELLED_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CellID-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellid_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLID_CANCELLED_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CellID-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CellBasedMDT_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cellbasedmdt_ext( + LIBLTE_S1AP_MESSAGE_CELLBASEDMDT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CellBasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cellbasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLBASEDMDT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CellBasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Cdma2000OneXSRVCCInfo_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cdma2000onexsrvccinfo_ext( + LIBLTE_S1AP_MESSAGE_CDMA2000ONEXSRVCCINFO_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("Cdma2000OneXSRVCCInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cdma2000onexsrvccinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CDMA2000ONEXSRVCCINFO_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("Cdma2000OneXSRVCCInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CellType_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltype_ext( + LIBLTE_S1AP_MESSAGE_CELLTYPE_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CellType-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltype_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLTYPE_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CellType-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CGI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_cgi_ext( + LIBLTE_S1AP_MESSAGE_CGI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CGI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_cgi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CGI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CGI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CSG_IdList_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_csg_idlist_item_ext( + LIBLTE_S1AP_MESSAGE_CSG_IDLIST_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CSG-IdList-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_csg_idlist_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CSG_IDLIST_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CSG-IdList-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message COUNTvalue_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalue_ext( + LIBLTE_S1AP_MESSAGE_COUNTVALUE_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("COUNTvalue-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalue_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COUNTVALUE_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("COUNTvalue-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message COUNTValueExtended_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_countvalueextended_ext( + LIBLTE_S1AP_MESSAGE_COUNTVALUEEXTENDED_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("COUNTValueExtended-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_countvalueextended_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COUNTVALUEEXTENDED_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("COUNTValueExtended-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CriticalityDiagnostics_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ext( + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CriticalityDiagnostics-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CriticalityDiagnostics-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CriticalityDiagnostics_IE_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_criticalitydiagnostics_ie_item_ext( + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_IE_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CriticalityDiagnostics-IE-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_criticalitydiagnostics_ie_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CRITICALITYDIAGNOSTICS_IE_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CriticalityDiagnostics-IE-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message EmergencyAreaID_Broadcast_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_BROADCAST_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("EmergencyAreaID-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_BROADCAST_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("EmergencyAreaID-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message EmergencyAreaID_Cancelled_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_emergencyareaid_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_CANCELLED_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("EmergencyAreaID-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_emergencyareaid_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EMERGENCYAREAID_CANCELLED_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("EmergencyAreaID-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CompletedCellinEAI_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellineai_item_ext( + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINEAI_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CompletedCellinEAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellineai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINEAI_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CompletedCellinEAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message GERAN_Cell_ID_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_geran_cell_id_ext( + LIBLTE_S1AP_MESSAGE_GERAN_CELL_ID_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("GERAN-Cell-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_geran_cell_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GERAN_CELL_ID_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("GERAN-Cell-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message GlobalENB_ID_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_globalenb_id_ext( + LIBLTE_S1AP_MESSAGE_GLOBALENB_ID_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("GlobalENB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_globalenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GLOBALENB_ID_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("GlobalENB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENB_StatusTransfer_TransparentContainer_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enb_statustransfer_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENB-StatusTransfer-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enb_statustransfer_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENB-StatusTransfer-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABInformationListItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlistitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLISTITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABInformationListItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlistitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLISTITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABInformationListItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABQoSParameters_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabqosparameters_ext( + LIBLTE_S1AP_MESSAGE_E_RABQOSPARAMETERS_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABQoSParameters-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabqosparameters_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABQOSPARAMETERS_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABQoSParameters-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message EUTRAN_CGI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_eutran_cgi_ext( + LIBLTE_S1AP_MESSAGE_EUTRAN_CGI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("EUTRAN-CGI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_eutran_cgi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_EUTRAN_CGI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("EUTRAN-CGI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ForbiddenTAs_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddentas_item_ext( + LIBLTE_S1AP_MESSAGE_FORBIDDENTAS_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ForbiddenTAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddentas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_FORBIDDENTAS_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ForbiddenTAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ForbiddenLAs_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_forbiddenlas_item_ext( + LIBLTE_S1AP_MESSAGE_FORBIDDENLAS_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ForbiddenLAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_forbiddenlas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_FORBIDDENLAS_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ForbiddenLAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message GBR_QosInformation_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gbr_qosinformation_ext( + LIBLTE_S1AP_MESSAGE_GBR_QOSINFORMATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("GBR-QosInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gbr_qosinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GBR_QOSINFORMATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("GBR-QosInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message GUMMEI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_gummei_ext( + LIBLTE_S1AP_MESSAGE_GUMMEI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("GUMMEI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_gummei_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_GUMMEI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("GUMMEI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverRestrictionList_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrestrictionlist_ext( + LIBLTE_S1AP_MESSAGE_HANDOVERRESTRICTIONLIST_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRestrictionList-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrestrictionlist_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERRESTRICTIONLIST_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRestrictionList-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LAI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lai_ext( + LIBLTE_S1AP_MESSAGE_LAI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LAI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lai_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LAI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LAI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LoggedMDT_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_loggedmdt_ext( + LIBLTE_S1AP_MESSAGE_LOGGEDMDT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LoggedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_loggedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOGGEDMDT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LoggedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message M3Configuration_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m3configuration_ext( + LIBLTE_S1AP_MESSAGE_M3CONFIGURATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("M3Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m3configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M3CONFIGURATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("M3Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message M4Configuration_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m4configuration_ext( + LIBLTE_S1AP_MESSAGE_M4CONFIGURATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("M4Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m4configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M4CONFIGURATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("M4Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message M5Configuration_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m5configuration_ext( + LIBLTE_S1AP_MESSAGE_M5CONFIGURATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("M5Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m5configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M5CONFIGURATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("M5Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message M1PeriodicReporting_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1periodicreporting_ext( + LIBLTE_S1AP_MESSAGE_M1PERIODICREPORTING_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("M1PeriodicReporting-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1periodicreporting_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M1PERIODICREPORTING_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("M1PeriodicReporting-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message RequestType_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_requesttype_ext( + LIBLTE_S1AP_MESSAGE_REQUESTTYPE_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("RequestType-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_requesttype_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_REQUESTTYPE_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("RequestType-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message RIMTransfer_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_rimtransfer_ext( + LIBLTE_S1AP_MESSAGE_RIMTRANSFER_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("RIMTransfer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_rimtransfer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RIMTRANSFER_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("RIMTransfer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SecurityContext_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_securitycontext_ext( + LIBLTE_S1AP_MESSAGE_SECURITYCONTEXT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SecurityContext-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_securitycontext_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SECURITYCONTEXT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SecurityContext-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SourceeNB_ID_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_id_ext( + LIBLTE_S1AP_MESSAGE_SOURCEENB_ID_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SourceeNB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SOURCEENB_ID_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SourceeNB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ServedGUMMEIsItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_servedgummeisitem_ext( + LIBLTE_S1AP_MESSAGE_SERVEDGUMMEISITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ServedGUMMEIsItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_servedgummeisitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SERVEDGUMMEISITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ServedGUMMEIsItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SupportedTAs_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_supportedtas_item_ext( + LIBLTE_S1AP_MESSAGE_SUPPORTEDTAS_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SupportedTAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_supportedtas_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SUPPORTEDTAS_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SupportedTAs-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TimeSynchronizationInfo_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_timesynchronizationinfo_ext( + LIBLTE_S1AP_MESSAGE_TIMESYNCHRONIZATIONINFO_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TimeSynchronizationInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_timesynchronizationinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TIMESYNCHRONIZATIONINFO_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TimeSynchronizationInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message S_TMSI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s_tmsi_ext( + LIBLTE_S1AP_MESSAGE_S_TMSI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("S-TMSI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s_tmsi_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S_TMSI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("S-TMSI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAIBasedMDT_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taibasedmdt_ext( + LIBLTE_S1AP_MESSAGE_TAIBASEDMDT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIBasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taibasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIBASEDMDT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIBasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAI_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_ext( + LIBLTE_S1AP_MESSAGE_TAI_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAI_Broadcast_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_broadcast_item_ext( + LIBLTE_S1AP_MESSAGE_TAI_BROADCAST_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_broadcast_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_BROADCAST_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-Broadcast-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAI_Cancelled_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tai_cancelled_item_ext( + LIBLTE_S1AP_MESSAGE_TAI_CANCELLED_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tai_cancelled_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAI_CANCELLED_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAI-Cancelled-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TABasedMDT_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tabasedmdt_ext( + LIBLTE_S1AP_MESSAGE_TABASEDMDT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TABasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tabasedmdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TABASEDMDT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TABasedMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CompletedCellinTAI_Item_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_completedcellintai_item_ext( + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINTAI_ITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CompletedCellinTAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_completedcellintai_item_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_COMPLETEDCELLINTAI_ITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CompletedCellinTAI-Item-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TargeteNB_ID_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_id_ext( + LIBLTE_S1AP_MESSAGE_TARGETENB_ID_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TargeteNB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETENB_ID_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TargeteNB-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TargetRNC_ID_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetrnc_id_ext( + LIBLTE_S1AP_MESSAGE_TARGETRNC_ID_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TargetRNC-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetrnc_id_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETRNC_ID_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TargetRNC-ID-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TargeteNB_ToSourceeNB_TransparentContainer_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_targetenb_tosourceenb_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TargeteNB-ToSourceeNB-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_targetenb_tosourceenb_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TargeteNB-ToSourceeNB-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message M1ThresholdEventA2_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_m1thresholdeventa2_ext( + LIBLTE_S1AP_MESSAGE_M1THRESHOLDEVENTA2_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("M1ThresholdEventA2-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_m1thresholdeventa2_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_M1THRESHOLDEVENTA2_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("M1ThresholdEventA2-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Tunnel_Information_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tunnel_information_ext( + LIBLTE_S1AP_MESSAGE_TUNNEL_INFORMATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("Tunnel-Information-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tunnel_information_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TUNNEL_INFORMATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("Tunnel-Information-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEAggregate_MaximumBitrates_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueaggregate_maximumbitrates_ext( + LIBLTE_S1AP_MESSAGE_UEAGGREGATE_MAXIMUMBITRATES_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEAggregate-MaximumBitrates-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueaggregate_maximumbitrates_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UEAGGREGATE_MAXIMUMBITRATES_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEAggregate-MaximumBitrates-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UE_S1AP_ID_pair_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_s1ap_id_pair_ext( + LIBLTE_S1AP_MESSAGE_UE_S1AP_ID_PAIR_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-S1AP-ID-pair-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_s1ap_id_pair_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_S1AP_ID_PAIR_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-S1AP-ID-pair-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItemExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitemext( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UESecurityCapabilities_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uesecuritycapabilities_ext( + LIBLTE_S1AP_MESSAGE_UESECURITYCAPABILITIES_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UESecurityCapabilities-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uesecuritycapabilities_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UESECURITYCAPABILITIES_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UESecurityCapabilities-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UserLocationInformation_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_userlocationinformation_ext( + LIBLTE_S1AP_MESSAGE_USERLOCATIONINFORMATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UserLocationInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_userlocationinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_USERLOCATIONINFORMATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UserLocationInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBX2ExtTLA_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbx2exttla_ext( + LIBLTE_S1AP_MESSAGE_ENBX2EXTTLA_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBX2ExtTLA-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbx2exttla_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBX2EXTTLA_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBX2ExtTLA-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SourceeNB_ToTargeteNB_TransparentContainer_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sourceenb_totargetenb_transparentcontainer_ext( + LIBLTE_S1AP_MESSAGE_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SourceeNB-ToTargeteNB-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->MobilityInformation_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MobilityInformation + if(msg->MobilityInformation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mobilityinformation(&msg->MobilityInformation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MOBILITYINFORMATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sourceenb_totargetenb_transparentcontainer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SOURCEENB_TOTARGETENB_TRANSPARENTCONTAINER_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->MobilityInformation_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SourceeNB-ToTargeteNB-TransparentContainer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMobilityInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MobilityInformation_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABInformationList STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabinformationlist( + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLIST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABInformationListIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABInformationListItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabinformationlistitem(&msg->E_RABInformationListItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABINFORMATIONLISTITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabinformationlist( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABINFORMATIONLIST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABInformationListIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABInformationListItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LastVisitedEUTRANCellInformation_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_lastvisitedeutrancellinformation_ext( + LIBLTE_S1AP_MESSAGE_LASTVISITEDEUTRANCELLINFORMATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LastVisitedEUTRANCellInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + if(!msg->Time_UE_StayedInCell_EnhancedGranularity_present) + n_ie--; + if(!msg->HO_Cause_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Time_UE_StayedInCell_EnhancedGranularity + if(msg->Time_UE_StayedInCell_EnhancedGranularity_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_time_ue_stayedincell_enhancedgranularity(&msg->Time_UE_StayedInCell_EnhancedGranularity, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TIME_UE_STAYEDINCELL_ENHANCEDGRANULARITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - HO_Cause + if(msg->HO_Cause_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->HO_Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HO_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_lastvisitedeutrancellinformation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LASTVISITEDEUTRANCELLINFORMATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Time_UE_StayedInCell_EnhancedGranularity_present = false; + msg->HO_Cause_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LastVisitedEUTRANCellInformation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iTime_UE_StayedInCell_EnhancedGranularity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Time_UE_StayedInCell_EnhancedGranularity_present = true; + } else if(LIBLTE_S1AP_IE_ID_HO_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->HO_Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->HO_Cause_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SONInformationReply_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_soninformationreply_ext( + LIBLTE_S1AP_MESSAGE_SONINFORMATIONREPLY_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SONInformationReply-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->Time_Synchronization_Info_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Time_Synchronization_Info + if(msg->Time_Synchronization_Info_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_timesynchronizationinfo(&msg->Time_Synchronization_Info, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TIME_SYNCHRONIZATION_INFO, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_soninformationreply_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SONINFORMATIONREPLY_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Time_Synchronization_Info_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SONInformationReply-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iTime_Synchronization_Info) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Time_Synchronization_Info_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Bearers_SubjectToStatusTransfer_ItemExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_itemext( + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEMEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("Bearers-SubjectToStatusTransfer-ItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->ULCOUNTValueExtended_present) + n_ie--; + if(!msg->DLCOUNTValueExtended_present) + n_ie--; + if(!msg->ReceiveStatusOfULPDCPSDUsExtended_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - ULCOUNTValueExtended + if(msg->ULCOUNTValueExtended_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_countvalueextended(&msg->ULCOUNTValueExtended, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ULCOUNTVALUEEXTENDED, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - DLCOUNTValueExtended + if(msg->DLCOUNTValueExtended_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_countvalueextended(&msg->DLCOUNTValueExtended, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DLCOUNTVALUEEXTENDED, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ReceiveStatusOfULPDCPSDUsExtended + if(msg->ReceiveStatusOfULPDCPSDUsExtended_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_receivestatusofulpdcpsdusextended(&msg->ReceiveStatusOfULPDCPSDUsExtended, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RECEIVESTATUSOFULPDCPSDUSEXTENDED, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_itemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEMEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->ULCOUNTValueExtended_present = false; + msg->DLCOUNTValueExtended_present = false; + msg->ReceiveStatusOfULPDCPSDUsExtended_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("Bearers-SubjectToStatusTransfer-ItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iULCOUNTValueExtended) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ULCOUNTValueExtended_present = true; + } else if(LIBLTE_S1AP_IE_ID_DLCOUNTVALUEEXTENDED == ie_id) { + if(liblte_s1ap_unpack_countvalueextended(ptr, &msg->DLCOUNTValueExtended) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->DLCOUNTValueExtended_present = true; + } else if(LIBLTE_S1AP_IE_ID_RECEIVESTATUSOFULPDCPSDUSEXTENDED == ie_id) { + if(liblte_s1ap_unpack_receivestatusofulpdcpsdusextended(ptr, &msg->ReceiveStatusOfULPDCPSDUsExtended) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ReceiveStatusOfULPDCPSDUsExtended_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabitem( + LIBLTE_S1AP_MESSAGE_E_RABITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabitem(&msg->E_RABItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MDT_Configuration_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mdt_configuration_ext( + LIBLTE_S1AP_MESSAGE_MDT_CONFIGURATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MDT-Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->SignallingBasedMDTPLMNList_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - SignallingBasedMDTPLMNList + if(msg->SignallingBasedMDTPLMNList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mdtplmnlist(&msg->SignallingBasedMDTPLMNList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SIGNALLINGBASEDMDTPLMNLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mdt_configuration_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MDT_CONFIGURATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->SignallingBasedMDTPLMNList_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MDT-Configuration-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iSignallingBasedMDTPLMNList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SignallingBasedMDTPLMNList_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message X2TNLConfigurationInfo_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_x2tnlconfigurationinfo_ext( + LIBLTE_S1AP_MESSAGE_X2TNLCONFIGURATIONINFO_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("X2TNLConfigurationInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->eNBX2ExtendedTransportLayerAddresses_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - eNBX2ExtendedTransportLayerAddresses + if(msg->eNBX2ExtendedTransportLayerAddresses_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enbx2exttlas(&msg->eNBX2ExtendedTransportLayerAddresses, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENBX2EXTENDEDTRANSPORTLAYERADDRESSES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_x2tnlconfigurationinfo_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_X2TNLCONFIGURATIONINFO_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->eNBX2ExtendedTransportLayerAddresses_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("X2TNLConfigurationInfo-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;ieNBX2ExtendedTransportLayerAddresses) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->eNBX2ExtendedTransportLayerAddresses_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Bearers_SubjectToStatusTransfer_Item STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_bearers_subjecttostatustransfer_item( + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("Bearers-SubjectToStatusTransfer-ItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Bearers_SubjectToStatusTransfer_Item + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_bearers_subjecttostatustransfer_item(&msg->Bearers_SubjectToStatusTransfer_Item, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_bearers_subjecttostatustransfer_item( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_BEARERS_SUBJECTTOSTATUSTRANSFER_ITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("Bearers-SubjectToStatusTransfer-ItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iBearers_SubjectToStatusTransfer_Item) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ImmediateMDT_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_immediatemdt_ext( + LIBLTE_S1AP_MESSAGE_IMMEDIATEMDT_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ImmediateMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->M3Configuration_present) + n_ie--; + if(!msg->M4Configuration_present) + n_ie--; + if(!msg->M5Configuration_present) + n_ie--; + if(!msg->MDT_Location_Info_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - M3Configuration + if(msg->M3Configuration_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_m3configuration(&msg->M3Configuration, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_M3CONFIGURATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - M4Configuration + if(msg->M4Configuration_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_m4configuration(&msg->M4Configuration, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_M4CONFIGURATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - M5Configuration + if(msg->M5Configuration_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_m5configuration(&msg->M5Configuration, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_M5CONFIGURATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MDT_Location_Info + if(msg->MDT_Location_Info_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mdt_location_info(&msg->MDT_Location_Info, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MDT_LOCATION_INFO, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_immediatemdt_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_IMMEDIATEMDT_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->M3Configuration_present = false; + msg->M4Configuration_present = false; + msg->M5Configuration_present = false; + msg->MDT_Location_Info_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ImmediateMDT-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iM3Configuration) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_M4CONFIGURATION == ie_id) { + if(liblte_s1ap_unpack_m4configuration(ptr, &msg->M4Configuration) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_M5CONFIGURATION == ie_id) { + if(liblte_s1ap_unpack_m5configuration(ptr, &msg->M5Configuration) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_MDT_LOCATION_INFO == ie_id) { + if(liblte_s1ap_unpack_mdt_location_info(ptr, &msg->MDT_Location_Info) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MDT_Location_Info_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message SONConfigurationTransfer_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_sonconfigurationtransfer_ext( + LIBLTE_S1AP_MESSAGE_SONCONFIGURATIONTRANSFER_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("SONConfigurationTransfer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->x2TNLConfigurationInfo_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - x2TNLConfigurationInfo + if(msg->x2TNLConfigurationInfo_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_x2tnlconfigurationinfo(&msg->x2TNLConfigurationInfo, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_X2TNLCONFIGURATIONINFO, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_sonconfigurationtransfer_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_SONCONFIGURATIONTRANSFER_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->x2TNLConfigurationInfo_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("SONConfigurationTransfer-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;ix2TNLConfigurationInfo) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TraceActivation_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_traceactivation_ext( + LIBLTE_S1AP_MESSAGE_TRACEACTIVATION_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceActivation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->MDTConfiguration_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MDTConfiguration + if(msg->MDTConfiguration_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mdt_configuration(&msg->MDTConfiguration, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MDTCONFIGURATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_traceactivation_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACEACTIVATION_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->MDTConfiguration_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceActivation-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMDTConfiguration) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MDTConfiguration_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverRequired STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequired( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequiredIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 14; + if(!msg->Direct_Forwarding_Path_Availability_present) + n_ie--; + if(!msg->SRVCCHOIndication_present) + n_ie--; + if(!msg->Source_ToTarget_TransparentContainer_Secondary_present) + n_ie--; + if(!msg->MSClassmark2_present) + n_ie--; + if(!msg->MSClassmark3_present) + n_ie--; + if(!msg->CSG_Id_present) + n_ie--; + if(!msg->CellAccessMode_present) + n_ie--; + if(!msg->PS_ServiceNotAvailable_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - HandoverType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handovertype(&msg->HandoverType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TargetID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_targetid(&msg->TargetID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TARGETID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Direct_Forwarding_Path_Availability + if(msg->Direct_Forwarding_Path_Availability_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_direct_forwarding_path_availability(&msg->Direct_Forwarding_Path_Availability, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DIRECT_FORWARDING_PATH_AVAILABILITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SRVCCHOIndication + if(msg->SRVCCHOIndication_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_srvcchoindication(&msg->SRVCCHOIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SRVCCHOINDICATION, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Source_ToTarget_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_source_totarget_transparentcontainer(&msg->Source_ToTarget_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Source_ToTarget_TransparentContainer_Secondary + if(msg->Source_ToTarget_TransparentContainer_Secondary_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_source_totarget_transparentcontainer(&msg->Source_ToTarget_TransparentContainer_Secondary, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER_SECONDARY, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MSClassmark2 + if(msg->MSClassmark2_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_msclassmark2(&msg->MSClassmark2, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MSCLASSMARK2, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MSClassmark3 + if(msg->MSClassmark3_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_msclassmark3(&msg->MSClassmark3, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MSCLASSMARK3, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSG_Id + if(msg->CSG_Id_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_id(&msg->CSG_Id, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CellAccessMode + if(msg->CellAccessMode_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cellaccessmode(&msg->CellAccessMode, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CELLACCESSMODE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - PS_ServiceNotAvailable + if(msg->PS_ServiceNotAvailable_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ps_servicenotavailable(&msg->PS_ServiceNotAvailable, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_PS_SERVICENOTAVAILABLE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequired( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUIRED_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Direct_Forwarding_Path_Availability_present = false; + msg->SRVCCHOIndication_present = false; + msg->Source_ToTarget_TransparentContainer_Secondary_present = false; + msg->MSClassmark2_present = false; + msg->MSClassmark3_present = false; + msg->CSG_Id_present = false; + msg->CellAccessMode_present = false; + msg->PS_ServiceNotAvailable_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequiredIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_HANDOVERTYPE == ie_id) { + if(liblte_s1ap_unpack_handovertype(ptr, &msg->HandoverType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TARGETID == ie_id) { + if(liblte_s1ap_unpack_targetid(ptr, &msg->TargetID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_DIRECT_FORWARDING_PATH_AVAILABILITY == ie_id) { + if(liblte_s1ap_unpack_direct_forwarding_path_availability(ptr, &msg->Direct_Forwarding_Path_Availability) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Direct_Forwarding_Path_Availability_present = true; + } else if(LIBLTE_S1AP_IE_ID_SRVCCHOINDICATION == ie_id) { + if(liblte_s1ap_unpack_srvcchoindication(ptr, &msg->SRVCCHOIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SRVCCHOIndication_present = true; + } else if(LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_source_totarget_transparentcontainer(ptr, &msg->Source_ToTarget_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER_SECONDARY == ie_id) { + if(liblte_s1ap_unpack_source_totarget_transparentcontainer(ptr, &msg->Source_ToTarget_TransparentContainer_Secondary) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Source_ToTarget_TransparentContainer_Secondary_present = true; + } else if(LIBLTE_S1AP_IE_ID_MSCLASSMARK2 == ie_id) { + if(liblte_s1ap_unpack_msclassmark2(ptr, &msg->MSClassmark2) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_MSCLASSMARK3 == ie_id) { + if(liblte_s1ap_unpack_msclassmark3(ptr, &msg->MSClassmark3) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_ID == ie_id) { + if(liblte_s1ap_unpack_csg_id(ptr, &msg->CSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_Id_present = true; + } else if(LIBLTE_S1AP_IE_ID_CELLACCESSMODE == ie_id) { + if(liblte_s1ap_unpack_cellaccessmode(ptr, &msg->CellAccessMode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CellAccessMode_present = true; + } else if(LIBLTE_S1AP_IE_ID_PS_SERVICENOTAVAILABLE == ie_id) { + if(liblte_s1ap_unpack_ps_servicenotavailable(ptr, &msg->PS_ServiceNotAvailable) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->PS_ServiceNotAvailable_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABDataForwardingItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABDataForwardingItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABDataForwardingItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverPreparationFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverpreparationfailure( + LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverPreparationFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverpreparationfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverPreparationFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemHOReq_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemHOReq-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->Data_Forwarding_Not_Possible_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Data_Forwarding_Not_Possible + if(msg->Data_Forwarding_Not_Possible_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_data_forwarding_not_possible(&msg->Data_Forwarding_Not_Possible, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DATA_FORWARDING_NOT_POSSIBLE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Data_Forwarding_Not_Possible_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemHOReq-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iData_Forwarding_Not_Possible) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Data_Forwarding_Not_Possible_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABAdmittedItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem_ext( + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABAdmittedItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABAdmittedItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABFailedToSetupItemHOReqAckExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqackext( + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACKEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABFailedToSetupItemHOReqAckExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqackext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACKEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABFailedToSetupItemHOReqAckExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverfailure( + LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverNotify STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovernotify( + LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverNotifyIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 6; + if(!msg->Tunnel_Information_for_BBF_present) + n_ie--; + if(!msg->LHN_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tai(&msg->TAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Tunnel_Information_for_BBF + if(msg->Tunnel_Information_for_BBF_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tunnelinformation(&msg->Tunnel_Information_for_BBF, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - LHN_ID + if(msg->LHN_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lhn_id(&msg->LHN_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LHN_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovernotify( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERNOTIFY_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Tunnel_Information_for_BBF_present = false; + msg->LHN_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverNotifyIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAI == ie_id) { + if(liblte_s1ap_unpack_tai(ptr, &msg->TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF == ie_id) { + if(liblte_s1ap_unpack_tunnelinformation(ptr, &msg->Tunnel_Information_for_BBF) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Tunnel_Information_for_BBF_present = true; + } else if(LIBLTE_S1AP_IE_ID_LHN_ID == ie_id) { + if(liblte_s1ap_unpack_lhn_id(ptr, &msg->LHN_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->LHN_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedDLItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedDLItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedDLItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedULItem_Ext STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem_ext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_EXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedULItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem_ext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_EXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedULItem-ExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message PathSwitchRequestFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequestfailure( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequestfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverCancel STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercancel( + LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCancelIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercancel( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCANCEL_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCancelIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverCancelAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercancelacknowledge( + LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCancelAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercancelacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCANCELACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCancelAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemBearerSUReqExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemBearerSUReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + if(!msg->Correlation_ID_present) + n_ie--; + if(!msg->SIPTO_Correlation_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Correlation_ID + if(msg->Correlation_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_correlation_id(&msg->Correlation_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CORRELATION_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SIPTO_Correlation_ID + if(msg->SIPTO_Correlation_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_correlation_id(&msg->SIPTO_Correlation_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SIPTO_CORRELATION_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Correlation_ID_present = false; + msg->SIPTO_Correlation_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemBearerSUReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCorrelation_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Correlation_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_SIPTO_CORRELATION_ID == ie_id) { + if(liblte_s1ap_unpack_correlation_id(ptr, &msg->SIPTO_Correlation_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SIPTO_Correlation_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupItemBearerSUResExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersuresext( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURESEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemBearerSUResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersuresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURESEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemBearerSUResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeModifyItemBearerModReqExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifyitembearermodreqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFYITEMBEARERMODREQEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeModifyItemBearerModReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->TransportInformation_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - TransportInformation + if(msg->TransportInformation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportinformation(&msg->TransportInformation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRANSPORTINFORMATION, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifyitembearermodreqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFYITEMBEARERMODREQEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->TransportInformation_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeModifyItemBearerModReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iTransportInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TransportInformation_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABModifyItemBearerModResExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodresext( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRESEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyItemBearerModResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRESEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyItemBearerModResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABReleaseCommand STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleasecommand( + LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseCommandIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->uEaggregateMaximumBitrate_present) + n_ie--; + if(!msg->NAS_PDU_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + if(msg->uEaggregateMaximumBitrate_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABToBeReleasedList + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABToBeReleasedList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBERELEASEDLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NAS_PDU + if(msg->NAS_PDU_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nas_pdu(&msg->NAS_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NAS_PDU, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleasecommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASECOMMAND_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->uEaggregateMaximumBitrate_present = false; + msg->NAS_PDU_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseCommandIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->uEaggregateMaximumBitrate_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBERELEASEDLIST == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABToBeReleasedList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NAS_PDU == ie_id) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &msg->NAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->NAS_PDU_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABReleaseItemBearerRelCompExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcompext( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMPEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseItemBearerRelCompExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcompext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMPEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseItemBearerRelCompExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABReleaseIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseindication( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->UserLocationInformation_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABReleasedList + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABReleasedList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABRELEASEDLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UserLocationInformation + if(msg->UserLocationInformation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_userlocationinformation(&msg->UserLocationInformation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->UserLocationInformation_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABRELEASEDLIST == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABReleasedList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION == ie_id) { + if(liblte_s1ap_unpack_userlocationinformation(ptr, &msg->UserLocationInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UserLocationInformation_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemCtxtSUReqExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureqext( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemCtxtSUReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + if(!msg->Correlation_ID_present) + n_ie--; + if(!msg->SIPTO_Correlation_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Correlation_ID + if(msg->Correlation_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_correlation_id(&msg->Correlation_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CORRELATION_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SIPTO_Correlation_ID + if(msg->SIPTO_Correlation_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_correlation_id(&msg->SIPTO_Correlation_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SIPTO_CORRELATION_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureqext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->Correlation_ID_present = false; + msg->SIPTO_Correlation_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemCtxtSUReqExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCorrelation_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Correlation_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_SIPTO_CORRELATION_ID == ie_id) { + if(liblte_s1ap_unpack_correlation_id(ptr, &msg->SIPTO_Correlation_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SIPTO_Correlation_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupItemCtxtSUResExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsuresext( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURESEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemCtxtSUResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsuresext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURESEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemCtxtSUResExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message InitialContextSetupFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetupfailure( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetupfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAIItemExt STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitemext( + LIBLTE_S1AP_MESSAGE_TAIITEMEXT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitemext( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIITEMEXT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIItemExtIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextReleaseRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleaserequest( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseRequest-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->GWContextReleaseIndication_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - GWContextReleaseIndication + if(msg->GWContextReleaseIndication_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gwcontextreleaseindication(&msg->GWContextReleaseIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GWCONTEXTRELEASEINDICATION, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleaserequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->GWContextReleaseIndication_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseRequest-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_GWCONTEXTRELEASEINDICATION == ie_id) { + if(liblte_s1ap_unpack_gwcontextreleaseindication(ptr, &msg->GWContextReleaseIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GWContextReleaseIndication_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextReleaseCommand STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleasecommand( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseCommand-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - UE_S1AP_IDs + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_s1ap_ids(&msg->UE_S1AP_IDs, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_S1AP_IDS, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleasecommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseCommand-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iUE_S1AP_IDs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextReleaseComplete STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextreleasecomplete( + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseComplete-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + if(!msg->UserLocationInformation_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - UserLocationInformation + if(msg->UserLocationInformation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_userlocationinformation(&msg->UserLocationInformation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextreleasecomplete( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + msg->UserLocationInformation_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextReleaseComplete-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } else if(LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION == ie_id) { + if(liblte_s1ap_unpack_userlocationinformation(ptr, &msg->UserLocationInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UserLocationInformation_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextModificationRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationrequest( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 10; + if(!msg->SecurityKey_present) + n_ie--; + if(!msg->SubscriberProfileIDforRFP_present) + n_ie--; + if(!msg->uEaggregateMaximumBitrate_present) + n_ie--; + if(!msg->CSFallbackIndicator_present) + n_ie--; + if(!msg->UESecurityCapabilities_present) + n_ie--; + if(!msg->CSGMembershipStatus_present) + n_ie--; + if(!msg->RegisteredLAI_present) + n_ie--; + if(!msg->AdditionalCSFallbackIndicator_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SecurityKey + if(msg->SecurityKey_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_securitykey(&msg->SecurityKey, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SECURITYKEY, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SubscriberProfileIDforRFP + if(msg->SubscriberProfileIDforRFP_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_subscriberprofileidforrfp(&msg->SubscriberProfileIDforRFP, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - uEaggregateMaximumBitrate + if(msg->uEaggregateMaximumBitrate_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSFallbackIndicator + if(msg->CSFallbackIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csfallbackindicator(&msg->CSFallbackIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSFALLBACKINDICATOR, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - UESecurityCapabilities + if(msg->UESecurityCapabilities_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_uesecuritycapabilities(&msg->UESecurityCapabilities, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSGMembershipStatus + if(msg->CSGMembershipStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csgmembershipstatus(&msg->CSGMembershipStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RegisteredLAI + if(msg->RegisteredLAI_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lai(&msg->RegisteredLAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REGISTEREDLAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - AdditionalCSFallbackIndicator + if(msg->AdditionalCSFallbackIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_additionalcsfallbackindicator(&msg->AdditionalCSFallbackIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ADDITIONALCSFALLBACKINDICATOR, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->SecurityKey_present = false; + msg->SubscriberProfileIDforRFP_present = false; + msg->uEaggregateMaximumBitrate_present = false; + msg->CSFallbackIndicator_present = false; + msg->UESecurityCapabilities_present = false; + msg->CSGMembershipStatus_present = false; + msg->RegisteredLAI_present = false; + msg->AdditionalCSFallbackIndicator_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SECURITYKEY == ie_id) { + if(liblte_s1ap_unpack_securitykey(ptr, &msg->SecurityKey) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SecurityKey_present = true; + } else if(LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP == ie_id) { + if(liblte_s1ap_unpack_subscriberprofileidforrfp(ptr, &msg->SubscriberProfileIDforRFP) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SubscriberProfileIDforRFP_present = true; + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->uEaggregateMaximumBitrate_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSFALLBACKINDICATOR == ie_id) { + if(liblte_s1ap_unpack_csfallbackindicator(ptr, &msg->CSFallbackIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSFallbackIndicator_present = true; + } else if(LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES == ie_id) { + if(liblte_s1ap_unpack_uesecuritycapabilities(ptr, &msg->UESecurityCapabilities) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UESecurityCapabilities_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS == ie_id) { + if(liblte_s1ap_unpack_csgmembershipstatus(ptr, &msg->CSGMembershipStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSGMembershipStatus_present = true; + } else if(LIBLTE_S1AP_IE_ID_REGISTEREDLAI == ie_id) { + if(liblte_s1ap_unpack_lai(ptr, &msg->RegisteredLAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->RegisteredLAI_present = true; + } else if(LIBLTE_S1AP_IE_ID_ADDITIONALCSFALLBACKINDICATOR == ie_id) { + if(liblte_s1ap_unpack_additionalcsfallbackindicator(ptr, &msg->AdditionalCSFallbackIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextModificationResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationresponse( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UEContextModificationFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecontextmodificationfailure( + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecontextmodificationfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UEContextModificationFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UERadioCapabilityMatchRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapabilitymatchrequest( + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UERadioCapabilityMatchRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->UERadioCapability_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UERadioCapability + if(msg->UERadioCapability_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueradiocapability(&msg->UERadioCapability, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapabilitymatchrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->UERadioCapability_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UERadioCapabilityMatchRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY == ie_id) { + if(liblte_s1ap_unpack_ueradiocapability(ptr, &msg->UERadioCapability) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UERadioCapability_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UERadioCapabilityMatchResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ueradiocapabilitymatchresponse( + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UERadioCapabilityMatchResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - VoiceSupportMatchIndicator + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_voicesupportmatchindicator(&msg->VoiceSupportMatchIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_VOICESUPPORTMATCHINDICATOR, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ueradiocapabilitymatchresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UERADIOCAPABILITYMATCHRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UERadioCapabilityMatchResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_VOICESUPPORTMATCHINDICATOR == ie_id) { + if(liblte_s1ap_unpack_voicesupportmatchindicator(ptr, &msg->VoiceSupportMatchIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message DownlinkNASTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinknastransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkNASTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->HandoverRestrictionList_present) + n_ie--; + if(!msg->SubscriberProfileIDforRFP_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NAS_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nas_pdu(&msg->NAS_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NAS_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - HandoverRestrictionList + if(msg->HandoverRestrictionList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handoverrestrictionlist(&msg->HandoverRestrictionList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SubscriberProfileIDforRFP + if(msg->SubscriberProfileIDforRFP_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_subscriberprofileidforrfp(&msg->SubscriberProfileIDforRFP, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinknastransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->HandoverRestrictionList_present = false; + msg->SubscriberProfileIDforRFP_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkNASTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NAS_PDU == ie_id) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &msg->NAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST == ie_id) { + if(liblte_s1ap_unpack_handoverrestrictionlist(ptr, &msg->HandoverRestrictionList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->HandoverRestrictionList_present = true; + } else if(LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP == ie_id) { + if(liblte_s1ap_unpack_subscriberprofileidforrfp(ptr, &msg->SubscriberProfileIDforRFP) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SubscriberProfileIDforRFP_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message InitialUEMessage STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialuemessage( + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialUEMessage-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 15; + if(!msg->S_TMSI_present) + n_ie--; + if(!msg->CSG_Id_present) + n_ie--; + if(!msg->GUMMEI_ID_present) + n_ie--; + if(!msg->CellAccessMode_present) + n_ie--; + if(!msg->GW_TransportLayerAddress_present) + n_ie--; + if(!msg->RelayNode_Indicator_present) + n_ie--; + if(!msg->GUMMEIType_present) + n_ie--; + if(!msg->Tunnel_Information_for_BBF_present) + n_ie--; + if(!msg->SIPTO_L_GW_TransportLayerAddress_present) + n_ie--; + if(!msg->LHN_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NAS_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nas_pdu(&msg->NAS_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NAS_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tai(&msg->TAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAI, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - RRC_Establishment_Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_rrc_establishment_cause(&msg->RRC_Establishment_Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RRC_ESTABLISHMENT_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - S_TMSI + if(msg->S_TMSI_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_s_tmsi(&msg->S_TMSI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_S_TMSI, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSG_Id + if(msg->CSG_Id_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_id(&msg->CSG_Id, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - GUMMEI_ID + if(msg->GUMMEI_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummei(&msg->GUMMEI_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEI_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CellAccessMode + if(msg->CellAccessMode_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cellaccessmode(&msg->CellAccessMode, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CELLACCESSMODE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - GW_TransportLayerAddress + if(msg->GW_TransportLayerAddress_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportlayeraddress(&msg->GW_TransportLayerAddress, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GW_TRANSPORTLAYERADDRESS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RelayNode_Indicator + if(msg->RelayNode_Indicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_relaynode_indicator(&msg->RelayNode_Indicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RELAYNODE_INDICATOR, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - GUMMEIType + if(msg->GUMMEIType_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummeitype(&msg->GUMMEIType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEITYPE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Tunnel_Information_for_BBF + if(msg->Tunnel_Information_for_BBF_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tunnelinformation(&msg->Tunnel_Information_for_BBF, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SIPTO_L_GW_TransportLayerAddress + if(msg->SIPTO_L_GW_TransportLayerAddress_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportlayeraddress(&msg->SIPTO_L_GW_TransportLayerAddress, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SIPTO_L_GW_TRANSPORTLAYERADDRESS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - LHN_ID + if(msg->LHN_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lhn_id(&msg->LHN_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LHN_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialuemessage( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->S_TMSI_present = false; + msg->CSG_Id_present = false; + msg->GUMMEI_ID_present = false; + msg->CellAccessMode_present = false; + msg->GW_TransportLayerAddress_present = false; + msg->RelayNode_Indicator_present = false; + msg->GUMMEIType_present = false; + msg->Tunnel_Information_for_BBF_present = false; + msg->SIPTO_L_GW_TransportLayerAddress_present = false; + msg->LHN_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialUEMessage-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;ieNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NAS_PDU == ie_id) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &msg->NAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAI == ie_id) { + if(liblte_s1ap_unpack_tai(ptr, &msg->TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_RRC_ESTABLISHMENT_CAUSE == ie_id) { + if(liblte_s1ap_unpack_rrc_establishment_cause(ptr, &msg->RRC_Establishment_Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_S_TMSI == ie_id) { + if(liblte_s1ap_unpack_s_tmsi(ptr, &msg->S_TMSI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->S_TMSI_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSG_ID == ie_id) { + if(liblte_s1ap_unpack_csg_id(ptr, &msg->CSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_Id_present = true; + } else if(LIBLTE_S1AP_IE_ID_GUMMEI_ID == ie_id) { + if(liblte_s1ap_unpack_gummei(ptr, &msg->GUMMEI_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEI_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_CELLACCESSMODE == ie_id) { + if(liblte_s1ap_unpack_cellaccessmode(ptr, &msg->CellAccessMode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CellAccessMode_present = true; + } else if(LIBLTE_S1AP_IE_ID_GW_TRANSPORTLAYERADDRESS == ie_id) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &msg->GW_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GW_TransportLayerAddress_present = true; + } else if(LIBLTE_S1AP_IE_ID_RELAYNODE_INDICATOR == ie_id) { + if(liblte_s1ap_unpack_relaynode_indicator(ptr, &msg->RelayNode_Indicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->RelayNode_Indicator_present = true; + } else if(LIBLTE_S1AP_IE_ID_GUMMEITYPE == ie_id) { + if(liblte_s1ap_unpack_gummeitype(ptr, &msg->GUMMEIType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEIType_present = true; + } else if(LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF == ie_id) { + if(liblte_s1ap_unpack_tunnelinformation(ptr, &msg->Tunnel_Information_for_BBF) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Tunnel_Information_for_BBF_present = true; + } else if(LIBLTE_S1AP_IE_ID_SIPTO_L_GW_TRANSPORTLAYERADDRESS == ie_id) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &msg->SIPTO_L_GW_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SIPTO_L_GW_TransportLayerAddress_present = true; + } else if(LIBLTE_S1AP_IE_ID_LHN_ID == ie_id) { + if(liblte_s1ap_unpack_lhn_id(ptr, &msg->LHN_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->LHN_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UplinkNASTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinknastransport( + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkNASTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 8; + if(!msg->GW_TransportLayerAddress_present) + n_ie--; + if(!msg->SIPTO_L_GW_TransportLayerAddress_present) + n_ie--; + if(!msg->LHN_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NAS_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nas_pdu(&msg->NAS_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NAS_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tai(&msg->TAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - GW_TransportLayerAddress + if(msg->GW_TransportLayerAddress_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportlayeraddress(&msg->GW_TransportLayerAddress, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GW_TRANSPORTLAYERADDRESS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SIPTO_L_GW_TransportLayerAddress + if(msg->SIPTO_L_GW_TransportLayerAddress_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportlayeraddress(&msg->SIPTO_L_GW_TransportLayerAddress, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SIPTO_L_GW_TRANSPORTLAYERADDRESS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - LHN_ID + if(msg->LHN_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lhn_id(&msg->LHN_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LHN_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinknastransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->GW_TransportLayerAddress_present = false; + msg->SIPTO_L_GW_TransportLayerAddress_present = false; + msg->LHN_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkNASTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NAS_PDU == ie_id) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &msg->NAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAI == ie_id) { + if(liblte_s1ap_unpack_tai(ptr, &msg->TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_GW_TRANSPORTLAYERADDRESS == ie_id) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &msg->GW_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GW_TransportLayerAddress_present = true; + } else if(LIBLTE_S1AP_IE_ID_SIPTO_L_GW_TRANSPORTLAYERADDRESS == ie_id) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &msg->SIPTO_L_GW_TransportLayerAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SIPTO_L_GW_TransportLayerAddress_present = true; + } else if(LIBLTE_S1AP_IE_ID_LHN_ID == ie_id) { + if(liblte_s1ap_unpack_lhn_id(ptr, &msg->LHN_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->LHN_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message NASNonDeliveryIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_nasnondeliveryindication( + LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("NASNonDeliveryIndication-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NAS_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nas_pdu(&msg->NAS_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NAS_PDU, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_nasnondeliveryindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_NASNONDELIVERYINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("NASNonDeliveryIndication-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NAS_PDU == ie_id) { + if(liblte_s1ap_unpack_nas_pdu(ptr, &msg->NAS_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitem( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemRes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - UE_associatedLogicalS1_ConnectionItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionitem(&msg->UE_associatedLogicalS1_ConnectionItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemRes error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iUE_associatedLogicalS1_ConnectionItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UE_associatedLogicalS1_ConnectionItemRes STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_ue_associatedlogicals1_connectionitemres( + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMRES_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemResAck error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - UE_associatedLogicalS1_ConnectionItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionitem(&msg->UE_associatedLogicalS1_ConnectionItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_ue_associatedlogicals1_connectionitemres( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UE_ASSOCIATEDLOGICALS1_CONNECTIONITEMRES_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UE-associatedLogicalS1-ConnectionItemResAck error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iUE_associatedLogicalS1_ConnectionItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ErrorIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_errorindication( + LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ErrorIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->MME_UE_S1AP_ID_present) + n_ie--; + if(!msg->eNB_UE_S1AP_ID_present) + n_ie--; + if(!msg->Cause_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + if(msg->MME_UE_S1AP_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - eNB_UE_S1AP_ID + if(msg->eNB_UE_S1AP_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Cause + if(msg->Cause_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_errorindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ERRORINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->MME_UE_S1AP_ID_present = false; + msg->eNB_UE_S1AP_ID_present = false; + msg->Cause_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ErrorIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MME_UE_S1AP_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->eNB_UE_S1AP_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Cause_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message S1SetupRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setuprequest( + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->eNBname_present) + n_ie--; + if(!msg->CSG_IdList_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Global_ENB_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_global_enb_id(&msg->Global_ENB_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GLOBAL_ENB_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNBname + if(msg->eNBname_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enbname(&msg->eNBname, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENBNAME, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SupportedTAs + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_supportedtas(&msg->SupportedTAs, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SUPPORTEDTAS, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - DefaultPagingDRX + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_pagingdrx(&msg->DefaultPagingDRX, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DEFAULTPAGINGDRX, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CSG_IdList + if(msg->CSG_IdList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_idlist(&msg->CSG_IdList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_IDLIST, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->eNBname_present = false; + msg->CSG_IdList_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iGlobal_ENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENBNAME == ie_id) { + if(liblte_s1ap_unpack_enbname(ptr, &msg->eNBname) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->eNBname_present = true; + } else if(LIBLTE_S1AP_IE_ID_SUPPORTEDTAS == ie_id) { + if(liblte_s1ap_unpack_supportedtas(ptr, &msg->SupportedTAs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_DEFAULTPAGINGDRX == ie_id) { + if(liblte_s1ap_unpack_pagingdrx(ptr, &msg->DefaultPagingDRX) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_IDLIST == ie_id) { + if(liblte_s1ap_unpack_csg_idlist(ptr, &msg->CSG_IdList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_IdList_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message S1SetupResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setupresponse( + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->MMEname_present) + n_ie--; + if(!msg->MMERelaySupportIndicator_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MMEname + if(msg->MMEname_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mmename(&msg->MMEname, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MMENAME, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ServedGUMMEIs + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_servedgummeis(&msg->ServedGUMMEIs, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERVEDGUMMEIS, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - RelativeMMECapacity + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_relativemmecapacity(&msg->RelativeMMECapacity, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RELATIVEMMECAPACITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - MMERelaySupportIndicator + if(msg->MMERelaySupportIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mmerelaysupportindicator(&msg->MMERelaySupportIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MMERELAYSUPPORTINDICATOR, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->MMEname_present = false; + msg->MMERelaySupportIndicator_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMMEname) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MMEname_present = true; + } else if(LIBLTE_S1AP_IE_ID_SERVEDGUMMEIS == ie_id) { + if(liblte_s1ap_unpack_servedgummeis(ptr, &msg->ServedGUMMEIs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_RELATIVEMMECAPACITY == ie_id) { + if(liblte_s1ap_unpack_relativemmecapacity(ptr, &msg->RelativeMMECapacity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_MMERELAYSUPPORTINDICATOR == ie_id) { + if(liblte_s1ap_unpack_mmerelaysupportindicator(ptr, &msg->MMERelaySupportIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MMERelaySupportIndicator_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message S1SetupFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1setupfailure( + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->TimeToWait_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TimeToWait + if(msg->TimeToWait_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_timetowait(&msg->TimeToWait, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TIMETOWAIT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1setupfailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->TimeToWait_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("S1SetupFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TIMETOWAIT == ie_id) { + if(liblte_s1ap_unpack_timetowait(ptr, &msg->TimeToWait) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TimeToWait_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdate STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdate( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->eNBname_present) + n_ie--; + if(!msg->SupportedTAs_present) + n_ie--; + if(!msg->CSG_IdList_present) + n_ie--; + if(!msg->DefaultPagingDRX_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - eNBname + if(msg->eNBname_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enbname(&msg->eNBname, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENBNAME, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SupportedTAs + if(msg->SupportedTAs_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_supportedtas(&msg->SupportedTAs, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SUPPORTEDTAS, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSG_IdList + if(msg->CSG_IdList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_idlist(&msg->CSG_IdList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_IDLIST, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - DefaultPagingDRX + if(msg->DefaultPagingDRX_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_pagingdrx(&msg->DefaultPagingDRX, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DEFAULTPAGINGDRX, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdate( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->eNBname_present = false; + msg->SupportedTAs_present = false; + msg->CSG_IdList_present = false; + msg->DefaultPagingDRX_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;ieNBname) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->eNBname_present = true; + } else if(LIBLTE_S1AP_IE_ID_SUPPORTEDTAS == ie_id) { + if(liblte_s1ap_unpack_supportedtas(ptr, &msg->SupportedTAs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SupportedTAs_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSG_IDLIST == ie_id) { + if(liblte_s1ap_unpack_csg_idlist(ptr, &msg->CSG_IdList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_IdList_present = true; + } else if(LIBLTE_S1AP_IE_ID_DEFAULTPAGINGDRX == ie_id) { + if(liblte_s1ap_unpack_pagingdrx(ptr, &msg->DefaultPagingDRX) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->DefaultPagingDRX_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdateAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdateacknowledge( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdateacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBConfigurationUpdateFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationupdatefailure( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->TimeToWait_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TimeToWait + if(msg->TimeToWait_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_timetowait(&msg->TimeToWait, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TIMETOWAIT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationupdatefailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONUPDATEFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->TimeToWait_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationUpdateFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TIMETOWAIT == ie_id) { + if(liblte_s1ap_unpack_timetowait(ptr, &msg->TimeToWait) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TimeToWait_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdate STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdate( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->MMEname_present) + n_ie--; + if(!msg->ServedGUMMEIs_present) + n_ie--; + if(!msg->RelativeMMECapacity_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MMEname + if(msg->MMEname_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mmename(&msg->MMEname, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MMENAME, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ServedGUMMEIs + if(msg->ServedGUMMEIs_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_servedgummeis(&msg->ServedGUMMEIs, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERVEDGUMMEIS, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RelativeMMECapacity + if(msg->RelativeMMECapacity_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_relativemmecapacity(&msg->RelativeMMECapacity, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RELATIVEMMECAPACITY, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdate( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->MMEname_present = false; + msg->ServedGUMMEIs_present = false; + msg->RelativeMMECapacity_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMMEname) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MMEname_present = true; + } else if(LIBLTE_S1AP_IE_ID_SERVEDGUMMEIS == ie_id) { + if(liblte_s1ap_unpack_servedgummeis(ptr, &msg->ServedGUMMEIs) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ServedGUMMEIs_present = true; + } else if(LIBLTE_S1AP_IE_ID_RELATIVEMMECAPACITY == ie_id) { + if(liblte_s1ap_unpack_relativemmecapacity(ptr, &msg->RelativeMMECapacity) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->RelativeMMECapacity_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdateAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdateacknowledge( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdateacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEConfigurationUpdateFailure STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationupdatefailure( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->TimeToWait_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TimeToWait + if(msg->TimeToWait_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_timetowait(&msg->TimeToWait, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TIMETOWAIT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationupdatefailure( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONUPDATEFAILURE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->TimeToWait_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationUpdateFailureIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TIMETOWAIT == ie_id) { + if(liblte_s1ap_unpack_timetowait(ptr, &msg->TimeToWait) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TimeToWait_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UplinkS1cdma2000tunneling STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinks1cdma2000tunneling( + LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkS1cdma2000tunnelingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 9; + if(!msg->cdma2000HORequiredIndication_present) + n_ie--; + if(!msg->cdma2000OneXSRVCCInfo_present) + n_ie--; + if(!msg->cdma2000OneXRAND_present) + n_ie--; + if(!msg->EUTRANRoundTripDelayEstimationInfo_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - cdma2000RATType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000rattype(&msg->cdma2000RATType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000RATTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - cdma2000SectorID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000sectorid(&msg->cdma2000SectorID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000SECTORID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - cdma2000HORequiredIndication + if(msg->cdma2000HORequiredIndication_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000horequiredindication(&msg->cdma2000HORequiredIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000HOREQUIREDINDICATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - cdma2000OneXSRVCCInfo + if(msg->cdma2000OneXSRVCCInfo_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000onexsrvccinfo(&msg->cdma2000OneXSRVCCInfo, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000ONEXSRVCCINFO, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - cdma2000OneXRAND + if(msg->cdma2000OneXRAND_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000onexrand(&msg->cdma2000OneXRAND, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000ONEXRAND, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - cdma2000PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000pdu(&msg->cdma2000PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRANRoundTripDelayEstimationInfo + if(msg->EUTRANRoundTripDelayEstimationInfo_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutranroundtripdelayestimationinfo(&msg->EUTRANRoundTripDelayEstimationInfo, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRANROUNDTRIPDELAYESTIMATIONINFO, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinks1cdma2000tunneling( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKS1CDMA2000TUNNELING_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->cdma2000HORequiredIndication_present = false; + msg->cdma2000OneXSRVCCInfo_present = false; + msg->cdma2000OneXRAND_present = false; + msg->EUTRANRoundTripDelayEstimationInfo_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkS1cdma2000tunnelingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CDMA2000RATTYPE == ie_id) { + if(liblte_s1ap_unpack_cdma2000rattype(ptr, &msg->cdma2000RATType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CDMA2000SECTORID == ie_id) { + if(liblte_s1ap_unpack_cdma2000sectorid(ptr, &msg->cdma2000SectorID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CDMA2000HOREQUIREDINDICATION == ie_id) { + if(liblte_s1ap_unpack_cdma2000horequiredindication(ptr, &msg->cdma2000HORequiredIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->cdma2000HORequiredIndication_present = true; + } else if(LIBLTE_S1AP_IE_ID_CDMA2000ONEXSRVCCINFO == ie_id) { + if(liblte_s1ap_unpack_cdma2000onexsrvccinfo(ptr, &msg->cdma2000OneXSRVCCInfo) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->cdma2000OneXSRVCCInfo_present = true; + } else if(LIBLTE_S1AP_IE_ID_CDMA2000ONEXRAND == ie_id) { + if(liblte_s1ap_unpack_cdma2000onexrand(ptr, &msg->cdma2000OneXRAND) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->cdma2000OneXRAND_present = true; + } else if(LIBLTE_S1AP_IE_ID_CDMA2000PDU == ie_id) { + if(liblte_s1ap_unpack_cdma2000pdu(ptr, &msg->cdma2000PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRANROUNDTRIPDELAYESTIMATIONINFO == ie_id) { + if(liblte_s1ap_unpack_eutranroundtripdelayestimationinfo(ptr, &msg->EUTRANRoundTripDelayEstimationInfo) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->EUTRANRoundTripDelayEstimationInfo_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UECapabilityInfoIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uecapabilityinfoindication( + LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UECapabilityInfoIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UERadioCapability + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueradiocapability(&msg->UERadioCapability, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uecapabilityinfoindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UECapabilityInfoIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY == ie_id) { + if(liblte_s1ap_unpack_ueradiocapability(ptr, &msg->UERadioCapability) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBStatusTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbstatustransfer( + LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBStatusTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_StatusTransfer_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_statustransfer_transparentcontainer(&msg->eNB_StatusTransfer_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbstatustransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBSTATUSTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBStatusTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_enb_statustransfer_transparentcontainer(ptr, &msg->eNB_StatusTransfer_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEStatusTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmestatustransfer( + LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEStatusTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_StatusTransfer_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_statustransfer_transparentcontainer(&msg->eNB_StatusTransfer_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmestatustransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMESTATUSTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEStatusTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_STATUSTRANSFER_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_enb_statustransfer_transparentcontainer(ptr, &msg->eNB_StatusTransfer_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TraceStart STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracestart( + LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceStartIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TraceActivation + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_traceactivation(&msg->TraceActivation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRACEACTIVATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracestart( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACESTART_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceStartIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TRACEACTIVATION == ie_id) { + if(liblte_s1ap_unpack_traceactivation(ptr, &msg->TraceActivation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TraceFailureIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_tracefailureindication( + LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceFailureIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_UTRAN_Trace_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_utran_trace_id(&msg->E_UTRAN_Trace_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_tracefailureindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TRACEFAILUREINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TraceFailureIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID == ie_id) { + if(liblte_s1ap_unpack_e_utran_trace_id(ptr, &msg->E_UTRAN_Trace_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message DeactivateTrace STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_deactivatetrace( + LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("DeactivateTraceIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_UTRAN_Trace_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_utran_trace_id(&msg->E_UTRAN_Trace_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_deactivatetrace( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DEACTIVATETRACE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("DeactivateTraceIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID == ie_id) { + if(liblte_s1ap_unpack_e_utran_trace_id(ptr, &msg->E_UTRAN_Trace_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message CellTrafficTrace STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_celltraffictrace( + LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("CellTrafficTraceIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 6; + if(!msg->PrivacyIndicator_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_UTRAN_Trace_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_utran_trace_id(&msg->E_UTRAN_Trace_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TraceCollectionEntityIPAddress + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_transportlayeraddress(&msg->TraceCollectionEntityIPAddress, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRACECOLLECTIONENTITYIPADDRESS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - PrivacyIndicator + if(msg->PrivacyIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_privacyindicator(&msg->PrivacyIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_PRIVACYINDICATOR, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_celltraffictrace( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_CELLTRAFFICTRACE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->PrivacyIndicator_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("CellTrafficTraceIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_UTRAN_TRACE_ID == ie_id) { + if(liblte_s1ap_unpack_e_utran_trace_id(ptr, &msg->E_UTRAN_Trace_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TRACECOLLECTIONENTITYIPADDRESS == ie_id) { + if(liblte_s1ap_unpack_transportlayeraddress(ptr, &msg->TraceCollectionEntityIPAddress) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_PRIVACYINDICATOR == ie_id) { + if(liblte_s1ap_unpack_privacyindicator(ptr, &msg->PrivacyIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->PrivacyIndicator_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LocationReportingControl STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreportingcontrol( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportingControlIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - RequestType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_requesttype(&msg->RequestType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REQUESTTYPE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreportingcontrol( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGCONTROL_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportingControlIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_REQUESTTYPE == ie_id) { + if(liblte_s1ap_unpack_requesttype(ptr, &msg->RequestType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LocationReportingFailureIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreportingfailureindication( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportingFailureIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreportingfailureindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORTINGFAILUREINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportingFailureIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message LocationReport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_locationreport( + LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tai(&msg->TAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - RequestType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_requesttype(&msg->RequestType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REQUESTTYPE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_locationreport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_LOCATIONREPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("LocationReportIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAI == ie_id) { + if(liblte_s1ap_unpack_tai(ptr, &msg->TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_REQUESTTYPE == ie_id) { + if(liblte_s1ap_unpack_requesttype(ptr, &msg->RequestType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message OverloadStart STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadstart( + LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("OverloadStartIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 3; + if(!msg->GUMMEIList_present) + n_ie--; + if(!msg->TrafficLoadReductionIndication_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - OverloadResponse + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_overloadresponse(&msg->OverloadResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_OVERLOADRESPONSE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - GUMMEIList + if(msg->GUMMEIList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummeilist(&msg->GUMMEIList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEILIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - TrafficLoadReductionIndication + if(msg->TrafficLoadReductionIndication_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_trafficloadreductionindication(&msg->TrafficLoadReductionIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRAFFICLOADREDUCTIONINDICATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadstart( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_OVERLOADSTART_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->GUMMEIList_present = false; + msg->TrafficLoadReductionIndication_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("OverloadStartIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iOverloadResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_GUMMEILIST == ie_id) { + if(liblte_s1ap_unpack_gummeilist(ptr, &msg->GUMMEIList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEIList_present = true; + } else if(LIBLTE_S1AP_IE_ID_TRAFFICLOADREDUCTIONINDICATION == ie_id) { + if(liblte_s1ap_unpack_trafficloadreductionindication(ptr, &msg->TrafficLoadReductionIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TrafficLoadReductionIndication_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message OverloadStop STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_overloadstop( + LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("OverloadStopIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->GUMMEIList_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - GUMMEIList + if(msg->GUMMEIList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummeilist(&msg->GUMMEIList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEILIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_overloadstop( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_OVERLOADSTOP_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->GUMMEIList_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("OverloadStopIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iGUMMEIList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEIList_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message WriteReplaceWarningRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_writereplacewarningrequest( + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("WriteReplaceWarningRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 11; + if(!msg->WarningAreaList_present) + n_ie--; + if(!msg->ExtendedRepetitionPeriod_present) + n_ie--; + if(!msg->WarningType_present) + n_ie--; + if(!msg->WarningSecurityInfo_present) + n_ie--; + if(!msg->DataCodingScheme_present) + n_ie--; + if(!msg->WarningMessageContents_present) + n_ie--; + if(!msg->ConcurrentWarningMessageIndicator_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MessageIdentifier + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_messageidentifier(&msg->MessageIdentifier, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MESSAGEIDENTIFIER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SerialNumber + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_serialnumber(&msg->SerialNumber, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERIALNUMBER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - WarningAreaList + if(msg->WarningAreaList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_warningarealist(&msg->WarningAreaList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_WARNINGAREALIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RepetitionPeriod + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_repetitionperiod(&msg->RepetitionPeriod, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REPETITIONPERIOD, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - ExtendedRepetitionPeriod + if(msg->ExtendedRepetitionPeriod_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_extendedrepetitionperiod(&msg->ExtendedRepetitionPeriod, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EXTENDEDREPETITIONPERIOD, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - NumberofBroadcastRequest + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_numberofbroadcastrequest(&msg->NumberofBroadcastRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NUMBEROFBROADCASTREQUEST, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - WarningType + if(msg->WarningType_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_warningtype(&msg->WarningType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_WARNINGTYPE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - WarningSecurityInfo + if(msg->WarningSecurityInfo_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_warningsecurityinfo(&msg->WarningSecurityInfo, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_WARNINGSECURITYINFO, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - DataCodingScheme + if(msg->DataCodingScheme_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_datacodingscheme(&msg->DataCodingScheme, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_DATACODINGSCHEME, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - WarningMessageContents + if(msg->WarningMessageContents_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_warningmessagecontents(&msg->WarningMessageContents, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_WARNINGMESSAGECONTENTS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ConcurrentWarningMessageIndicator + if(msg->ConcurrentWarningMessageIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_concurrentwarningmessageindicator(&msg->ConcurrentWarningMessageIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CONCURRENTWARNINGMESSAGEINDICATOR, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_writereplacewarningrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->WarningAreaList_present = false; + msg->ExtendedRepetitionPeriod_present = false; + msg->WarningType_present = false; + msg->WarningSecurityInfo_present = false; + msg->DataCodingScheme_present = false; + msg->WarningMessageContents_present = false; + msg->ConcurrentWarningMessageIndicator_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("WriteReplaceWarningRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMessageIdentifier) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SERIALNUMBER == ie_id) { + if(liblte_s1ap_unpack_serialnumber(ptr, &msg->SerialNumber) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_WARNINGAREALIST == ie_id) { + if(liblte_s1ap_unpack_warningarealist(ptr, &msg->WarningAreaList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->WarningAreaList_present = true; + } else if(LIBLTE_S1AP_IE_ID_REPETITIONPERIOD == ie_id) { + if(liblte_s1ap_unpack_repetitionperiod(ptr, &msg->RepetitionPeriod) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EXTENDEDREPETITIONPERIOD == ie_id) { + if(liblte_s1ap_unpack_extendedrepetitionperiod(ptr, &msg->ExtendedRepetitionPeriod) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ExtendedRepetitionPeriod_present = true; + } else if(LIBLTE_S1AP_IE_ID_NUMBEROFBROADCASTREQUEST == ie_id) { + if(liblte_s1ap_unpack_numberofbroadcastrequest(ptr, &msg->NumberofBroadcastRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_WARNINGTYPE == ie_id) { + if(liblte_s1ap_unpack_warningtype(ptr, &msg->WarningType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->WarningType_present = true; + } else if(LIBLTE_S1AP_IE_ID_WARNINGSECURITYINFO == ie_id) { + if(liblte_s1ap_unpack_warningsecurityinfo(ptr, &msg->WarningSecurityInfo) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->WarningSecurityInfo_present = true; + } else if(LIBLTE_S1AP_IE_ID_DATACODINGSCHEME == ie_id) { + if(liblte_s1ap_unpack_datacodingscheme(ptr, &msg->DataCodingScheme) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->DataCodingScheme_present = true; + } else if(LIBLTE_S1AP_IE_ID_WARNINGMESSAGECONTENTS == ie_id) { + if(liblte_s1ap_unpack_warningmessagecontents(ptr, &msg->WarningMessageContents) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->WarningMessageContents_present = true; + } else if(LIBLTE_S1AP_IE_ID_CONCURRENTWARNINGMESSAGEINDICATOR == ie_id) { + if(liblte_s1ap_unpack_concurrentwarningmessageindicator(ptr, &msg->ConcurrentWarningMessageIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ConcurrentWarningMessageIndicator_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message WriteReplaceWarningResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_writereplacewarningresponse( + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("WriteReplaceWarningResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->BroadcastCompletedAreaList_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MessageIdentifier + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_messageidentifier(&msg->MessageIdentifier, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MESSAGEIDENTIFIER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SerialNumber + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_serialnumber(&msg->SerialNumber, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERIALNUMBER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - BroadcastCompletedAreaList + if(msg->BroadcastCompletedAreaList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_broadcastcompletedarealist(&msg->BroadcastCompletedAreaList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_BROADCASTCOMPLETEDAREALIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_writereplacewarningresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_WRITEREPLACEWARNINGRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->BroadcastCompletedAreaList_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("WriteReplaceWarningResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMessageIdentifier) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SERIALNUMBER == ie_id) { + if(liblte_s1ap_unpack_serialnumber(ptr, &msg->SerialNumber) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_BROADCASTCOMPLETEDAREALIST == ie_id) { + if(liblte_s1ap_unpack_broadcastcompletedarealist(ptr, &msg->BroadcastCompletedAreaList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->BroadcastCompletedAreaList_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEDirectInformationTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmedirectinformationtransfer( + LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEDirectInformationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Inter_SystemInformationTransferTypeMDT + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_inter_systeminformationtransfertype(&msg->Inter_SystemInformationTransferTypeMDT, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_INTER_SYSTEMINFORMATIONTRANSFERTYPEMDT, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmedirectinformationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMEDIRECTINFORMATIONTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEDirectInformationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iInter_SystemInformationTransferTypeMDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBConfigurationTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbconfigurationtransfer( + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->SONConfigurationTransferECT_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - SONConfigurationTransferECT + if(msg->SONConfigurationTransferECT_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_sonconfigurationtransfer(&msg->SONConfigurationTransferECT, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SONCONFIGURATIONTRANSFERECT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBCONFIGURATIONTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->SONConfigurationTransferECT_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBConfigurationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iSONConfigurationTransferECT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SONConfigurationTransferECT_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message MMEConfigurationTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_mmeconfigurationtransfer( + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + if(!msg->SONConfigurationTransferMCT_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - SONConfigurationTransferMCT + if(msg->SONConfigurationTransferMCT_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_sonconfigurationtransfer(&msg->SONConfigurationTransferMCT, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SONCONFIGURATIONTRANSFERMCT, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_mmeconfigurationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_MMECONFIGURATIONTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->SONConfigurationTransferMCT_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("MMEConfigurationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iSONConfigurationTransferMCT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SONConfigurationTransferMCT_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message PrivateMessage STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_privatemessage( + LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PrivateMessageIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 0; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_privatemessage( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PRIVATEMESSAGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PrivateMessageIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message KillRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killrequest( + LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("KillRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->WarningAreaList_present) + n_ie--; + if(!msg->KillAllWarningMessages_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MessageIdentifier + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_messageidentifier(&msg->MessageIdentifier, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MESSAGEIDENTIFIER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SerialNumber + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_serialnumber(&msg->SerialNumber, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERIALNUMBER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - WarningAreaList + if(msg->WarningAreaList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_warningarealist(&msg->WarningAreaList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_WARNINGAREALIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - KillAllWarningMessages + if(msg->KillAllWarningMessages_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_killallwarningmessages(&msg->KillAllWarningMessages, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_KILLALLWARNINGMESSAGES, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_KILLREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->WarningAreaList_present = false; + msg->KillAllWarningMessages_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("KillRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMessageIdentifier) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SERIALNUMBER == ie_id) { + if(liblte_s1ap_unpack_serialnumber(ptr, &msg->SerialNumber) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_WARNINGAREALIST == ie_id) { + if(liblte_s1ap_unpack_warningarealist(ptr, &msg->WarningAreaList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->WarningAreaList_present = true; + } else if(LIBLTE_S1AP_IE_ID_KILLALLWARNINGMESSAGES == ie_id) { + if(liblte_s1ap_unpack_killallwarningmessages(ptr, &msg->KillAllWarningMessages) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->KillAllWarningMessages_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message KillResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_killresponse( + LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("KillResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->BroadcastCancelledAreaList_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MessageIdentifier + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_messageidentifier(&msg->MessageIdentifier, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MESSAGEIDENTIFIER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SerialNumber + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_serialnumber(&msg->SerialNumber, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SERIALNUMBER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - BroadcastCancelledAreaList + if(msg->BroadcastCancelledAreaList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_broadcastcancelledarealist(&msg->BroadcastCancelledAreaList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_BROADCASTCANCELLEDAREALIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_killresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_KILLRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->BroadcastCancelledAreaList_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("KillResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMessageIdentifier) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SERIALNUMBER == ie_id) { + if(liblte_s1ap_unpack_serialnumber(ptr, &msg->SerialNumber) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_BROADCASTCANCELLEDAREALIST == ie_id) { + if(liblte_s1ap_unpack_broadcastcancelledarealist(ptr, &msg->BroadcastCancelledAreaList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->BroadcastCancelledAreaList_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message PWSRestartIndication STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pwsrestartindication( + LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PWSRestartIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->EmergencyAreaIDListForRestart_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - ECGIListForRestart + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ecgilistforrestart(&msg->ECGIListForRestart, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ECGILISTFORRESTART, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Global_ENB_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_global_enb_id(&msg->Global_ENB_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GLOBAL_ENB_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAIListForRestart + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tailistforrestart(&msg->TAIListForRestart, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAILISTFORRESTART, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EmergencyAreaIDListForRestart + if(msg->EmergencyAreaIDListForRestart_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_emergencyareaidlistforrestart(&msg->EmergencyAreaIDListForRestart, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EMERGENCYAREAIDLISTFORRESTART, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pwsrestartindication( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PWSRESTARTINDICATION_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->EmergencyAreaIDListForRestart_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PWSRestartIndicationIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iECGIListForRestart) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_GLOBAL_ENB_ID == ie_id) { + if(liblte_s1ap_unpack_global_enb_id(ptr, &msg->Global_ENB_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAILISTFORRESTART == ie_id) { + if(liblte_s1ap_unpack_tailistforrestart(ptr, &msg->TAIListForRestart) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EMERGENCYAREAIDLISTFORRESTART == ie_id) { + if(liblte_s1ap_unpack_emergencyareaidlistforrestart(ptr, &msg->EmergencyAreaIDListForRestart) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->EmergencyAreaIDListForRestart_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message DownlinkUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinkueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Routing_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_routing_id(&msg->Routing_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ROUTING_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - LPPa_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lppa_pdu(&msg->LPPa_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LPPA_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinkueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ROUTING_ID == ie_id) { + if(liblte_s1ap_unpack_routing_id(ptr, &msg->Routing_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_LPPA_PDU == ie_id) { + if(liblte_s1ap_unpack_lppa_pdu(ptr, &msg->LPPa_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UplinkUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinkueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Routing_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_routing_id(&msg->Routing_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ROUTING_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - LPPa_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lppa_pdu(&msg->LPPa_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LPPA_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinkueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKUEASSOCIATEDLPPATRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ROUTING_ID == ie_id) { + if(liblte_s1ap_unpack_routing_id(ptr, &msg->Routing_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_LPPA_PDU == ie_id) { + if(liblte_s1ap_unpack_lppa_pdu(ptr, &msg->LPPa_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message DownlinkNonUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinknonueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkNonUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Routing_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_routing_id(&msg->Routing_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ROUTING_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - LPPa_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lppa_pdu(&msg->LPPa_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LPPA_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinknonueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkNonUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iRouting_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_LPPA_PDU == ie_id) { + if(liblte_s1ap_unpack_lppa_pdu(ptr, &msg->LPPa_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message UplinkNonUEAssociatedLPPaTransport STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_uplinknonueassociatedlppatransport( + LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkNonUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Routing_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_routing_id(&msg->Routing_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ROUTING_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - LPPa_PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lppa_pdu(&msg->LPPa_PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LPPA_PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_uplinknonueassociatedlppatransport( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_UPLINKNONUEASSOCIATEDLPPATRANSPORT_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("UplinkNonUEAssociatedLPPaTransport-IEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iRouting_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_LPPA_PDU == ie_id) { + if(liblte_s1ap_unpack_lppa_pdu(ptr, &msg->LPPa_PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ENBDirectInformationTransfer STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_enbdirectinformationtransfer( + LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBDirectInformationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Inter_SystemInformationTransferTypeEDT + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_inter_systeminformationtransfertype(&msg->Inter_SystemInformationTransferTypeEDT, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_INTER_SYSTEMINFORMATIONTRANSFERTYPEEDT, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_enbdirectinformationtransfer( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_ENBDIRECTINFORMATIONTRANSFER_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ENBDirectInformationTransferIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iInter_SystemInformationTransferTypeEDT) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABDataForwardingItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabdataforwardingitem( + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABDataForwardingItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABDataForwardingItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabdataforwardingitem(&msg->E_RABDataForwardingItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABDATAFORWARDINGITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabdataforwardingitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABDATAFORWARDINGITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABDataForwardingItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABDataForwardingItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemHOReq STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemhoreq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemHOReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeSetupItemHOReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitemhoreq(&msg->E_RABToBeSetupItemHOReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMHOREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemhoreq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMHOREQ_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemHOReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeSetupItemHOReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABAdmittedItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabadmitteditem( + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABAdmittedItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABAdmittedItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabadmitteditem(&msg->E_RABAdmittedItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABADMITTEDITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabadmitteditem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABADMITTEDITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABAdmittedItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABAdmittedItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABFailedtoSetupItemHOReqAck STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack( + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABFailedtoSetupItemHOReqAckIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABFailedtoSetupItemHOReqAck + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabfailedtosetupitemhoreqack(&msg->E_RABFailedtoSetupItemHOReqAck, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPITEMHOREQACK, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabfailedtosetupitemhoreqack( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABFAILEDTOSETUPITEMHOREQACK_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABFailedtoSetupItemHOReqAckIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABFailedtoSetupItemHOReqAck) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedDLItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitcheddlitem( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedDLItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeSwitchedDLItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitcheddlitem(&msg->E_RABToBeSwitchedDLItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLITEM, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitcheddlitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDDLITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedDLItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeSwitchedDLItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSwitchedULItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobeswitchedulitem( + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedULItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeSwitchedULItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitchedulitem(&msg->E_RABToBeSwitchedULItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobeswitchedulitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESWITCHEDULITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSwitchedULItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeSwitchedULItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemBearerSUReq STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitembearersureq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemBearerSUReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeSetupItemBearerSUReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitembearersureq(&msg->E_RABToBeSetupItemBearerSUReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMBEARERSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitembearersureq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemBearerSUReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeSetupItemBearerSUReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupItemBearerSURes STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitembearersures( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURES_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemBearerSUResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABSetupItemBearerSURes + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetupitembearersures(&msg->E_RABSetupItemBearerSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMBEARERSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitembearersures( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMBEARERSURES_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemBearerSUResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABSetupItemBearerSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeModifiedItemBearerModReq STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobemodifieditembearermodreq( + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeModifiedItemBearerModReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeModifiedItemBearerModReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobemodifieditembearermodreq(&msg->E_RABToBeModifiedItemBearerModReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDITEMBEARERMODREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobemodifieditembearermodreq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBEMODIFIEDITEMBEARERMODREQ_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeModifiedItemBearerModReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeModifiedItemBearerModReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABModifyItemBearerModRes STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyitembearermodres( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRES_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyItemBearerModResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABModifyItemBearerModRes + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabmodifyitembearermodres(&msg->E_RABModifyItemBearerModRes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABMODIFYITEMBEARERMODRES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyitembearermodres( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYITEMBEARERMODRES_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyItemBearerModResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABModifyItemBearerModRes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABReleaseItemBearerRelComp STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseitembearerrelcomp( + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseItemBearerRelCompIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABReleaseItemBearerRelComp + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabreleaseitembearerrelcomp(&msg->E_RABReleaseItemBearerRelComp, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABRELEASEITEMBEARERRELCOMP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseitembearerrelcomp( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASEITEMBEARERRELCOMP_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseItemBearerRelCompIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABReleaseItemBearerRelComp) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABToBeSetupItemCtxtSUReq STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabtobesetupitemctxtsureq( + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemCtxtSUReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABToBeSetupItemCtxtSUReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetupitemctxtsureq(&msg->E_RABToBeSetupItemCtxtSUReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPITEMCTXTSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabtobesetupitemctxtsureq( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABToBeSetupItemCtxtSUReqIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABToBeSetupItemCtxtSUReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupItemCtxtSURes STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupitemctxtsures( + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURES_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemCtxtSUResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - E_RABSetupItemCtxtSURes + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetupitemctxtsures(&msg->E_RABSetupItemCtxtSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPITEMCTXTSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupitemctxtsures( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPITEMCTXTSURES_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupItemCtxtSUResIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iE_RABSetupItemCtxtSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message TAIItem STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_taiitem( + LIBLTE_S1AP_MESSAGE_TAIITEM_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 1; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - TAIItem + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_taiitem(&msg->TAIItem, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAIITEM, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_taiitem( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_TAIITEM_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("TAIItemIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iTAIItem) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message ResetAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_resetacknowledge( + LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ResetAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + if(!msg->UE_associatedLogicalS1_ConnectionListResAck_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - UE_associatedLogicalS1_ConnectionListResAck + if(msg->UE_associatedLogicalS1_ConnectionListResAck_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ue_associatedlogicals1_connectionlistresack(&msg->UE_associatedLogicalS1_ConnectionListResAck, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UE_ASSOCIATEDLOGICALS1_CONNECTIONLISTRESACK, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_resetacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RESETACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->UE_associatedLogicalS1_ConnectionListResAck_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ResetAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iUE_associatedLogicalS1_ConnectionListResAck) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UE_associatedLogicalS1_ConnectionListResAck_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Reset STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_reset( + LIBLTE_S1AP_MESSAGE_RESET_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("ResetIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 2; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - ResetType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_resettype(&msg->ResetType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_RESETTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_reset( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_RESET_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("ResetIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iCause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_RESETTYPE == ie_id) { + if(liblte_s1ap_unpack_resettype(ptr, &msg->ResetType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message DownlinkS1cdma2000tunneling STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_downlinks1cdma2000tunneling( + LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkS1cdma2000tunnelingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 6; + if(!msg->E_RABSubjecttoDataForwardingList_present) + n_ie--; + if(!msg->cdma2000HOStatus_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABSubjecttoDataForwardingList + if(msg->E_RABSubjecttoDataForwardingList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsubjecttodataforwardinglist(&msg->E_RABSubjecttoDataForwardingList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSUBJECTTODATAFORWARDINGLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - cdma2000HOStatus + if(msg->cdma2000HOStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000hostatus(&msg->cdma2000HOStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000HOSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - cdma2000RATType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000rattype(&msg->cdma2000RATType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000RATTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - cdma2000PDU + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cdma2000pdu(&msg->cdma2000PDU, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CDMA2000PDU, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_downlinks1cdma2000tunneling( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_DOWNLINKS1CDMA2000TUNNELING_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABSubjecttoDataForwardingList_present = false; + msg->cdma2000HOStatus_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("DownlinkS1cdma2000tunnelingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABSUBJECTTODATAFORWARDINGLIST == ie_id) { + if(liblte_s1ap_unpack_e_rabsubjecttodataforwardinglist(ptr, &msg->E_RABSubjecttoDataForwardingList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABSubjecttoDataForwardingList_present = true; + } else if(LIBLTE_S1AP_IE_ID_CDMA2000HOSTATUS == ie_id) { + if(liblte_s1ap_unpack_cdma2000hostatus(ptr, &msg->cdma2000HOStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->cdma2000HOStatus_present = true; + } else if(LIBLTE_S1AP_IE_ID_CDMA2000RATTYPE == ie_id) { + if(liblte_s1ap_unpack_cdma2000rattype(ptr, &msg->cdma2000RATType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CDMA2000PDU == ie_id) { + if(liblte_s1ap_unpack_cdma2000pdu(ptr, &msg->cdma2000PDU) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverCommand STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handovercommand( + LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCommandIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 9; + if(!msg->NASSecurityParametersfromE_UTRAN_present) + n_ie--; + if(!msg->E_RABSubjecttoDataForwardingList_present) + n_ie--; + if(!msg->E_RABtoReleaseListHOCmd_present) + n_ie--; + if(!msg->Target_ToSource_TransparentContainer_Secondary_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - HandoverType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handovertype(&msg->HandoverType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NASSecurityParametersfromE_UTRAN + if(msg->NASSecurityParametersfromE_UTRAN_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nassecurityparametersfrome_utran(&msg->NASSecurityParametersfromE_UTRAN, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSFROME_UTRAN, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABSubjecttoDataForwardingList + if(msg->E_RABSubjecttoDataForwardingList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsubjecttodataforwardinglist(&msg->E_RABSubjecttoDataForwardingList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSUBJECTTODATAFORWARDINGLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABtoReleaseListHOCmd + if(msg->E_RABtoReleaseListHOCmd_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABtoReleaseListHOCmd, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTORELEASELISTHOCMD, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Target_ToSource_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_target_tosource_transparentcontainer(&msg->Target_ToSource_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Target_ToSource_TransparentContainer_Secondary + if(msg->Target_ToSource_TransparentContainer_Secondary_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_target_tosource_transparentcontainer(&msg->Target_ToSource_TransparentContainer_Secondary, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER_SECONDARY, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handovercommand( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->NASSecurityParametersfromE_UTRAN_present = false; + msg->E_RABSubjecttoDataForwardingList_present = false; + msg->E_RABtoReleaseListHOCmd_present = false; + msg->Target_ToSource_TransparentContainer_Secondary_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverCommandIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_HANDOVERTYPE == ie_id) { + if(liblte_s1ap_unpack_handovertype(ptr, &msg->HandoverType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSFROME_UTRAN == ie_id) { + if(liblte_s1ap_unpack_nassecurityparametersfrome_utran(ptr, &msg->NASSecurityParametersfromE_UTRAN) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABSUBJECTTODATAFORWARDINGLIST == ie_id) { + if(liblte_s1ap_unpack_e_rabsubjecttodataforwardinglist(ptr, &msg->E_RABSubjecttoDataForwardingList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABSubjecttoDataForwardingList_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTORELEASELISTHOCMD == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABtoReleaseListHOCmd) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABtoReleaseListHOCmd_present = true; + } else if(LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_target_tosource_transparentcontainer(ptr, &msg->Target_ToSource_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER_SECONDARY == ie_id) { + if(liblte_s1ap_unpack_target_tosource_transparentcontainer(ptr, &msg->Target_ToSource_TransparentContainer_Secondary) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Target_ToSource_TransparentContainer_Secondary_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequest( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 19; + if(!msg->HandoverRestrictionList_present) + n_ie--; + if(!msg->TraceActivation_present) + n_ie--; + if(!msg->RequestType_present) + n_ie--; + if(!msg->SRVCCOperationPossible_present) + n_ie--; + if(!msg->NASSecurityParameterstoE_UTRAN_present) + n_ie--; + if(!msg->CSG_Id_present) + n_ie--; + if(!msg->CSGMembershipStatus_present) + n_ie--; + if(!msg->GUMMEI_ID_present) + n_ie--; + if(!msg->MME_UE_S1AP_ID_2_present) + n_ie--; + if(!msg->ManagementBasedMDTAllowed_present) + n_ie--; + if(!msg->ManagementBasedMDTPLMNList_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - HandoverType + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handovertype(&msg->HandoverType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERTYPE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Cause + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cause(&msg->Cause, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CAUSE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABToBeSetupListHOReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetuplisthoreq(&msg->E_RABToBeSetupListHOReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTHOREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - Source_ToTarget_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_source_totarget_transparentcontainer(&msg->Source_ToTarget_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UESecurityCapabilities + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_uesecuritycapabilities(&msg->UESecurityCapabilities, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - HandoverRestrictionList + if(msg->HandoverRestrictionList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handoverrestrictionlist(&msg->HandoverRestrictionList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - TraceActivation + if(msg->TraceActivation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_traceactivation(&msg->TraceActivation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRACEACTIVATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RequestType + if(msg->RequestType_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_requesttype(&msg->RequestType, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REQUESTTYPE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SRVCCOperationPossible + if(msg->SRVCCOperationPossible_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_srvccoperationpossible(&msg->SRVCCOperationPossible, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SRVCCOPERATIONPOSSIBLE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SecurityContext + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_securitycontext(&msg->SecurityContext, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SECURITYCONTEXT, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - NASSecurityParameterstoE_UTRAN + if(msg->NASSecurityParameterstoE_UTRAN_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_nassecurityparameterstoe_utran(&msg->NASSecurityParameterstoE_UTRAN, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSTOE_UTRAN, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSG_Id + if(msg->CSG_Id_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_id(&msg->CSG_Id, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSGMembershipStatus + if(msg->CSGMembershipStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csgmembershipstatus(&msg->CSGMembershipStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - GUMMEI_ID + if(msg->GUMMEI_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummei(&msg->GUMMEI_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEI_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MME_UE_S1AP_ID_2 + if(msg->MME_UE_S1AP_ID_2_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID_2, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ManagementBasedMDTAllowed + if(msg->ManagementBasedMDTAllowed_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_managementbasedmdtallowed(&msg->ManagementBasedMDTAllowed, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTALLOWED, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ManagementBasedMDTPLMNList + if(msg->ManagementBasedMDTPLMNList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mdtplmnlist(&msg->ManagementBasedMDTPLMNList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTPLMNLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->HandoverRestrictionList_present = false; + msg->TraceActivation_present = false; + msg->RequestType_present = false; + msg->SRVCCOperationPossible_present = false; + msg->NASSecurityParameterstoE_UTRAN_present = false; + msg->CSG_Id_present = false; + msg->CSGMembershipStatus_present = false; + msg->GUMMEI_ID_present = false; + msg->MME_UE_S1AP_ID_2_present = false; + msg->ManagementBasedMDTAllowed_present = false; + msg->ManagementBasedMDTPLMNList_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_HANDOVERTYPE == ie_id) { + if(liblte_s1ap_unpack_handovertype(ptr, &msg->HandoverType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CAUSE == ie_id) { + if(liblte_s1ap_unpack_cause(ptr, &msg->Cause) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTHOREQ == ie_id) { + if(liblte_s1ap_unpack_e_rabtobesetuplisthoreq(ptr, &msg->E_RABToBeSetupListHOReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SOURCE_TOTARGET_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_source_totarget_transparentcontainer(ptr, &msg->Source_ToTarget_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES == ie_id) { + if(liblte_s1ap_unpack_uesecuritycapabilities(ptr, &msg->UESecurityCapabilities) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST == ie_id) { + if(liblte_s1ap_unpack_handoverrestrictionlist(ptr, &msg->HandoverRestrictionList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->HandoverRestrictionList_present = true; + } else if(LIBLTE_S1AP_IE_ID_TRACEACTIVATION == ie_id) { + if(liblte_s1ap_unpack_traceactivation(ptr, &msg->TraceActivation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TraceActivation_present = true; + } else if(LIBLTE_S1AP_IE_ID_REQUESTTYPE == ie_id) { + if(liblte_s1ap_unpack_requesttype(ptr, &msg->RequestType) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->RequestType_present = true; + } else if(LIBLTE_S1AP_IE_ID_SRVCCOPERATIONPOSSIBLE == ie_id) { + if(liblte_s1ap_unpack_srvccoperationpossible(ptr, &msg->SRVCCOperationPossible) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SRVCCOperationPossible_present = true; + } else if(LIBLTE_S1AP_IE_ID_SECURITYCONTEXT == ie_id) { + if(liblte_s1ap_unpack_securitycontext(ptr, &msg->SecurityContext) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_NASSECURITYPARAMETERSTOE_UTRAN == ie_id) { + if(liblte_s1ap_unpack_nassecurityparameterstoe_utran(ptr, &msg->NASSecurityParameterstoE_UTRAN) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_ID == ie_id) { + if(liblte_s1ap_unpack_csg_id(ptr, &msg->CSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_Id_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS == ie_id) { + if(liblte_s1ap_unpack_csgmembershipstatus(ptr, &msg->CSGMembershipStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSGMembershipStatus_present = true; + } else if(LIBLTE_S1AP_IE_ID_GUMMEI_ID == ie_id) { + if(liblte_s1ap_unpack_gummei(ptr, &msg->GUMMEI_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEI_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2 == ie_id) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &msg->MME_UE_S1AP_ID_2) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MME_UE_S1AP_ID_2_present = true; + } else if(LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTALLOWED == ie_id) { + if(liblte_s1ap_unpack_managementbasedmdtallowed(ptr, &msg->ManagementBasedMDTAllowed) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ManagementBasedMDTAllowed_present = true; + } else if(LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTPLMNLIST == ie_id) { + if(liblte_s1ap_unpack_mdtplmnlist(ptr, &msg->ManagementBasedMDTPLMNList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ManagementBasedMDTPLMNList_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message PathSwitchRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequest( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 12; + if(!msg->CSG_Id_present) + n_ie--; + if(!msg->CellAccessMode_present) + n_ie--; + if(!msg->SourceMME_GUMMEI_present) + n_ie--; + if(!msg->CSGMembershipStatus_present) + n_ie--; + if(!msg->Tunnel_Information_for_BBF_present) + n_ie--; + if(!msg->LHN_ID_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABToBeSwitchedDLList + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitcheddllist(&msg->E_RABToBeSwitchedDLList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLLIST, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SourceMME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->SourceMME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SOURCEMME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - EUTRAN_CGI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_eutran_cgi(&msg->EUTRAN_CGI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_EUTRAN_CGI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAI + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tai(&msg->TAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UESecurityCapabilities + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_uesecuritycapabilities(&msg->UESecurityCapabilities, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CSG_Id + if(msg->CSG_Id_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_id(&msg->CSG_Id, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CellAccessMode + if(msg->CellAccessMode_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cellaccessmode(&msg->CellAccessMode, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CELLACCESSMODE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SourceMME_GUMMEI + if(msg->SourceMME_GUMMEI_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummei(&msg->SourceMME_GUMMEI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SOURCEMME_GUMMEI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSGMembershipStatus + if(msg->CSGMembershipStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csgmembershipstatus(&msg->CSGMembershipStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Tunnel_Information_for_BBF + if(msg->Tunnel_Information_for_BBF_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tunnelinformation(&msg->Tunnel_Information_for_BBF, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - LHN_ID + if(msg->LHN_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lhn_id(&msg->LHN_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_LHN_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->CSG_Id_present = false; + msg->CellAccessMode_present = false; + msg->SourceMME_GUMMEI_present = false; + msg->CSGMembershipStatus_present = false; + msg->Tunnel_Information_for_BBF_present = false; + msg->LHN_ID_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;ieNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDDLLIST == ie_id) { + if(liblte_s1ap_unpack_e_rabtobeswitcheddllist(ptr, &msg->E_RABToBeSwitchedDLList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SOURCEMME_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &msg->SourceMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_EUTRAN_CGI == ie_id) { + if(liblte_s1ap_unpack_eutran_cgi(ptr, &msg->EUTRAN_CGI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAI == ie_id) { + if(liblte_s1ap_unpack_tai(ptr, &msg->TAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES == ie_id) { + if(liblte_s1ap_unpack_uesecuritycapabilities(ptr, &msg->UESecurityCapabilities) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_ID == ie_id) { + if(liblte_s1ap_unpack_csg_id(ptr, &msg->CSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_Id_present = true; + } else if(LIBLTE_S1AP_IE_ID_CELLACCESSMODE == ie_id) { + if(liblte_s1ap_unpack_cellaccessmode(ptr, &msg->CellAccessMode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CellAccessMode_present = true; + } else if(LIBLTE_S1AP_IE_ID_SOURCEMME_GUMMEI == ie_id) { + if(liblte_s1ap_unpack_gummei(ptr, &msg->SourceMME_GUMMEI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SourceMME_GUMMEI_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS == ie_id) { + if(liblte_s1ap_unpack_csgmembershipstatus(ptr, &msg->CSGMembershipStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSGMembershipStatus_present = true; + } else if(LIBLTE_S1AP_IE_ID_TUNNEL_INFORMATION_FOR_BBF == ie_id) { + if(liblte_s1ap_unpack_tunnelinformation(ptr, &msg->Tunnel_Information_for_BBF) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->Tunnel_Information_for_BBF_present = true; + } else if(LIBLTE_S1AP_IE_ID_LHN_ID == ie_id) { + if(liblte_s1ap_unpack_lhn_id(ptr, &msg->LHN_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->LHN_ID_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message PathSwitchRequestAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_pathswitchrequestacknowledge( + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 9; + if(!msg->uEaggregateMaximumBitrate_present) + n_ie--; + if(!msg->E_RABToBeSwitchedULList_present) + n_ie--; + if(!msg->E_RABToBeReleasedList_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + if(!msg->MME_UE_S1AP_ID_2_present) + n_ie--; + if(!msg->CSGMembershipStatus_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + if(msg->uEaggregateMaximumBitrate_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABToBeSwitchedULList + if(msg->E_RABToBeSwitchedULList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobeswitchedullist(&msg->E_RABToBeSwitchedULList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABToBeReleasedList + if(msg->E_RABToBeReleasedList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABToBeReleasedList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBERELEASEDLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SecurityContext + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_securitycontext(&msg->SecurityContext, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SECURITYCONTEXT, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MME_UE_S1AP_ID_2 + if(msg->MME_UE_S1AP_ID_2_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID_2, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSGMembershipStatus + if(msg->CSGMembershipStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csgmembershipstatus(&msg->CSGMembershipStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_pathswitchrequestacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PATHSWITCHREQUESTACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->uEaggregateMaximumBitrate_present = false; + msg->E_RABToBeSwitchedULList_present = false; + msg->E_RABToBeReleasedList_present = false; + msg->CriticalityDiagnostics_present = false; + msg->MME_UE_S1AP_ID_2_present = false; + msg->CSGMembershipStatus_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PathSwitchRequestAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->uEaggregateMaximumBitrate_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBESWITCHEDULLIST == ie_id) { + if(liblte_s1ap_unpack_e_rabtobeswitchedullist(ptr, &msg->E_RABToBeSwitchedULList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABToBeSwitchedULList_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBERELEASEDLIST == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABToBeReleasedList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABToBeReleasedList_present = true; + } else if(LIBLTE_S1AP_IE_ID_SECURITYCONTEXT == ie_id) { + if(liblte_s1ap_unpack_securitycontext(ptr, &msg->SecurityContext) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } else if(LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2 == ie_id) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &msg->MME_UE_S1AP_ID_2) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MME_UE_S1AP_ID_2_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS == ie_id) { + if(liblte_s1ap_unpack_csgmembershipstatus(ptr, &msg->CSGMembershipStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSGMembershipStatus_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetuprequest( + LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->uEaggregateMaximumBitrate_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + if(msg->uEaggregateMaximumBitrate_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABToBeSetupListBearerSUReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetuplistbearersureq(&msg->E_RABToBeSetupListBearerSUReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTBEARERSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->uEaggregateMaximumBitrate_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->uEaggregateMaximumBitrate_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTBEARERSUREQ == ie_id) { + if(liblte_s1ap_unpack_e_rabtobesetuplistbearersureq(ptr, &msg->E_RABToBeSetupListBearerSUReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABSetupResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabsetupresponse( + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->E_RABSetupListBearerSURes_present) + n_ie--; + if(!msg->E_RABFailedToSetupListBearerSURes_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABSetupListBearerSURes + if(msg->E_RABSetupListBearerSURes_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetuplistbearersures(&msg->E_RABSetupListBearerSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPLISTBEARERSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABFailedToSetupListBearerSURes + if(msg->E_RABFailedToSetupListBearerSURes_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABFailedToSetupListBearerSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTBEARERSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabsetupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABSetupListBearerSURes_present = false; + msg->E_RABFailedToSetupListBearerSURes_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABSetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABSETUPLISTBEARERSURES == ie_id) { + if(liblte_s1ap_unpack_e_rabsetuplistbearersures(ptr, &msg->E_RABSetupListBearerSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABSetupListBearerSURes_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTBEARERSURES == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABFailedToSetupListBearerSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABFailedToSetupListBearerSURes_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABModifyRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyrequest( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 4; + if(!msg->uEaggregateMaximumBitrate_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + if(msg->uEaggregateMaximumBitrate_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABToBeModifiedListBearerModReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobemodifiedlistbearermodreq(&msg->E_RABToBeModifiedListBearerModReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDLISTBEARERMODREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyrequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->uEaggregateMaximumBitrate_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->uEaggregateMaximumBitrate_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBEMODIFIEDLISTBEARERMODREQ == ie_id) { + if(liblte_s1ap_unpack_e_rabtobemodifiedlistbearermodreq(ptr, &msg->E_RABToBeModifiedListBearerModReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABModifyResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabmodifyresponse( + LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->E_RABModifyListBearerModRes_present) + n_ie--; + if(!msg->E_RABFailedToModifyList_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABModifyListBearerModRes + if(msg->E_RABModifyListBearerModRes_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabmodifylistbearermodres(&msg->E_RABModifyListBearerModRes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABMODIFYLISTBEARERMODRES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABFailedToModifyList + if(msg->E_RABFailedToModifyList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABFailedToModifyList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOMODIFYLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabmodifyresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABMODIFYRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABModifyListBearerModRes_present = false; + msg->E_RABFailedToModifyList_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABModifyResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABMODIFYLISTBEARERMODRES == ie_id) { + if(liblte_s1ap_unpack_e_rabmodifylistbearermodres(ptr, &msg->E_RABModifyListBearerModRes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABModifyListBearerModRes_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTOMODIFYLIST == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABFailedToModifyList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABFailedToModifyList_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message E_RABReleaseResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_e_rabreleaseresponse( + LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 6; + if(!msg->E_RABReleaseListBearerRelComp_present) + n_ie--; + if(!msg->E_RABFailedToReleaseList_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + if(!msg->UserLocationInformation_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABReleaseListBearerRelComp + if(msg->E_RABReleaseListBearerRelComp_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabreleaselistbearerrelcomp(&msg->E_RABReleaseListBearerRelComp, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABRELEASELISTBEARERRELCOMP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - E_RABFailedToReleaseList + if(msg->E_RABFailedToReleaseList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABFailedToReleaseList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTORELEASELIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - UserLocationInformation + if(msg->UserLocationInformation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_userlocationinformation(&msg->UserLocationInformation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_e_rabreleaseresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_E_RABRELEASERESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABReleaseListBearerRelComp_present = false; + msg->E_RABFailedToReleaseList_present = false; + msg->CriticalityDiagnostics_present = false; + msg->UserLocationInformation_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("E-RABReleaseResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABRELEASELISTBEARERRELCOMP == ie_id) { + if(liblte_s1ap_unpack_e_rabreleaselistbearerrelcomp(ptr, &msg->E_RABReleaseListBearerRelComp) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABReleaseListBearerRelComp_present = true; + } else if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTORELEASELIST == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABFailedToReleaseList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABFailedToReleaseList_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } else if(LIBLTE_S1AP_IE_ID_USERLOCATIONINFORMATION == ie_id) { + if(liblte_s1ap_unpack_userlocationinformation(ptr, &msg->UserLocationInformation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UserLocationInformation_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message InitialContextSetupRequest STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetuprequest( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 19; + if(!msg->TraceActivation_present) + n_ie--; + if(!msg->HandoverRestrictionList_present) + n_ie--; + if(!msg->UERadioCapability_present) + n_ie--; + if(!msg->SubscriberProfileIDforRFP_present) + n_ie--; + if(!msg->CSFallbackIndicator_present) + n_ie--; + if(!msg->SRVCCOperationPossible_present) + n_ie--; + if(!msg->CSGMembershipStatus_present) + n_ie--; + if(!msg->RegisteredLAI_present) + n_ie--; + if(!msg->GUMMEI_ID_present) + n_ie--; + if(!msg->MME_UE_S1AP_ID_2_present) + n_ie--; + if(!msg->ManagementBasedMDTAllowed_present) + n_ie--; + if(!msg->ManagementBasedMDTPLMNList_present) + n_ie--; + if(!msg->AdditionalCSFallbackIndicator_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - uEaggregateMaximumBitrate + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueaggregatemaximumbitrate(&msg->uEaggregateMaximumBitrate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABToBeSetupListCtxtSUReq + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabtobesetuplistctxtsureq(&msg->E_RABToBeSetupListCtxtSUReq, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTCTXTSUREQ, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UESecurityCapabilities + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_uesecuritycapabilities(&msg->UESecurityCapabilities, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - SecurityKey + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_securitykey(&msg->SecurityKey, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SECURITYKEY, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TraceActivation + if(msg->TraceActivation_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_traceactivation(&msg->TraceActivation, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TRACEACTIVATION, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - HandoverRestrictionList + if(msg->HandoverRestrictionList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_handoverrestrictionlist(&msg->HandoverRestrictionList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - UERadioCapability + if(msg->UERadioCapability_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueradiocapability(&msg->UERadioCapability, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SubscriberProfileIDforRFP + if(msg->SubscriberProfileIDforRFP_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_subscriberprofileidforrfp(&msg->SubscriberProfileIDforRFP, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSFallbackIndicator + if(msg->CSFallbackIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csfallbackindicator(&msg->CSFallbackIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSFALLBACKINDICATOR, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - SRVCCOperationPossible + if(msg->SRVCCOperationPossible_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_srvccoperationpossible(&msg->SRVCCOperationPossible, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_SRVCCOPERATIONPOSSIBLE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CSGMembershipStatus + if(msg->CSGMembershipStatus_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csgmembershipstatus(&msg->CSGMembershipStatus, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - RegisteredLAI + if(msg->RegisteredLAI_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_lai(&msg->RegisteredLAI, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_REGISTEREDLAI, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - GUMMEI_ID + if(msg->GUMMEI_ID_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_gummei(&msg->GUMMEI_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_GUMMEI_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - MME_UE_S1AP_ID_2 + if(msg->MME_UE_S1AP_ID_2_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID_2, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ManagementBasedMDTAllowed + if(msg->ManagementBasedMDTAllowed_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_managementbasedmdtallowed(&msg->ManagementBasedMDTAllowed, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTALLOWED, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - ManagementBasedMDTPLMNList + if(msg->ManagementBasedMDTPLMNList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mdtplmnlist(&msg->ManagementBasedMDTPLMNList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTPLMNLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - AdditionalCSFallbackIndicator + if(msg->AdditionalCSFallbackIndicator_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_additionalcsfallbackindicator(&msg->AdditionalCSFallbackIndicator, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ADDITIONALCSFALLBACKINDICATOR, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetuprequest( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->TraceActivation_present = false; + msg->HandoverRestrictionList_present = false; + msg->UERadioCapability_present = false; + msg->SubscriberProfileIDforRFP_present = false; + msg->CSFallbackIndicator_present = false; + msg->SRVCCOperationPossible_present = false; + msg->CSGMembershipStatus_present = false; + msg->RegisteredLAI_present = false; + msg->GUMMEI_ID_present = false; + msg->MME_UE_S1AP_ID_2_present = false; + msg->ManagementBasedMDTAllowed_present = false; + msg->ManagementBasedMDTPLMNList_present = false; + msg->AdditionalCSFallbackIndicator_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupRequestIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEAGGREGATEMAXIMUMBITRATE == ie_id) { + if(liblte_s1ap_unpack_ueaggregatemaximumbitrate(ptr, &msg->uEaggregateMaximumBitrate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABTOBESETUPLISTCTXTSUREQ == ie_id) { + if(liblte_s1ap_unpack_e_rabtobesetuplistctxtsureq(ptr, &msg->E_RABToBeSetupListCtxtSUReq) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UESECURITYCAPABILITIES == ie_id) { + if(liblte_s1ap_unpack_uesecuritycapabilities(ptr, &msg->UESecurityCapabilities) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_SECURITYKEY == ie_id) { + if(liblte_s1ap_unpack_securitykey(ptr, &msg->SecurityKey) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TRACEACTIVATION == ie_id) { + if(liblte_s1ap_unpack_traceactivation(ptr, &msg->TraceActivation) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->TraceActivation_present = true; + } else if(LIBLTE_S1AP_IE_ID_HANDOVERRESTRICTIONLIST == ie_id) { + if(liblte_s1ap_unpack_handoverrestrictionlist(ptr, &msg->HandoverRestrictionList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->HandoverRestrictionList_present = true; + } else if(LIBLTE_S1AP_IE_ID_UERADIOCAPABILITY == ie_id) { + if(liblte_s1ap_unpack_ueradiocapability(ptr, &msg->UERadioCapability) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->UERadioCapability_present = true; + } else if(LIBLTE_S1AP_IE_ID_SUBSCRIBERPROFILEIDFORRFP == ie_id) { + if(liblte_s1ap_unpack_subscriberprofileidforrfp(ptr, &msg->SubscriberProfileIDforRFP) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SubscriberProfileIDforRFP_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSFALLBACKINDICATOR == ie_id) { + if(liblte_s1ap_unpack_csfallbackindicator(ptr, &msg->CSFallbackIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSFallbackIndicator_present = true; + } else if(LIBLTE_S1AP_IE_ID_SRVCCOPERATIONPOSSIBLE == ie_id) { + if(liblte_s1ap_unpack_srvccoperationpossible(ptr, &msg->SRVCCOperationPossible) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->SRVCCOperationPossible_present = true; + } else if(LIBLTE_S1AP_IE_ID_CSGMEMBERSHIPSTATUS == ie_id) { + if(liblte_s1ap_unpack_csgmembershipstatus(ptr, &msg->CSGMembershipStatus) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSGMembershipStatus_present = true; + } else if(LIBLTE_S1AP_IE_ID_REGISTEREDLAI == ie_id) { + if(liblte_s1ap_unpack_lai(ptr, &msg->RegisteredLAI) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->RegisteredLAI_present = true; + } else if(LIBLTE_S1AP_IE_ID_GUMMEI_ID == ie_id) { + if(liblte_s1ap_unpack_gummei(ptr, &msg->GUMMEI_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->GUMMEI_ID_present = true; + } else if(LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID_2 == ie_id) { + if(liblte_s1ap_unpack_mme_ue_s1ap_id(ptr, &msg->MME_UE_S1AP_ID_2) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->MME_UE_S1AP_ID_2_present = true; + } else if(LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTALLOWED == ie_id) { + if(liblte_s1ap_unpack_managementbasedmdtallowed(ptr, &msg->ManagementBasedMDTAllowed) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ManagementBasedMDTAllowed_present = true; + } else if(LIBLTE_S1AP_IE_ID_MANAGEMENTBASEDMDTPLMNLIST == ie_id) { + if(liblte_s1ap_unpack_mdtplmnlist(ptr, &msg->ManagementBasedMDTPLMNList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->ManagementBasedMDTPLMNList_present = true; + } else if(LIBLTE_S1AP_IE_ID_ADDITIONALCSFALLBACKINDICATOR == ie_id) { + if(liblte_s1ap_unpack_additionalcsfallbackindicator(ptr, &msg->AdditionalCSFallbackIndicator) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message InitialContextSetupResponse STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initialcontextsetupresponse( + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 5; + if(!msg->E_RABFailedToSetupListCtxtSURes_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABSetupListCtxtSURes + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabsetuplistctxtsures(&msg->E_RABSetupListCtxtSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABSETUPLISTCTXTSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABFailedToSetupListCtxtSURes + if(msg->E_RABFailedToSetupListCtxtSURes_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rablist(&msg->E_RABFailedToSetupListCtxtSURes, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTCTXTSURES, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initialcontextsetupresponse( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABFailedToSetupListCtxtSURes_present = false; + msg->CriticalityDiagnostics_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("InitialContextSetupResponseIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABSETUPLISTCTXTSURES == ie_id) { + if(liblte_s1ap_unpack_e_rabsetuplistctxtsures(ptr, &msg->E_RABSetupListCtxtSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTCTXTSURES == ie_id) { + if(liblte_s1ap_unpack_e_rablist(ptr, &msg->E_RABFailedToSetupListCtxtSURes) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABFailedToSetupListCtxtSURes_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message Paging STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_paging( + LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("PagingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 7; + if(!msg->pagingDRX_present) + n_ie--; + if(!msg->CSG_IdList_present) + n_ie--; + if(!msg->PagingPriority_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - UEIdentityIndexValue + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_ueidentityindexvalue(&msg->UEIdentityIndexValue, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEIDENTITYINDEXVALUE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - UEPagingID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_uepagingid(&msg->UEPagingID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_UEPAGINGID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - pagingDRX + if(msg->pagingDRX_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_pagingdrx(&msg->pagingDRX, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_PAGINGDRX, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CNDomain + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cndomain(&msg->CNDomain, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CNDOMAIN, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - TAIList + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_tailist(&msg->TAIList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TAILIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CSG_IdList + if(msg->CSG_IdList_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_idlist(&msg->CSG_IdList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_IDLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - PagingPriority + if(msg->PagingPriority_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_pagingpriority(&msg->PagingPriority, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_PAGINGPRIORITY, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_paging( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->pagingDRX_present = false; + msg->CSG_IdList_present = false; + msg->PagingPriority_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("PagingIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iUEIdentityIndexValue) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_UEPAGINGID == ie_id) { + if(liblte_s1ap_unpack_uepagingid(ptr, &msg->UEPagingID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_PAGINGDRX == ie_id) { + if(liblte_s1ap_unpack_pagingdrx(ptr, &msg->pagingDRX) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->pagingDRX_present = true; + } else if(LIBLTE_S1AP_IE_ID_CNDOMAIN == ie_id) { + if(liblte_s1ap_unpack_cndomain(ptr, &msg->CNDomain) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_TAILIST == ie_id) { + if(liblte_s1ap_unpack_tailist(ptr, &msg->TAIList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_IDLIST == ie_id) { + if(liblte_s1ap_unpack_csg_idlist(ptr, &msg->CSG_IdList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_IdList_present = true; + } else if(LIBLTE_S1AP_IE_ID_PAGINGPRIORITY == ie_id) { + if(liblte_s1ap_unpack_pagingpriority(ptr, &msg->PagingPriority) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->PagingPriority_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* Protocol Message HandoverRequestAcknowledge STRUCT +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_handoverrequestacknowledge( + LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + + // Extension + liblte_value_2_bits(msg->ext?1:0, ptr, 1); + liblte_align_up_zero(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequestAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_ENCODE_FAIL; + } + + // No. of ProtocolIE + uint32_t n_ie = 8; + if(!msg->E_RABFailedToSetupListHOReqAck_present) + n_ie--; + if(!msg->CSG_Id_present) + n_ie--; + if(!msg->CriticalityDiagnostics_present) + n_ie--; + if(!msg->CellAccessMode_present) + n_ie--; + liblte_value_2_bits(n_ie, ptr, 16); + + // Temp container for IEs + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr; + + // ProtocolIE - MME_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_mme_ue_s1ap_id(&msg->MME_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_MME_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - eNB_UE_S1AP_ID + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_enb_ue_s1ap_id(&msg->eNB_UE_S1AP_ID, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABAdmittedList + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabadmittedlist(&msg->E_RABAdmittedList, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABADMITTEDLIST, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - E_RABFailedToSetupListHOReqAck + if(msg->E_RABFailedToSetupListHOReqAck_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_e_rabfailedtosetuplisthoreqack(&msg->E_RABFailedToSetupListHOReqAck, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTHOREQACK, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - Target_ToSource_TransparentContainer + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_target_tosource_transparentcontainer(&msg->Target_ToSource_TransparentContainer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER, + LIBLTE_S1AP_CRITICALITY_REJECT, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + // ProtocolIE - CSG_Id + if(msg->CSG_Id_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_csg_id(&msg->CSG_Id, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CSG_ID, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CriticalityDiagnostics + if(msg->CriticalityDiagnostics_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_criticalitydiagnostics(&msg->CriticalityDiagnostics, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + // ProtocolIE - CellAccessMode + if(msg->CellAccessMode_present) { + + tmp_ptr = tmp_msg.msg; + if(liblte_s1ap_pack_cellaccessmode(&msg->CellAccessMode, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + liblte_align_up_zero(&tmp_ptr, 8); + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + if(liblte_s1ap_pack_protocolie_header(tmp_msg.N_bits / 8, + LIBLTE_S1AP_IE_ID_CELLACCESSMODE, + LIBLTE_S1AP_CRITICALITY_IGNORE, + ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_handoverrequestacknowledge( + uint8_t **ptr, + LIBLTE_S1AP_MESSAGE_HANDOVERREQUESTACKNOWLEDGE_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_S1AP_CRITICALITY_ENUM crit; + uint32_t ie_id; + uint32_t len; + uint32_t n_ie; + uint32_t i; + + // Set booleans + msg->E_RABFailedToSetupListHOReqAck_present = false; + msg->CSG_Id_present = false; + msg->CriticalityDiagnostics_present = false; + msg->CellAccessMode_present = false; + + // Extension + msg->ext = liblte_bits_2_value(ptr, 1); + liblte_align_up(ptr, 8); + if(msg->ext) { + liblte_log_print("HandoverRequestAcknowledgeIEs error: S1AP ASN extensions not currently supported\n"); + return LIBLTE_ERROR_DECODE_FAIL; + } + + // No. of ProtocolIE-Container + n_ie = liblte_bits_2_value(ptr, 16); + + // Unpack ProtocolIE Fields + for(i=0;iMME_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_ENB_UE_S1AP_ID == ie_id) { + if(liblte_s1ap_unpack_enb_ue_s1ap_id(ptr, &msg->eNB_UE_S1AP_ID) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABADMITTEDLIST == ie_id) { + if(liblte_s1ap_unpack_e_rabadmittedlist(ptr, &msg->E_RABAdmittedList) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_E_RABFAILEDTOSETUPLISTHOREQACK == ie_id) { + if(liblte_s1ap_unpack_e_rabfailedtosetuplisthoreqack(ptr, &msg->E_RABFailedToSetupListHOReqAck) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->E_RABFailedToSetupListHOReqAck_present = true; + } else if(LIBLTE_S1AP_IE_ID_TARGET_TOSOURCE_TRANSPARENTCONTAINER == ie_id) { + if(liblte_s1ap_unpack_target_tosource_transparentcontainer(ptr, &msg->Target_ToSource_TransparentContainer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + } else if(LIBLTE_S1AP_IE_ID_CSG_ID == ie_id) { + if(liblte_s1ap_unpack_csg_id(ptr, &msg->CSG_Id) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CSG_Id_present = true; + } else if(LIBLTE_S1AP_IE_ID_CRITICALITYDIAGNOSTICS == ie_id) { + if(liblte_s1ap_unpack_criticalitydiagnostics(ptr, &msg->CriticalityDiagnostics) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CriticalityDiagnostics_present = true; + } else if(LIBLTE_S1AP_IE_ID_CELLACCESSMODE == ie_id) { + if(liblte_s1ap_unpack_cellaccessmode(ptr, &msg->CellAccessMode) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + liblte_align_up(ptr, 8); + msg->CellAccessMode_present = true; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* ProtocolIE-Field +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_protocolie_header( + uint32_t len, + uint32_t ie_id, + LIBLTE_S1AP_CRITICALITY_ENUM crit, + uint8_t **ptr) +{ + liblte_value_2_bits(ie_id, ptr, 16); // ProtocolIE-ID + liblte_value_2_bits(crit, ptr, 2); // Criticality + liblte_align_up_zero(ptr, 8); + if(len < 128) { // Length + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 7); + } else if(len < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + return LIBLTE_SUCCESS; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_protocolie_header( + uint8_t **ptr, + uint32_t *ie_id, + LIBLTE_S1AP_CRITICALITY_ENUM *crit, + uint32_t *len) +{ + *ie_id = liblte_bits_2_value(ptr, 16); // ProtocolIE-ID + *crit = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); // Criticality + liblte_align_up(ptr, 8); + if(0 == liblte_bits_2_value(ptr, 1)) { // Length + *len = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + *len = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + return LIBLTE_SUCCESS; +} + +/******************************************************************************* +/* InitiatingMessage CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_initiatingmessage( + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr = tmp_msg.msg; + + // Message + if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGCONTROL) { + if(liblte_s1ap_pack_locationreportingcontrol(&msg->choice.LocationReportingControl, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKS1CDMA2000TUNNELING) { + if(liblte_s1ap_pack_downlinks1cdma2000tunneling(&msg->choice.DownlinkS1cdma2000tunneling, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST) { + if(liblte_s1ap_pack_s1setuprequest(&msg->choice.S1SetupRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION) { + if(liblte_s1ap_pack_uecapabilityinfoindication(&msg->choice.UECapabilityInfoIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORT) { + if(liblte_s1ap_pack_locationreport(&msg->choice.LocationReport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNONUEASSOCIATEDLPPATRANSPORT) { + if(liblte_s1ap_pack_uplinknonueassociatedlppatransport(&msg->choice.UplinkNonUEAssociatedLPPaTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKS1CDMA2000TUNNELING) { + if(liblte_s1ap_pack_uplinks1cdma2000tunneling(&msg->choice.UplinkS1cdma2000tunneling, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONTRANSFER) { + if(liblte_s1ap_pack_mmeconfigurationtransfer(&msg->choice.MMEConfigurationTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACESTART) { + if(liblte_s1ap_pack_tracestart(&msg->choice.TraceStart, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERCANCEL) { + if(liblte_s1ap_pack_handovercancel(&msg->choice.HandoverCancel, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UERADIOCAPABILITYMATCHREQUEST) { + if(liblte_s1ap_pack_ueradiocapabilitymatchrequest(&msg->choice.UERadioCapabilityMatchRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT) { + if(liblte_s1ap_pack_downlinknastransport(&msg->choice.DownlinkNASTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST) { + if(liblte_s1ap_pack_initialcontextsetuprequest(&msg->choice.InitialContextSetupRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED) { + if(liblte_s1ap_pack_handoverrequired(&msg->choice.HandoverRequired, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMEDIRECTINFORMATIONTRANSFER) { + if(liblte_s1ap_pack_mmedirectinformationtransfer(&msg->choice.MMEDirectInformationTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACEFAILUREINDICATION) { + if(liblte_s1ap_pack_tracefailureindication(&msg->choice.TraceFailureIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONUPDATE) { + if(liblte_s1ap_pack_mmeconfigurationupdate(&msg->choice.MMEConfigurationUpdate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_WRITEREPLACEWARNINGREQUEST) { + if(liblte_s1ap_pack_writereplacewarningrequest(&msg->choice.WriteReplaceWarningRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBDIRECTINFORMATIONTRANSFER) { + if(liblte_s1ap_pack_enbdirectinformationtransfer(&msg->choice.ENBDirectInformationTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKUEASSOCIATEDLPPATRANSPORT) { + if(liblte_s1ap_pack_downlinkueassociatedlppatransport(&msg->choice.DownlinkUEAssociatedLPPaTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASECOMMAND) { + if(liblte_s1ap_pack_e_rabreleasecommand(&msg->choice.E_RABReleaseCommand, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_NASNONDELIVERYINDICATION) { + if(liblte_s1ap_pack_nasnondeliveryindication(&msg->choice.NASNonDeliveryIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONUPDATE) { + if(liblte_s1ap_pack_enbconfigurationupdate(&msg->choice.ENBConfigurationUpdate, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKUEASSOCIATEDLPPATRANSPORT) { + if(liblte_s1ap_pack_uplinkueassociatedlppatransport(&msg->choice.UplinkUEAssociatedLPPaTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE) { + if(liblte_s1ap_pack_initialuemessage(&msg->choice.InitialUEMessage, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABMODIFYREQUEST) { + if(liblte_s1ap_pack_e_rabmodifyrequest(&msg->choice.E_RABModifyRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTMODIFICATIONREQUEST) { + if(liblte_s1ap_pack_uecontextmodificationrequest(&msg->choice.UEContextModificationRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST) { + if(liblte_s1ap_pack_e_rabsetuprequest(&msg->choice.E_RABSetupRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_RESET) { + if(liblte_s1ap_pack_reset(&msg->choice.Reset, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTART) { + if(liblte_s1ap_pack_overloadstart(&msg->choice.OverloadStart, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASEINDICATION) { + if(liblte_s1ap_pack_e_rabreleaseindication(&msg->choice.E_RABReleaseIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGFAILUREINDICATION) { + if(liblte_s1ap_pack_locationreportingfailureindication(&msg->choice.LocationReportingFailureIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DEACTIVATETRACE) { + if(liblte_s1ap_pack_deactivatetrace(&msg->choice.DeactivateTrace, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PATHSWITCHREQUEST) { + if(liblte_s1ap_pack_pathswitchrequest(&msg->choice.PathSwitchRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUEST) { + if(liblte_s1ap_pack_handoverrequest(&msg->choice.HandoverRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT) { + if(liblte_s1ap_pack_downlinknonueassociatedlppatransport(&msg->choice.DownlinkNonUEAssociatedLPPaTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTOP) { + if(liblte_s1ap_pack_overloadstop(&msg->choice.OverloadStop, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING) { + if(liblte_s1ap_pack_paging(&msg->choice.Paging, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERNOTIFY) { + if(liblte_s1ap_pack_handovernotify(&msg->choice.HandoverNotify, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PWSRESTARTINDICATION) { + if(liblte_s1ap_pack_pwsrestartindication(&msg->choice.PWSRestartIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST) { + if(liblte_s1ap_pack_uecontextreleaserequest(&msg->choice.UEContextReleaseRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT) { + if(liblte_s1ap_pack_uplinknastransport(&msg->choice.UplinkNASTransport, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONTRANSFER) { + if(liblte_s1ap_pack_enbconfigurationtransfer(&msg->choice.ENBConfigurationTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMESTATUSTRANSFER) { + if(liblte_s1ap_pack_mmestatustransfer(&msg->choice.MMEStatusTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_CELLTRAFFICTRACE) { + if(liblte_s1ap_pack_celltraffictrace(&msg->choice.CellTrafficTrace, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND) { + if(liblte_s1ap_pack_uecontextreleasecommand(&msg->choice.UEContextReleaseCommand, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_KILLREQUEST) { + if(liblte_s1ap_pack_killrequest(&msg->choice.KillRequest, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PRIVATEMESSAGE) { + if(liblte_s1ap_pack_privatemessage(&msg->choice.PrivateMessage, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBSTATUSTRANSFER) { + if(liblte_s1ap_pack_enbstatustransfer(&msg->choice.ENBStatusTransfer, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ERRORINDICATION) { + if(liblte_s1ap_pack_errorindication(&msg->choice.ErrorIndication, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + + // Procedure code + liblte_value_2_bits(msg->procedureCode, ptr, 8); + + // Criticality + LIBLTE_S1AP_CRITICALITY_ENUM crit = liblte_s1ap_procedure_criticality[msg->procedureCode]; + liblte_value_2_bits(crit, ptr, 2); + liblte_align_up_zero(ptr, 8); + + // Length + uint32_t len = (tmp_msg.N_bits + 7) / 8; + if(len < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 7); + } else if(len < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_initiatingmessage( + uint8_t **ptr, + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg) +{ LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + // Procedure code + msg->procedureCode = liblte_bits_2_value(ptr, 8); + + // Criticality + msg->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + + // Length + uint32_t len = 0; + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Message + if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_LOCATIONREPORTINGCONTROL) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGCONTROL; + if(liblte_s1ap_unpack_locationreportingcontrol(ptr, &msg->choice.LocationReportingControl) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_DOWNLINKS1CDMA2000TUNNELING) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKS1CDMA2000TUNNELING; + if(liblte_s1ap_unpack_downlinks1cdma2000tunneling(ptr, &msg->choice.DownlinkS1cdma2000tunneling) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_S1SETUP) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST; + if(liblte_s1ap_unpack_s1setuprequest(ptr, &msg->choice.S1SetupRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECAPABILITYINFOINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION; + if(liblte_s1ap_unpack_uecapabilityinfoindication(ptr, &msg->choice.UECapabilityInfoIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_LOCATIONREPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORT; + if(liblte_s1ap_unpack_locationreport(ptr, &msg->choice.LocationReport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UPLINKNONUEASSOCIATEDLPPATRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNONUEASSOCIATEDLPPATRANSPORT; + if(liblte_s1ap_unpack_uplinknonueassociatedlppatransport(ptr, &msg->choice.UplinkNonUEAssociatedLPPaTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UPLINKS1CDMA2000TUNNELING) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKS1CDMA2000TUNNELING; + if(liblte_s1ap_unpack_uplinks1cdma2000tunneling(ptr, &msg->choice.UplinkS1cdma2000tunneling) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONTRANSFER; + if(liblte_s1ap_unpack_mmeconfigurationtransfer(ptr, &msg->choice.MMEConfigurationTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_TRACESTART) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACESTART; + if(liblte_s1ap_unpack_tracestart(ptr, &msg->choice.TraceStart) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERCANCEL) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERCANCEL; + if(liblte_s1ap_unpack_handovercancel(ptr, &msg->choice.HandoverCancel) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UERADIOCAPABILITYMATCH) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UERADIOCAPABILITYMATCHREQUEST; + if(liblte_s1ap_unpack_ueradiocapabilitymatchrequest(ptr, &msg->choice.UERadioCapabilityMatchRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + if(liblte_s1ap_unpack_downlinknastransport(ptr, &msg->choice.DownlinkNASTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST; + if(liblte_s1ap_unpack_initialcontextsetuprequest(ptr, &msg->choice.InitialContextSetupRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUIRED; + if(liblte_s1ap_unpack_handoverrequired(ptr, &msg->choice.HandoverRequired) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMEDIRECTINFORMATIONTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMEDIRECTINFORMATIONTRANSFER; + if(liblte_s1ap_unpack_mmedirectinformationtransfer(ptr, &msg->choice.MMEDirectInformationTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_TRACEFAILUREINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_TRACEFAILUREINDICATION; + if(liblte_s1ap_unpack_tracefailureindication(ptr, &msg->choice.TraceFailureIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMECONFIGURATIONUPDATE; + if(liblte_s1ap_unpack_mmeconfigurationupdate(ptr, &msg->choice.MMEConfigurationUpdate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_WRITEREPLACEWARNING) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_WRITEREPLACEWARNINGREQUEST; + if(liblte_s1ap_unpack_writereplacewarningrequest(ptr, &msg->choice.WriteReplaceWarningRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBDIRECTINFORMATIONTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBDIRECTINFORMATIONTRANSFER; + if(liblte_s1ap_unpack_enbdirectinformationtransfer(ptr, &msg->choice.ENBDirectInformationTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_DOWNLINKUEASSOCIATEDLPPATRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKUEASSOCIATEDLPPATRANSPORT; + if(liblte_s1ap_unpack_downlinkueassociatedlppatransport(ptr, &msg->choice.DownlinkUEAssociatedLPPaTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABRELEASE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASECOMMAND; + if(liblte_s1ap_unpack_e_rabreleasecommand(ptr, &msg->choice.E_RABReleaseCommand) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_NASNONDELIVERYINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_NASNONDELIVERYINDICATION; + if(liblte_s1ap_unpack_nasnondeliveryindication(ptr, &msg->choice.NASNonDeliveryIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONUPDATE; + if(liblte_s1ap_unpack_enbconfigurationupdate(ptr, &msg->choice.ENBConfigurationUpdate) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UPLINKUEASSOCIATEDLPPATRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKUEASSOCIATEDLPPATRANSPORT; + if(liblte_s1ap_unpack_uplinkueassociatedlppatransport(ptr, &msg->choice.UplinkUEAssociatedLPPaTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_INITIALUEMESSAGE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE; + if(liblte_s1ap_unpack_initialuemessage(ptr, &msg->choice.InitialUEMessage) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABMODIFY) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABMODIFYREQUEST; + if(liblte_s1ap_unpack_e_rabmodifyrequest(ptr, &msg->choice.E_RABModifyRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTMODIFICATIONREQUEST; + if(liblte_s1ap_unpack_uecontextmodificationrequest(ptr, &msg->choice.UEContextModificationRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABSETUP) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST; + if(liblte_s1ap_unpack_e_rabsetuprequest(ptr, &msg->choice.E_RABSetupRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_RESET) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_RESET; + if(liblte_s1ap_unpack_reset(ptr, &msg->choice.Reset) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_OVERLOADSTART) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTART; + if(liblte_s1ap_unpack_overloadstart(ptr, &msg->choice.OverloadStart) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABRELEASEINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABRELEASEINDICATION; + if(liblte_s1ap_unpack_e_rabreleaseindication(ptr, &msg->choice.E_RABReleaseIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_LOCATIONREPORTINGFAILUREINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_LOCATIONREPORTINGFAILUREINDICATION; + if(liblte_s1ap_unpack_locationreportingfailureindication(ptr, &msg->choice.LocationReportingFailureIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_DEACTIVATETRACE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DEACTIVATETRACE; + if(liblte_s1ap_unpack_deactivatetrace(ptr, &msg->choice.DeactivateTrace) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PATHSWITCHREQUEST) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PATHSWITCHREQUEST; + if(liblte_s1ap_unpack_pathswitchrequest(ptr, &msg->choice.PathSwitchRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERRESOURCEALLOCATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERREQUEST; + if(liblte_s1ap_unpack_handoverrequest(ptr, &msg->choice.HandoverRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNONUEASSOCIATEDLPPATRANSPORT; + if(liblte_s1ap_unpack_downlinknonueassociatedlppatransport(ptr, &msg->choice.DownlinkNonUEAssociatedLPPaTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_OVERLOADSTOP) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_OVERLOADSTOP; + if(liblte_s1ap_unpack_overloadstop(ptr, &msg->choice.OverloadStop) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PAGING) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING; + if(liblte_s1ap_unpack_paging(ptr, &msg->choice.Paging) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERNOTIFICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_HANDOVERNOTIFY; + if(liblte_s1ap_unpack_handovernotify(ptr, &msg->choice.HandoverNotify) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PWSRESTARTINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PWSRESTARTINDICATION; + if(liblte_s1ap_unpack_pwsrestartindication(ptr, &msg->choice.PWSRestartIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASEREQUEST) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST; + if(liblte_s1ap_unpack_uecontextreleaserequest(ptr, &msg->choice.UEContextReleaseRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT; + if(liblte_s1ap_unpack_uplinknastransport(ptr, &msg->choice.UplinkNASTransport) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBCONFIGURATIONTRANSFER; + if(liblte_s1ap_unpack_enbconfigurationtransfer(ptr, &msg->choice.ENBConfigurationTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMESTATUSTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_MMESTATUSTRANSFER; + if(liblte_s1ap_unpack_mmestatustransfer(ptr, &msg->choice.MMEStatusTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_CELLTRAFFICTRACE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_CELLTRAFFICTRACE; + if(liblte_s1ap_unpack_celltraffictrace(ptr, &msg->choice.CellTrafficTrace) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND; + if(liblte_s1ap_unpack_uecontextreleasecommand(ptr, &msg->choice.UEContextReleaseCommand) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_KILL) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_KILLREQUEST; + if(liblte_s1ap_unpack_killrequest(ptr, &msg->choice.KillRequest) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PRIVATEMESSAGE) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PRIVATEMESSAGE; + if(liblte_s1ap_unpack_privatemessage(ptr, &msg->choice.PrivateMessage) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBSTATUSTRANSFER) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ENBSTATUSTRANSFER; + if(liblte_s1ap_unpack_enbstatustransfer(ptr, &msg->choice.ENBStatusTransfer) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ERRORINDICATION) { + msg->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_ERRORINDICATION; + if(liblte_s1ap_unpack_errorindication(ptr, &msg->choice.ErrorIndication) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* UnsuccessfulOutcome CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_unsuccessfuloutcome( + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr = tmp_msg.msg; + + // Message + if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE) { + if(liblte_s1ap_pack_s1setupfailure(&msg->choice.S1SetupFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTFAILURE) { + if(liblte_s1ap_pack_pathswitchrequestfailure(&msg->choice.PathSwitchRequestFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONFAILURE) { + if(liblte_s1ap_pack_uecontextmodificationfailure(&msg->choice.UEContextModificationFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE) { + if(liblte_s1ap_pack_initialcontextsetupfailure(&msg->choice.InitialContextSetupFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEFAILURE) { + if(liblte_s1ap_pack_enbconfigurationupdatefailure(&msg->choice.ENBConfigurationUpdateFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERPREPARATIONFAILURE) { + if(liblte_s1ap_pack_handoverpreparationfailure(&msg->choice.HandoverPreparationFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERFAILURE) { + if(liblte_s1ap_pack_handoverfailure(&msg->choice.HandoverFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEFAILURE) { + if(liblte_s1ap_pack_mmeconfigurationupdatefailure(&msg->choice.MMEConfigurationUpdateFailure, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + + // Procedure code + liblte_value_2_bits(msg->procedureCode, ptr, 8); + + // Criticality + LIBLTE_S1AP_CRITICALITY_ENUM crit = liblte_s1ap_procedure_criticality[msg->procedureCode]; + liblte_value_2_bits(crit, ptr, 2); + liblte_align_up_zero(ptr, 8); + + // Length + uint32_t len = (tmp_msg.N_bits + 7) / 8; + if(len < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 7); + } else if(len < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_unsuccessfuloutcome( + uint8_t **ptr, + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg) +{ LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + // Procedure code + msg->procedureCode = liblte_bits_2_value(ptr, 8); + + // Criticality + msg->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + + // Length + uint32_t len = 0; + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Message + if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_S1SETUP) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE; + if(liblte_s1ap_unpack_s1setupfailure(ptr, &msg->choice.S1SetupFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PATHSWITCHREQUEST) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTFAILURE; + if(liblte_s1ap_unpack_pathswitchrequestfailure(ptr, &msg->choice.PathSwitchRequestFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONFAILURE; + if(liblte_s1ap_unpack_uecontextmodificationfailure(ptr, &msg->choice.UEContextModificationFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE; + if(liblte_s1ap_unpack_initialcontextsetupfailure(ptr, &msg->choice.InitialContextSetupFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEFAILURE; + if(liblte_s1ap_unpack_enbconfigurationupdatefailure(ptr, &msg->choice.ENBConfigurationUpdateFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERPREPARATIONFAILURE; + if(liblte_s1ap_unpack_handoverpreparationfailure(ptr, &msg->choice.HandoverPreparationFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERRESOURCEALLOCATION) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_HANDOVERFAILURE; + if(liblte_s1ap_unpack_handoverfailure(ptr, &msg->choice.HandoverFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEFAILURE; + if(liblte_s1ap_unpack_mmeconfigurationupdatefailure(ptr, &msg->choice.MMEConfigurationUpdateFailure) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* +/* SuccessfulOutcome CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_successfuloutcome( + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg, + uint8_t **ptr) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + LIBLTE_BIT_MSG_STRUCT tmp_msg; + uint8_t *tmp_ptr = tmp_msg.msg; + + // Message + if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERREQUESTACKNOWLEDGE) { + if(liblte_s1ap_pack_handoverrequestacknowledge(&msg->choice.HandoverRequestAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE) { + if(liblte_s1ap_pack_uecontextreleasecomplete(&msg->choice.UEContextReleaseComplete, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UERADIOCAPABILITYMATCHRESPONSE) { + if(liblte_s1ap_pack_ueradiocapabilitymatchresponse(&msg->choice.UERadioCapabilityMatchResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE) { + if(liblte_s1ap_pack_initialcontextsetupresponse(&msg->choice.InitialContextSetupResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE) { + if(liblte_s1ap_pack_e_rabsetupresponse(&msg->choice.E_RABSetupResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTACKNOWLEDGE) { + if(liblte_s1ap_pack_pathswitchrequestacknowledge(&msg->choice.PathSwitchRequestAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEACKNOWLEDGE) { + if(liblte_s1ap_pack_mmeconfigurationupdateacknowledge(&msg->choice.MMEConfigurationUpdateAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_RESETACKNOWLEDGE) { + if(liblte_s1ap_pack_resetacknowledge(&msg->choice.ResetAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEACKNOWLEDGE) { + if(liblte_s1ap_pack_enbconfigurationupdateacknowledge(&msg->choice.ENBConfigurationUpdateAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABMODIFYRESPONSE) { + if(liblte_s1ap_pack_e_rabmodifyresponse(&msg->choice.E_RABModifyResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_WRITEREPLACEWARNINGRESPONSE) { + if(liblte_s1ap_pack_writereplacewarningresponse(&msg->choice.WriteReplaceWarningResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE) { + if(liblte_s1ap_pack_s1setupresponse(&msg->choice.S1SetupResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_KILLRESPONSE) { + if(liblte_s1ap_pack_killresponse(&msg->choice.KillResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONRESPONSE) { + if(liblte_s1ap_pack_uecontextmodificationresponse(&msg->choice.UEContextModificationResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCOMMAND) { + if(liblte_s1ap_pack_handovercommand(&msg->choice.HandoverCommand, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCANCELACKNOWLEDGE) { + if(liblte_s1ap_pack_handovercancelacknowledge(&msg->choice.HandoverCancelAcknowledge, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(msg->choice_type == LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABRELEASERESPONSE) { + if(liblte_s1ap_pack_e_rabreleaseresponse(&msg->choice.E_RABReleaseResponse, &tmp_ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + tmp_msg.N_bits = tmp_ptr - tmp_msg.msg; + + // Procedure code + liblte_value_2_bits(msg->procedureCode, ptr, 8); + + // Criticality + LIBLTE_S1AP_CRITICALITY_ENUM crit = liblte_s1ap_procedure_criticality[msg->procedureCode]; + liblte_value_2_bits(crit, ptr, 2); + liblte_align_up_zero(ptr, 8); + + // Length + uint32_t len = (tmp_msg.N_bits + 7) / 8; + if(len < 128) { + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 7); + } else if(len < 16383) { + liblte_value_2_bits(1, ptr, 1); + liblte_value_2_bits(0, ptr, 1); + liblte_value_2_bits(len, ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + + memcpy(*ptr, tmp_msg.msg, tmp_msg.N_bits); + *ptr += tmp_msg.N_bits; + + err = LIBLTE_SUCCESS; + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_successfuloutcome( + uint8_t **ptr, + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg) +{ LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(msg != NULL && + ptr != NULL) + { + // Procedure code + msg->procedureCode = liblte_bits_2_value(ptr, 8); + + // Criticality + msg->criticality = (LIBLTE_S1AP_CRITICALITY_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + + // Length + uint32_t len = 0; + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 7); + } else { + if(0 == liblte_bits_2_value(ptr, 1)) { + len = liblte_bits_2_value(ptr, 14); + } else { + // FIXME: Unlikely to have more than 16K of octets + } + } + + // Message + if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERRESOURCEALLOCATION) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERREQUESTACKNOWLEDGE; + if(liblte_s1ap_unpack_handoverrequestacknowledge(ptr, &msg->choice.HandoverRequestAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE; + if(liblte_s1ap_unpack_uecontextreleasecomplete(ptr, &msg->choice.UEContextReleaseComplete) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UERADIOCAPABILITYMATCH) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UERADIOCAPABILITYMATCHRESPONSE; + if(liblte_s1ap_unpack_ueradiocapabilitymatchresponse(ptr, &msg->choice.UERadioCapabilityMatchResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE; + if(liblte_s1ap_unpack_initialcontextsetupresponse(ptr, &msg->choice.InitialContextSetupResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABSETUP) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE; + if(liblte_s1ap_unpack_e_rabsetupresponse(ptr, &msg->choice.E_RABSetupResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_PATHSWITCHREQUEST) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_PATHSWITCHREQUESTACKNOWLEDGE; + if(liblte_s1ap_unpack_pathswitchrequestacknowledge(ptr, &msg->choice.PathSwitchRequestAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_MMECONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_MMECONFIGURATIONUPDATEACKNOWLEDGE; + if(liblte_s1ap_unpack_mmeconfigurationupdateacknowledge(ptr, &msg->choice.MMEConfigurationUpdateAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_RESET) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_RESETACKNOWLEDGE; + if(liblte_s1ap_unpack_resetacknowledge(ptr, &msg->choice.ResetAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_ENBCONFIGURATIONUPDATE) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_ENBCONFIGURATIONUPDATEACKNOWLEDGE; + if(liblte_s1ap_unpack_enbconfigurationupdateacknowledge(ptr, &msg->choice.ENBConfigurationUpdateAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABMODIFY) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABMODIFYRESPONSE; + if(liblte_s1ap_unpack_e_rabmodifyresponse(ptr, &msg->choice.E_RABModifyResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_WRITEREPLACEWARNING) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_WRITEREPLACEWARNINGRESPONSE; + if(liblte_s1ap_unpack_writereplacewarningresponse(ptr, &msg->choice.WriteReplaceWarningResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_S1SETUP) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE; + if(liblte_s1ap_unpack_s1setupresponse(ptr, &msg->choice.S1SetupResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_KILL) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_KILLRESPONSE; + if(liblte_s1ap_unpack_killresponse(ptr, &msg->choice.KillResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_UECONTEXTMODIFICATION) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTMODIFICATIONRESPONSE; + if(liblte_s1ap_unpack_uecontextmodificationresponse(ptr, &msg->choice.UEContextModificationResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERPREPARATION) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCOMMAND; + if(liblte_s1ap_unpack_handovercommand(ptr, &msg->choice.HandoverCommand) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_HANDOVERCANCEL) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCANCELACKNOWLEDGE; + if(liblte_s1ap_unpack_handovercancelacknowledge(ptr, &msg->choice.HandoverCancelAcknowledge) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } else if(msg->procedureCode == LIBLTE_S1AP_PROC_ID_E_RABRELEASE) { + msg->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABRELEASERESPONSE; + if(liblte_s1ap_unpack_e_rabreleaseresponse(ptr, &msg->choice.E_RABReleaseResponse) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + err = LIBLTE_SUCCESS; + } + return err; +} + + +/******************************************************************************* +/* S1AP_PDU CHOICE +********************************************************************************/ +LIBLTE_ERROR_ENUM liblte_s1ap_pack_s1ap_pdu( + LIBLTE_S1AP_S1AP_PDU_STRUCT *s1ap_pdu, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + LIBLTE_BIT_MSG_STRUCT bit_msg; + + if(s1ap_pdu != NULL && + msg != NULL) + { + uint8_t *p = bit_msg.msg; + uint8_t **ptr = &p; + + // Extension + liblte_value_2_bits(s1ap_pdu->ext?1:0, ptr, 1); + + // Message choice + liblte_value_2_bits(s1ap_pdu->choice_type, ptr, 2); + liblte_align_up_zero(ptr, 8); + + // Message + if(LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE == s1ap_pdu->choice_type) { + if(liblte_s1ap_pack_initiatingmessage(&s1ap_pdu->choice.initiatingMessage, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } else if(LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME == s1ap_pdu->choice_type) { + if(liblte_s1ap_pack_successfuloutcome(&s1ap_pdu->choice.successfulOutcome, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + }else if(LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME == s1ap_pdu->choice_type) { + if(liblte_s1ap_pack_unsuccessfuloutcome(&s1ap_pdu->choice.unsuccessfulOutcome, ptr) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_ENCODE_FAIL; + } + } + + liblte_align_up_zero(ptr, 8); + bit_msg.N_bits = (*ptr - bit_msg.msg); + + liblte_pack(&bit_msg, msg); + err = LIBLTE_SUCCESS; + + } + return err; +} + +LIBLTE_ERROR_ENUM liblte_s1ap_unpack_s1ap_pdu( + LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_S1AP_S1AP_PDU_STRUCT *s1ap_pdu) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + LIBLTE_BIT_MSG_STRUCT bit_msg; + + if(s1ap_pdu != NULL && + msg != NULL) + { + liblte_unpack(msg, &bit_msg); + + uint8_t *p = bit_msg.msg; + uint8_t **ptr = &p; + + // Extension + s1ap_pdu->ext = liblte_bits_2_value(ptr, 1); + + // Message choice + s1ap_pdu->choice_type = (LIBLTE_S1AP_S1AP_PDU_CHOICE_ENUM)liblte_bits_2_value(ptr, 2); + liblte_align_up(ptr, 8); + + // Message + if(LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE == s1ap_pdu->choice_type) { + if(liblte_s1ap_unpack_initiatingmessage(ptr, &s1ap_pdu->choice.initiatingMessage) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + }else if(LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME == s1ap_pdu->choice_type) { + if(liblte_s1ap_unpack_successfuloutcome(ptr, &s1ap_pdu->choice.successfulOutcome) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + }else if(LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME == s1ap_pdu->choice_type) { + if(liblte_s1ap_unpack_unsuccessfuloutcome(ptr, &s1ap_pdu->choice.unsuccessfulOutcome) != LIBLTE_SUCCESS) { + return LIBLTE_ERROR_DECODE_FAIL; + } + } + + err = LIBLTE_SUCCESS; + } + return err; +} diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt new file mode 100644 index 0000000..010f8f7 --- /dev/null +++ b/lib/src/common/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB CXX_SOURCES "*.cc") +file(GLOB C_SOURCES "*.c") + +add_library(srslte_common STATIC ${C_SOURCES} ${CXX_SOURCES}) +add_custom_target(gen_build_info COMMAND cmake -P ${CMAKE_BINARY_DIR}/SRSLTEbuildinfo.cmake) +add_dependencies(srslte_common gen_build_info) + +target_include_directories(srslte_common PUBLIC ${SEC_INCLUDE_DIRS}) +target_link_libraries(srslte_common ${SEC_LIBRARIES}) +install(TARGETS srslte_common DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/common/buffer_pool.cc b/lib/src/common/buffer_pool.cc new file mode 100644 index 0000000..5d74da1 --- /dev/null +++ b/lib/src/common/buffer_pool.cc @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/common/buffer_pool.h" +#include +#include + +namespace srslte{ + +byte_buffer_pool *byte_buffer_pool::instance = NULL; +pthread_mutex_t instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +byte_buffer_pool* byte_buffer_pool::get_instance(int capacity) +{ + pthread_mutex_lock(&instance_mutex); + if(NULL == instance) { + instance = new byte_buffer_pool(capacity); + } + pthread_mutex_unlock(&instance_mutex); + return instance; +} + +void byte_buffer_pool::cleanup(void) +{ + pthread_mutex_lock(&instance_mutex); + if(NULL != instance) + { + delete instance; + instance = NULL; + } + pthread_mutex_unlock(&instance_mutex); +} + +} // namespace srsue diff --git a/lib/src/common/gen_mch_tables.c b/lib/src/common/gen_mch_tables.c new file mode 100644 index 0000000..510bf26 --- /dev/null +++ b/lib/src/common/gen_mch_tables.c @@ -0,0 +1,62 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/common/gen_mch_tables.h" + + +/****************************************************************************** + * Key Generation + *****************************************************************************/ + +void generate_frame_table(uint8_t *table, uint8_t alloc) +{ + table[1] = (alloc >> 5) & 0x01; + table[2] = (alloc >> 4) & 0x01; + table[3] = (alloc >> 3) & 0x01; + table[6] = (alloc >> 2) & 0x01; + table[7] = (alloc >> 1) & 0x01; + table[8] = (alloc >> 0) & 0x01; +} + +void generate_mch_table(uint8_t *table, uint32_t sf_alloc, uint8_t num_frames) +{ + if(num_frames == 1){ + uint8_t alloc = (sf_alloc) & 0x3F; + generate_frame_table(table, alloc); + } else if(num_frames == 4){ + for(uint32_t j=0; j<4; j++){ + uint8_t alloc = (sf_alloc >> 6*(3-j)) & 0x3F; + generate_frame_table(&table[j*10], alloc); + } + } +} + +void generate_mcch_table(uint8_t *table, uint32_t sf_alloc) +{ + uint8_t alloc = (sf_alloc) & 0x3F; + generate_frame_table(table, alloc); +} diff --git a/lib/src/common/liblte_security.cc b/lib/src/common/liblte_security.cc new file mode 100644 index 0000000..09e79fb --- /dev/null +++ b/lib/src/common/liblte_security.cc @@ -0,0 +1,1940 @@ +/******************************************************************************* + + Copyright 2014 Ben Wojtowicz + + This program 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. + + This program 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. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +******************************************************************************* + + File: liblte_security.cc + + Description: Contains all the implementations for the LTE security + algorithm library. + + Revision History + ---------- ------------- -------------------------------------------- + 08/03/2014 Ben Wojtowicz Created file. + 09/03/2014 Ben Wojtowicz Added key generation and EIA2 and fixed MCC + and MNC packing. + +*******************************************************************************/ + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include "srslte/common/liblte_security.h" +#include "srslte/common/liblte_ssl.h" +#include "math.h" + +/******************************************************************************* + DEFINES +*******************************************************************************/ + + +/******************************************************************************* + TYPEDEFS +*******************************************************************************/ + +typedef struct{ + uint8 rk[11][4][4]; +}ROUND_KEY_STRUCT; + +typedef struct{ + uint8 state[4][4]; +}STATE_STRUCT; + +typedef struct{ + uint32 * lfsr; + uint32 * fsm; +}S3G_STATE; + +/******************************************************************************* + GLOBAL VARIABLES +*******************************************************************************/ + +static const uint8 S[256] = { 99,124,119,123,242,107,111,197, 48, 1,103, 43,254,215,171,118, + 202,130,201,125,250, 89, 71,240,173,212,162,175,156,164,114,192, + 183,253,147, 38, 54, 63,247,204, 52,165,229,241,113,216, 49, 21, + 4,199, 35,195, 24,150, 5,154, 7, 18,128,226,235, 39,178,117, + 9,131, 44, 26, 27,110, 90,160, 82, 59,214,179, 41,227, 47,132, + 83,209, 0,237, 32,252,177, 91,106,203,190, 57, 74, 76, 88,207, + 208,239,170,251, 67, 77, 51,133, 69,249, 2,127, 80, 60,159,168, + 81,163, 64,143,146,157, 56,245,188,182,218, 33, 16,255,243,210, + 205, 12, 19,236, 95,151, 68, 23,196,167,126, 61,100, 93, 25,115, + 96,129, 79,220, 34, 42,144,136, 70,238,184, 20,222, 94, 11,219, + 224, 50, 58, 10, 73, 6, 36, 92,194,211,172, 98,145,149,228,121, + 231,200, 55,109,141,213, 78,169,108, 86,244,234,101,122,174, 8, + 186,120, 37, 46, 28,166,180,198,232,221,116, 31, 75,189,139,138, + 112, 62,181,102, 72, 3,246, 14, 97, 53, 87,185,134,193, 29,158, + 225,248,152, 17,105,217,142,148,155, 30,135,233,206, 85, 40,223, + 140,161,137, 13,191,230, 66,104, 65,153, 45, 15,176, 84,187, 22}; + +/* S-box SQ */ +static const uint8 SQ[256] = { 0x25, 0x24, 0x73, 0x67, 0xD7, 0xAE, + 0x5C, 0x30, 0xA4, 0xEE, 0x6E, 0xCB, 0x7D, 0xB5, 0x82, 0xDB, + 0xE4, 0x8E, 0x48, 0x49, 0x4F, 0x5D, 0x6A, 0x78, 0x70, 0x88, + 0xE8, 0x5F, 0x5E, 0x84, 0x65, 0xE2, 0xD8, 0xE9, 0xCC, 0xED, + 0x40, 0x2F, 0x11, 0x28, 0x57, 0xD2, 0xAC, 0xE3, 0x4A, 0x15, + 0x1B, 0xB9, 0xB2, 0x80, 0x85, 0xA6, 0x2E, 0x02, 0x47, 0x29, + 0x07, 0x4B, 0x0E, 0xC1, 0x51, 0xAA, 0x89, 0xD4, 0xCA, 0x01, + 0x46, 0xB3, 0xEF, 0xDD, 0x44, 0x7B, 0xC2, 0x7F, 0xBE, 0xC3, + 0x9F, 0x20, 0x4C, 0x64, 0x83, 0xA2, 0x68, 0x42, 0x13, 0xB4, + 0x41, 0xCD, 0xBA, 0xC6, 0xBB, 0x6D, 0x4D, 0x71, 0x21, 0xF4, + 0x8D, 0xB0, 0xE5, 0x93, 0xFE, 0x8F, 0xE6, 0xCF, 0x43, 0x45, + 0x31, 0x22, 0x37, 0x36, 0x96, 0xFA, 0xBC, 0x0F, 0x08, 0x52, + 0x1D, 0x55, 0x1A, 0xC5, 0x4E, 0x23, 0x69, 0x7A, 0x92, 0xFF, + 0x5B, 0x5A, 0xEB, 0x9A, 0x1C, 0xA9, 0xD1, 0x7E, 0x0D, 0xFC, + 0x50, 0x8A, 0xB6, 0x62, 0xF5, 0x0A, 0xF8, 0xDC, 0x03, 0x3C, + 0x0C, 0x39, 0xF1, 0xB8, 0xF3, 0x3D, 0xF2, 0xD5, 0x97, 0x66, + 0x81, 0x32, 0xA0, 0x00, 0x06, 0xCE, 0xF6, 0xEA, 0xB7, 0x17, + 0xF7, 0x8C, 0x79, 0xD6, 0xA7, 0xBF, 0x8B, 0x3F, 0x1F, 0x53, + 0x63, 0x75, 0x35, 0x2C, 0x60, 0xFD, 0x27, 0xD3, 0x94, 0xA5, + 0x7C, 0xA1, 0x05, 0x58, 0x2D, 0xBD, 0xD9, 0xC7, 0xAF, 0x6B, + 0x54, 0x0B, 0xE0, 0x38, 0x04, 0xC8, 0x9D, 0xE7, 0x14, 0xB1, + 0x87, 0x9C, 0xDF, 0x6F, 0xF9, 0xDA, 0x2A, 0xC4, 0x59, 0x16, + 0x74, 0x91, 0xAB, 0x26, 0x61, 0x76, 0x34, 0x2B, 0xAD, 0x99, + 0xFB, 0x72, 0xEC, 0x33, 0x12, 0xDE, 0x98, 0x3B, 0xC0, 0x9B, + 0x3E, 0x18, 0x10, 0x3A, 0x56, 0xE1, 0x77, 0xC9, 0x1E, 0x9E, + 0x95, 0xA3, 0x90, 0x19, 0xA8, 0x6C, 0x09, 0xD0, 0xF0, 0x86 }; + + +static const uint8 X_TIME[256] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, + 96, 98,100,102,104,106,108,110,112,114,116,118,120,122,124,126, + 128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158, + 160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190, + 192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222, + 224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,254, + 27, 25, 31, 29, 19, 17, 23, 21, 11, 9, 15, 13, 3, 1, 7, 5, + 59, 57, 63, 61, 51, 49, 55, 53, 43, 41, 47, 45, 35, 33, 39, 37, + 91, 89, 95, 93, 83, 81, 87, 85, 75, 73, 79, 77, 67, 65, 71, 69, + 123,121,127,125,115,113,119,117,107,105,111,109, 99, 97,103,101, + 155,153,159,157,147,145,151,149,139,137,143,141,131,129,135,133, + 187,185,191,189,179,177,183,181,171,169,175,173,163,161,167,165, + 219,217,223,221,211,209,215,213,203,201,207,205,195,193,199,197, + 251,249,255,253,243,241,247,245,235,233,239,237,227,225,231,229}; + +/******************************************************************************* + LOCAL FUNCTION PROTOTYPES +*******************************************************************************/ + +/********************************************************************* + Name: compute_OPc + + Description: Computes OPc from OP and K. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void compute_OPc(ROUND_KEY_STRUCT *rk, + uint8 *op, + uint8 *op_c); + +/********************************************************************* + Name: rijndael_key_schedule + + Description: Computes all Rijndael's internal subkeys from key. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void rijndael_key_schedule(uint8 *key, + ROUND_KEY_STRUCT *rk); + +/********************************************************************* + Name: rijndael_encrypt + + Description: Computes output using input and round keys. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void rijndael_encrypt(uint8 *input, + ROUND_KEY_STRUCT *rk, + uint8 *output); + +/********************************************************************* + Name: key_add + + Description: Round key addition function. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void key_add(STATE_STRUCT *state, + ROUND_KEY_STRUCT *rk, + uint32 round); + +/********************************************************************* + Name: byte_sub + + Description: Byte substitution transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void byte_sub(STATE_STRUCT *state); + +/********************************************************************* + Name: shift_row + + Description: Row shift transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void shift_row(STATE_STRUCT *state); + +/********************************************************************* + Name: mix_column + + Description: Mix column transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +// Defines +// Enums +// Structs +// Functions +void mix_column(STATE_STRUCT *state); + +/********************************************************************* + Name: zero_tailing_bits + + Description: Fill tailing bits with zeros. + + Document Reference: - +*********************************************************************/ +void zero_tailing_bits(uint8 * data, uint32 length_bits); + +/********************************************************************* + Name: s3g_mul_x + + Description: Multiplication with reduction. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.1.1 +*********************************************************************/ +uint8 s3g_mul_x(uint8 v, uint8 c); + +/********************************************************************* + Name: s3g_mul_x_pow + + Description: Recursive multiplication with reduction. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.1.2 +*********************************************************************/ +uint8 s3g_mul_x_pow(uint8 v, uint8 i, uint8 c); + +/********************************************************************* + Name: s3g_mul_alpha + + Description: Multiplication with alpha. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.2 +*********************************************************************/ +uint32 s3g_mul_alpha(uint8 c); + +/********************************************************************* + Name: s3g_div_alpha + + Description: Division by alpha. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.3 +*********************************************************************/ +uint32 s3g_div_alpha(uint8 c); + +/********************************************************************* + Name: s3g_s1 + + Description: S-Box S1. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.3.1 +*********************************************************************/ +uint32 s3g_s1(uint32 w); + +/********************************************************************* + Name: s3g_s2 + + Description: S-Box S2. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.3.2 +*********************************************************************/ +uint32 s3g_s2(uint32 w); + +/********************************************************************* + Name: s3g_clock_lfsr + + Description: Clocking LFSR. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.4 and Section 3.4.5 +*********************************************************************/ +void s3g_clock_lfsr(S3G_STATE * state, uint32 f); + +/********************************************************************* + Name: s3g_clock_fsm + + Description: Clocking FSM. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.6 +*********************************************************************/ +uint32 s3g_clock_fsm(S3G_STATE * state); + +/********************************************************************* + Name: s3g_initialize + + Description: Initialization. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 4.1 +*********************************************************************/ +void s3g_initialize(S3G_STATE * state, uint32 k[4], uint32 iv[4]); + +/********************************************************************* + Name: s3g_deinitialize + + Description: Deinitialization. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 +*********************************************************************/ +void s3g_deinitialize(S3G_STATE * state); + +/********************************************************************* + Name: s3g_generate_keystream + + Description: Generation of Keystream. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 4.2 +*********************************************************************/ +void s3g_generate_keystream(S3G_STATE * state, uint32 n, uint32 *ks); + + +/******************************************************************************* + FUNCTIONS +*******************************************************************************/ + +/********************************************************************* + Name: liblte_security_generate_k_asme + + Description: Generate the security key Kasme. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_asme(uint8 *ck, + uint8 *ik, + uint8 *ak, + uint8 *sqn, + uint16 mcc, + uint16 mnc, + uint8 *k_asme) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint32 i; + uint8 s[14]; + uint8 key[32]; + + if(ck != NULL && + ik != NULL && + ak != NULL && + sqn != NULL && + k_asme != NULL) + { + // Construct S + s[0] = 0x10; // FC + s[1] = (mcc & 0x00F0) | ((mcc & 0x0F00) >> 8); // First byte of P0 + if((mnc & 0xFF00) == 0xFF00) + { + // 2-digit MNC + s[2] = 0xF0 | (mcc & 0x000F); // Second byte of P0 + s[3] = ((mnc & 0x000F) << 4) | ((mnc & 0x00F0) >> 4); // Third byte of P0 + }else{ + // 3-digit MNC + s[2] = ((mnc & 0x000F) << 4) | (mcc & 0x000F); // Second byte of P0 + s[3] = ((mnc & 0x00F0)) | ((mnc & 0x0F00) >> 8); // Third byte of P0 + } + s[4] = 0x00; // First byte of L0 + s[5] = 0x03; // Second byte of L0 + for(i=0; i<6; i++) + { + s[6+i] = sqn[i] ^ ak[i]; // P1 + } + s[12] = 0x00; // First byte of L1 + s[13] = 0x06; // Second byte of L1 + + // Construct Key + for(i=0; i<16; i++) + { + key[i] = ck[i]; + key[16+i] = ik[i]; + } + + // Derive Kasme + sha256(key, 32, s, 14, k_asme, 0); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_generate_k_enb + + Description: Generate the security key Kenb. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_enb(uint8 *k_asme, + uint32 nas_count, + uint8 *k_enb) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[7]; + + if(k_asme != NULL && + k_enb != NULL) + { + // Construct S + s[0] = 0x11; // FC + s[1] = (nas_count >> 24) & 0xFF; // First byte of P0 + s[2] = (nas_count >> 16) & 0xFF; // Second byte of P0 + s[3] = (nas_count >> 8) & 0xFF; // Third byte of P0 + s[4] = nas_count & 0xFF; // Fourth byte of P0 + s[5] = 0x00; // First byte of L0 + s[6] = 0x04; // Second byte of L0 + + // Derive Kenb + sha256(k_asme, 32, s, 7, k_enb, 0); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_generate_k_enb_star + + Description: Generate the security key Kenb*. + + Document Reference: 33.401 v10.0.0 Annex A.5 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_enb_star(uint8 *k_enb, + uint32 pci, + uint32_t earfcn, + uint8 *k_enb_star) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[9]; + + if (k_enb_star != NULL && + k_enb != NULL) { + // Construct S + s[0] = 0x13; // FC + s[1] = (pci >> 8) & 0xFF; // First byte of P0 + s[2] = pci & 0xFF; // Second byte of P0 + s[3] = 0x00; // First byte of L0 + s[4] = 0x02; // Second byte of L0 + s[5] = (earfcn >> 8) & 0xFF; // First byte of P0 + s[6] = earfcn & 0xFF; // Second byte of P0 + s[7] = 0x00; // First byte of L0 + s[8] = 0x02; // Second byte of L0 + + // Derive Kenb + sha256(k_enb, 32, s, 9, k_enb_star, 0); + + err = LIBLTE_SUCCESS; + } + + return (err); +} + +LIBLTE_ERROR_ENUM liblte_security_generate_nh( uint8_t *k_asme, + uint8_t *sync, + uint8_t *nh) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[35]; + + if (k_asme != NULL && + sync != NULL && + nh != NULL) + { + // Construct S + s[0] = 0x12; // FC + for (int i=0;i<32;i++) { + s[1+i] = sync[i]; + } + s[33] = 0x00; // First byte of L0 + s[34] = 0x20, // Second byte of L0 + + // Derive NH + sha256(k_asme, 32, s, 35, nh, 0); + + err = LIBLTE_SUCCESS; + } + + return (err); +} + +/********************************************************************* + Name: liblte_security_generate_k_nas + + Description: Generate the NAS security keys KNASenc and KNASint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_nas(uint8 *k_asme, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_nas_enc, + uint8 *k_nas_int) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[7]; + + if(k_asme != NULL && + k_nas_enc != NULL && + k_nas_int != NULL) + { + // Construct S for KNASenc + s[0] = 0x15; // FC + s[1] = 0x01; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = enc_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KNASenc + sha256(k_asme, 32, s, 7, k_nas_enc, 0); + + // Construct S for KNASint + s[0] = 0x15; // FC + s[1] = 0x02; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = int_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KNASint + sha256(k_asme, 32, s, 7, k_nas_int, 0); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_generate_k_rrc + + Description: Generate the RRC security keys KRRCenc and KRRCint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_rrc(uint8 *k_enb, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_rrc_enc, + uint8 *k_rrc_int) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[7]; + + if(k_enb != NULL && + k_rrc_enc != NULL && + k_rrc_int != NULL) + { + // Construct S for KRRCenc + s[0] = 0x15; // FC + s[1] = 0x03; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = enc_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KRRCenc + sha256(k_enb, 32, s, 7, k_rrc_enc, 0); + + // Construct S for KRRCint + s[0] = 0x15; // FC + s[1] = 0x04; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = int_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KRRCint + sha256(k_enb, 32, s, 7, k_rrc_int, 0); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_generate_k_up + + Description: Generate the user plane security keys KUPenc and + KUPint. + + Document Reference: 33.401 v10.0.0 Annex A.2 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_generate_k_up(uint8 *k_enb, + LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8 *k_up_enc, + uint8 *k_up_int) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 s[7]; + + if(k_enb != NULL && + k_up_enc != NULL && + k_up_int != NULL) + { + // Construct S for KUPenc + s[0] = 0x15; // FC + s[1] = 0x05; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = enc_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KUPenc + sha256(k_enb, 32, s, 7, k_up_enc, 0); + + // Construct S for KUPint + s[0] = 0x15; // FC + s[1] = 0x06; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = int_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 + + // Derive KUPint + sha256(k_enb, 32, s, 7, k_up_int, 0); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_128_eia2 + + Description: 128-bit integrity algorithm EIA2. + + Document Reference: 33.401 v10.0.0 Annex B.2.3 + 33.102 v10.0.0 Section 6.5.4 + RFC4493 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_128_eia2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *mac) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 M[msg_len+8+16]; + aes_context ctx; + uint32 i; + uint32 j; + uint32 n; + uint32 pad_bits; + uint8 const_zero[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + uint8 L[16]; + uint8 K1[16]; + uint8 K2[16]; + uint8 T[16]; + uint8 tmp[16]; + + if(key != NULL && + msg != NULL && + mac != NULL) + { + // Subkey L generation + aes_setkey_enc(&ctx, key, 128); + aes_crypt_ecb(&ctx, AES_ENCRYPT, const_zero, L); + + // Subkey K1 generation + for(i=0; i<15; i++) + { + K1[i] = (L[i] << 1) | ((L[i+1] >> 7) & 0x01); + } + K1[15] = L[15] << 1; + if(L[0] & 0x80) + { + K1[15] ^= 0x87; + } + + // Subkey K2 generation + for(i=0; i<15; i++) + { + K2[i] = (K1[i] << 1) | ((K1[i+1] >> 7) & 0x01); + } + K2[15] = K1[15] << 1; + if(K1[0] & 0x80) + { + K2[15] ^= 0x87; + } + + // Construct M + memset(M, 0, msg_len+8+16); + M[0] = (count >> 24) & 0xFF; + M[1] = (count >> 16) & 0xFF; + M[2] = (count >> 8) & 0xFF; + M[3] = count & 0xFF; + M[4] = (bearer << 3) | (direction << 2); + for(i=0; iN_bits*8+8+16]; + aes_context ctx; + uint32 i; + uint32 j; + uint32 n; + uint32 pad_bits; + uint8 const_zero[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + uint8 L[16]; + uint8 K1[16]; + uint8 K2[16]; + uint8 T[16]; + uint8 tmp[16]; + + if(key != NULL && + msg != NULL && + mac != NULL) + { + // Subkey L generation + aes_setkey_enc(&ctx, key, 128); + aes_crypt_ecb(&ctx, AES_ENCRYPT, const_zero, L); + + // Subkey K1 generation + for(i=0; i<15; i++) + { + K1[i] = (L[i] << 1) | ((L[i+1] >> 7) & 0x01); + } + K1[15] = L[15] << 1; + if(L[0] & 0x80) + { + K1[15] ^= 0x87; + } + + // Subkey K2 generation + for(i=0; i<15; i++) + { + K2[i] = (K1[i] << 1) | ((K1[i+1] >> 7) & 0x01); + } + K2[15] = K1[15] << 1; + if(K1[0] & 0x80) + { + K2[15] ^= 0x87; + } + + // Construct M + memset(M, 0, msg->N_bits*8+8+16); + M[0] = (count >> 24) & 0xFF; + M[1] = (count >> 16) & 0xFF; + M[2] = (count >> 8) & 0xFF; + M[3] = count & 0xFF; + M[4] = (bearer << 3) | (direction << 2); + for(i=0; iN_bits/8; i++) + { + M[8+i] = 0; + for(j=0; j<8; j++) + { + M[8+i] |= msg->msg[i*8+j] << (7-j); + } + } + if((msg->N_bits % 8) != 0) + { + M[8+i] = 0; + for(j=0; jN_bits % 8; j++) + { + M[8+i] |= msg->msg[i*8+j] << (7-j); + } + } + + // MAC generation + n = (uint32)(ceilf((float)(msg->N_bits+64)/(float)(128))); + for(i=0; i<16; i++) + { + T[i] = 0; + } + for(i=0; iN_bits + 64) % 128; + if(pad_bits == 0) + { + for(j=0; j<16; j++) + { + tmp[j] = T[j] ^ K1[j] ^ M[i*16 + j]; + } + aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); + }else{ + pad_bits = (128 - pad_bits) - 1; + M[i*16 + (15 - (pad_bits/8))] |= 0x1 << (pad_bits % 8); + for(j=0; j<16; j++) + { + tmp[j] = T[j] ^ K2[j] ^ M[i*16 + j]; + } + aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); + } + + for(i=0; i<4; i++) + { + mac[i] = T[i]; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_encryption_eea1 + + Description: 128-bit encryption algorithm EEA1. + + Document Reference: 33.401 v13.1.0 Annex B.1.2 + 35.215 v13.0.0 References + Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D1 v2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_encryption_eea1(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *out) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + S3G_STATE state, *state_ptr; + uint32 k[] = {0,0,0,0}; + uint32 iv[] = {0,0,0,0}; + uint32 *ks; + int32 i; + uint32 msg_len_block_8, msg_len_block_32, m; + + if (key != NULL && + msg != NULL && + out != NULL) + { + state_ptr = &state; + msg_len_block_8 = (msg_len + 7) / 8; + msg_len_block_32 = (msg_len + 31) / 32; + + // Transform key + for (i = 3; i >= 0; i--) { + k[i] = (key[4 * (3 - i) + 0] << 24) | + (key[4 * (3 - i) + 1] << 16) | + (key[4 * (3 - i) + 2] << 8) | + (key[4 * (3 - i) + 3]); + } + + // Construct iv + iv[3] = count; + iv[2] = ((bearer & 0x1F) << 27) | ((direction & 0x01) << 26); + iv[1] = iv[3]; + iv[0] = iv[2]; + + // Initialize keystream + s3g_initialize(state_ptr, k, iv); + + // Generate keystream + + ks = (uint32 *) calloc(msg_len_block_32, sizeof(uint32)); + s3g_generate_keystream(state_ptr, msg_len_block_32, ks); + + // Generate output except last block + for (i = 0; i < (int32_t)msg_len_block_32 - 1; i++) { + out[4 * i + 0] = msg[4 * i + 0] ^ ((ks[i] >> 24) & 0xFF); + out[4 * i + 1] = msg[4 * i + 1] ^ ((ks[i] >> 16) & 0xFF); + out[4 * i + 2] = msg[4 * i + 2] ^ ((ks[i] >> 8) & 0xFF); + out[4 * i + 3] = msg[4 * i + 3] ^ ((ks[i] & 0xFF)); + } + + // Process last bytes + for (i = (msg_len_block_32 - 1) * 4; i < (int32_t)msg_len_block_8; i++) { + out[i] = msg[i] ^ ((ks[i / 4] >> ((3 - (i % 4)) * 8)) & 0xFF); + } + + // Zero tailing bits + zero_tailing_bits(out, msg_len); + + // Clean up + free(ks); + s3g_deinitialize(state_ptr); + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_decryption_eea1 + + Description: 128-bit decryption algorithm EEA1. + + Document Reference: 33.401 v13.1.0 Annex B.1.2 + 35.215 v13.0.0 References + Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D1 v2.1 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_decryption_eea1(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *ct, + uint32 ct_len, + uint8 *out) { + return liblte_security_encryption_eea1(key, count, bearer, + direction, ct, ct_len, out); +} + +/********************************************************************* + Name: liblte_security_encryption_eea2 + + Description: 128-bit encryption algorithm EEA2. + + Document Reference: 33.401 v13.1.0 Annex B.1.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_encryption_eea2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *msg, + uint32 msg_len, + uint8 *out) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + aes_context ctx; + unsigned char stream_blk[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + unsigned char nonce_cnt[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int32 i; + int ret; + size_t nc_off = 0; + + if(key != NULL && + msg != NULL && + out != NULL) + { + ret = aes_setkey_enc(&ctx, key, 128); + + if (ret == 0) { + // Construct nonce + nonce_cnt[0] = (count >> 24) & 0xFF; + nonce_cnt[1] = (count >> 16) & 0xFF; + nonce_cnt[2] = (count >> 8) & 0xFF; + nonce_cnt[3] = (count) & 0xFF; + nonce_cnt[4] = ((bearer & 0x1F) << 3) | + ((direction & 0x01) << 2); + + // Encryption + ret = aes_crypt_ctr(&ctx, (msg_len + 7) / 8, &nc_off, nonce_cnt, + stream_blk, msg, out); + } + + if (ret == 0) { + // Zero tailing bits + zero_tailing_bits(out, msg_len); + err = LIBLTE_SUCCESS; + } + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_decryption_eea2 + + Description: 128-bit decryption algorithm EEA2. + + Document Reference: 33.401 v13.1.0 Annex B.1.3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_decryption_eea2(uint8 *key, + uint32 count, + uint8 bearer, + uint8 direction, + uint8 *ct, + uint32 ct_len, + uint8 *out) +{ + return liblte_security_encryption_eea2(key, count, bearer, + direction, ct, ct_len, out); +} + + + +/********************************************************************* + Name: liblte_security_milenage_f1 + + Description: Milenage security function F1. Computes network + authentication code MAC-A from key K, random + challenge RAND, sequence number SQN, and + authentication management field AMF. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_milenage_f1(uint8 *k, + uint8 *op_c, + uint8 *rand, + uint8 *sqn, + uint8 *amf, + uint8 *mac_a) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + ROUND_KEY_STRUCT round_keys; + uint32 i; + uint8 temp[16]; + uint8 in1[16]; + uint8 out1[16]; + uint8 rijndael_input[16]; + + if(k != NULL && + op_c != NULL && + rand != NULL && + sqn != NULL && + amf != NULL && + mac_a != NULL) + { + // Initialize the round keys + rijndael_key_schedule(k, &round_keys); + + // Compute temp + for(i=0; i<16; i++) + { + rijndael_input[i] = rand[i] ^ op_c[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, temp); + + // Construct in1 + for(i=0; i<6; i++) + { + in1[i] = sqn[i]; + in1[i+8] = sqn[i]; + } + for(i=0; i<2; i++) + { + in1[i+6] = amf[i]; + in1[i+14] = amf[i]; + } + + // Compute out1 + for(i=0; i<16; i++) + { + rijndael_input[(i+8) % 16] = in1[i] ^ op_c[i]; + } + for(i=0; i<16; i++) + { + rijndael_input[i] ^= temp[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, out1); + for(i=0; i<16; i++) + { + out1[i] ^= op_c[i]; + } + + // Return MAC-A + for(i=0; i<8; i++) + { + mac_a[i] = out1[i]; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_milenage_f1_star + + Description: Milenage security function F1*. Computes resynch + authentication code MAC-S from key K, random + challenge RAND, sequence number SQN, and + authentication management field AMF. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_milenage_f1_star(uint8 *k, + uint8 *op_c, + uint8 *rand, + uint8 *sqn, + uint8 *amf, + uint8 *mac_s) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + ROUND_KEY_STRUCT round_keys; + uint32 i; + uint8 temp[16]; + uint8 in1[16]; + uint8 out1[16]; + uint8 rijndael_input[16]; + + if(k != NULL && + op_c != NULL && + rand != NULL && + sqn != NULL && + amf != NULL && + mac_s != NULL) + { + // Initialize the round keys + rijndael_key_schedule(k, &round_keys); + + // Compute temp + for(i=0; i<16; i++) + { + rijndael_input[i] = rand[i] ^ op_c[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, temp); + + // Construct in1 + for(i=0; i<6; i++) + { + in1[i] = sqn[i]; + in1[i+8] = sqn[i]; + } + for(i=0; i<2; i++) + { + in1[i+6] = amf[i]; + in1[i+14] = amf[i]; + } + + // Compute out1 + for(i=0; i<16; i++) + { + rijndael_input[(i+8) % 16] = in1[i] ^ op_c[i]; + } + for(i=0; i<16; i++) + { + rijndael_input[i] ^= temp[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, out1); + for(i=0; i<16; i++) + { + out1[i] ^= op_c[i]; + } + + // Return MAC-S + for(i=0; i<8; i++) + { + mac_s[i] = out1[i+8]; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_milenage_f2345 + + Description: Milenage security functions F2, F3, F4, and F5. + Computes response RES, confidentiality key CK, + integrity key IK, and anonymity key AK from random + challenge RAND. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_milenage_f2345(uint8 *k, + uint8 *op_c, + uint8 *rand, + uint8 *res, + uint8 *ck, + uint8 *ik, + uint8 *ak) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + ROUND_KEY_STRUCT round_keys; + uint32 i; + uint8 temp[16]; + uint8 out[16]; + uint8 rijndael_input[16]; + + if(k != NULL && + op_c != NULL && + rand != NULL && + res != NULL && + ck != NULL && + ik != NULL && + ak != NULL) + { + // Initialize the round keys + rijndael_key_schedule(k, &round_keys); + + // Compute temp + for(i=0; i<16; i++) + { + rijndael_input[i] = rand[i] ^ op_c[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, temp); + + // Compute out for RES and AK + for(i=0; i<16; i++) + { + rijndael_input[i] = temp[i] ^ op_c[i]; + } + rijndael_input[15] ^= 1; + rijndael_encrypt(rijndael_input, &round_keys, out); + for(i=0; i<16; i++) + { + out[i] ^= op_c[i]; + } + + // Return RES + for(i=0; i<8; i++) + { + res[i] = out[i+8]; + } + + // Return AK + for(i=0; i<6; i++) + { + ak[i] = out[i]; + } + + // Compute out for CK + for(i=0; i<16; i++) + { + rijndael_input[(i+12) % 16] = temp[i] ^ op_c[i]; + } + rijndael_input[15] ^= 2; + rijndael_encrypt(rijndael_input, &round_keys, out); + for(i=0; i<16; i++) + { + out[i] ^= op_c[i]; + } + + // Return CK + for(i=0; i<16; i++) + { + ck[i] = out[i]; + } + + // Compute out for IK + for(i=0; i<16; i++) + { + rijndael_input[(i+8) % 16] = temp[i] ^ op_c[i]; + } + rijndael_input[15] ^= 4; + rijndael_encrypt(rijndael_input, &round_keys, out); + for(i=0; i<16; i++) + { + out[i] ^= op_c[i]; + } + + // Return IK + for(i=0; i<16; i++) + { + ik[i] = out[i]; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + +/********************************************************************* + Name: liblte_security_milenage_f5_star + + Description: Milenage security function F5*. Computes resynch + anonymity key AK from key K and random challenge + RAND. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_security_milenage_f5_star(uint8 *k, + uint8 *op_c, + uint8 *rand, + uint8 *ak) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + ROUND_KEY_STRUCT round_keys; + uint32 i; + uint8 temp[16]; + uint8 out[16]; + uint8 rijndael_input[16]; + + if(k != NULL && + op_c != NULL && + rand != NULL && + ak != NULL) + { + // Initialize the round keys + rijndael_key_schedule(k, &round_keys); + + // Compute temp + for(i=0; i<16; i++) + { + rijndael_input[i] = rand[i] ^ op_c[i]; + } + rijndael_encrypt(rijndael_input, &round_keys, temp); + + // Compute out + for(i=0; i<16; i++) + { + rijndael_input[(i+4) % 16] = temp[i] ^ op_c[i]; + } + rijndael_input[15] ^= 8; + rijndael_encrypt(rijndael_input, &round_keys, out); + for(i=0; i<16; i++) + { + out[i] ^= op_c[i]; + } + + // Return AK + for(i=0; i<6; i++) + { + ak[i] = out[i]; + } + err = LIBLTE_SUCCESS; + } + return(err); +} + +/********************************************************************* + Name: liblte_compute_opc + + Description: Computes OPc from OP and K. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ + +LIBLTE_ERROR_ENUM liblte_compute_opc(uint8 *k, + uint8 *op, + uint8 *op_c) +{ + uint32 i; + ROUND_KEY_STRUCT round_keys; + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + + if(k != NULL && + op != NULL && + op_c != NULL) + { + + rijndael_key_schedule(k, &round_keys); + rijndael_encrypt(op, &round_keys, op_c); + for(i=0; i<16; i++) + { + op_c[i] ^= op[i]; + } + err = LIBLTE_SUCCESS; + } + return err; +} + +/******************************************************************************* + LOCAL FUNCTIONS +*******************************************************************************/ + + +/********************************************************************* + Name: rijndael_key_schedule + + Description: Computes all Rijndael's internal subkeys from key. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void rijndael_key_schedule(uint8 *key, + ROUND_KEY_STRUCT *rk) +{ + uint32 i; + uint32 j; + uint8 round_const; + + // Set first round key to key + for(i=0; i<16; i++) + { + rk->rk[0][i & 0x03][i >> 2] = key[i]; + } + + round_const = 1; + + // Compute the remaining round keys + for(i=1; i<11; i++) + { + rk->rk[i][0][0] = S[rk->rk[i-1][1][3]] ^ rk->rk[i-1][0][0] ^ round_const; + rk->rk[i][1][0] = S[rk->rk[i-1][2][3]] ^ rk->rk[i-1][1][0]; + rk->rk[i][2][0] = S[rk->rk[i-1][3][3]] ^ rk->rk[i-1][2][0]; + rk->rk[i][3][0] = S[rk->rk[i-1][0][3]] ^ rk->rk[i-1][3][0]; + + for(j=0; j<4; j++) + { + rk->rk[i][j][1] = rk->rk[i-1][j][1] ^ rk->rk[i][j][0]; + rk->rk[i][j][2] = rk->rk[i-1][j][2] ^ rk->rk[i][j][1]; + rk->rk[i][j][3] = rk->rk[i-1][j][3] ^ rk->rk[i][j][2]; + } + + round_const = X_TIME[round_const]; + } +} + +/********************************************************************* + Name: rijndael_encrypt + + Description: Computes output using input and round keys. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void rijndael_encrypt(uint8 *input, + ROUND_KEY_STRUCT *rk, + uint8 *output) +{ + STATE_STRUCT state; + uint32 i; + uint32 r; + + // Initialize and perform round 0 + for(i=0; i<16; i++) + { + state.state[i & 0x03][i >> 2] = input[i]; + } + key_add(&state, rk, 0); + + // Perform rounds 1 through 9 + for(r=1; r<=9; r++) + { + byte_sub(&state); + shift_row(&state); + mix_column(&state); + key_add(&state, rk, r); + } + + // Perform round 10 + byte_sub(&state); + shift_row(&state); + key_add(&state, rk, r); + + // Return output + for(i=0; i<16; i++) + { + output[i] = state.state[i & 0x03][i >> 2]; + } +} + +/********************************************************************* + Name: key_add + + Description: Round key addition function. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void key_add(STATE_STRUCT *state, + ROUND_KEY_STRUCT *rk, + uint32 round) +{ + uint32 i; + uint32 j; + + for(i=0; i<4; i++) + { + for(j=0; j<4; j++) + { + state->state[i][j] ^= rk->rk[round][i][j]; + } + } +} + +/********************************************************************* + Name: byte_sub + + Description: Byte substitution transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void byte_sub(STATE_STRUCT *state) +{ + uint32 i; + uint32 j; + + for(i=0; i<4; i++) + { + for(j=0; j<4; j++) + { + state->state[i][j] = S[state->state[i][j]]; + } + } +} + +/********************************************************************* + Name: shift_row + + Description: Row shift transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void shift_row(STATE_STRUCT *state) +{ + uint8 temp; + + // Left rotate row 1 by 1 + temp = state->state[1][0]; + state->state[1][0] = state->state[1][1]; + state->state[1][1] = state->state[1][2]; + state->state[1][2] = state->state[1][3]; + state->state[1][3] = temp; + + // Left rotate row 2 by 2 + temp = state->state[2][0]; + state->state[2][0] = state->state[2][2]; + state->state[2][2] = temp; + temp = state->state[2][1]; + state->state[2][1] = state->state[2][3]; + state->state[2][3] = temp; + + // Left rotate row 3 by 3 + temp = state->state[3][0]; + state->state[3][0] = state->state[3][3]; + state->state[3][3] = state->state[3][2]; + state->state[3][2] = state->state[3][1]; + state->state[3][1] = temp; +} + +/********************************************************************* + Name: mix_column + + Description: Mix column transformation. + + Document Reference: 35.206 v10.0.0 Annex 3 +*********************************************************************/ +void mix_column(STATE_STRUCT *state) +{ + uint32 i; + uint8 temp; + uint8 tmp0; + uint8 tmp; + + for(i=0; i<4; i++) + { + temp = state->state[0][i] ^ state->state[1][i] ^ state->state[2][i] ^ state->state[3][i]; + tmp0 = state->state[0][i]; + + tmp = X_TIME[state->state[0][i] ^ state->state[1][i]]; + state->state[0][i] ^= temp ^ tmp; + + tmp = X_TIME[state->state[1][i] ^ state->state[2][i]]; + state->state[1][i] ^= temp ^ tmp; + + tmp = X_TIME[state->state[2][i] ^ state->state[3][i]]; + state->state[2][i] ^= temp ^ tmp; + + tmp = X_TIME[state->state[3][i] ^ tmp0]; + state->state[3][i] ^= temp ^ tmp; + } +} + +/********************************************************************* + Name: zero_tailing_bits + + Description: Fill tailing bits with zeros. + + Document Reference: - +*********************************************************************/ +void zero_tailing_bits(uint8 * data, uint32 length_bits) { + uint8 bits = (8 - (length_bits & 0x07)) & 0x07; + data[(length_bits + 7) / 8 - 1] &= (uint8) (0xFF << bits); +} + +/********************************************************************* + Name: s3g_mul_x + + Description: Multiplication with reduction. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.1.1 +*********************************************************************/ +uint8 s3g_mul_x(uint8 v, uint8 c) { + if (v & 0x80) + return ((v << 1) ^ c); + else + return (v << 1); +} + +/********************************************************************* + Name: s3g_mul_x_pow + + Description: Recursive multiplication with reduction. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.1.2 +*********************************************************************/ +uint8 s3g_mul_x_pow(uint8 v, uint8 i, uint8 c) { + if (i == 0) + return v; + else + return s3g_mul_x(s3g_mul_x_pow(v, i - 1, c), c); +} + +/********************************************************************* + Name: s3g_mul_alpha + + Description: Multiplication with alpha. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.2 +*********************************************************************/ +uint32 s3g_mul_alpha(uint8 c) { + return ((((uint32) s3g_mul_x_pow(c, 23, 0xa9)) << 24) | + (((uint32) s3g_mul_x_pow(c, 245, 0xa9)) << 16) | + (((uint32) s3g_mul_x_pow(c, 48, 0xa9)) << 8) | + (((uint32) s3g_mul_x_pow(c, 239, 0xa9)))); +} + +/********************************************************************* + Name: s3g_div_alpha + + Description: Division by alpha. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.3 +*********************************************************************/ +uint32 s3g_div_alpha(uint8 c) { + return ((((uint32) s3g_mul_x_pow(c, 16, 0xa9)) << 24) | + (((uint32) s3g_mul_x_pow(c, 39, 0xa9)) << 16) | + (((uint32) s3g_mul_x_pow(c, 6, 0xa9)) << 8) | + (((uint32) s3g_mul_x_pow(c, 64, 0xa9)))); +} + +/********************************************************************* + Name: s3g_s1 + + Description: S-Box S1. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.3.1 +*********************************************************************/ +uint32 s3g_s1(uint32 w) { + uint8 r0 = 0, r1 = 0, r2 = 0, r3 = 0; + uint8 srw0 = S[(uint8) ((w >> 24) & 0xff)]; + uint8 srw1 = S[(uint8) ((w >> 16) & 0xff)]; + uint8 srw2 = S[(uint8) ((w >> 8) & 0xff)]; + uint8 srw3 = S[(uint8) ((w) & 0xff)]; + + r0 = ((s3g_mul_x(srw0, 0x1b)) ^ + (srw1) ^ + (srw2) ^ + ((s3g_mul_x(srw3, 0x1b)) ^ srw3)); + + r1 = (((s3g_mul_x(srw0, 0x1b)) ^ srw0) ^ + (s3g_mul_x(srw1, 0x1b)) ^ + (srw2) ^ + (srw3)); + + r2 = ((srw0) ^ + ((s3g_mul_x(srw1, 0x1b)) ^ srw1) ^ + (s3g_mul_x(srw2, 0x1b)) ^ + (srw3)); + + r3 = ((srw0) ^ + (srw1) ^ + ((s3g_mul_x(srw2, 0x1b)) ^ srw2) ^ + (s3g_mul_x(srw3, 0x1b))); + + return ((((uint32) r0) << 24) | + (((uint32) r1) << 16) | + (((uint32) r2) << 8) | + (((uint32) r3))); +} + +/********************************************************************* + Name: s3g_s2 + + Description: S-Box S2. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.3.2 +*********************************************************************/ +uint32 s3g_s2(uint32 w) { + uint8 r0 = 0, r1 = 0, r2 = 0, r3 = 0; + uint8 sqw0 = SQ[(uint8) ((w >> 24) & 0xff)]; + uint8 sqw1 = SQ[(uint8) ((w >> 16) & 0xff)]; + uint8 sqw2 = SQ[(uint8) ((w >> 8) & 0xff)]; + uint8 sqw3 = SQ[(uint8) ((w) & 0xff)]; + + r0 = ((s3g_mul_x(sqw0, 0x69)) ^ + (sqw1) ^ + (sqw2) ^ + ((s3g_mul_x(sqw3, 0x69)) ^ sqw3)); + + r1 = (((s3g_mul_x(sqw0, 0x69)) ^ sqw0) ^ + (s3g_mul_x(sqw1, 0x69)) ^ + (sqw2) ^ + (sqw3)); + + r2 = ((sqw0) ^ + ((s3g_mul_x(sqw1, 0x69)) ^ sqw1) ^ + (s3g_mul_x(sqw2, 0x69)) ^ + (sqw3)); + + r3 = ((sqw0) ^ + (sqw1) ^ + ((s3g_mul_x(sqw2, 0x69)) ^ sqw2) ^ + (s3g_mul_x(sqw3, 0x69))); + + return ((((uint32) r0) << 24) | + (((uint32) r1) << 16) | + (((uint32) r2) << 8) | + (((uint32) r3))); +} + +/********************************************************************* + Name: s3g_clock_lfsr + + Description: Clocking LFSR. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.4 and Section 3.4.5 +*********************************************************************/ +void s3g_clock_lfsr(S3G_STATE * state, uint32 f) { + uint32 v = ( + ((state->lfsr[0] << 8) & 0xffffff00) ^ + (s3g_mul_alpha((uint8) ((state->lfsr[0] >> 24) & 0xff))) ^ + (state->lfsr[2]) ^ + ((state->lfsr[11] >> 8) & 0x00ffffff) ^ + (s3g_div_alpha((uint8) ((state->lfsr[11]) & 0xff))) ^ + (f) + ); + uint8 i; + + for (i = 0; i < 15; i++) { + state->lfsr[i] = state->lfsr[i + 1]; + } + state->lfsr[15] = v; +} + +/********************************************************************* + Name: s3g_clock_fsm + + Description: Clocking FSM. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 3.4.6 +*********************************************************************/ +uint32 s3g_clock_fsm(S3G_STATE * state) { + uint32 f = ((state->lfsr[15] + state->fsm[0]) & 0xffffffff) ^ + state->fsm[1]; + uint32 r = (state->fsm[1] + (state->fsm[2] ^ state->lfsr[5])) & + 0xffffffff; + + state->fsm[2] = s3g_s2(state->fsm[1]); + state->fsm[1] = s3g_s1(state->fsm[0]); + state->fsm[0] = r; + + return f; +} + +/********************************************************************* + Name: s3g_initialize + + Description: Initialization. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 4.1 +*********************************************************************/ +void s3g_initialize(S3G_STATE * state, uint32 k[4], uint32 iv[4]) { + uint8 i = 0; + uint32 f = 0x0; + + state->lfsr = (uint32 *) calloc(16, sizeof(uint32)); + state->fsm = (uint32 *) calloc( 3, sizeof(uint32)); + + state->lfsr[15] = k[3] ^ iv[0]; + state->lfsr[14] = k[2]; + state->lfsr[13] = k[1]; + state->lfsr[12] = k[0] ^ iv[1]; + + state->lfsr[11] = k[3] ^ 0xffffffff; + state->lfsr[10] = k[2] ^ 0xffffffff ^ iv[2]; + state->lfsr[ 9] = k[1] ^ 0xffffffff ^ iv[3]; + state->lfsr[ 8] = k[0] ^ 0xffffffff; + state->lfsr[ 7] = k[3]; + state->lfsr[ 6] = k[2]; + state->lfsr[ 5] = k[1]; + state->lfsr[ 4] = k[0]; + state->lfsr[ 3] = k[3] ^ 0xffffffff; + state->lfsr[ 2] = k[2] ^ 0xffffffff; + state->lfsr[ 1] = k[1] ^ 0xffffffff; + state->lfsr[ 0] = k[0] ^ 0xffffffff; + + state->fsm[0] = 0x0; + state->fsm[1] = 0x0; + state->fsm[2] = 0x0; + for (i = 0; i < 32; i++) { + f = s3g_clock_fsm(state); + s3g_clock_lfsr(state, f); + } +} + +/********************************************************************* + Name: s3g_deinitialize + + Description: Deinitialization. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 +*********************************************************************/ +void s3g_deinitialize(S3G_STATE * state) { + free(state->lfsr); + free(state->fsm); +} + +/********************************************************************* + Name: s3g_generate_keystream + + Description: Generation of Keystream. + + Document Reference: Specification of the 3GPP Confidentiality and + Integrity Algorithms UEA2 & UIA2 D2 v1.1 + Section 4.2 +*********************************************************************/ +void s3g_generate_keystream(S3G_STATE * state, uint32 n, uint32 *ks) { + uint32 t = 0; + uint32 f = 0x0; + + // Clock FSM once. Discard the output. + s3g_clock_fsm(state); + // Clock LFSR in keystream mode once. + s3g_clock_lfsr(state, 0x0); + + for (t = 0; t < n; t++) { + f = s3g_clock_fsm(state); + // Note that ks[t] corresponds to z_{t+1} in section 4.2 + ks[t] = f ^ state->lfsr[0]; + s3g_clock_lfsr(state, 0x0); + } +} diff --git a/lib/src/common/log_filter.cc b/lib/src/common/log_filter.cc new file mode 100644 index 0000000..9602e20 --- /dev/null +++ b/lib/src/common/log_filter.cc @@ -0,0 +1,300 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include + +#include "srslte/common/log_filter.h" + +namespace srslte{ + +log_filter::log_filter() +{ + do_tti = false; + time_src = NULL; + time_format = TIME; + logger_h = NULL; +} + +log_filter::log_filter(std::string layer) +{ + do_tti = false; + time_src = NULL; + time_format = TIME; + init(layer, &def_logger_stdout, tti); +} + +log_filter::log_filter(std::string layer, logger *logger_, bool tti) +{ + do_tti = false; + time_src = NULL; + time_format = TIME; + init(layer, logger_, tti); +} + +void log_filter::init(std::string layer, logger *logger_, bool tti) +{ + service_name = layer; + logger_h = logger_; + do_tti = tti; +} + +void log_filter::all_log(srslte::LOG_LEVEL_ENUM level, + uint32_t tti, + const char *msg) +{ + if(logger_h) { + std::stringstream ss; + + ss << now_time() << " "; + if (show_layer_en) { + ss << "[" <log(s_ptr); + } +} + +void log_filter::all_log(srslte::LOG_LEVEL_ENUM level, + uint32_t tti, + const char *msg, + const uint8_t *hex, + int size) +{ + if(logger_h) { + std::stringstream ss; + + ss << now_time() << " "; + if (show_layer_en) { + ss << "[" < 0 && hex && size > 0) { + ss << hex_string(hex, size); + } + str_ptr s_ptr(new std::string(ss.str())); + logger_h->log(s_ptr); + } +} + +void log_filter::console(const char * message, ...) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + printf("%s",args_msg); // Print directly to stdout + va_end(args); + free(args_msg); +} + +void log_filter::error(const char * message, ...) { + if (level >= LOG_LEVEL_ERROR) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_ERROR, tti, args_msg); + va_end(args); + free(args_msg); + } +} +void log_filter::warning(const char * message, ...) { + if (level >= LOG_LEVEL_WARNING) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_WARNING, tti, args_msg); + va_end(args); + free(args_msg); + } +} +void log_filter::info(const char * message, ...) { + if (level >= LOG_LEVEL_INFO) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_INFO, tti, args_msg); + va_end(args); + free(args_msg); + } +} +void log_filter::debug(const char * message, ...) { + if (level >= LOG_LEVEL_DEBUG) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_DEBUG, tti, args_msg); + va_end(args); + free(args_msg); + } +} + +void log_filter::error_hex(const uint8_t *hex, int size, const char * message, ...) { + if (level >= LOG_LEVEL_ERROR) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_ERROR, tti, args_msg, hex, size); + va_end(args); + free(args_msg); + } +} +void log_filter::warning_hex(const uint8_t *hex, int size, const char * message, ...) { + if (level >= LOG_LEVEL_WARNING) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_WARNING, tti, args_msg, hex, size); + va_end(args); + free(args_msg); + } +} +void log_filter::info_hex(const uint8_t *hex, int size, const char * message, ...) { + if (level >= LOG_LEVEL_INFO) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_INFO, tti, args_msg, hex, size); + va_end(args); + free(args_msg); + } +} +void log_filter::debug_hex(const uint8_t *hex, int size, const char * message, ...) { + if (level >= LOG_LEVEL_DEBUG) { + char *args_msg = NULL; + va_list args; + va_start(args, message); + if(vasprintf(&args_msg, message, args) > 0) + all_log(LOG_LEVEL_DEBUG, tti, args_msg, hex, size); + va_end(args); + free(args_msg); + } +} + +void log_filter::set_time_src(time_itf *source, time_format_t format) { + this->time_src = source; + this->time_format = format; +} + +std::string log_filter::now_time() +{ + struct timeval rawtime; + struct tm * timeinfo; + char buffer[64]; + char us[16]; + + srslte_timestamp_t now; + uint64_t usec_epoch; + + if (!time_src) { + gettimeofday(&rawtime, NULL); + timeinfo = localtime(&rawtime.tv_sec); + + if (time_format == TIME) { + strftime(buffer, 64, "%H:%M:%S", timeinfo); + strcat(buffer, "."); + snprintf(us, 16, "%06ld", rawtime.tv_usec); + strcat(buffer, us); + } else { + usec_epoch = rawtime.tv_sec * 1000000 + rawtime.tv_usec; + snprintf(buffer, 64, "%ld", usec_epoch); + } + } else { + now = time_src->get_time(); + + if (time_format == TIME) { + snprintf(buffer, 64, "%ld:%06u", now.full_secs, (uint32_t) (now.frac_secs * 1e6)); + } else { + usec_epoch = now.full_secs * 1000000 + (uint32_t) (now.frac_secs * 1e6); + snprintf(buffer, 64, "%ld", usec_epoch); + } + } + + return std::string(buffer); +} + +std::string log_filter::hex_string(const uint8_t *hex, int size) +{ + std::stringstream ss; + int c = 0; + + ss << std::hex << std::setfill('0'); + if(hex_limit >= 0) { + size = (size > hex_limit) ? hex_limit : size; + } + while(c < size) { + ss << " " << std::setw(4) << static_cast(c) << ": "; + int tmp = (size-c < 16) ? size-c : 16; + for(int i=0;i(hex[c++]) << " "; + } + ss << "\n"; + } + return ss.str(); +} + +} // namespace srsue diff --git a/lib/src/common/logger_file.cc b/lib/src/common/logger_file.cc new file mode 100644 index 0000000..d89dfac --- /dev/null +++ b/lib/src/common/logger_file.cc @@ -0,0 +1,128 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +#define LOG_BUFFER_SIZE 1024*32 + +#include "srslte/common/logger_file.h" + +using namespace std; + +namespace srslte{ + +logger_file::logger_file() + :logfile(NULL) + ,is_running(false) + ,cur_length(0) + ,max_length(0) +{} + +logger_file::~logger_file() { + if(is_running) { + log(new std::string("Closing log\n")); + pthread_mutex_lock(&mutex); + is_running = false; + pthread_cond_signal(¬_empty); // wakeup thread and let it terminate + pthread_mutex_unlock(&mutex); + wait_thread_finish(); + flush(); + if (logfile) { + fclose(logfile); + } + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(¬_empty); + } +} + +void logger_file::init(std::string file, int max_length_) { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(¬_empty, NULL); + max_length = (int64_t)max_length_*1024; + name_idx = 0; + filename = file; + logfile = fopen(filename.c_str(), "w"); + if(logfile == NULL) { + printf("Error: could not create log file, no messages will be logged!\n"); + } + is_running = true; + start(-2); +} + +void logger_file::log(const char *msg) { + log(new std::string(msg)); +} + +void logger_file::log(str_ptr msg) { + pthread_mutex_lock(&mutex); + buffer.push_back(msg); + pthread_cond_signal(¬_empty); + pthread_mutex_unlock(&mutex); +} + +void logger_file::run_thread() { + while(is_running) { + pthread_mutex_lock(&mutex); + while(buffer.empty()) { + pthread_cond_wait(¬_empty, &mutex); + if(!is_running) return; // Thread done. Messages in buffer will be handled in flush. + } + str_ptr s = buffer.front(); + int n = 0; + if(logfile) + n = fprintf(logfile, "%s", s->c_str()); + delete s; + buffer.pop_front(); + pthread_mutex_unlock(&mutex); + if (n > 0) { + cur_length += (int64_t) n; + if (cur_length >= max_length && max_length > 0) { + fclose(logfile); + name_idx++; + char numstr[21]; // enough to hold all numbers up to 64-bits + sprintf(numstr, ".%d", name_idx); + string newfilename = filename + numstr ; + logfile = fopen(newfilename.c_str(), "w"); + if(logfile==NULL) { + printf("Error: could not create log file, no messages will be logged!\n"); + } + cur_length = 0; + } + } + } +} + +void logger_file::flush() { + std::deque::iterator it; + for(it=buffer.begin();it!=buffer.end();it++) + { + str_ptr s = *it; + if(logfile) + fprintf(logfile, "%s", s->c_str()); + delete s; + } +} + +} // namespace srsue diff --git a/lib/src/common/mac_pcap.cc b/lib/src/common/mac_pcap.cc new file mode 100644 index 0000000..1e378b9 --- /dev/null +++ b/lib/src/common/mac_pcap.cc @@ -0,0 +1,107 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/srslte.h" +#include "srslte/common/pcap.h" +#include "srslte/common/mac_pcap.h" + + + +namespace srslte { + +void mac_pcap::enable(bool en) +{ + enable_write = true; +} +void mac_pcap::open(const char* filename, uint32_t ue_id) +{ + pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, filename); + this->ue_id = ue_id; + enable_write = true; +} +void mac_pcap::close() +{ + fprintf(stdout, "Saving MAC PCAP file\n"); + LTE_PCAP_Close(pcap_file); +} + +void mac_pcap::set_ue_id(uint16_t ue_id) { + this->ue_id = ue_id; +} + +void mac_pcap::pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes, uint32_t reTX, bool crc_ok, uint32_t tti, + uint16_t crnti, uint8_t direction, uint8_t rnti_type) +{ + if (enable_write) { + MAC_Context_Info_t context = + { + FDD_RADIO, direction, rnti_type, + crnti, /* RNTI */ + (uint16_t)ue_id, /* UEId */ + (uint8_t)reTX, /* Retx */ + crc_ok, /* CRC Stsatus (i.e. OK) */ + (uint16_t)(tti/10), /* Sysframe number */ + (uint16_t)(tti%10) /* Subframe number */ + }; + if (pdu) { + LTE_PCAP_MAC_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +void mac_pcap::write_dl_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t rnti, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, rnti, DIRECTION_DOWNLINK, C_RNTI); +} +void mac_pcap::write_dl_ranti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t rnti, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, rnti, DIRECTION_DOWNLINK, RA_RNTI); +} +void mac_pcap::write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t rnti, uint32_t reTX, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, reTX, true, tti, rnti, DIRECTION_UPLINK, C_RNTI); +} +void mac_pcap::write_dl_bch(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, 0, DIRECTION_DOWNLINK, NO_RNTI); +} +void mac_pcap::write_dl_pch(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_PRNTI, DIRECTION_DOWNLINK, P_RNTI); +} +void mac_pcap::write_dl_mch(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_MRNTI, DIRECTION_DOWNLINK, M_RNTI); +} +void mac_pcap::write_dl_sirnti(uint8_t* pdu, uint32_t pdu_len_bytes, bool crc_ok, uint32_t tti) +{ + pack_and_write(pdu, pdu_len_bytes, 0, crc_ok, tti, SRSLTE_SIRNTI, DIRECTION_DOWNLINK, SI_RNTI); +} + + +} diff --git a/lib/src/common/nas_pcap.cc b/lib/src/common/nas_pcap.cc new file mode 100644 index 0000000..94e2cd9 --- /dev/null +++ b/lib/src/common/nas_pcap.cc @@ -0,0 +1,35 @@ +#include +#include "srslte/srslte.h" +#include "srslte/common/pcap.h" +#include "srslte/common/nas_pcap.h" + + +namespace srslte { + +void nas_pcap::enable() +{ + enable_write = true; +} +void nas_pcap::open(const char* filename, uint32_t ue_id) +{ + pcap_file = LTE_PCAP_Open(NAS_LTE_DLT, filename); + ue_id = ue_id; + enable_write = true; +} +void nas_pcap::close() +{ + fprintf(stdout, "Saving NAS PCAP file\n"); + LTE_PCAP_Close(pcap_file); +} + +void nas_pcap::write_nas(uint8_t *pdu, uint32_t pdu_len_bytes) +{ + if (enable_write) { + NAS_Context_Info_t context; + if (pdu) { + LTE_PCAP_NAS_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +} diff --git a/lib/src/common/pdu.cc b/lib/src/common/pdu.cc new file mode 100644 index 0000000..d36b67f --- /dev/null +++ b/lib/src/common/pdu.cc @@ -0,0 +1,947 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include + +#include "srslte/common/pdu.h" +#include "srslte/srslte.h" + +// Table 6.1.3.1-1 Buffer size levels for BSR +static uint32_t btable[64] = { + 0, 1, 10, 12, 14, 17, 19, 22, 26, 31, 36, 42, 49, 57, 67, 78, 91, 107, 125, 146, 171, 200, 234, 274, 321, 376, 440, 515, 603, 706, 826, 967, 1132, + 1326, 1552, 1817, 2127, 2490, 2915, 3413, 3995, 4667, 5476, 6411, 7505, 8787, 10287, 12043, 14099, 16507, 19325, 22624, 26487, 31009, 36304, + 42502, 49759, 58255, 68201, 79846, 93479, 109439, 128125, 150000}; + + + +namespace srslte { + +void sch_pdu::fprint(FILE* stream) +{ + fprintf(stream, "MAC SDU for UL/DL-SCH. "); + pdu::fprint(stream); +} + +void sch_pdu::parse_packet(uint8_t *ptr) +{ + pdu::parse_packet(ptr); + + // Correct size for last SDU + if (nof_subheaders > 0) { + uint32_t read_len = 0; + for (int i=0;i= 0) { + subheaders[nof_subheaders-1].set_payload_size(n_sub); + } else { + fprintf(stderr,"Reading MAC PDU: negative payload for last subheader\n"); + } + } +} + +uint8_t* sch_pdu::write_packet() { + return write_packet(NULL); +} + +/* Writes the MAC PDU in the packet, including the MAC headers and CE payload. Section 6.1.2 */ +uint8_t* sch_pdu::write_packet(srslte::log *log_h) +{ + int init_rem_len=rem_len; + sch_subh padding; + padding.set_padding(); + + if (nof_subheaders <= 0 && nof_subheaders < (int)max_subheaders) { + log_h->error("Trying to write packet with invalid number of subheaders (nof_subheaders=%d).\n", nof_subheaders); + return NULL; + } + + if (init_rem_len < 0) { + log_h->error("init_rem_len=%d\n", init_rem_len); + return NULL; + } + + /* If last SDU has zero payload, remove it. FIXME: Why happens this?? */ + if (subheaders[nof_subheaders-1].get_payload_size() == 0) { + del_subh(); + } + + /* Determine if we are transmitting CEs only. */ + bool ce_only = last_sdu_idx<0?true:false; + + /* Determine if we will need multi-byte padding or 1/2 bytes padding */ + bool multibyte_padding = false; + uint32_t onetwo_padding = 0; + if (rem_len > 2) { + multibyte_padding = true; + // Add 1 header for padding + rem_len--; + // Add the header for the last SDU + if (!ce_only) { + rem_len -= (subheaders[last_sdu_idx].get_header_size(false)-1); // Becuase we were assuming it was the one + } + } else if (rem_len > 0) { + onetwo_padding = rem_len; + rem_len = 0; + } + + + /* Determine the header size and CE payload size */ + uint32_t header_sz = 0; + uint32_t ce_payload_sz = 0; + for (int i=0;i= sdu_offset_start) { + fprintf(stderr, "Writing PDU: header sz + ce_payload_sz >= sdu_offset_start (%d>=%d). pdu_len=%d, total_sdu_len=%d\n", + header_sz + ce_payload_sz, sdu_offset_start, pdu_len, total_sdu_len); + return NULL; + } + + /* Start writing header and CE payload before the start of the SDU payload */ + uint8_t *ptr = &buffer_tx[sdu_offset_start-header_sz-ce_payload_sz]; + uint8_t *pdu_start_ptr = ptr; + + // Add single/two byte padding first + for (uint32_t i=0;i 0) { + bzero(&pdu_start_ptr[pdu_len-rem_len], rem_len*sizeof(uint8_t)); + } + + /* Sanity check and print if error */ + if (log_h) { + log_h->debug("Wrote PDU: pdu_len=%d, header_and_ce=%d (%d+%d), nof_subh=%d, last_sdu=%d, sdu_len=%d, onepad=%d, multi=%d\n", + pdu_len, header_sz+ce_payload_sz, header_sz, ce_payload_sz, + nof_subheaders, last_sdu_idx, total_sdu_len, onetwo_padding, rem_len); + } else { + printf("Wrote PDU: pdu_len=%d, header_and_ce=%d (%d+%d), nof_subh=%d, last_sdu=%d, sdu_len=%d, onepad=%d, multi=%d, init_rem_len=%d\n", + pdu_len, header_sz+ce_payload_sz, header_sz, ce_payload_sz, + nof_subheaders, last_sdu_idx, total_sdu_len, onetwo_padding, rem_len, init_rem_len); + } + + if (rem_len + header_sz + ce_payload_sz + total_sdu_len != pdu_len) { + printf("\n------------------------------\n"); + for (int i=0;ierror("Wrote PDU: pdu_len=%d, header_and_ce=%d (%d+%d), nof_subh=%d, last_sdu=%d, sdu_len=%d, onepad=%d, multi=%d, init_rem_len=%d\n", + pdu_len, header_sz+ce_payload_sz, header_sz, ce_payload_sz, + nof_subheaders, last_sdu_idx, total_sdu_len, onetwo_padding, rem_len, init_rem_len); + + } + + return NULL; + } + + if ((int)(header_sz + ce_payload_sz) != (int) (ptr - pdu_start_ptr)) { + fprintf(stderr, "Expected a header and CE payload of %d bytes but wrote %d\n", + header_sz+ce_payload_sz,(int) (ptr - pdu_start_ptr)); + return NULL; + } + + return pdu_start_ptr; +} + +int sch_pdu::rem_size() { + return rem_len; +} + +int sch_pdu::get_pdu_len() +{ + return pdu_len; +} + +uint32_t sch_pdu::size_header_sdu(uint32_t nbytes) +{ + if (nbytes < 128) { + return 2; + } else { + return 3; + } +} + +bool sch_pdu::has_space_ce(uint32_t nbytes, bool var_len) +{ + uint32_t head_len = var_len ? size_header_sdu(nbytes) : 1; + if (rem_len >= nbytes + head_len) { + return true; + } else { + return false; + } +} + +bool sch_pdu::update_space_ce(uint32_t nbytes, bool var_len) +{ + uint32_t head_len = var_len ? size_header_sdu(nbytes) : 1; + if (has_space_ce(nbytes)) { + rem_len -= nbytes + head_len; + return true; + } else { + return false; + } +} + +bool sch_pdu::has_space_sdu(uint32_t nbytes) +{ + int s = get_sdu_space(); + + if (s < 0) { + return false; + } else { + return (uint32_t)s >= nbytes; + } +} + +bool sch_pdu::update_space_sdu(uint32_t nbytes) +{ + int init_rem = rem_len; + if (has_space_sdu(nbytes)) { + if (last_sdu_idx < 0) { + rem_len -= (nbytes+1); + } else { + rem_len -= (nbytes+1 + (size_header_sdu(subheaders[last_sdu_idx].get_payload_size())-1)); + } + last_sdu_idx = cur_idx; + return true; + } else { + return false; + } +} + +int sch_pdu::get_sdu_space() +{ + int ret; + if (last_sdu_idx < 0) { + ret = rem_len - 1; + } else { + ret = rem_len - (size_header_sdu(subheaders[last_sdu_idx].get_payload_size())-1) - 1; + } + return ret; +} + +void sch_subh::init() +{ + lcid = 0; + nof_bytes = 0; + payload = NULL; + nof_mch_sched_ce = 0; + cur_mch_sched_ce = 0; +} + +sch_subh::cetype sch_subh::ce_type() +{ + if (lcid >= PHR_REPORT && type == SCH_SUBH_TYPE) { + return (cetype)lcid; + } + if(lcid >= MCH_SCHED_INFO && type == MCH_SUBH_TYPE) { + return (cetype)lcid; + } + return (cetype)SDU; +} + +void sch_subh::set_payload_size(uint32_t size) { + nof_bytes = size; +} + +uint32_t sch_subh::size_plus_header() { + if (is_sdu() || is_var_len_ce()) { + return sch_pdu::size_header_sdu(nof_bytes) + nof_bytes; + } + // All others are 1-byte headers + return 1 + nof_bytes; +} + +uint32_t sch_subh::sizeof_ce(uint32_t lcid, bool is_ul) +{ + if (type == SCH_SUBH_TYPE) { + if (is_ul) { + switch(lcid) { + case PHR_REPORT: + return 1; + case CRNTI: + return 2; + case TRUNC_BSR: + return 1; + case SHORT_BSR: + return 1; + case LONG_BSR: + return 3; + case PADDING: + return 0; + } + } else { + switch(lcid) { + case CON_RES_ID: + return 6; + case TA_CMD: + return 1; + case DRX_CMD: + return 0; + case PADDING: + return 0; + } + } + } + if (type == MCH_SUBH_TYPE) { + switch (lcid) { + case MCH_SCHED_INFO: + return nof_mch_sched_ce*2; + case PADDING: + return 0; + } + } + return 0; +} + +bool sch_subh::is_sdu() +{ + return ce_type() == SDU; +} + +bool sch_subh::is_var_len_ce() +{ + return (MCH_SCHED_INFO == ce_type()) && (MCH_SUBH_TYPE == type); +} + +uint16_t sch_subh::get_c_rnti() +{ + if (payload) { + return (uint16_t) payload[0]<<8 | payload[1]; + } else { + return (uint16_t) w_payload_ce[0]<<8 | w_payload_ce[1]; + } +} + +uint64_t sch_subh::get_con_res_id() +{ + if (payload) { + return ((uint64_t) payload[5]) | (((uint64_t) payload[4])<<8) | (((uint64_t) payload[3])<<16) | (((uint64_t) payload[2])<<24) | + (((uint64_t) payload[1])<<32) | (((uint64_t) payload[0])<<40); + } else { + return ((uint64_t) w_payload_ce[5]) | (((uint64_t) w_payload_ce[4])<<8) | (((uint64_t) w_payload_ce[3])<<16) | (((uint64_t) w_payload_ce[2])<<24) | + (((uint64_t) w_payload_ce[1])<<32) | (((uint64_t) w_payload_ce[0])<<40); + return 0; + } +} + +float sch_subh::get_phr() +{ + if (payload) { + return (float) (payload[0]&0x3f) - 23; + } else { + return (float) (w_payload_ce[0]&0x3f) - 23; + } +} + +int sch_subh::get_bsr(uint32_t buff_size[4]) +{ + if (payload) { + uint32_t nonzero_lcg = 0; + if (ce_type()==LONG_BSR) { + buff_size[0] = (payload[0]&0xFC) >> 2; + buff_size[1] = (payload[0]&0x03) << 4 | (payload[1]&0xF0) >> 4; + buff_size[2] = (payload[1]&0x0F) << 4 | (payload[1]&0xC0) >> 6; + buff_size[3] = (payload[2]&0x3F); + } else { + nonzero_lcg = (payload[0]&0xc0) >> 6; + buff_size[nonzero_lcg%4] = payload[0]&0x3f; + } + for (int i=0;i<4;i++) { + if (buff_size[i]) { + if (buff_size[i]<63) { + buff_size[i] = btable[1+buff_size[i]]; + } else { + buff_size[i] = btable[63]; + } + } + } + return nonzero_lcg; + } else { + return -1; + } +} + +bool sch_subh::get_next_mch_sched_info(uint8_t *lcid_, uint16_t *mtch_stop) +{ + if(payload) { + nof_mch_sched_ce = nof_bytes/2; + if(cur_mch_sched_ce < nof_mch_sched_ce) { + *lcid_ = (payload[cur_mch_sched_ce*2]&0xF8) >> 3; + *mtch_stop = ((uint16_t)(payload[cur_mch_sched_ce*2]&0x07)) << 8; + *mtch_stop += payload[cur_mch_sched_ce*2+1]; + cur_mch_sched_ce++; + return true; + } + } + return false; +} + +uint8_t sch_subh::get_ta_cmd() +{ + if (payload) { + return (uint8_t) payload[0]&0x3f; + } else { + return 0; + } +} + +uint32_t sch_subh::get_sdu_lcid() +{ + return lcid; +} + + +uint32_t sch_subh::get_payload_size() + +{ + return nof_bytes; +} + +uint32_t sch_subh::get_header_size(bool is_last) { + if (!is_last) { + if (is_sdu()) { + return sch_pdu::size_header_sdu(nof_bytes); + } + if (lcid == MCH_SCHED_INFO && type == MCH_SUBH_TYPE) { + return sch_pdu::size_header_sdu(nof_bytes); + } + return 1; // All others are 1-byte + } else { + return 1; // Last subheader (CE or SDU) has always 1 byte header + } +} + +uint8_t* sch_subh::get_sdu_ptr() +{ + return payload; +} + +void sch_subh::set_padding(uint32_t padding_len) +{ + lcid = PADDING; + nof_bytes = padding_len; +} + +void sch_subh::set_padding() +{ + set_padding(0); +} + +bool sch_subh::set_bsr(uint32_t buff_size[4], sch_subh::cetype format) +{ + uint32_t nonzero_lcg=0; + for (int i=0;i<4;i++) { + if (buff_size[i]) { + nonzero_lcg=i; + } + } + uint32_t ce_size = format==LONG_BSR?3:1; + if (((sch_pdu*)parent)->has_space_ce(ce_size)) { + if (format==LONG_BSR) { + w_payload_ce[0] = (buff_size_table(buff_size[0])&0x3f) << 2 | (buff_size_table(buff_size[1])&0xc0)>>6; + w_payload_ce[1] = (buff_size_table(buff_size[1])&0xf) << 4 | (buff_size_table(buff_size[2])&0xf0)>>4; + w_payload_ce[2] = (buff_size_table(buff_size[2])&0x3) << 6 | (buff_size_table(buff_size[3])&0x3f); + } else { + w_payload_ce[0] = (nonzero_lcg&0x3)<<6 | (buff_size_table(buff_size[nonzero_lcg])&0x3f); + } + lcid = format; + ((sch_pdu*)parent)->update_space_ce(ce_size); + nof_bytes = ce_size; + return true; + } else { + return false; + } +} + +bool sch_subh::set_c_rnti(uint16_t crnti) +{ + if (((sch_pdu*)parent)->has_space_ce(2)) { + w_payload_ce[0] = (uint8_t) ((crnti&0xff00)>>8); + w_payload_ce[1] = (uint8_t) ((crnti&0x00ff)); + lcid = CRNTI; + ((sch_pdu*)parent)->update_space_ce(2); + nof_bytes = 2; + return true; + } else { + return false; + } +} +bool sch_subh::set_con_res_id(uint64_t con_res_id) +{ + if (((sch_pdu*)parent)->has_space_ce(6)) { + w_payload_ce[0] = (uint8_t) ((con_res_id&0xff0000000000)>>40); + w_payload_ce[1] = (uint8_t) ((con_res_id&0x00ff00000000)>>32); + w_payload_ce[2] = (uint8_t) ((con_res_id&0x0000ff000000)>>24); + w_payload_ce[3] = (uint8_t) ((con_res_id&0x000000ff0000)>>16); + w_payload_ce[4] = (uint8_t) ((con_res_id&0x00000000ff00)>>8); + w_payload_ce[5] = (uint8_t) ((con_res_id&0x0000000000ff)); + lcid = CON_RES_ID; + ((sch_pdu*)parent)->update_space_ce(6); + nof_bytes = 6; + return true; + } else { + return false; + } +} +bool sch_subh::set_phr(float phr) +{ + if (((sch_pdu*)parent)->has_space_ce(1)) { + w_payload_ce[0] = phr_report_table(phr)&0x3f; + lcid = PHR_REPORT; + ((sch_pdu*)parent)->update_space_ce(1); + nof_bytes = 1; + return true; + } else { + return false; + } +} + +bool sch_subh::set_ta_cmd(uint8_t ta_cmd) +{ + if (((sch_pdu*)parent)->has_space_ce(1)) { + w_payload_ce[0] = ta_cmd&0x3f; + lcid = TA_CMD; + ((sch_pdu*)parent)->update_space_ce(1); + nof_bytes = 1; + return true; + } else { + return false; + } +} + +bool sch_subh::set_next_mch_sched_info(uint8_t lcid_, uint16_t mtch_stop) +{ + if (((sch_pdu*)parent)->has_space_ce(2, true)) { + w_payload_ce[nof_mch_sched_ce*2] = (lcid_&0x1F) << 3 | (uint8_t) ((mtch_stop&0x0700)>>8); + w_payload_ce[nof_mch_sched_ce*2+1] = (uint8_t) (mtch_stop&0xff); + nof_mch_sched_ce++; + lcid = MCH_SCHED_INFO; + ((sch_pdu*)parent)->update_space_ce(2, true); + nof_bytes += 2; + return true; + } + return false; +} + +int sch_subh::set_sdu(uint32_t lcid_, uint32_t requested_bytes, read_pdu_interface *sdu_itf) +{ + if (((sch_pdu*)parent)->has_space_sdu(requested_bytes)) { + lcid = lcid_; + + payload = ((sch_pdu*)parent)->get_current_sdu_ptr(); + // Copy data and get final number of bytes written to the MAC PDU + int sdu_sz = sdu_itf->read_pdu(lcid, payload, requested_bytes); + + if (sdu_sz < 0) { + return -1; + } + if (sdu_sz == 0) { + return 0; + } + else { + // Save final number of written bytes + nof_bytes = sdu_sz; + + if(nof_bytes > (int32_t)requested_bytes) { + return -1; + } + } + + ((sch_pdu*)parent)->add_sdu(nof_bytes); + ((sch_pdu*)parent)->update_space_sdu(nof_bytes); + return nof_bytes; + } else { + return -1; + } +} + +int sch_subh::set_sdu(uint32_t lcid_, uint32_t nof_bytes_, uint8_t *payload) +{ + if (((sch_pdu*)parent)->has_space_sdu(nof_bytes_)) { + lcid = lcid_; + + memcpy(((sch_pdu*)parent)->get_current_sdu_ptr(), payload, nof_bytes_); + + ((sch_pdu*)parent)->add_sdu(nof_bytes_); + ((sch_pdu*)parent)->update_space_sdu(nof_bytes_); + nof_bytes = nof_bytes_; + + return (int) nof_bytes; + } else { + return -1; + } +} + + +// Section 6.2.1 +void sch_subh::write_subheader(uint8_t** ptr, bool is_last) +{ + *(*ptr) = (uint8_t) (is_last?0:(1<<5)) | ((uint8_t) lcid & 0x1f); + *ptr += 1; + if (is_sdu() || is_var_len_ce()) { + // MAC SDU: R/R/E/LCID/F/L subheader + // 2nd and 3rd octet + if (!is_last) { + if (nof_bytes >= 128) { + *(*ptr) = (uint8_t) 1<<7 | ((nof_bytes & 0x7f00) >> 8); + *ptr += 1; + *(*ptr) = (uint8_t) (nof_bytes & 0xff); + *ptr += 1; + } else { + *(*ptr) = (uint8_t) (nof_bytes & 0x7f); + *ptr += 1; + } + } + } +} + +void sch_subh::write_payload(uint8_t** ptr) +{ + if (is_sdu()) { + // SDU is written directly during subheader creation + } else { + nof_bytes = sizeof_ce(lcid, parent->is_ul()); + memcpy(*ptr, w_payload_ce, nof_bytes*sizeof(uint8_t)); + } + *ptr += nof_bytes; +} + +bool sch_subh::read_subheader(uint8_t** ptr) +{ + // Skip R + bool e_bit = (bool) (*(*ptr) & 0x20)?true:false; + lcid = (uint8_t) *(*ptr) & 0x1f; + *ptr += 1; + if (is_sdu() || is_var_len_ce()) { + if (e_bit) { + F_bit = (bool) (*(*ptr) & 0x80)?true:false; + nof_bytes = (uint32_t)*(*ptr) & 0x7f; + *ptr += 1; + if (F_bit) { + nof_bytes = nof_bytes<<8 | ((uint32_t) *(*ptr) & 0xff); + *ptr += 1; + } + } else { + nof_bytes = 0; + F_bit = 0; + } + } else { + nof_bytes = sizeof_ce(lcid, parent->is_ul()); + } + return e_bit; +} + +void sch_subh::read_payload(uint8_t** ptr) +{ + payload = *ptr; + *ptr += nof_bytes; +} + +void sch_subh::fprint(FILE* stream) +{ + if (is_sdu()) { + fprintf(stream, "SDU LCHID=%d, SDU nof_bytes=%d\n", lcid, nof_bytes); + } else if (type == SCH_SUBH_TYPE) { + if (parent->is_ul()) { + switch(lcid) { + case CRNTI: + fprintf(stream, "C-RNTI CE\n"); + break; + case PHR_REPORT: + fprintf(stream, "PHR\n"); + break; + case TRUNC_BSR: + fprintf(stream, "Truncated BSR CE\n"); + break; + case SHORT_BSR: + fprintf(stream, "Short BSR CE\n"); + break; + case LONG_BSR: + fprintf(stream, "Long BSR CE\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } else { + switch(lcid) { + case CON_RES_ID: + fprintf(stream, "Contention Resolution ID CE: 0x%lx\n", get_con_res_id()); + break; + case TA_CMD: + fprintf(stream, "Time Advance Command CE: %d\n", get_ta_cmd()); + break; + case DRX_CMD: + fprintf(stream, "DRX Command CE: Not implemented\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } + } else if (type == MCH_SUBH_TYPE) { + switch(lcid) { + case MCH_SCHED_INFO: + fprintf(stream, "MCH Scheduling Info CE\n"); + break; + case PADDING: + fprintf(stream, "PADDING\n"); + } + } +} + +uint8_t sch_subh::buff_size_table(uint32_t buffer_size) { + if (buffer_size == 0) { + return 0; + } else if (buffer_size > 150000) { + return 63; + } else { + for (int i=0;i<61;i++) { + if (buffer_size < btable[i+2]) { + return 1+i; + } + } + return 62; + } +} + +// Implements Table 9.1.8.4-1 Power headroom report mapping (36.133) +uint8_t sch_subh::phr_report_table(float phr_value) +{ + if (phr_value < -23) { + phr_value = -23; + } + if (phr_value > 40) { + phr_value = 40; + } + return (uint8_t) floor(phr_value+23); +} + +void rar_pdu::fprint(FILE* stream) +{ + fprintf(stream, "MAC PDU for RAR. "); + if (has_backoff_indicator) { + fprintf(stream, "Backoff Indicator %d. ", backoff_indicator); + } + pdu::fprint(stream); +} + +rar_pdu::rar_pdu(uint32_t max_rars_) : pdu(max_rars_) +{ + backoff_indicator = 0; + has_backoff_indicator = false; +} + +uint8_t rar_pdu::get_backoff() +{ + return backoff_indicator; +} + +bool rar_pdu::has_backoff() +{ + return has_backoff_indicator; +} + +void rar_pdu::set_backoff(uint8_t bi) +{ + has_backoff_indicator = true; + backoff_indicator = bi; +} + +// Section 6.1.5 +bool rar_pdu::write_packet(uint8_t* ptr) +{ + // Write Backoff Indicator, if any + if (has_backoff_indicator) { + *(ptr) = backoff_indicator&0xf; + if (nof_subheaders > 0) { + *(ptr) = 1<<7; + } + ptr++; + } + + // Write RAR subheaders + for (int i=0;i>4); + *(*ptr + 1) = (uint8_t) ((ta&0xf) <<4) | (grant[0]<<3) | (grant[1]<<2) | (grant[2]<<1) | grant[3]; + uint8_t *x = &grant[4]; + *(*ptr + 2) = (uint8_t) srslte_bit_pack(&x, 8); + *(*ptr + 3) = (uint8_t) srslte_bit_pack(&x, 8); + *(*ptr + 4) = (uint8_t) ((temp_rnti&0xff00) >> 8); + *(*ptr + 5) = (uint8_t) (temp_rnti&0x00ff); + *ptr += 6; +} + +void rar_subh::read_payload(uint8_t** ptr) +{ + ta = ((uint32_t) *(*ptr + 0)&0x7f)<<4 | (*(*ptr + 1)&0xf0)>>4; + grant[0] = *(*ptr + 1)&0x8?1:0; + grant[1] = *(*ptr + 1)&0x4?1:0; + grant[2] = *(*ptr + 1)&0x2?1:0; + grant[3] = *(*ptr + 1)&0x1?1:0; + uint8_t *x = &grant[4]; + srslte_bit_unpack(*(*ptr+2), &x, 8); + srslte_bit_unpack(*(*ptr+3), &x, 8); + temp_rnti = ((uint16_t) *(*ptr + 4))<<8 | *(*ptr + 5); + *ptr += 6; +} + +bool rar_subh::read_subheader(uint8_t** ptr) +{ + bool e_bit = *(*ptr) & 0x80?true:false; + bool type = *(*ptr) & 0x40?true:false; + if (type) { + preamble = *(*ptr) & 0x3f; + } else { + ((rar_pdu*)parent)->set_backoff(*(*ptr) & 0xf); + } + *ptr += 1; + return e_bit; +} + +} diff --git a/lib/src/common/pdu_queue.cc b/lib/src/common/pdu_queue.cc new file mode 100644 index 0000000..4a45f07 --- /dev/null +++ b/lib/src/common/pdu_queue.cc @@ -0,0 +1,111 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srslte/common/pdu_queue.h" + + +namespace srslte { + + +void pdu_queue::init(process_callback *callback_, log* log_h_) +{ + callback = callback_; + log_h = log_h_; +} + +uint8_t* pdu_queue::request(uint32_t len) +{ + if (len > MAX_PDU_LEN) { + fprintf(stderr, "Error request buffer of invalid size %d. Max bytes %d\n", len, MAX_PDU_LEN); + return NULL; + } + pdu_t *pdu = pool.allocate("pdu_queue::request"); + if (!pdu) { + if (log_h) { + log_h->error("Not enough buffers for MAC PDU\n"); + } + fprintf(stderr, "Not enough buffers for MAC PDU\n"); + } + if ((void*) pdu->ptr != (void*) pdu) { + fprintf(stderr, "Fatal error in memory alignment in struct pdu_queue::pdu_t\n"); + exit(-1); + } + + return pdu->ptr; +} + +void pdu_queue::deallocate(uint8_t* pdu) +{ + if (!pool.deallocate((pdu_t*) pdu)) { + log_h->warning("Error deallocating from buffer pool in deallocate(): buffer not created in this pool.\n"); + } +} + +/* Demultiplexing of logical channels and dissassemble of MAC CE + * This function enqueues the packet and returns quicly because ACK + * deadline is important here. + */ +void pdu_queue::push(uint8_t *ptr, uint32_t len, channel_t channel, uint32_t tstamp) +{ + if (ptr) { + pdu_t *pdu = (pdu_t*) ptr; + pdu->len = len; + pdu->tstamp = tstamp; + pdu->channel = channel; + pdu_q.push(pdu); + } else { + log_h->warning("Error pushing pdu: ptr is empty\n"); + } +} + +bool pdu_queue::process_pdus() +{ + bool have_data = false; + uint32_t cnt = 0; + pdu_t *pdu; + while(pdu_q.try_pop(&pdu)) { + if (callback) { + callback->process_pdu(pdu->ptr, pdu->len, pdu->channel, pdu->tstamp); + } + cnt++; + have_data = true; + } + if (cnt > 20) { + if (log_h) { + log_h->warning("PDU queue dispatched %d packets\n", cnt); + } + printf("Warning PDU queue dispatched %d packets\n", cnt); + } + return have_data; +} + +} diff --git a/lib/src/common/rlc_pcap.cc b/lib/src/common/rlc_pcap.cc new file mode 100644 index 0000000..e6bcbe6 --- /dev/null +++ b/lib/src/common/rlc_pcap.cc @@ -0,0 +1,90 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/srslte.h" +#include "srslte/common/pcap.h" +#include "srslte/common/rlc_pcap.h" + +namespace srslte { + +void rlc_pcap::enable(bool en) +{ + enable_write = true; +} +void rlc_pcap::open(const char* filename, uint32_t ue_id) +{ + fprintf(stdout, "Opening RLC PCAP with DLT=%d\n", RLC_LTE_DLT); + pcap_file = LTE_PCAP_Open(RLC_LTE_DLT, filename); + this->ue_id = ue_id; + enable_write = true; +} +void rlc_pcap::close() +{ + fprintf(stdout, "Saving RLC PCAP file\n"); + LTE_PCAP_Close(pcap_file); +} + +void rlc_pcap::set_ue_id(uint16_t ue_id) { + this->ue_id = ue_id; +} + +void rlc_pcap::pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes, uint8_t mode, uint8_t direction, uint8_t priority, uint8_t seqnumberlength, uint16_t ueid, uint16_t channel_type, uint16_t channel_id) +{ + if (enable_write) { + RLC_Context_Info_t context; + context.rlcMode = mode; + context.direction = direction; + context.priority = priority; + context.sequenceNumberLength = seqnumberlength; + context.ueid = ueid; + context.channelType = channel_type; + context.channelId = channel_id; + context.pduLength = pdu_len_bytes; + if (pdu) { + LTE_PCAP_RLC_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +void rlc_pcap::write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + uint8_t priority = 0; + uint8_t seqnumberlength = 0; // normal length of 10bit + uint8_t channel_id = 0; + pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_DOWNLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id); +} + +void rlc_pcap::write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + uint8_t priority = 0; + uint8_t seqnumberlength = 0; // normal length of 10bit + uint8_t channel_id = 0; + pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_UPLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id); +} + +} diff --git a/lib/src/common/security.cc b/lib/src/common/security.cc new file mode 100644 index 0000000..65ef5e0 --- /dev/null +++ b/lib/src/common/security.cc @@ -0,0 +1,300 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/common/security.h" +#include "srslte/common/liblte_security.h" +#include "srslte/common/snow_3g.h" + +#ifdef HAVE_MBEDTLS +#include "mbedtls/md5.h" +#endif +#ifdef HAVE_POLARSSL +#include "polarssl/md5.h" +#endif + +namespace srslte { + +/****************************************************************************** + * Key Generation + *****************************************************************************/ + +uint8_t security_generate_k_asme( uint8_t *ck, + uint8_t *ik, + uint8_t *ak, + uint8_t *sqn, + uint16_t mcc, + uint16_t mnc, + uint8_t *k_asme) +{ + return liblte_security_generate_k_asme(ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); +} + +uint8_t security_generate_k_enb( uint8_t *k_asme, + uint32_t nas_count, + uint8_t *k_enb) +{ + return liblte_security_generate_k_enb(k_asme, + nas_count, + k_enb); +} + +uint8_t security_generate_k_enb_star( uint8_t *k_enb, + uint32_t pci, + uint32_t earfcn, + uint8_t *k_enb_star) +{ + return liblte_security_generate_k_enb_star(k_enb, + pci, + earfcn, + k_enb_star); +} + +uint8_t security_generate_nh( uint8_t *k_asme, + uint8_t *sync, + uint8_t *nh) +{ + return liblte_security_generate_nh( k_asme, + sync, + nh); +} + +uint8_t security_generate_k_nas( uint8_t *k_asme, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_nas_enc, + uint8_t *k_nas_int) +{ + return liblte_security_generate_k_nas( k_asme, + (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, + (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, + k_nas_enc, + k_nas_int); +} + +uint8_t security_generate_k_rrc( uint8_t *k_enb, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int) +{ + return liblte_security_generate_k_rrc(k_enb, + (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, + (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, + k_rrc_enc, + k_rrc_int); +} + +uint8_t security_generate_k_up( uint8_t *k_enb, + CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t *k_up_enc, + uint8_t *k_up_int) +{ + return liblte_security_generate_k_up(k_enb, + (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, + (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, + k_up_enc, + k_up_int); +} + +/****************************************************************************** + * Integrity Protection + *****************************************************************************/ + +uint8_t security_128_eia1( uint8_t *key, + uint32_t count, + uint32_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac) +{ + uint32_t msg_len_bits; + uint32_t i; + uint8_t *m_ptr; + + msg_len_bits = msg_len*8; + m_ptr = snow3g_f9(key, + count, + bearer, + direction, + msg, + msg_len_bits); + for(i=0; i<4; i++) { + mac[i] = m_ptr[i]; + } + return ERROR_NONE; +} + +uint8_t security_128_eia2( uint8_t *key, + uint32_t count, + uint32_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac) +{ + return liblte_security_128_eia2(key, + count, + bearer, + direction, + msg, + msg_len, + mac); +} + +uint8_t security_md5(const uint8_t *input, size_t len, uint8_t *output) +{ + memset(output, 0x00, 16); +#ifdef HAVE_MBEDTLS + mbedtls_md5(input, len, output); +#endif // HAVE_MBEDTLS +#ifdef HAVE_POLARSSL + md5(input, len, output); +#endif + return SRSLTE_SUCCESS; +} + + +/****************************************************************************** + * Encryption / Decryption + *****************************************************************************/ + +uint8_t security_128_eea1(uint8_t *key, + uint32_t count, + uint8_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *msg_out){ + + return liblte_security_encryption_eea1(key, + count, + bearer, + direction, + msg, + msg_len * 8, + msg_out); + +} + + +uint8_t security_128_eea2(uint8_t *key, + uint32_t count, + uint8_t bearer, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *msg_out){ + + return liblte_security_encryption_eea2(key, + count, + bearer, + direction, + msg, + msg_len * 8, + msg_out); +} + +/****************************************************************************** + * Authentication + *****************************************************************************/ +uint8_t compute_opc( uint8_t *k, + uint8_t *op, + uint8_t *opc) +{ + return liblte_compute_opc(k, + op, + opc); +} + +uint8_t security_milenage_f1( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *sqn, + uint8_t *amf, + uint8_t *mac_a) +{ + return liblte_security_milenage_f1(k, + op, + rand, + sqn, + amf, + mac_a); +} + +uint8_t security_milenage_f1_star( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *sqn, + uint8_t *amf, + uint8_t *mac_s) +{ + return liblte_security_milenage_f1_star(k, + op, + rand, + sqn, + amf, + mac_s); +} + +uint8_t security_milenage_f2345( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *res, + uint8_t *ck, + uint8_t *ik, + uint8_t *ak) +{ + return liblte_security_milenage_f2345(k, + op, + rand, + res, + ck, + ik, + ak); +} + +uint8_t security_milenage_f5_star( uint8_t *k, + uint8_t *op, + uint8_t *rand, + uint8_t *ak) +{ + return liblte_security_milenage_f5_star(k, + op, + rand, + ak); +} + + +} // namespace srsue diff --git a/lib/src/common/snow_3g.cc b/lib/src/common/snow_3g.cc new file mode 100644 index 0000000..3c56586 --- /dev/null +++ b/lib/src/common/snow_3g.cc @@ -0,0 +1,577 @@ +/*------------------------------------------------------------------------ +* snow_3g.c +* +* Adapted from ETSI/SAGE specifications: +* "Specification of the 3GPP Confidentiality and +* Integrity Algorithms UEA2 & UIA2. +* Document 1: UEA2 and UIA2 Specification" +* "Specification of the 3GPP Confidentiality +* and Integrity Algorithms UEA2 & UIA2. +* Document 2: SNOW 3G Specification" +*------------------------------------------------------------------------*/ + +#include "srslte/common/snow_3g.h" + +/* LFSR */ + +u32 LFSR_S0 = 0x00; +u32 LFSR_S1 = 0x00; +u32 LFSR_S2 = 0x00; +u32 LFSR_S3 = 0x00; +u32 LFSR_S4 = 0x00; +u32 LFSR_S5 = 0x00; +u32 LFSR_S6 = 0x00; +u32 LFSR_S7 = 0x00; +u32 LFSR_S8 = 0x00; +u32 LFSR_S9 = 0x00; +u32 LFSR_S10 = 0x00; +u32 LFSR_S11 = 0x00; +u32 LFSR_S12 = 0x00; +u32 LFSR_S13 = 0x00; +u32 LFSR_S14 = 0x00; +u32 LFSR_S15 = 0x00; + +/* FSM */ + +u32 FSM_R1 = 0x00; +u32 FSM_R2 = 0x00; +u32 FSM_R3 = 0x00; + +/* Rijndael S-box SR */ + +u8 SR[256] = { +0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, +0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, +0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, +0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, +0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, +0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, +0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, +0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, +0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, +0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, +0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, +0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, +0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, +0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, +0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, +0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16 +}; + +/* S-box SQ */ + +u8 SQ[256] = { +0x25,0x24,0x73,0x67,0xD7,0xAE,0x5C,0x30,0xA4,0xEE,0x6E,0xCB,0x7D,0xB5,0x82,0xDB, +0xE4,0x8E,0x48,0x49,0x4F,0x5D,0x6A,0x78,0x70,0x88,0xE8,0x5F,0x5E,0x84,0x65,0xE2, +0xD8,0xE9,0xCC,0xED,0x40,0x2F,0x11,0x28,0x57,0xD2,0xAC,0xE3,0x4A,0x15,0x1B,0xB9, +0xB2,0x80,0x85,0xA6,0x2E,0x02,0x47,0x29,0x07,0x4B,0x0E,0xC1,0x51,0xAA,0x89,0xD4, +0xCA,0x01,0x46,0xB3,0xEF,0xDD,0x44,0x7B,0xC2,0x7F,0xBE,0xC3,0x9F,0x20,0x4C,0x64, +0x83,0xA2,0x68,0x42,0x13,0xB4,0x41,0xCD,0xBA,0xC6,0xBB,0x6D,0x4D,0x71,0x21,0xF4, +0x8D,0xB0,0xE5,0x93,0xFE,0x8F,0xE6,0xCF,0x43,0x45,0x31,0x22,0x37,0x36,0x96,0xFA, +0xBC,0x0F,0x08,0x52,0x1D,0x55,0x1A,0xC5,0x4E,0x23,0x69,0x7A,0x92,0xFF,0x5B,0x5A, +0xEB,0x9A,0x1C,0xA9,0xD1,0x7E,0x0D,0xFC,0x50,0x8A,0xB6,0x62,0xF5,0x0A,0xF8,0xDC, +0x03,0x3C,0x0C,0x39,0xF1,0xB8,0xF3,0x3D,0xF2,0xD5,0x97,0x66,0x81,0x32,0xA0,0x00, +0x06,0xCE,0xF6,0xEA,0xB7,0x17,0xF7,0x8C,0x79,0xD6,0xA7,0xBF,0x8B,0x3F,0x1F,0x53, +0x63,0x75,0x35,0x2C,0x60,0xFD,0x27,0xD3,0x94,0xA5,0x7C,0xA1,0x05,0x58,0x2D,0xBD, +0xD9,0xC7,0xAF,0x6B,0x54,0x0B,0xE0,0x38,0x04,0xC8,0x9D,0xE7,0x14,0xB1,0x87,0x9C, +0xDF,0x6F,0xF9,0xDA,0x2A,0xC4,0x59,0x16,0x74,0x91,0xAB,0x26,0x61,0x76,0x34,0x2B, +0xAD,0x99,0xFB,0x72,0xEC,0x33,0x12,0xDE,0x98,0x3B,0xC0,0x9B,0x3E,0x18,0x10,0x3A, +0x56,0xE1,0x77,0xC9,0x1E,0x9E,0x95,0xA3,0x90,0x19,0xA8,0x6C,0x09,0xD0,0xF0,0x86 +}; + +/* MULx. +* Input V: an 8-bit input. +* Input c: an 8-bit input. +* Output : an 8-bit output. +* See section 3.1.1 for details. +*/ + +u8 MULx(u8 V, u8 c) +{ + if ( V & 0x80 ) + return ( (V << 1) ^ c); + else + return ( V << 1); +} + +/* MULxPOW. +* Input V: an 8-bit input. +* Input i: a positive integer. +* Input c: an 8-bit input. +* Output : an 8-bit output. +* See section 3.1.2 for details. +*/ + +u8 MULxPOW(u8 V, u8 i, u8 c) +{ + if ( i == 0) + return V; + else + return MULx( MULxPOW( V, i-1, c ), c); +} + +/* The function MUL alpha. +* Input c: 8-bit input. +* Output : 32-bit output. +* See section 3.4.2 for details. +*/ + +u32 MULalpha(u8 c) +{ + return ( ( ((u32)MULxPOW(c, 23, 0xa9)) << 24 ) | + ( ((u32)MULxPOW(c, 245, 0xa9)) << 16 ) | + ( ((u32)MULxPOW(c, 48, 0xa9)) << 8 ) | + ( ((u32)MULxPOW(c, 239, 0xa9)) ) ) ; +} + +/* The function DIV alpha. +* Input c: 8-bit input. +* Output : 32-bit output. +* See section 3.4.3 for details. +*/ + +u32 DIValpha(u8 c) +{ + return ( ( ((u32)MULxPOW(c, 16, 0xa9)) << 24 ) | + ( ((u32)MULxPOW(c, 39, 0xa9)) << 16 ) | + ( ((u32)MULxPOW(c, 6, 0xa9)) << 8 ) | + ( ((u32)MULxPOW(c, 64, 0xa9)) ) ) ; +} + +/* The 32x32-bit S-Box S1 +* Input: a 32-bit input. +* Output: a 32-bit output of S1 box. +* See section 3.3.1. +*/ + +u32 S1(u32 w) +{ + u8 r0=0, r1=0, r2=0, r3=0; + u8 srw0 = SR[ (u8)((w >> 24) & 0xff) ]; + u8 srw1 = SR[ (u8)((w >> 16) & 0xff) ]; + u8 srw2 = SR[ (u8)((w >> 8) & 0xff) ]; + u8 srw3 = SR[ (u8)((w) & 0xff) ]; + r0 = ( ( MULx( srw0 , 0x1b) ) ^ + ( srw1 ) ^ + ( srw2 ) ^ + ( (MULx( srw3, 0x1b)) ^ srw3 ) + ); + r1 = ( ( ( MULx( srw0 , 0x1b) ) ^ srw0 ) ^ + ( MULx(srw1, 0x1b) ) ^ + ( srw2 ) ^ + ( srw3 ) + ); + r2 = ( ( srw0 ) ^ + ( ( MULx( srw1 , 0x1b) ) ^ srw1 ) ^ + ( MULx(srw2, 0x1b) ) ^ + ( srw3 ) + ); + r3 = ( ( srw0 ) ^ + ( srw1 ) ^ + ( ( MULx( srw2 , 0x1b) ) ^ srw2 ) ^ + ( MULx( srw3, 0x1b) ) + ); + + return ( ( ((u32)r0) << 24 ) | ( ((u32)r1) << 16 ) | ( ((u32)r2) << 8 ) | + ( ((u32)r3) ) ); +} + +/* The 32x32-bit S-Box S2 +* Input: a 32-bit input. +* Output: a 32-bit output of S2 box. +* See section 3.3.2. +*/ + +u32 S2(u32 w) +{ + u8 r0=0, r1=0, r2=0, r3=0; + u8 sqw0 = SQ[ (u8)((w >> 24) & 0xff) ]; + u8 sqw1 = SQ[ (u8)((w >> 16) & 0xff) ]; + u8 sqw2 = SQ[ (u8)((w >> 8) & 0xff) ]; + u8 sqw3 = SQ[ (u8)((w) & 0xff) ]; + r0 = ( ( MULx( sqw0 , 0x69) ) ^ + ( sqw1 ) ^ + ( sqw2 ) ^ + ( (MULx( sqw3, 0x69)) ^ sqw3 ) + ); + r1 = ( ( ( MULx( sqw0 , 0x69) ) ^ sqw0 ) ^ + ( MULx(sqw1, 0x69) ) ^ + ( sqw2 ) ^ + ( sqw3 ) + ); + r2 = ( ( sqw0 ) ^ + ( ( MULx( sqw1 , 0x69) ) ^ sqw1 ) ^ + ( MULx(sqw2, 0x69) ) ^ + ( sqw3 ) + ); + r3 = ( ( sqw0 ) ^ + ( sqw1 ) ^ + ( ( MULx( sqw2 , 0x69) ) ^ sqw2 ) ^ + ( MULx( sqw3, 0x69) ) + ); + return ( ( ((u32)r0) << 24 ) | ( ((u32)r1) << 16 ) | ( ((u32)r2) << 8 ) | + ( ((u32)r3) ) ); +} + +/* Clocking LFSR in initialization mode. +* LFSR Registers S0 to S15 are updated as the LFSR receives a single clock. +* Input F: a 32-bit word comes from output of FSM. +* See section 3.4.4. +*/ + +void ClockLFSRInitializationMode(u32 F) +{ + u32 v = ( ( (LFSR_S0 << 8) & 0xffffff00 ) ^ + ( MULalpha( (u8)((LFSR_S0>>24) & 0xff) ) ) ^ + ( LFSR_S2 ) ^ + ( (LFSR_S11 >> 8) & 0x00ffffff ) ^ + ( DIValpha( (u8)( ( LFSR_S11) & 0xff ) ) ) ^ + ( F ) + ); + LFSR_S0 = LFSR_S1; + LFSR_S1 = LFSR_S2; + LFSR_S2 = LFSR_S3; + LFSR_S3 = LFSR_S4; + LFSR_S4 = LFSR_S5; + LFSR_S5 = LFSR_S6; + LFSR_S6 = LFSR_S7; + LFSR_S7 = LFSR_S8; + LFSR_S8 = LFSR_S9; + LFSR_S9 = LFSR_S10; + LFSR_S10 = LFSR_S11; + LFSR_S11 = LFSR_S12; + LFSR_S12 = LFSR_S13; + LFSR_S13 = LFSR_S14; + LFSR_S14 = LFSR_S15; + LFSR_S15 = v; +} + +/* Clocking LFSR in keystream mode. +* LFSR Registers S0 to S15 are updated as the LFSR receives a single clock. +* See section 3.4.5. +*/ + +void ClockLFSRKeyStreamMode() +{ + u32 v = ( ( (LFSR_S0 << 8) & 0xffffff00 ) ^ + ( MULalpha( (u8)((LFSR_S0>>24) & 0xff) ) ) ^ + ( LFSR_S2 ) ^ + ( (LFSR_S11 >> 8) & 0x00ffffff ) ^ + ( DIValpha( (u8)( ( LFSR_S11) & 0xff ) ) ) + ); + LFSR_S0 = LFSR_S1; + LFSR_S1 = LFSR_S2; + LFSR_S2 = LFSR_S3; + LFSR_S3 = LFSR_S4; + LFSR_S4 = LFSR_S5; + LFSR_S5 = LFSR_S6; + LFSR_S6 = LFSR_S7; + LFSR_S7 = LFSR_S8; + LFSR_S8 = LFSR_S9; + LFSR_S9 = LFSR_S10; + LFSR_S10 = LFSR_S11; + LFSR_S11 = LFSR_S12; + LFSR_S12 = LFSR_S13; + LFSR_S13 = LFSR_S14; + LFSR_S14 = LFSR_S15; + LFSR_S15 = v; +} + +/* Clocking FSM. +* Produces a 32-bit word F. +* Updates FSM registers R1, R2, R3. +* See Section 3.4.6. +*/ + +u32 ClockFSM() +{ + u32 F = ( ( LFSR_S15 + FSM_R1 ) & 0xffffffff ) ^ FSM_R2 ; + u32 r = ( FSM_R2 + ( FSM_R3 ^ LFSR_S5 ) ) & 0xffffffff ; + FSM_R3 = S2(FSM_R2); + FSM_R2 = S1(FSM_R1); + FSM_R1 = r; + return F; +} + +/* Initialization. +* Input k[4]: Four 32-bit words making up 128-bit key. +* Input IV[4]: Four 32-bit words making 128-bit initialization variable. +* Output: All the LFSRs and FSM are initialized for key generation. +* See Section 4.1. +*/ + +void snow3g_initialize(u32 k[4], u32 IV[4]) +{ + u8 i=0; + u32 F = 0x0; + LFSR_S15 = k[3] ^ IV[0]; + LFSR_S14 = k[2]; + LFSR_S13 = k[1]; + LFSR_S12 = k[0] ^ IV[1]; + LFSR_S11 = k[3] ^ 0xffffffff; + LFSR_S10 = k[2] ^ 0xffffffff ^ IV[2]; + LFSR_S9 = k[1] ^ 0xffffffff ^ IV[3]; + LFSR_S8 = k[0] ^ 0xffffffff; + LFSR_S7 = k[3]; + LFSR_S6 = k[2]; + LFSR_S5 = k[1]; + LFSR_S4 = k[0]; + LFSR_S3 = k[3] ^ 0xffffffff; + LFSR_S2 = k[2] ^ 0xffffffff; + LFSR_S1 = k[1] ^ 0xffffffff; + LFSR_S0 = k[0] ^ 0xffffffff; + FSM_R1 = 0x0; + FSM_R2 = 0x0; + FSM_R3 = 0x0; + for(i=0;i<32;i++) + { + F = ClockFSM(); + ClockLFSRInitializationMode(F); + } +} + +/* Generation of Keystream. +* input n: number of 32-bit words of keystream. +* input z: space for the generated keystream, assumes +* memory is allocated already. +* output: generated keystream which is filled in z +* See section 4.2. +*/ + +void snow3g_generate_keystream(u32 n, u32 *ks) +{ + u32 t = 0; + u32 F = 0x0; + ClockFSM(); /* Clock FSM once. Discard the output. */ + ClockLFSRKeyStreamMode(); /* Clock LFSR in keystream mode once. */ + for ( t=0; t> 24) & 0xff; + data[4*i+1] ^= (u8) (KS[i] >> 16) & 0xff; + data[4*i+2] ^= (u8) (KS[i] >> 8) & 0xff; + data[4*i+3] ^= (u8) (KS[i] ) & 0xff; + } + + free(KS); + + /* zero last bits of data in case its length is not byte-aligned + this is an addition to the C reference code, which did not handle it */ + if (lastbits) + data[length/8] &= 256 - (1<>i ) & 0x1 ) + result ^= MUL64xPOW(V,i,c); + } + return result; +} + +/* mask8bit. + * Input n: an integer in 1-7. + * Output : an 8 bit mask. + * Prepares an 8 bit mask with required number of 1 bits on the MSB side. + */ +u8 mask8bit(int n) +{ + return 0xFF ^ ((1<<(8-n)) - 1); +} + +/* f9. + * Input key: 128 bit Integrity Key. + * Input count:32-bit Count, Frame dependent input. + * Input fresh: 32-bit Random number. + * Input dir:1 bit, direction of transmission (in the LSB). + * Input data: length number of bits, input bit stream. + * Input length: 64 bit Length, i.e., the number of bits to be MAC'd. + * Output : 32 bit block used as MAC + * Generates 32-bit MAC using UIA2 algorithm as defined in Section 4. + */ +u8* snow3g_f9( u8* key, u32 count, u32 fresh, u32 dir, u8 *data, u64 length) +{ + u32 K[4],IV[4], z[5]; + u32 i=0, D; + static u8 MAC_I[4] = {0,0,0,0}; /* static memory for the result */ + u64 EVAL; + u64 V; + u64 P; + u64 Q; + u64 c; + + u64 M_D_2; + int rem_bits = 0; + + /* Load the Integrity Key for SNOW3G initialization as in section 4.4. */ + for (i=0; i<4; i++) + K[3-i] = (key[4*i] << 24) ^ (key[4*i+1] << 16) ^ + (key[4*i+2] << 8) ^ (key[4*i+3]); + + /* Prepare the Initialization Vector (IV) for SNOW3G initialization as + in section 4.4. */ + IV[3] = count; + IV[2] = fresh; + IV[1] = count ^ ( dir << 31 ) ; + IV[0] = fresh ^ (dir << 15); + + z[0] = z[1] = z[2] = z[3] = z[4] = 0; + + /* Run SNOW 3G to produce 5 keystream words z_1, z_2, z_3, z_4 and z_5. */ + snow3g_initialize(K, IV); + snow3g_generate_keystream(5, z); + + P = (u64)z[0] << 32 | (u64)z[1]; + Q = (u64)z[2] << 32 | (u64)z[3]; + + /* Calculation */ + if ((length % 64) == 0) + D = (length>>6) + 1; + else + D = (length>>6) + 2; + EVAL = 0; + c = 0x1b; + + /* for 0 <= i <= D-3 */ + for (i=0; i 7) + { + M_D_2 |= (u64)data[8*(D-2)+i] << (8*(7-i)); + rem_bits -= 8; + i++; + } + if (rem_bits > 0) + M_D_2 |= (u64)(data[8*(D-2)+i] & mask8bit(rem_bits)) << (8*(7-i)); + + V = EVAL ^ M_D_2; + EVAL = MUL64(V,P,c); + + /* for D-1 */ + EVAL ^= length; + + /* Multiply by Q */ + EVAL = MUL64(EVAL,Q,c); + + /* XOR with z_5: this is a modification to the reference C code, + which forgot to XOR z[5] */ + for (i=0; i<4; i++) + /* + MAC_I[i] = (mac32 >> (8*(3-i))) & 0xff; + */ + MAC_I[i] = ((EVAL >> (56-(i*8))) ^ (z[4] >> (24-(i*8)))) & 0xff; + + return MAC_I; +} diff --git a/lib/src/common/task_dispatcher.cc b/lib/src/common/task_dispatcher.cc new file mode 100644 index 0000000..df27a02 --- /dev/null +++ b/lib/src/common/task_dispatcher.cc @@ -0,0 +1,75 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/common/task_dispatcher.h" +#include + +namespace srslte { + +task_dispatcher::task_dispatcher(uint32_t max_pending_tasks) +{ + pthread_cond_init(&cvar, NULL); + pthread_mutex_init(&mutex, NULL); +} + +task_dispatcher::~task_dispatcher() +{ + running = false; + pthread_cond_signal(&cvar); + wait_thread_finish(); + pthread_cond_destroy(&cvar); + pthread_mutex_destroy(&mutex); +} + +void task_dispatcher::push_task(uint32_t task_code) +{ + pthread_mutex_lock(&mutex); + pending_tasks.push(task_code); + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); +} + +void task_dispatcher::run_thread() +{ + running = true; + while(running) { + uint32_t task = 0; + pthread_mutex_lock(&mutex); + while(pending_tasks.empty()) { + pthread_cond_wait(&cvar, &mutex); + } + task = (uint32_t) pending_tasks.front(); + pending_tasks.pop(); + pthread_mutex_unlock(&mutex); + if (running) { + run_task(task); + } + } +} + + +} \ No newline at end of file diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc new file mode 100644 index 0000000..17b5392 --- /dev/null +++ b/lib/src/common/thread_pool.cc @@ -0,0 +1,288 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/common/thread_pool.h" + +#define DEBUG 0 +#define debug_thread(fmt, ...) do { if(DEBUG) printf(fmt, __VA_ARGS__); } while(0) + +#define USE_QUEUE + +namespace srslte { + + +void thread_pool::worker::setup(uint32_t id, thread_pool *parent, uint32_t prio, uint32_t mask) +{ + my_id = id; + my_parent = parent; + if(mask == 255) + { + start(prio); + } + else + { + start_cpu_mask(prio,mask); + } + +} + +void thread_pool::worker::run_thread() +{ + running = true; + while(running) { + wait_to_start(); + if (running) { + work_imp(); + finished(); + } + } +} + +uint32_t thread_pool::worker::get_id() +{ + return my_id; +} + +void thread_pool::worker::stop() +{ + running = false; + pthread_cond_signal(&my_parent->cvar[my_id]); + wait_thread_finish(); +} + +thread_pool::thread_pool(uint32_t max_workers_) : + workers(max_workers_), + status(max_workers_), + cvar(max_workers_), + mutex(max_workers_) +{ + max_workers = max_workers_; + for (uint32_t i=0;i= nof_workers) { + nof_workers = id+1; + } + pthread_mutex_lock(&mutex_queue); + workers[id] = obj; + available_workers.push(obj); + obj->setup(id, this, prio, mask); + pthread_cond_signal(&cvar_queue); + pthread_mutex_unlock(&mutex_queue); + } +} + +void thread_pool::stop() +{ + /* Stop any thread waiting for available worker */ + running = false; + + /* Now stop all workers */ + for (uint32_t i=0;istop(); + // Need to call start to wake it up + start_worker(i); + workers[i]->wait_thread_finish(); + } + pthread_cond_destroy(&cvar[i]); + pthread_mutex_destroy(&mutex[i]); + } + pthread_cond_destroy(&cvar_queue); + pthread_mutex_destroy(&mutex_queue); +} + + +void thread_pool::worker::release() +{ + finished(); +} + +void thread_pool::worker::wait_to_start() +{ + + debug_thread("wait_to_start() id=%d, status=%d, enter\n", my_id, my_parent->status[my_id]); + + pthread_mutex_lock(&my_parent->mutex[my_id]); + while(my_parent->status[my_id] != START_WORK && running) { + pthread_cond_wait(&my_parent->cvar[my_id], &my_parent->mutex[my_id]); + } + my_parent->status[my_id] = WORKING; + pthread_mutex_unlock(&my_parent->mutex[my_id]); + + debug_thread("wait_to_start() id=%d, status=%d, exit\n", my_id, my_parent->status[my_id]); +} + +void thread_pool::worker::finished() +{ +#ifdef USE_QUEUE + pthread_mutex_lock(&my_parent->mutex[my_id]); + my_parent->status[my_id] = IDLE; + pthread_mutex_unlock(&my_parent->mutex[my_id]); + + pthread_mutex_lock(&my_parent->mutex_queue); + pthread_cond_signal(&my_parent->cvar_queue); + pthread_mutex_unlock(&my_parent->mutex_queue); +#else + pthread_mutex_lock(&my_parent->mutex[my_id]); + my_parent->status[my_id] = IDLE; + pthread_cond_signal(&my_parent->cvar[my_id]); + pthread_mutex_unlock(&my_parent->mutex[my_id]); +#endif +} + + +thread_pool::worker* thread_pool::wait_worker() +{ + return wait_worker(0); +} + +bool thread_pool::find_finished_worker(uint32_t tti, uint32_t *id) { + for(uint32_t i=0;i +#include +#include +#include +#include +#include + +#include "srslte/common/threads.h" + +bool threads_new_rt(pthread_t *thread, void *(*start_routine) (void*), void *arg) { + return threads_new_rt_prio(thread, start_routine, arg, -1); +} + +bool threads_new_rt_prio(pthread_t *thread, void *(*start_routine) (void*), void *arg, int prio_offset) { + return threads_new_rt_cpu(thread, start_routine, arg, -1, prio_offset); +} + +bool threads_new_rt_mask(pthread_t *thread, void *(*start_routine) (void*), void *arg,int mask, int prio_offset){ +return threads_new_rt_cpu(thread, start_routine, arg, mask*100, prio_offset);// we multiply mask by 100 to distinguish it from a single cpu core id +} + +bool threads_new_rt_cpu(pthread_t *thread, void *(*start_routine) (void*), void *arg, int cpu, int prio_offset) { + bool ret = false; + + pthread_attr_t attr; + struct sched_param param; + cpu_set_t cpuset; + bool attr_enable = false; + + if (prio_offset >= 0) { + param.sched_priority = sched_get_priority_max(SCHED_FIFO) - prio_offset; + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + perror("pthread_attr_setinheritsched"); + } + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + perror("pthread_attr_setschedpolicy"); + } + if (pthread_attr_setschedparam(&attr, ¶m)) { + perror("pthread_attr_setschedparam"); + fprintf(stderr, "Error not enough privileges to set Scheduling priority\n"); + } + attr_enable = true; + } else if (prio_offset == -1) { + param.sched_priority = sched_get_priority_max(SCHED_FIFO) - DEFAULT_PRIORITY; + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + perror("pthread_attr_setinheritsched"); + } + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + perror("pthread_attr_setschedpolicy"); + } + if (pthread_attr_setschedparam(&attr, ¶m)) { + perror("pthread_attr_setschedparam"); + fprintf(stderr, "Error not enough privileges to set Scheduling priority\n"); + } + attr_enable = true; + } else if (prio_offset == -2) { + param.sched_priority = 0; + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + perror("pthread_attr_setinheritsched"); + } + if (pthread_attr_setschedpolicy(&attr, SCHED_OTHER)) { + perror("pthread_attr_setschedpolicy"); + } + if (pthread_attr_setschedparam(&attr, ¶m)) { + perror("pthread_attr_setschedparam"); + fprintf(stderr, "Error not enough privileges to set Scheduling priority\n"); + } + attr_enable = true; + } + if(cpu > 0) { + if(cpu > 50) { + int mask; + mask = cpu/100; + + CPU_ZERO(&cpuset); + for(int i = 0; i < 8;i++){ + if(((mask >> i) & 0x01) == 1){ + CPU_SET((size_t) i , &cpuset); + } + } + } else { + CPU_ZERO(&cpuset); + CPU_SET((size_t) cpu, &cpuset); + } + + if(pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset)) { + perror("pthread_attr_setaffinity_np"); + } + } + + int err = pthread_create(thread, attr_enable ? &attr : NULL, start_routine, arg); + if (err) { + if (EPERM == err) { + perror("Warning: Failed to create thread with real-time priority. Creating it with normal priority"); + err = pthread_create(thread, NULL, start_routine, arg); + if (err) { + perror("pthread_create"); + } else { + ret = true; + } + } else { + perror("pthread_create"); + } + } else { + ret = true; + } + if (attr_enable) { + pthread_attr_destroy(&attr); + } + return ret; +} + +void threads_print_self() { + pthread_t thread; + cpu_set_t cpuset; + struct sched_param param; + int policy; + const char *p; + int s,j; + + thread = pthread_self(); + + s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset); + if (s != 0) { + printf("error pthread_getaffinity_np: %s\n",strerror(s)); + } + + printf("Set returned by pthread_getaffinity_np() contained:\n"); + for (j = 0; j < CPU_SETSIZE; j++) { + if (CPU_ISSET(j, &cpuset)) { + printf(" CPU %d\n", j); + } + } + + s = pthread_getschedparam(thread, &policy, ¶m); + if (s != 0) { + printf("error pthread_getaffinity_np: %s\n", strerror(s)); + } + + switch(policy) { + case SCHED_FIFO: + p = "SCHED_FIFO"; + break; + case SCHED_RR: + p = "SCHED_RR"; + break; + default: + p = "Other"; + break; + } + + printf("Sched policy is %s. Priority is %d\n",p,param.sched_priority); +} diff --git a/lib/src/common/tti_sync_cv.cc b/lib/src/common/tti_sync_cv.cc new file mode 100644 index 0000000..c48f077 --- /dev/null +++ b/lib/src/common/tti_sync_cv.cc @@ -0,0 +1,85 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/common/tti_sync_cv.h" + + +namespace srslte { + + tti_sync_cv::tti_sync_cv(uint32_t modulus): tti_sync(modulus) + { + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cond, NULL); + } + + tti_sync_cv::~tti_sync_cv() + { + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); + } + + uint32_t tti_sync_cv::wait() + { + pthread_mutex_lock(&mutex); + while(wait_condition()) { + pthread_cond_wait(&cond, &mutex); + } + uint32_t x = consumer_cntr; + increase_consumer(); + pthread_mutex_unlock(&mutex); + return x; + } + + void tti_sync_cv::resync() + { + consumer_cntr = producer_cntr; + } + + void tti_sync_cv::set_producer_cntr(uint32_t producer_cntr) + { + pthread_mutex_lock(&mutex); + init_counters(producer_cntr); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + } + + void tti_sync_cv::increase() + { + pthread_mutex_lock(&mutex); + increase_producer(); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + } + void tti_sync_cv::increase(uint32_t tti) + { + pthread_mutex_lock(&mutex); + increase_producer(tti); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + } +} diff --git a/lib/src/common/version.c b/lib/src/common/version.c new file mode 100644 index 0000000..2cf1d4b --- /dev/null +++ b/lib/src/common/version.c @@ -0,0 +1,45 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/version.h" + +char* srslte_get_version() { + return SRSLTE_VERSION_STRING; +} + +int srslte_get_version_major() { + return SRSLTE_VERSION_MAJOR; +} +int srslte_get_version_minor() { + return SRSLTE_VERSION_MINOR; +} +int srslte_get_version_patch() { + return SRSLTE_VERSION_PATCH; +} + +int srslte_check_version(int major, int minor, int patch) { + return (SRSLTE_VERSION >= SRSLTE_VERSION_ENCODE(major,minor,patch)); +} diff --git a/lib/src/phy/CMakeLists.txt b/lib/src/phy/CMakeLists.txt new file mode 100644 index 0000000..b52af7b --- /dev/null +++ b/lib/src/phy/CMakeLists.txt @@ -0,0 +1,61 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(agc) +add_subdirectory(ch_estimation) +add_subdirectory(common) +add_subdirectory(fec) +add_subdirectory(mimo) +add_subdirectory(phch) +add_subdirectory(rf) +add_subdirectory(sync) +add_subdirectory(utils) +add_subdirectory(channel) +add_subdirectory(dft) +add_subdirectory(io) +add_subdirectory(modem) +add_subdirectory(resampling) +add_subdirectory(scrambling) +add_subdirectory(ue) +add_subdirectory(enb) + +set(srslte_srcs $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + +add_library(srslte_phy STATIC ${srslte_srcs}) +target_link_libraries(srslte_phy ${FFT_LIBRARIES}) + +target_link_libraries(srslte_phy pthread m) +install(TARGETS srslte_phy DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/phy/agc/CMakeLists.txt b/lib/src/phy/agc/CMakeLists.txt new file mode 100644 index 0000000..fed3467 --- /dev/null +++ b/lib/src/phy/agc/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_agc OBJECT ${SOURCES}) diff --git a/lib/src/phy/agc/agc.c b/lib/src/phy/agc/agc.c new file mode 100644 index 0000000..56277eb --- /dev/null +++ b/lib/src/phy/agc/agc.c @@ -0,0 +1,197 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/utils/debug.h" + +#include "srslte/phy/agc/agc.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +int srslte_agc_init (srslte_agc_t *q, srslte_agc_mode_t mode) { + return srslte_agc_init_acc(q, mode, 0); +} + +int srslte_agc_init_acc(srslte_agc_t *q, srslte_agc_mode_t mode, uint32_t nof_frames) { + bzero(q, sizeof(srslte_agc_t)); + q->mode = mode; + q->nof_frames = nof_frames; + q->max_gain = 90.0; + q->min_gain = 0.0; + if (nof_frames > 0) { + q->y_tmp = srslte_vec_malloc(sizeof(float) * nof_frames); + if (!q->y_tmp) { + return SRSLTE_ERROR; + } + } else { + q->y_tmp = NULL; + } + q->target = SRSLTE_AGC_DEFAULT_TARGET; + srslte_agc_reset(q); + return SRSLTE_SUCCESS; +} + +int srslte_agc_init_uhd(srslte_agc_t *q, srslte_agc_mode_t mode, uint32_t nof_frames, double (set_gain_callback)(void*, double), void *uhd_handler) { + if (!srslte_agc_init_acc(q, mode, nof_frames)) { + q->set_gain_callback = set_gain_callback; + q->uhd_handler = uhd_handler; + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +void srslte_agc_free(srslte_agc_t *q) { + if (q->y_tmp) { + free(q->y_tmp); + } + bzero(q, sizeof(srslte_agc_t)); +} + +void srslte_agc_reset(srslte_agc_t *q) { + q->bandwidth = SRSLTE_AGC_DEFAULT_BW; + q->lock = false; + q->gain = pow(10,50/10); + q->y_out = 1.0; + q->isfirst = true; + if (q->set_gain_callback && q->uhd_handler) { + q->set_gain_callback(q->uhd_handler, 10*log10(q->gain)); + } +} + +void srslte_agc_set_gain_range(srslte_agc_t *q, double min_gain, double max_gain) { + if (q) { + q->min_gain = min_gain; + q->max_gain = max_gain; + } +} + +void srslte_agc_set_bandwidth(srslte_agc_t *q, float bandwidth) { + q->bandwidth = bandwidth; +} + +void srslte_agc_set_target(srslte_agc_t *q, float target) { + q->target = target; +} + +float srslte_agc_get_rssi(srslte_agc_t *q) { + return q->target/q->gain; +} + +float srslte_agc_get_output_level(srslte_agc_t *q) { + return q->y_out; +} + +float srslte_agc_get_gain(srslte_agc_t *q) { + return q->gain; +} + +void srslte_agc_set_gain(srslte_agc_t *q, float init_gain_value) { + q->gain = init_gain_value; +} + +void srslte_agc_lock(srslte_agc_t *q, bool enable) { + q->lock = enable; +} + +void srslte_agc_process(srslte_agc_t *q, cf_t *signal, uint32_t len) { + if (!q->lock) { + double gain_db = 10.0 * log10(q->gain); + double gain_uhd_db = 50.0; + + float y = 0; + // Apply current gain to input signal + if (!q->uhd_handler) { + srslte_vec_sc_prod_cfc(signal, q->gain, signal, len); + } else { + if (gain_db < q->min_gain) { + gain_db = q->min_gain + 5.0; + INFO("Warning: Rx signal strength is too high. Forcing minimum Rx gain %.2fdB\n", gain_db); + } else if (gain_db > q->max_gain) { + gain_db = q->max_gain; + INFO("Warning: Rx signal strength is too weak. Forcing maximum Rx gain %.2fdB\n", gain_db); + } else if (isinf(gain_db) || isnan(gain_db)) { + gain_db = (q->min_gain + q->max_gain) / 2.0; + INFO("Warning: AGC went to an unknown state. Setting Rx gain to %.2fdB\n", gain_db); + } else { + gain_uhd_db = q->set_gain_callback(q->uhd_handler, gain_db); + q->gain = pow(10, gain_uhd_db/10); + } + } + float *t; + switch(q->mode) { + case SRSLTE_AGC_MODE_ENERGY: + y = sqrtf(crealf(srslte_vec_dot_prod_conj_ccc(signal, signal, len))/len); + break; + case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: + t = (float*) signal; + y = t[srslte_vec_max_fi(t, 2*len)];// take only positive max to avoid abs() (should be similar) + break; + default: + fprintf(stderr, "Unsupported AGC mode\n"); + return; + } + + if (q->nof_frames > 0) { + q->y_tmp[q->frame_cnt++] = y; + if (q->frame_cnt == q->nof_frames) { + q->frame_cnt = 0; + switch(q->mode) { + case SRSLTE_AGC_MODE_ENERGY: + y = srslte_vec_acc_ff(q->y_tmp, q->nof_frames)/q->nof_frames; + break; + case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: + y = q->y_tmp[srslte_vec_max_fi(q->y_tmp, q->nof_frames)]; + break; + default: + fprintf(stderr, "Unsupported AGC mode\n"); + return; + } + } + } + + double gg = 1.0; + if (q->isfirst) { + q->y_out = y; + q->isfirst = false; + } else { + if (q->frame_cnt == 0) { + q->y_out = (1-q->bandwidth) * q->y_out + q->bandwidth * y; + if (!q->lock) { + gg = expf(-0.5*q->bandwidth*logf(q->y_out/q->target)); + q->gain *= gg; + } + INFO("AGC gain: %.2f (%.2f) y_out=%.3f, y=%.3f target=%.1f gg=%.2f\n", gain_db, gain_uhd_db, q->y_out, y, q->target, gg); + } + } + } +} diff --git a/lib/src/phy/ch_estimation/CMakeLists.txt b/lib/src/phy/ch_estimation/CMakeLists.txt new file mode 100644 index 0000000..ac069bd --- /dev/null +++ b/lib/src/phy/ch_estimation/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_ch_estimation OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/ch_estimation/chest_common.c b/lib/src/phy/ch_estimation/chest_common.c new file mode 100644 index 0000000..2ef8013 --- /dev/null +++ b/lib/src/phy/ch_estimation/chest_common.c @@ -0,0 +1,82 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/chest_common.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/convolution.h" + +void srslte_chest_set_triangle_filter(float *fil, int filter_len) +{ + for (int i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/config.h" + +#include "srslte/phy/ch_estimation/chest_dl.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/convolution.h" + +//#define DEFAULT_FILTER_LEN 3 + +#ifdef DEFAULT_FILTER_LEN +static void set_default_filter(srslte_chest_dl_t *q, int filter_len) { + + float fil[SRSLTE_CHEST_DL_MAX_SMOOTH_FIL_LEN]; + + for (int i=0;icsr_refs, max_prb); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + goto clean_exit; + } + + q->mbsfn_refs = calloc(SRSLTE_MAX_MBSFN_AREA_IDS, sizeof(srslte_refsignal_t)); + if (!q->mbsfn_refs) { + fprintf(stderr, "Calloc error initializing mbsfn_refs (%d)\n", ret); + goto clean_exit; + } + + int pilot_vec_size; + if(SRSLTE_REFSIGNAL_MAX_NUM_SF_MBSFN(max_prb)>SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb)) { + pilot_vec_size = SRSLTE_REFSIGNAL_MAX_NUM_SF_MBSFN(max_prb); + }else{ + pilot_vec_size = SRSLTE_REFSIGNAL_MAX_NUM_SF(max_prb); + } + + q->tmp_noise = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); + if (!q->tmp_noise) { + perror("malloc"); + goto clean_exit; + } + + q->tmp_cfo_estimate = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); + if (!q->tmp_cfo_estimate) { + perror("malloc"); + goto clean_exit; + } + + q->pilot_estimates = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); + if (!q->pilot_estimates) { + perror("malloc"); + goto clean_exit; + } + + q->pilot_estimates_average = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); + if (!q->pilot_estimates_average) { + perror("malloc"); + goto clean_exit; + } + + q->pilot_recv_signal = srslte_vec_malloc(sizeof(cf_t) * pilot_vec_size); + if (!q->pilot_recv_signal) { + perror("malloc"); + goto clean_exit; + } + + if (srslte_interp_linear_vector_init(&q->srslte_interp_linvec, SRSLTE_NRE*max_prb)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + goto clean_exit; + } + + if (srslte_interp_linear_init(&q->srslte_interp_lin, 2*max_prb, SRSLTE_NRE/2)) { + fprintf(stderr, "Error initializing interpolator\n"); + goto clean_exit; + } + + if (srslte_interp_linear_init(&q->srslte_interp_lin_3, 4*max_prb, SRSLTE_NRE/4)) { + fprintf(stderr, "Error initializing interpolator\n"); + goto clean_exit; + } + + if (srslte_interp_linear_init(&q->srslte_interp_lin_mbsfn, 6*max_prb, SRSLTE_NRE/6)) { + fprintf(stderr, "Error initializing interpolator\n"); + goto clean_exit; + } + + q->noise_alg = SRSLTE_NOISE_ALG_REFS; + + q->rsrp_neighbour = false; + q->average_subframe = false; + q->smooth_filter_auto = false; + q->smooth_filter_len = 3; + srslte_chest_dl_set_smooth_filter3_coeff(q, 0.1); + + } + + ret = SRSLTE_SUCCESS; + +clean_exit: + if (ret != SRSLTE_SUCCESS) { + srslte_chest_dl_free(q); + } + return ret; +} + +void srslte_chest_dl_free(srslte_chest_dl_t *q) +{ + if(&q->csr_refs) + srslte_refsignal_free(&q->csr_refs); + + if (q->mbsfn_refs) { + for (int i=0; imbsfn_refs[i]) { + srslte_refsignal_free(q->mbsfn_refs[i]); + free(q->mbsfn_refs[i]); + } + } + free(q->mbsfn_refs); + } + + if (q->tmp_noise) { + free(q->tmp_noise); + } + if (q->tmp_cfo_estimate) { + free(q->tmp_cfo_estimate); + } + srslte_interp_linear_vector_free(&q->srslte_interp_linvec); + srslte_interp_linear_free(&q->srslte_interp_lin); + srslte_interp_linear_free(&q->srslte_interp_lin_3); + srslte_interp_linear_free(&q->srslte_interp_lin_mbsfn); + if (q->pilot_estimates) { + free(q->pilot_estimates); + } + if (q->pilot_estimates_average) { + free(q->pilot_estimates_average); + } + if (q->pilot_recv_signal) { + free(q->pilot_recv_signal); + } + bzero(q, sizeof(srslte_chest_dl_t)); +} + + +int srslte_chest_dl_set_mbsfn_area_id(srslte_chest_dl_t *q, uint16_t mbsfn_area_id){ + if (mbsfn_area_id < SRSLTE_MAX_MBSFN_AREA_IDS) { + if(!q->mbsfn_refs[mbsfn_area_id]) { + q->mbsfn_refs[mbsfn_area_id] = calloc(1, sizeof(srslte_refsignal_t)); + if(srslte_refsignal_mbsfn_init(q->mbsfn_refs[mbsfn_area_id], q->cell.nof_prb)) { + return SRSLTE_ERROR; + } + } + if(q->mbsfn_refs[mbsfn_area_id]) { + if(srslte_refsignal_mbsfn_set_cell(q->mbsfn_refs[mbsfn_area_id], q->cell, mbsfn_area_id)) { + return SRSLTE_ERROR; + } + } + return SRSLTE_SUCCESS; + } + return SRSLTE_ERROR; +} + +int srslte_chest_dl_set_cell(srslte_chest_dl_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + ret = srslte_refsignal_cs_set_cell(&q->csr_refs, cell); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + return SRSLTE_ERROR; + } + if (srslte_pss_generate(q->pss_signal, cell.id%3)) { + fprintf(stderr, "Error initializing PSS signal for noise estimation\n"); + return SRSLTE_ERROR; + } + if (srslte_interp_linear_vector_resize(&q->srslte_interp_linvec, SRSLTE_NRE*q->cell.nof_prb)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + return SRSLTE_ERROR; + } + + if (srslte_interp_linear_resize(&q->srslte_interp_lin, 2*q->cell.nof_prb, SRSLTE_NRE/2)) { + fprintf(stderr, "Error initializing interpolator\n"); + return SRSLTE_ERROR; + } + + if (srslte_interp_linear_resize(&q->srslte_interp_lin_3, 4 * q->cell.nof_prb, SRSLTE_NRE / 4)) { + fprintf(stderr, "Error initializing interpolator\n"); + return SRSLTE_ERROR; + } + + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Uses the difference between the averaged and non-averaged pilot estimates */ +static float estimate_noise_pilots(srslte_chest_dl_t *q, uint32_t port_id, srslte_sf_t ch_mode) +{ + const float weight = 1.0f; + float sum_power = 0.0f; + uint32_t count = 0; + uint32_t npilots = (ch_mode == SRSLTE_SF_MBSFN)?SRSLTE_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id):SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id); + uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN) ? srslte_refsignal_mbsfn_nof_symbols() : srslte_refsignal_cs_nof_symbols(port_id); + uint32_t nref = npilots / nsymbols; + uint32_t fidx = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_fidx(1):srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0); + + cf_t *input2d[nsymbols + 2]; + cf_t *tmp_noise = q->tmp_noise; + + for (int i = 0; i < nsymbols; i++) { + input2d[i + 1] = &q->pilot_estimates[i * nref]; + } + + input2d[0] = &q->tmp_noise[nref]; + if (nsymbols > 3) { + srslte_vec_sc_prod_cfc(input2d[2], 2.0f, input2d[0], nref); + srslte_vec_sub_ccc(input2d[0], input2d[4], input2d[0], nref); + } else { + srslte_vec_sc_prod_cfc(input2d[2], 1.0f, input2d[0], nref); + } + + input2d[nsymbols + 1] = &q->tmp_noise[nref * 2]; + if (nsymbols > 3) { + srslte_vec_sc_prod_cfc(input2d[nsymbols - 1], 2.0f, input2d[nsymbols + 1], nref); + srslte_vec_sub_ccc(input2d[nsymbols + 1], input2d[nsymbols - 3], input2d[nsymbols + 1], nref); + } else { + srslte_vec_sc_prod_cfc(input2d[nsymbols - 1], 1.0f, input2d[nsymbols + 1], nref); + } + + for (int i = 1; i < nsymbols + 1; i++) { + uint32_t offset = ((fidx < 3) ^ (i & 1)) ? 0 : 1; + srslte_vec_sc_prod_cfc(input2d[i], weight, tmp_noise, nref); + + srslte_vec_sum_ccc(&input2d[i - 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); + srslte_vec_sum_ccc(&input2d[i - 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); + if (offset) { + tmp_noise[0] += 2.0f * input2d[i - 1][0] - input2d[i - 1][1]; + } else { + tmp_noise[nref - 1] += 2.0f * input2d[i - 1][nref - 2] - input2d[i - 1][nref - 1]; + } + + srslte_vec_sum_ccc(&input2d[i + 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); + srslte_vec_sum_ccc(&input2d[i + 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); + if (offset) { + tmp_noise[0] += 2.0f * input2d[i + 1][0] - input2d[i + 1][1]; + } else { + tmp_noise[nref - 1] += 2.0f * input2d[i + 1][nref - 2] - input2d[i + 1][nref - 1]; + } + + srslte_vec_sc_prod_cfc(tmp_noise, 1.0f / (weight + 4.0f), tmp_noise, nref); + + srslte_vec_sub_ccc(input2d[i], tmp_noise, tmp_noise, nref); + sum_power = srslte_vec_avg_power_cf(tmp_noise, nref); + count++; + } + + return sum_power / (float) count * sqrtf(weight + 4.0f); +} + +static float estimate_noise_pss(srslte_chest_dl_t *q, cf_t *input, cf_t *ce) +{ + /* Get PSS from received signal */ + srslte_pss_get_slot(input, q->tmp_pss, q->cell.nof_prb, q->cell.cp); + + /* Get channel estimates for PSS position */ + srslte_pss_get_slot(ce, q->tmp_pss_noisy, q->cell.nof_prb, q->cell.cp); + + /* Multiply known PSS by channel estimates */ + srslte_vec_prod_ccc(q->tmp_pss_noisy, q->pss_signal, q->tmp_pss_noisy, SRSLTE_PSS_LEN); + + /* Substract received signal */ + srslte_vec_sub_ccc(q->tmp_pss_noisy, q->tmp_pss, q->tmp_pss_noisy, SRSLTE_PSS_LEN); + + /* Compute average power */ + float power = q->cell.nof_ports*srslte_vec_avg_power_cf(q->tmp_pss_noisy, SRSLTE_PSS_LEN)/sqrt(2); + return power; +} + +/* Uses the 5 empty transmitted SC before and after the SSS and PSS sequences for noise estimation */ +static float estimate_noise_empty_sc(srslte_chest_dl_t *q, cf_t *input) { + int k_sss = (SRSLTE_CP_NSYMB(q->cell.cp) - 2) * q->cell.nof_prb * SRSLTE_NRE + q->cell.nof_prb * SRSLTE_NRE / 2 - 31; + float noise_power = 0; + noise_power += srslte_vec_avg_power_cf(&input[k_sss-5], 5); // 5 empty SC before SSS + noise_power += srslte_vec_avg_power_cf(&input[k_sss+62], 5); // 5 empty SC after SSS + int k_pss = (SRSLTE_CP_NSYMB(q->cell.cp) - 1) * q->cell.nof_prb * SRSLTE_NRE + q->cell.nof_prb * SRSLTE_NRE / 2 - 31; + noise_power += srslte_vec_avg_power_cf(&input[k_pss-5], 5); // 5 empty SC before PSS + noise_power += srslte_vec_avg_power_cf(&input[k_pss+62], 5); // 5 empty SC after PSS + + return noise_power; +} + +#define cesymb(i) ce[SRSLTE_RE_IDX(q->cell.nof_prb,i,0)] + +static void interpolate_pilots(srslte_chest_dl_t *q, cf_t *pilot_estimates, cf_t *ce, uint32_t port_id, srslte_sf_t ch_mode) +{ + /* interpolate the symbols with references in the freq domain */ + uint32_t l; + uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN ) ? srslte_refsignal_mbsfn_nof_symbols() + 1 : srslte_refsignal_cs_nof_symbols(port_id); + uint32_t fidx_offset = 0; + /* Interpolate in the frequency domain */ + + if (q->average_subframe) { + nsymbols = 1; + } + + // we add one to nsymbols to allow for inclusion of the non-mbms references in the channel estimation + for (l=0;lcell, l, port_id, 0); + srslte_interp_linear_offset(&q->srslte_interp_lin, &pilot_estimates[2*q->cell.nof_prb*l], + &ce[srslte_refsignal_cs_nsymbol(l,q->cell.cp, port_id) * q->cell.nof_prb * SRSLTE_NRE], + fidx_offset, SRSLTE_NRE/2-fidx_offset); + } else { + fidx_offset = srslte_refsignal_mbsfn_fidx(l - 1); + srslte_interp_linear_offset(&q->srslte_interp_lin_mbsfn, &pilot_estimates[(2*q->cell.nof_prb) + 6*q->cell.nof_prb*(l - 1)], + &ce[srslte_refsignal_mbsfn_nsymbol(l - 1) * q->cell.nof_prb * SRSLTE_NRE], + fidx_offset, SRSLTE_NRE/6-fidx_offset); + } + } else { + if (q->average_subframe) { + fidx_offset = q->cell.id % 3; + srslte_interp_linear_offset(&q->srslte_interp_lin_3, + pilot_estimates, + ce, + fidx_offset, + SRSLTE_NRE / 4 - fidx_offset); + } else { + fidx_offset = srslte_refsignal_cs_fidx(q->cell, l, port_id, 0); + srslte_interp_linear_offset(&q->srslte_interp_lin, &pilot_estimates[2 * q->cell.nof_prb * l], + &ce[srslte_refsignal_cs_nsymbol(l, q->cell.cp, port_id) * q->cell.nof_prb + * SRSLTE_NRE], fidx_offset, SRSLTE_NRE / 2 - fidx_offset); + } + } + } + + /* Now interpolate in the time domain between symbols */ + if (q->average_subframe) { + // If we average per subframe, just copy the estimates in the time domain + for (l=1;l<2*SRSLTE_CP_NSYMB(q->cell.cp);l++) { + memcpy(&ce[l*SRSLTE_NRE*q->cell.nof_prb], ce, sizeof(cf_t)*SRSLTE_NRE*q->cell.nof_prb); + } + } else { + if (ch_mode == SRSLTE_SF_MBSFN) { + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(0), &cesymb(2), &cesymb(1), 2, 1); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(2), &cesymb(6), &cesymb(3), 4, 3); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(6), &cesymb(10), &cesymb(7), 4, 3); + srslte_interp_linear_vector2(&q->srslte_interp_linvec, &cesymb(6), &cesymb(10), &cesymb(10), &cesymb(11), 4, 1); + } else { + if (SRSLTE_CP_ISNORM(q->cell.cp)) { + if (nsymbols == 4) { + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(0), &cesymb(4), &cesymb(1), 4, 3); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(4), &cesymb(7), &cesymb(5), 3, 2); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(7), &cesymb(11), &cesymb(8), 4, 3); + srslte_interp_linear_vector2(&q->srslte_interp_linvec, &cesymb(7), &cesymb(11), &cesymb(11), &cesymb(12), 4, 2); + } else { + srslte_interp_linear_vector2(&q->srslte_interp_linvec, &cesymb(8), &cesymb(1), &cesymb(1), &cesymb(0), 7, 1); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(1), &cesymb(8), &cesymb(2), 7, 6); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(1), &cesymb(8), &cesymb(9), 7, 5); + } + } else { + if (nsymbols == 4) { + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(0), &cesymb(3), &cesymb(1), 3, 2); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(3), &cesymb(6), &cesymb(4), 3, 2); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(6), &cesymb(9), &cesymb(7), 3, 2); + srslte_interp_linear_vector2(&q->srslte_interp_linvec, &cesymb(6), &cesymb(9), &cesymb(9), &cesymb(10), 3, 2); + } else { + srslte_interp_linear_vector2(&q->srslte_interp_linvec, &cesymb(7), &cesymb(1), &cesymb(1), &cesymb(0), 6, 1); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(1), &cesymb(7), &cesymb(2), 6, 5); + srslte_interp_linear_vector(&q->srslte_interp_linvec, &cesymb(1), &cesymb(7), &cesymb(8), 6, 4); + } + } + } + } +} + + +void srslte_chest_dl_set_smooth_filter(srslte_chest_dl_t *q, float *filter, uint32_t filter_len) { + if (filter_len < SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN) { + if (filter) { + memcpy(q->smooth_filter, filter, filter_len*sizeof(float)); + q->smooth_filter_len = filter_len; + } else { + q->smooth_filter_len = 0; + } + } else { + fprintf(stderr, "Error setting smoothing filter: filter len exceeds maximum (%d>%d)\n", + filter_len, SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN); + } +} + +void srslte_chest_dl_set_noise_alg(srslte_chest_dl_t *q, srslte_chest_dl_noise_alg_t noise_estimation_alg) { + q->noise_alg = noise_estimation_alg; +} + +void srslte_chest_dl_set_smooth_filter3_coeff(srslte_chest_dl_t* q, float w) +{ + q->smooth_filter_len = 3; + q->smooth_filter[0] = w; + q->smooth_filter[2] = w; + q->smooth_filter[1] = 1-2*w; +} + +void srslte_chest_dl_set_smooth_filter_gauss(srslte_chest_dl_t* q, uint32_t order, float std_dev) +{ + const uint32_t filterlen = order + 1; + const int center = (filterlen - 1) / 2; + float *filter = q->smooth_filter; + float norm_p = 0.0f; + + if (filterlen) { + + for (int i = 0; i < filterlen; i++) { + filter[i] = expf(-powf(i - center, 2) / (2.0f * powf(std_dev, 2))); + norm_p += powf(filter[i], 2); + } + + const float norm = srslte_vec_acc_ff(filter, filterlen); + + srslte_vec_sc_prod_fff(filter, 1.0f / norm, filter, filterlen); + q->smooth_filter_len = filterlen; + } +} + +void srslte_chest_dl_set_smooth_filter_auto(srslte_chest_dl_t *q, bool enable) { + q->smooth_filter_auto = enable; +} + +uint32_t srslte_chest_dl_interleave_pilots(srslte_chest_dl_t *q, cf_t *input, cf_t *tmp, cf_t *output, uint32_t port_id, srslte_sf_t ch_mode) { + uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_nof_symbols(port_id):srslte_refsignal_cs_nof_symbols(port_id); + uint32_t nref = (ch_mode == SRSLTE_SF_MBSFN)?6*q->cell.nof_prb:2*q->cell.nof_prb; + uint32_t fidx = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_fidx(1):srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0); + + if (fidx < 3) { + srslte_vec_interleave(input, &input[nref], tmp, nref); + for (int l = 2; l < nsymbols - 1; l += 2) { + srslte_vec_interleave_add(&input[l * nref], &input[(l + 1) * nref], tmp, nref); + } + } else { + srslte_vec_interleave(&input[nref], input, tmp, nref); + for (int l = 2; l < nsymbols - 1; l += 2) { + srslte_vec_interleave_add(&input[(l + 1) * nref], &input[l * nref], tmp, nref); + } + } + + nref *= 2; + srslte_vec_sc_prod_cfc(tmp, 2.0f / nsymbols, output, nref); + + return nref; +} + +static void average_pilots(srslte_chest_dl_t *q, cf_t *input, cf_t *output, uint32_t port_id, srslte_sf_t ch_mode) { + uint32_t nsymbols = (ch_mode == SRSLTE_SF_MBSFN)?srslte_refsignal_mbsfn_nof_symbols(port_id):srslte_refsignal_cs_nof_symbols(port_id); + uint32_t nref = (ch_mode == SRSLTE_SF_MBSFN)?6*q->cell.nof_prb:2*q->cell.nof_prb; + + // Average in the time domain if enabled + if (q->average_subframe) { + if (ch_mode == SRSLTE_SF_MBSFN) { + for (int l = 1; l < nsymbols; l++) { + srslte_vec_sum_ccc(&input[l * nref], input, input, nref); + } + srslte_vec_sc_prod_cfc(input, 1.0f / ((float) nsymbols), input, nref); + nsymbols = 1; + } else { + cf_t *temp = output; // Use ouput as temporal buffer + + if (srslte_refsignal_cs_fidx(q->cell, 0, port_id, 0) < 3) { + srslte_vec_interleave(input, &input[nref], temp, nref); + for (int l = 2; l < nsymbols - 1; l += 2) { + srslte_vec_interleave_add(&input[l * nref], &input[(l + 1) * nref], temp, nref); + } + } else { + srslte_vec_interleave(&input[nref], input, temp, nref); + for (int l = 2; l < nsymbols - 1; l += 2) { + srslte_vec_interleave_add(&input[(l + 1) * nref], &input[l * nref], temp, nref); + } + } + nref *= 2; + srslte_vec_sc_prod_cfc(temp, 2.0f / (float) nsymbols, input, nref); + + nsymbols = 1; + } + } + + + uint32_t skip = (ch_mode == SRSLTE_SF_MBSFN)?2*q->cell.nof_prb:0; + + if(ch_mode == SRSLTE_SF_MBSFN){ + memcpy(&output[0],&input[0],skip*sizeof(cf_t)); + } + + // Average in the frequency domain + for (int l=0;lsmooth_filter, &output[l*nref + skip], nref, q->smooth_filter_len); + } +} + +float srslte_chest_dl_rssi(srslte_chest_dl_t *q, cf_t *input, uint32_t port_id) { + uint32_t l; + + float rssi = 0; + uint32_t nsymbols = srslte_refsignal_cs_nof_symbols(port_id); + for (l=0;lcell.cp, port_id) * q->cell.nof_prb * SRSLTE_NRE]; + rssi += srslte_vec_dot_prod_conj_ccc(tmp, tmp, q->cell.nof_prb * SRSLTE_NRE); + } + return rssi/nsymbols; +} + +// CFO estimation algorithm taken from "Carrier Frequency Synchronization in the +// Downlink of 3GPP LTE", Qi Wang, C. Mehlfuhrer, M. Rupp +float chest_estimate_cfo(srslte_chest_dl_t *q) +{ + float n = (float) srslte_symbol_sz(q->cell.nof_prb); + float ns = (float) SRSLTE_CP_NSYMB(q->cell.cp); + float ng = (float) SRSLTE_CP_LEN_NORM(1, n); + + uint32_t npilots = SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, 0); + + // Compute angles between slots + for (int i=0;i<2;i++) { + srslte_vec_prod_conj_ccc(&q->pilot_estimates[i*npilots/4], + &q->pilot_estimates[(i+2)*npilots/4], + &q->tmp_cfo_estimate[i*npilots/4], + npilots/4); + } + // Average all angles + cf_t sum = srslte_vec_acc_cc(q->tmp_cfo_estimate, npilots/2); + + // Compute CFO + return -cargf(sum)*n/(ns*(n+ng))/2/M_PI; +} + +void chest_interpolate_noise_est(srslte_chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id, uint32_t rxant_id, srslte_sf_t ch_mode){ + if (q->cfo_estimate_enable && ((1<cfo_estimate_sf_mask) && ch_mode != SRSLTE_SF_MBSFN ) { + q->cfo = chest_estimate_cfo(q); + } + + /* Estimate noise */ + if (q->noise_alg == SRSLTE_NOISE_ALG_REFS && ch_mode != SRSLTE_SF_MBSFN ) { + q->noise_estimate[rxant_id][port_id] = estimate_noise_pilots(q, port_id, ch_mode); + } + + if (ce != NULL) { + if (q->smooth_filter_auto) { + srslte_chest_dl_set_smooth_filter_gauss(q, 4, q->noise_estimate[rxant_id][port_id] * 200.0f); + } + + /* Smooth estimates (if applicable) and interpolate */ + if (q->smooth_filter_len == 0 || (q->smooth_filter_len == 3 && q->smooth_filter[0] == 0)) { + interpolate_pilots(q, q->pilot_estimates, ce, port_id, ch_mode); + } else { + average_pilots(q, q->pilot_estimates, q->pilot_estimates_average, port_id, ch_mode); + interpolate_pilots(q, q->pilot_estimates_average, ce, port_id, ch_mode); + } + + /* Estimate noise power */ + if (q->noise_alg == SRSLTE_NOISE_ALG_PSS) { + if (sf_idx == 0 || sf_idx == 5) { + q->noise_estimate[rxant_id][port_id] = estimate_noise_pss(q, input, ce); + } + } else if (q->noise_alg != SRSLTE_NOISE_ALG_REFS) { + if (sf_idx == 0 || sf_idx == 5) { + q->noise_estimate[rxant_id][port_id] = estimate_noise_empty_sc(q, input); + } + } + } +} + +int srslte_chest_dl_estimate_port(srslte_chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id, uint32_t rxant_id) +{ + uint32_t npilots = SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, port_id); + + /* Get references from the input signal */ + srslte_refsignal_cs_get_sf(q->cell, port_id, input, q->pilot_recv_signal); + + /* Use the known CSR signal to compute Least-squares estimates */ + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->csr_refs.pilots[port_id/2][sf_idx], + q->pilot_estimates, npilots); + + /* Compute RSRP for the channel estimates in this port */ + if (q->rsrp_neighbour) { + double energy = cabs(srslte_vec_acc_cc(q->pilot_estimates, npilots)/npilots); + q->rsrp_corr[rxant_id][port_id] = energy*energy; + } + q->rsrp[rxant_id][port_id] = srslte_vec_avg_power_cf(q->pilot_recv_signal, npilots); + q->rssi[rxant_id][port_id] = srslte_chest_dl_rssi(q, input, port_id); + + chest_interpolate_noise_est(q, input, ce, sf_idx, port_id, rxant_id, SRSLTE_SF_NORM); + + return 0; +} + +int srslte_chest_dl_estimate_port_mbsfn(srslte_chest_dl_t *q, cf_t *input, cf_t *ce, uint32_t sf_idx, uint32_t port_id, uint32_t rxant_id, uint16_t mbsfn_area_id) +{ + + /* Use the known CSR signal to compute Least-squares estimates */ + srslte_refsignal_mbsfn_get_sf(q->cell, port_id, input, q->pilot_recv_signal); + // estimate for non-mbsfn section of subframe + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->csr_refs.pilots[port_id/2][sf_idx], + q->pilot_estimates, (2*q->cell.nof_prb)); + + srslte_vec_prod_conj_ccc(&q->pilot_recv_signal[(2*q->cell.nof_prb)], q->mbsfn_refs[mbsfn_area_id]->pilots[port_id/2][sf_idx], + &q->pilot_estimates[(2*q->cell.nof_prb)], SRSLTE_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id)-(2*q->cell.nof_prb)); + + + chest_interpolate_noise_est(q, input, ce, sf_idx, port_id, rxant_id, SRSLTE_SF_MBSFN); + + return 0; +} + +int srslte_chest_dl_estimate_multi(srslte_chest_dl_t *q, cf_t *input[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t sf_idx, uint32_t nof_rx_antennas) +{ + for (uint32_t rxant_id=0;rxant_idcell.nof_ports;port_id++) { + if (srslte_chest_dl_estimate_port(q, input[rxant_id], ce[port_id][rxant_id], sf_idx, port_id, rxant_id)) { + return SRSLTE_ERROR; + } + } + } + q->last_nof_antennas = nof_rx_antennas; + return SRSLTE_SUCCESS; +} + +int srslte_chest_dl_estimate(srslte_chest_dl_t *q, cf_t *input, cf_t *ce[SRSLTE_MAX_PORTS], uint32_t sf_idx) +{ + uint32_t port_id; + + for (port_id=0;port_idcell.nof_ports;port_id++) { + if (srslte_chest_dl_estimate_port(q, input, ce[port_id], sf_idx, port_id, 0)) { + return SRSLTE_ERROR; + } + } + q->last_nof_antennas = 1; + return SRSLTE_SUCCESS; +} + +int srslte_chest_dl_estimate_multi_mbsfn(srslte_chest_dl_t *q, cf_t *input[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t sf_idx, uint32_t nof_rx_antennas, uint16_t mbsfn_area_id) +{ + for (uint32_t rxant_id=0;rxant_idcell.nof_ports;port_id++) { + if (srslte_chest_dl_estimate_port_mbsfn(q, input[rxant_id], ce[port_id][rxant_id], sf_idx, port_id, rxant_id, mbsfn_area_id)) { + return SRSLTE_ERROR; + } + } + } + q->last_nof_antennas = nof_rx_antennas; + return SRSLTE_SUCCESS; +} + +void srslte_chest_dl_set_rsrp_neighbour(srslte_chest_dl_t *q, bool rsrp_for_neighbour) { + q->rsrp_neighbour = rsrp_for_neighbour; +} + +void srslte_chest_dl_average_subframe(srslte_chest_dl_t *q, bool enable) +{ + q->average_subframe = enable; +} + +void srslte_chest_dl_cfo_estimate_enable(srslte_chest_dl_t *q, bool enable, uint32_t mask) +{ + q->cfo_estimate_enable = enable; + q->cfo_estimate_sf_mask = mask; +} + +float srslte_chest_dl_get_cfo(srslte_chest_dl_t *q) { + return q->cfo; +} + +float srslte_chest_dl_get_noise_estimate(srslte_chest_dl_t *q) { + float n = 0; + for (int i=0;ilast_nof_antennas;i++) { + n += srslte_vec_acc_ff(q->noise_estimate[i], q->cell.nof_ports)/q->cell.nof_ports; + } + if (q->last_nof_antennas) { + n /= q->last_nof_antennas; + } + return n; +} + +float srslte_chest_dl_get_snr(srslte_chest_dl_t *q) { +#ifdef FREQ_SEL_SNR + int nref=SRSLTE_REFSIGNAL_NUM_SF(q->cell.nof_prb, 0); + return srslte_vec_acc_ff(q->snr_vector, nref)/nref; +#else + float rsrp = 0; + for (int i=0;ilast_nof_antennas;i++) { + for (int j=0;jcell.nof_ports;j++) { + rsrp += q->rsrp[i][j]/q->cell.nof_ports; + } + } + return rsrp/srslte_chest_dl_get_noise_estimate(q); +#endif +} + + +float srslte_chest_dl_get_snr_ant_port(srslte_chest_dl_t *q, uint32_t ant_idx, uint32_t port_idx) { + return srslte_chest_dl_get_rsrp_ant_port(q, ant_idx, port_idx)/srslte_chest_dl_get_noise_estimate(q); +} + +float srslte_chest_dl_get_rssi(srslte_chest_dl_t *q) { + float n = 0; + for (int i=0;ilast_nof_antennas;i++) { + n += 4*q->rssi[i][0]/q->cell.nof_prb/SRSLTE_NRE; + } + return n/q->last_nof_antennas; +} + +/* q->rssi[0] is the average power in all RE in all symbol containing references for port 0 . q->rssi[0]/q->cell.nof_prb is the average power per PRB + * q->rsrp[0] is the average power of RE containing references only (for port 0). +*/ +float srslte_chest_dl_get_rsrq(srslte_chest_dl_t *q) { + float n = 0; + for (int i=0;ilast_nof_antennas;i++) { + n += q->cell.nof_prb*q->rsrp[i][0] / q->rssi[i][0]; + } + return n/q->last_nof_antennas; + +} + +float srslte_chest_dl_get_rsrq_ant_port(srslte_chest_dl_t *q, uint32_t ant_idx, uint32_t port_idx) { + return q->cell.nof_prb*q->rsrp[ant_idx][port_idx] / q->rssi[ant_idx][port_idx]; +} + +float srslte_chest_dl_get_rsrp_ant_port(srslte_chest_dl_t *q, uint32_t ant_idx, uint32_t port) { + return q->rsrp[ant_idx][port]; +} + +float srslte_chest_dl_get_rsrp_port(srslte_chest_dl_t *q, uint32_t port) { + float sum = 0.0f; + for (int j = 0; j < q->cell.nof_ports; ++j) { + sum +=q->rsrp[port][j]; + } + + if (q->cell.nof_ports) { + sum /= q->cell.nof_ports; + } + + return sum; +} + +float srslte_chest_dl_get_rsrp_neighbour_port(srslte_chest_dl_t *q, uint32_t port) { + float sum = 0.0f; + for (int j = 0; j < q->cell.nof_ports; ++j) { + sum +=q->rsrp_corr[port][j]; + } + + if (q->cell.nof_ports) { + sum /= q->cell.nof_ports; + } + + return sum; +} + +float srslte_chest_dl_get_rsrp(srslte_chest_dl_t *q) { + float max = -1e9; + for (int i = 0; i < q->last_nof_antennas; ++i) { + float v = srslte_chest_dl_get_rsrp_port(q, i); + if (v > max) { + max = v; + } + } + return max; +} + +float srslte_chest_dl_get_rsrp_neighbour(srslte_chest_dl_t *q) { + float max = -1e9; + for (int i = 0; i < q->last_nof_antennas; ++i) { + float v = srslte_chest_dl_get_rsrp_neighbour_port(q, i); + if (v > max) { + max = v; + } + } + return max; +} diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c new file mode 100644 index 0000000..d7e6f3b --- /dev/null +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -0,0 +1,400 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/config.h" + +#include "srslte/phy/dft/dft_precoding.h" +#include "srslte/phy/ch_estimation/chest_ul.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/convolution.h" + +#define NOF_REFS_SYM (q->cell.nof_prb*SRSLTE_NRE) +#define NOF_REFS_SF (NOF_REFS_SYM*2) // 2 reference symbols per subframe + +#define MAX_REFS_SYM (max_prb*SRSLTE_NRE) +#define MAX_REFS_SF (max_prb*SRSLTE_NRE*2) // 2 reference symbols per subframe + +/** 3GPP LTE Downlink channel estimator and equalizer. + * Estimates the channel in the resource elements transmitting references and interpolates for the rest + * of the resource grid. + * + * The equalizer uses the channel estimates to produce an estimation of the transmitted symbol. + * + * This object depends on the srslte_refsignal_t object for creating the LTE CSR signal. +*/ + +int srslte_chest_ul_init(srslte_chest_ul_t *q, uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) + { + bzero(q, sizeof(srslte_chest_ul_t)); + + ret = srslte_refsignal_ul_init(&q->dmrs_signal, max_prb); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + goto clean_exit; + } + + q->tmp_noise = srslte_vec_malloc(sizeof(cf_t) * MAX_REFS_SF); + if (!q->tmp_noise) { + perror("malloc"); + goto clean_exit; + } + q->pilot_estimates = srslte_vec_malloc(sizeof(cf_t) * MAX_REFS_SF); + if (!q->pilot_estimates) { + perror("malloc"); + goto clean_exit; + } + for (int i=0;i<4;i++) { + q->pilot_estimates_tmp[i] = srslte_vec_malloc(sizeof(cf_t) * MAX_REFS_SF); + if (!q->pilot_estimates_tmp[i]) { + perror("malloc"); + goto clean_exit; + } + } + q->pilot_recv_signal = srslte_vec_malloc(sizeof(cf_t) * (MAX_REFS_SF+1)); + if (!q->pilot_recv_signal) { + perror("malloc"); + goto clean_exit; + } + + q->pilot_known_signal = srslte_vec_malloc(sizeof(cf_t) * (MAX_REFS_SF+1)); + if (!q->pilot_known_signal) { + perror("malloc"); + goto clean_exit; + } + + if (srslte_interp_linear_vector_init(&q->srslte_interp_linvec, MAX_REFS_SYM)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + goto clean_exit; + } + + q->smooth_filter_len = 3; + srslte_chest_ul_set_smooth_filter3_coeff(q, 0.3333); + + q->dmrs_signal_configured = false; + + if (srslte_refsignal_dmrs_pusch_pregen_init(&q->dmrs_signal, &q->dmrs_pregen, max_prb)) { + fprintf(stderr, "Error allocating memory for pregenerated signals\n"); + goto clean_exit; + } + + } + + ret = SRSLTE_SUCCESS; + +clean_exit: + if (ret != SRSLTE_SUCCESS) { + srslte_chest_ul_free(q); + } + return ret; +} + +void srslte_chest_ul_free(srslte_chest_ul_t *q) +{ + srslte_refsignal_dmrs_pusch_pregen_free(&q->dmrs_signal, &q->dmrs_pregen); + + srslte_refsignal_ul_free(&q->dmrs_signal); + if (q->tmp_noise) { + free(q->tmp_noise); + } + srslte_interp_linear_vector_free(&q->srslte_interp_linvec); + + if (q->pilot_estimates) { + free(q->pilot_estimates); + } + for (int i=0;i<4;i++) { + if (q->pilot_estimates_tmp[i]) { + free(q->pilot_estimates_tmp[i]); + } + } + if (q->pilot_recv_signal) { + free(q->pilot_recv_signal); + } + if (q->pilot_known_signal) { + free(q->pilot_known_signal); + } + bzero(q, sizeof(srslte_chest_ul_t)); +} + +int srslte_chest_ul_set_cell(srslte_chest_ul_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + ret = srslte_refsignal_ul_set_cell(&q->dmrs_signal, cell); + if (ret != SRSLTE_SUCCESS) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + return SRSLTE_ERROR; + } + + if (srslte_interp_linear_vector_resize(&q->srslte_interp_linvec, NOF_REFS_SYM)) { + fprintf(stderr, "Error initializing vector interpolator\n"); + return SRSLTE_ERROR; + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +void srslte_chest_ul_set_cfg(srslte_chest_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg) +{ + srslte_refsignal_ul_set_cfg(&q->dmrs_signal, pusch_cfg, pucch_cfg, srs_cfg); + srslte_refsignal_dmrs_pusch_pregen(&q->dmrs_signal, &q->dmrs_pregen); + q->dmrs_signal_configured = true; +} + +/* Uses the difference between the averaged and non-averaged pilot estimates */ +static float estimate_noise_pilots(srslte_chest_ul_t *q, cf_t *ce, uint32_t nrefs, uint32_t n_prb[2]) +{ + + float power = 0; + for (int i=0;i<2;i++) { + power += srslte_chest_estimate_noise_pilots(&q->pilot_estimates[i*nrefs], + &ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp)*q->cell.nof_prb*SRSLTE_NRE+n_prb[i]*SRSLTE_NRE], + q->tmp_noise, + nrefs); + } + + power/=2; + + if (q->smooth_filter_len == 3) { + // Calibrated for filter length 3 + float w=q->smooth_filter[0]; + float a=7.419*w*w+0.1117*w-0.005387; + return (power/(a*0.8)); + } else { + return power; + } +} + +// The interpolator currently only supports same frequency allocation for each subframe +#define cesymb(i) ce[SRSLTE_RE_IDX(q->cell.nof_prb,i,n_prb[0]*SRSLTE_NRE)] +static void interpolate_pilots(srslte_chest_ul_t *q, cf_t *ce, uint32_t nrefs, uint32_t n_prb[2]) +{ + uint32_t L1 = SRSLTE_REFSIGNAL_UL_L(0, q->cell.cp); + uint32_t L2 = SRSLTE_REFSIGNAL_UL_L(1, q->cell.cp); + uint32_t NL = 2*SRSLTE_CP_NSYMB(q->cell.cp); + + /* Interpolate in the time domain between symbols */ + srslte_interp_linear_vector3(&q->srslte_interp_linvec, + &cesymb(L2), &cesymb(L1), &cesymb(L1), &cesymb(L1-1), (L2-L1), L1, false, nrefs); + srslte_interp_linear_vector3(&q->srslte_interp_linvec, + &cesymb(L1), &cesymb(L2), NULL, &cesymb(L1+1), (L2-L1), (L2-L1)-1, true, nrefs); + srslte_interp_linear_vector3(&q->srslte_interp_linvec, + &cesymb(L1), &cesymb(L2), &cesymb(L2), &cesymb(L2+1), (L2-L1), (NL-L2)-1, true, nrefs); + +} + +void srslte_chest_ul_set_smooth_filter(srslte_chest_ul_t *q, float *filter, uint32_t filter_len) { + if (filter_len < SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN) { + if (filter) { + memcpy(q->smooth_filter, filter, filter_len*sizeof(float)); + q->smooth_filter_len = filter_len; + } else { + q->smooth_filter_len = 0; + } + } else { + fprintf(stderr, "Error setting smoothing filter: filter len exceeds maximum (%d>%d)\n", + filter_len, SRSLTE_CHEST_MAX_SMOOTH_FIL_LEN); + } +} + +void srslte_chest_ul_set_smooth_filter3_coeff(srslte_chest_ul_t* q, float w) +{ + srslte_chest_set_smooth_filter3_coeff(q->smooth_filter, w); + q->smooth_filter_len = 3; +} + +static void average_pilots(srslte_chest_ul_t *q, cf_t *input, cf_t *ce, uint32_t nrefs, uint32_t n_prb[2]) { + for (int i=0;i<2;i++) { + srslte_chest_average_pilots(&input[i*nrefs], + &ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp)*q->cell.nof_prb*SRSLTE_NRE+n_prb[i]*SRSLTE_NRE], + q->smooth_filter, nrefs, 1, q->smooth_filter_len); + } +} + +int srslte_chest_ul_estimate(srslte_chest_ul_t *q, cf_t *input, cf_t *ce, + uint32_t nof_prb, uint32_t sf_idx, uint32_t cyclic_shift_for_dmrs, uint32_t n_prb[2]) +{ + if (!q->dmrs_signal_configured) { + fprintf(stderr, "Error must call srslte_chest_ul_set_cfg() before using the UL estimator\n"); + return SRSLTE_ERROR; + } + + if (!srslte_dft_precoding_valid_prb(nof_prb)) { + fprintf(stderr, "Error invalid nof_prb=%d\n", nof_prb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + int nrefs_sym = nof_prb*SRSLTE_NRE; + int nrefs_sf = nrefs_sym*2; + + /* Get references from the input signal */ + srslte_refsignal_dmrs_pusch_get(&q->dmrs_signal, input, nof_prb, n_prb, q->pilot_recv_signal); + + /* Use the known DMRS signal to compute Least-squares estimates */ + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->dmrs_pregen.r[cyclic_shift_for_dmrs][sf_idx][nof_prb], + q->pilot_estimates, nrefs_sf); + + if (n_prb[0] != n_prb[1]) { + printf("ERROR: intra-subframe frequency hopping not supported in the estimator!!\n"); + } + + if (ce != NULL) { + if (q->smooth_filter_len > 0) { + average_pilots(q, q->pilot_estimates, ce, nrefs_sym, n_prb); + interpolate_pilots(q, ce, nrefs_sym, n_prb); + + /* If averaging, compute noise from difference between received and averaged estimates */ + q->noise_estimate = estimate_noise_pilots(q, ce, nrefs_sym, n_prb); + } else { + // Copy estimates to CE vector without averaging + for (int i=0;i<2;i++) { + memcpy(&ce[SRSLTE_REFSIGNAL_UL_L(i, q->cell.cp)*q->cell.nof_prb*SRSLTE_NRE+n_prb[i]*SRSLTE_NRE], + &q->pilot_estimates[i*nrefs_sym], + nrefs_sym*sizeof(cf_t)); + } + interpolate_pilots(q, ce, nrefs_sym, n_prb); + q->noise_estimate = 0; + } + } + // Estimate received pilot power + q->pilot_power = srslte_vec_avg_power_cf(q->pilot_recv_signal, nrefs_sf); + return 0; +} + +int srslte_chest_ul_estimate_pucch(srslte_chest_ul_t *q, cf_t *input, cf_t *ce, + srslte_pucch_format_t format, uint32_t n_pucch, uint32_t sf_idx, + uint8_t *pucch2_ack_bits) +{ + if (!q->dmrs_signal_configured) { + fprintf(stderr, "Error must call srslte_chest_ul_set_cfg() before using the UL estimator\n"); + return SRSLTE_ERROR; + } + + int n_rs = srslte_refsignal_dmrs_N_rs(format, q->cell.cp); + if (!n_rs) { + fprintf(stderr, "Error computing N_rs\n"); + return SRSLTE_ERROR; + } + int nrefs_sf = SRSLTE_NRE*n_rs*2; + + /* Get references from the input signal */ + srslte_refsignal_dmrs_pucch_get(&q->dmrs_signal, format, n_pucch, input, q->pilot_recv_signal); + + /* Generate known pilots */ + uint8_t pucch2_bits[2] = {0, 0}; + if (format == SRSLTE_PUCCH_FORMAT_2A || format == SRSLTE_PUCCH_FORMAT_2B) { + float max = -1e9; + int i_max = 0; + + int m = 0; + if (format == SRSLTE_PUCCH_FORMAT_2A) { + m = 2; + } else { + m = 4; + } + + for (int i=0;idmrs_signal, format, n_pucch, sf_idx, pucch2_bits, q->pilot_known_signal); + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates_tmp[i], nrefs_sf); + float x = cabsf(srslte_vec_acc_cc(q->pilot_estimates_tmp[i], nrefs_sf)); + if (x >= max) { + max = x; + i_max = i; + } + } + memcpy(q->pilot_estimates, q->pilot_estimates_tmp[i_max], nrefs_sf*sizeof(cf_t)); + pucch2_ack_bits[0] = i_max%2; + pucch2_ack_bits[1] = i_max/2; + } else { + srslte_refsignal_dmrs_pucch_gen(&q->dmrs_signal, format, n_pucch, sf_idx, pucch2_bits, q->pilot_known_signal); + /* Use the known DMRS signal to compute Least-squares estimates */ + srslte_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf); + } + + if (ce != NULL) { + /* FIXME: Currently averaging entire slot, performance good enough? */ + for (int ns=0;ns<2;ns++) { + // Average all slot + for (int i=1;ipilot_estimates[ns*n_rs*SRSLTE_NRE], &q->pilot_estimates[(i+ns*n_rs)*SRSLTE_NRE], + &q->pilot_estimates[ns*n_rs*SRSLTE_NRE], + SRSLTE_NRE); + } + srslte_vec_sc_prod_ccc(&q->pilot_estimates[ns*n_rs*SRSLTE_NRE], (float) 1.0/n_rs, + &q->pilot_estimates[ns*n_rs*SRSLTE_NRE], + SRSLTE_NRE); + + // Average in freq domain + srslte_chest_average_pilots(&q->pilot_estimates[ns*n_rs*SRSLTE_NRE], &q->pilot_recv_signal[ns*n_rs*SRSLTE_NRE], + q->smooth_filter, SRSLTE_NRE, 1, q->smooth_filter_len); + + // Determine n_prb + uint32_t n_prb = srslte_pucch_n_prb(&q->dmrs_signal.pucch_cfg, format, n_pucch, q->cell.nof_prb, q->cell.cp, ns); + + // copy estimates to slot + for (int i=0;icell.cp);i++) { + memcpy(&ce[SRSLTE_RE_IDX(q->cell.nof_prb, i+ns*SRSLTE_CP_NSYMB(q->cell.cp), n_prb*SRSLTE_NRE)], + &q->pilot_recv_signal[ns*n_rs*SRSLTE_NRE], sizeof(cf_t)*SRSLTE_NRE); + } + } + } + + return 0; +} + + +float srslte_chest_ul_get_noise_estimate(srslte_chest_ul_t *q) { + return q->noise_estimate; +} + +float srslte_chest_ul_get_snr(srslte_chest_ul_t *q) { + return q->pilot_power/srslte_chest_ul_get_noise_estimate(q); +} + diff --git a/lib/src/phy/ch_estimation/refsignal_dl.c b/lib/src/phy/ch_estimation/refsignal_dl.c new file mode 100644 index 0000000..1c949f7 --- /dev/null +++ b/lib/src/phy/ch_estimation/refsignal_dl.c @@ -0,0 +1,464 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/ch_estimation/refsignal_dl.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/common/sequence.h" + +uint32_t srslte_refsignal_cs_v(uint32_t port_id, uint32_t ref_symbol_idx) +{ + uint32_t v = 0; + switch (port_id) { + case 0: + if (!(ref_symbol_idx % 2)) { + v = 0; + } else { + v = 3; + } + break; + case 1: + if (!(ref_symbol_idx % 2)) { + v = 3; + } else { + v = 0; + } + break; + case 2: + if (ref_symbol_idx == 0) { + v = 0; + } else { + v = 3; + } + break; + case 3: + if (ref_symbol_idx == 0) { + v = 3; + } else { + v = 0; + } + break; + } + return v; +} + +uint32_t srslte_refsignal_cs_nof_symbols(uint32_t port_id) +{ + uint32_t ret; + if (port_id < 2) { + ret = 4; + } else { + ret = 2; + } + return ret; +} + +uint32_t srslte_refsignal_mbsfn_nof_symbols() +{ + return 3; +} + + +inline uint32_t srslte_refsignal_cs_fidx(srslte_cell_t cell, uint32_t l, uint32_t port_id, uint32_t m) { + return 6*m + ((srslte_refsignal_cs_v(port_id, l) + (cell.id % 6)) % 6); +} + +inline uint32_t srslte_refsignal_mbsfn_fidx(uint32_t l) +{ + + uint32_t ret = 0; + if(l == 0){ + ret = 0; + }else if (l == 1){ + ret = 1; + }else if(l == 2){ + ret = 0; + } + + return ret; +} + +inline uint32_t srslte_refsignal_cs_nsymbol(uint32_t l, srslte_cp_t cp, uint32_t port_id) { + if (port_id < 2) { + if (l % 2) { + return (l/2+1)*SRSLTE_CP_NSYMB(cp) - 3; + } else { + return (l/2)*SRSLTE_CP_NSYMB(cp); + } + } else { + return 1+l*SRSLTE_CP_NSYMB(cp); + } +} + +inline uint32_t srslte_refsignal_mbsfn_nsymbol(uint32_t l) +{ + uint32_t ret = 0; + if(l == 0){ + ret = 2; + } else if (l == 1) { + ret = 6; + } else if (l == 2){ + ret = 10; + } + + return ret; +} + +int srslte_refsignal_mbsfn_gen_seq(srslte_refsignal_t * q, srslte_cell_t cell, uint32_t N_mbsfn_id) +{ + uint32_t c_init; + uint32_t i, ns, l, p; + uint32_t mp; + int ret = SRSLTE_ERROR; + + srslte_sequence_t seq_mbsfn; + bzero(&seq_mbsfn, sizeof(srslte_sequence_t)); + if (srslte_sequence_init(&seq_mbsfn, 20* SRSLTE_MAX_PRB)) { + goto free_and_exit; + } + + for(ns=0; nscell.nof_prb;i++) { + mp = i + 3*(SRSLTE_MAX_PRB - cell.nof_prb); + q->pilots[p][ns][ SRSLTE_REFSIGNAL_PILOT_IDX_MBSFN(i, l ,q->cell)] = (1 - 2 * (float) seq_mbsfn.c[2 * mp]) / sqrt(2) +_Complex_I * (1 - 2 * (float) seq_mbsfn.c[2 * mp + 1]) / sqrt(2); + } + } + } + } + + srslte_sequence_free(&seq_mbsfn); + ret = SRSLTE_SUCCESS; + +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_sequence_free(&seq_mbsfn); + srslte_refsignal_free(q); + } + return ret; + +} + + +int srslte_refsignal_mbsfn_init(srslte_refsignal_t * q, uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t i, p; + if (q != NULL) + { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_refsignal_t)); + + q->type = SRSLTE_SF_MBSFN; + + + for (p=0;p<2;p++) { + for (i=0;ipilots[p][i] = srslte_vec_malloc(sizeof(cf_t) * max_prb * 18); + if (!q->pilots[p][i]) { + perror("malloc"); + goto free_and_exit; + } + } + } + + + ret = SRSLTE_SUCCESS; + } + +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_free(q); + } + return ret; +} + +int srslte_refsignal_mbsfn_set_cell(srslte_refsignal_t * q, srslte_cell_t cell, uint16_t mbsfn_area_id){ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + q->cell = cell; + + q->mbsfn_area_id = mbsfn_area_id; + if(srslte_refsignal_mbsfn_gen_seq(q, q->cell, q->mbsfn_area_id)) { + goto free_and_exit; + } + + ret = SRSLTE_SUCCESS; + + free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_free(q); + } + return ret; +} + + +/** Allocates memory for the 20 slots in a subframe + */ +int srslte_refsignal_cs_init(srslte_refsignal_t * q, uint32_t max_prb) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_refsignal_t)); + for (int p=0;p<2;p++) { + for (int i=0;ipilots[p][i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_REFSIGNAL_NUM_SF(max_prb, 2*p)); + if (!q->pilots[p][i]) { + perror("malloc"); + goto free_and_exit; + } + } + } + ret = SRSLTE_SUCCESS; + } +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_free(q); + } + return ret; +} + +/** Allocates and precomputes the Cell-Specific Reference (CSR) signal for + * the 20 slots in a subframe + */ +int srslte_refsignal_cs_set_cell(srslte_refsignal_t * q, srslte_cell_t cell) +{ + + uint32_t c_init; + uint32_t i, ns, l, p; + uint32_t N_cp, mp; + srslte_sequence_t seq; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + bzero(&seq, sizeof(srslte_sequence_t)); + if (srslte_sequence_init(&seq, 2*2*SRSLTE_MAX_PRB)) { + return SRSLTE_ERROR; + } + + if (SRSLTE_CP_ISNORM(cell.cp)) { + N_cp = 1; + } else { + N_cp = 0; + } + + for (ns=0;nscell.nof_prb; i++) { + mp = i + SRSLTE_MAX_PRB - cell.nof_prb; + /* save signal */ + q->pilots[p][ns/2][SRSLTE_REFSIGNAL_PILOT_IDX(i,(ns%2)*nsymbols+l,q->cell)] = + (1 - 2 * (float) seq.c[2 * mp]) / sqrt(2) + + _Complex_I * (1 - 2 * (float) seq.c[2 * mp + 1]) / sqrt(2); + } + } + } + } + srslte_sequence_free(&seq); + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/** Deallocates a srslte_refsignal_cs_t object allocated with srslte_refsignal_cs_init */ +void srslte_refsignal_free(srslte_refsignal_t * q) +{ + for (int p=0;p<2;p++) { + for (int i=0;ipilots[p][i]) { + free(q->pilots[p][i]); + } + } + } + bzero(q, sizeof(srslte_refsignal_t)); +} + + + + +/* Maps a reference signal initialized with srslte_refsignal_cs_init() into an array of subframe symbols */ +int srslte_refsignal_cs_put_sf(srslte_cell_t cell, uint32_t port_id, cf_t *pilots, cf_t *sf_symbols) +{ + uint32_t i, l; + uint32_t fidx; + + if (srslte_cell_isvalid(&cell) && + srslte_portid_isvalid(port_id) && + pilots != NULL && + sf_symbols != NULL) + { + + for (l=0;l +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/ch_estimation/refsignal_ul.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/dft/dft_precoding.h" + +#include "ul_rs_tables.h" + +// n_dmrs_2 table 5.5.2.1.1-1 from 36.211 +uint32_t n_dmrs_2[8] = { 0, 6, 3, 4, 2, 8, 10, 9 }; + +// n_dmrs_1 table 5.5.2.1.1-2 from 36.211 +uint32_t n_dmrs_1[8] = { 0, 2, 3, 4, 6, 8, 9, 10 }; + +/* Orthogonal sequences for PUCCH formats 1a, 1b and 1c. Table 5.5.2.2.1-2 + */ +float w_arg_pucch_format1_cpnorm[3][3] = {{0, 0, 0}, + {0, 2*M_PI/3, 4*M_PI/3}, + {0, 4*M_PI/3, 2*M_PI/3}}; + +float w_arg_pucch_format1_cpext[3][2] = {{0, 0}, + {0, M_PI}, + {0, 0}}; + +float w_arg_pucch_format2_cpnorm[2] = {0, 0}; +float w_arg_pucch_format2_cpext[1] = {0}; + +uint32_t pucch_dmrs_symbol_format1_cpnorm[3] = {2, 3, 4}; +uint32_t pucch_dmrs_symbol_format1_cpext[2] = {2, 3}; +uint32_t pucch_dmrs_symbol_format2_cpnorm[2] = {1, 5}; +uint32_t pucch_dmrs_symbol_format2_cpext[1] = {3}; + +/* Table 5.5.3.3-1: Frame structure type 1 sounding reference signal subframe configuration. */ +uint32_t T_sfc[15] = {1, 2, 2, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10}; +uint32_t Delta_sfc1[7] = {0, 0, 1, 0, 1, 2, 3}; +uint32_t Delta_sfc2[4] = {0, 1, 2, 3}; + + +uint32_t m_srs_b[4][4][8] = {{ + /* m_srs for 6cell.id / 30) << 5) + (((q->cell.id % 30) + delta_ss) % 30); + if (srslte_sequence_LTE_pr(&seq, 8 * SRSLTE_CP_NSYMB(q->cell.cp) * 20, c_init)) { + return SRSLTE_ERROR; + } + for (uint32_t ns=0;nscell.cp) * ns + i] << i); + } + q->n_prs_pusch[delta_ss][ns] = n_prs; + } + } + srslte_sequence_free(&seq); + + return SRSLTE_SUCCESS; +} + +void srslte_refsignal_r_uv_arg_1prb(float *arg, uint32_t u) { + for (int i = 0; i < SRSLTE_NRE; i++) { + arg[i] = phi_M_sc_12[u][i] * M_PI / 4; + } +} + +static int generate_srslte_sequence_hopping_v(srslte_refsignal_ul_t *q) { + srslte_sequence_t seq; + bzero(&seq, sizeof(srslte_sequence_t)); + + for (uint32_t ns=0;nscell.id / 30) << 5) + ((q->cell.id%30)+delta_ss)%30)) { + return SRSLTE_ERROR; + } + q->v_pusch[ns][delta_ss] = seq.c[ns]; + } + } + srslte_sequence_free(&seq); + return SRSLTE_SUCCESS; +} + + +/** Initializes srslte_refsignal_ul_t object according to 3GPP 36.211 5.5 + * + */ +int srslte_refsignal_ul_init(srslte_refsignal_ul_t * q, uint32_t max_prb) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_refsignal_ul_t)); + + // Allocate temporal buffer for computing signal argument + q->tmp_arg = srslte_vec_malloc(SRSLTE_NRE * max_prb * sizeof(cf_t)); + if (!q->tmp_arg) { + perror("malloc"); + goto free_and_exit; + } + + ret = SRSLTE_SUCCESS; + } +free_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_refsignal_ul_free(q); + } + return ret; +} + +void srslte_refsignal_ul_free(srslte_refsignal_ul_t * q) { + if (q->tmp_arg) { + free(q->tmp_arg); + } + bzero(q, sizeof(srslte_refsignal_ul_t)); +} + +/** Initializes srslte_refsignal_ul_t object according to 3GPP 36.211 5.5 + * + */ +int srslte_refsignal_ul_set_cell(srslte_refsignal_ul_t * q, srslte_cell_t cell) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_cell_isvalid(&cell)) { + + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + srslte_pucch_cfg_default(&q->pucch_cfg); + + // Precompute n_prs + if (generate_n_prs(q)) { + return SRSLTE_ERROR; + } + + // Precompute group hopping values u. + if (srslte_group_hopping_f_gh(q->f_gh, q->cell.id)) { + return SRSLTE_ERROR; + } + + // Precompute sequence hopping values v. Uses f_ss_pusch + if (generate_srslte_sequence_hopping_v(q)) { + return SRSLTE_ERROR; + } + + if (srslte_pucch_n_cs_cell(q->cell, q->n_cs_cell)) { + return SRSLTE_ERROR; + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +void srslte_refsignal_ul_set_cfg(srslte_refsignal_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg) +{ + if (pusch_cfg) { + memcpy(&q->pusch_cfg, pusch_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + } + if (pucch_cfg) { + if (srslte_pucch_cfg_isvalid(pucch_cfg, q->cell.nof_prb)) { + memcpy(&q->pucch_cfg, pucch_cfg, sizeof(srslte_pucch_cfg_t)); + } else { + fprintf(stderr, "Invalid PUCCH configuration in refsignal_ul\n"); + } + } + if (srs_cfg) { + memcpy(&q->srs_cfg, srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + } +} + +uint32_t largest_prime_lower_than(uint32_t x) { + /* get largest prime n_zc 0; i--) { + if (prime_numbers[i] < x) { + return prime_numbers[i]; + } + } + return 0; +} + +static void arg_r_uv_2prb(float *arg, uint32_t u) { + for (int i = 0; i < 2*SRSLTE_NRE; i++) { + arg[i] = phi_M_sc_24[u][i] * M_PI / 4; + } +} + +static uint32_t get_q(uint32_t u, uint32_t v, uint32_t N_sz) { + float q; + float q_hat; + float n_sz = (float) N_sz; + + q_hat = n_sz *(u + 1) / 31; + if ((((uint32_t) (2 * q_hat)) % 2) == 0) { + q = q_hat + 0.5 + v; + } else { + q = q_hat + 0.5 - v; + } + return (uint32_t) q; +} + +static void arg_r_uv_mprb(float *arg, uint32_t M_sc, uint32_t u, uint32_t v) { + + uint32_t N_sz = largest_prime_lower_than(M_sc); + if (N_sz > 0) { + float q = get_q(u,v,N_sz); + float n_sz = (float) N_sz; + for (uint32_t i = 0; i < M_sc; i++) { + float m = (float) (i%N_sz); + arg[i] = -M_PI * q * m * (m + 1) / n_sz; + } + } +} + +/* Computes argument of r_u_v signal */ +static void compute_r_uv_arg(srslte_refsignal_ul_t *q, uint32_t nof_prb, uint32_t u, uint32_t v) { + if (nof_prb == 1) { + srslte_refsignal_r_uv_arg_1prb(q->tmp_arg, u); + } else if (nof_prb == 2) { + arg_r_uv_2prb(q->tmp_arg, u); + } else { + arg_r_uv_mprb(q->tmp_arg, SRSLTE_NRE*nof_prb, u, v); + } +} + +/* Calculates alpha according to 5.5.2.1.1 of 36.211 */ +static float pusch_alpha(srslte_refsignal_ul_t *q, srslte_refsignal_dmrs_pusch_cfg_t *cfg, + uint32_t cyclic_shift_for_dmrs, uint32_t ns) +{ + uint32_t n_dmrs_2_val = n_dmrs_2[cyclic_shift_for_dmrs]; + uint32_t n_cs = (n_dmrs_1[cfg->cyclic_shift] + n_dmrs_2_val + q->n_prs_pusch[cfg->delta_ss][ns]) % 12; + + return 2 * M_PI * (n_cs) / 12; + +} + +bool srslte_refsignal_dmrs_pusch_cfg_isvalid(srslte_refsignal_ul_t *q, srslte_refsignal_dmrs_pusch_cfg_t *cfg, + uint32_t nof_prb) { + if (cfg->cyclic_shift < SRSLTE_NOF_CSHIFT && + cfg->delta_ss < SRSLTE_NOF_DELTA_SS && + nof_prb <= q->cell.nof_prb) { + return true; + } else { + return false; + } +} + +void srslte_refsignal_dmrs_pusch_put(srslte_refsignal_ul_t *q, cf_t *r_pusch, uint32_t nof_prb, uint32_t n_prb[2], cf_t *sf_symbols) +{ + for (uint32_t ns_idx=0;ns_idx<2;ns_idx++) { + INFO("Putting DRMS to n_prb: %d, L: %d, ns_idx: %d\n", n_prb[ns_idx], nof_prb, ns_idx); + uint32_t L = SRSLTE_REFSIGNAL_UL_L(ns_idx, q->cell.cp); + memcpy(&sf_symbols[SRSLTE_RE_IDX(q->cell.nof_prb, L, n_prb[ns_idx]*SRSLTE_NRE)], + &r_pusch[ns_idx*SRSLTE_NRE*nof_prb], nof_prb*SRSLTE_NRE*sizeof(cf_t)); + } +} + +void srslte_refsignal_dmrs_pusch_get(srslte_refsignal_ul_t *q, cf_t *sf_symbols, uint32_t nof_prb, uint32_t n_prb[2], cf_t *r_pusch) +{ + for (uint32_t ns_idx=0;ns_idx<2;ns_idx++) { + INFO("Getting DRMS from n_prb: %d, L: %d, ns_idx: %d\n", n_prb[ns_idx], nof_prb, ns_idx); + uint32_t L = SRSLTE_REFSIGNAL_UL_L(ns_idx, q->cell.cp); + memcpy(&r_pusch[ns_idx*SRSLTE_NRE*nof_prb], + &sf_symbols[SRSLTE_RE_IDX(q->cell.nof_prb, L, n_prb[ns_idx]*SRSLTE_NRE)], + nof_prb*SRSLTE_NRE*sizeof(cf_t)); + } +} + +/* Computes r sequence */ +void compute_r(srslte_refsignal_ul_t *q, uint32_t nof_prb, uint32_t ns, uint32_t delta_ss) { + // Get group hopping number u + uint32_t f_gh=0; + if (q->pusch_cfg.group_hopping_en) { + f_gh = q->f_gh[ns]; + } + uint32_t u = (f_gh + (q->cell.id%30)+delta_ss)%30; + + // Get sequence hopping number v + uint32_t v = 0; + if (nof_prb >= 6 && q->pusch_cfg.sequence_hopping_en) { + v = q->v_pusch[ns][q->pusch_cfg.delta_ss]; + } + + // Compute signal argument + compute_r_uv_arg(q, nof_prb, u, v); + +} + +int srslte_refsignal_dmrs_pusch_pregen_init(srslte_refsignal_ul_t *q, srslte_refsignal_ul_dmrs_pregen_t *pregen, + uint32_t max_prb) +{ + for (uint32_t sf_idx=0;sf_idxr[cs][sf_idx] = (cf_t**) calloc(sizeof(cf_t*), max_prb + 1); + if (pregen->r[cs][sf_idx]) { + for (uint32_t n=0;n<=max_prb;n++) { + if (srslte_dft_precoding_valid_prb(n)) { + pregen->r[cs][sf_idx][n] = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*n*2*SRSLTE_NRE); + if (pregen->r[cs][sf_idx][n]) { + } else { + return SRSLTE_ERROR; + } + } + } + } else { + return SRSLTE_ERROR; + } + } + } + return SRSLTE_SUCCESS; +} + + +int srslte_refsignal_dmrs_pusch_pregen(srslte_refsignal_ul_t *q, srslte_refsignal_ul_dmrs_pregen_t *pregen) +{ + for (uint32_t sf_idx=0;sf_idxr[cs][sf_idx]) { + for (uint32_t n=0;n<=q->cell.nof_prb;n++) { + if (srslte_dft_precoding_valid_prb(n)) { + if (pregen->r[cs][sf_idx][n]) { + if (srslte_refsignal_dmrs_pusch_gen(q, n, sf_idx, cs, pregen->r[cs][sf_idx][n])) { + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } + } + } + } else { + return SRSLTE_ERROR; + } + } + } + return SRSLTE_SUCCESS; +} + +void srslte_refsignal_dmrs_pusch_pregen_free(srslte_refsignal_ul_t *q, srslte_refsignal_ul_dmrs_pregen_t *pregen) +{ + for (uint32_t sf_idx=0;sf_idxr[cs][sf_idx]) { + for (uint32_t n=0;n<=q->cell.nof_prb;n++) { + if (srslte_dft_precoding_valid_prb(n)) { + if (pregen->r[cs][sf_idx][n]) { + free(pregen->r[cs][sf_idx][n]); + } + } + } + free(pregen->r[cs][sf_idx]); + } + } + } +} + +int srslte_refsignal_dmrs_pusch_pregen_put(srslte_refsignal_ul_t *q, + srslte_refsignal_ul_dmrs_pregen_t *pregen, + uint32_t nof_prb, + uint32_t sf_idx, + uint32_t cyclic_shift_for_dmrs, + uint32_t n_prb[2], + cf_t *sf_symbols) +{ + if (srslte_dft_precoding_valid_prb(nof_prb) && + sf_idx < SRSLTE_NSUBFRAMES_X_FRAME && + cyclic_shift_for_dmrs < SRSLTE_NOF_CSHIFT) + { + srslte_refsignal_dmrs_pusch_put(q, pregen->r[cyclic_shift_for_dmrs][sf_idx][nof_prb], nof_prb, n_prb, sf_symbols); + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + +/* Generate DRMS for PUSCH signal according to 5.5.2.1 of 36.211 */ +int srslte_refsignal_dmrs_pusch_gen(srslte_refsignal_ul_t *q, uint32_t nof_prb, uint32_t sf_idx, + uint32_t cyclic_shift_for_dmrs, cf_t *r_pusch) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (srslte_refsignal_dmrs_pusch_cfg_isvalid(q, &q->pusch_cfg, nof_prb)) { + ret = SRSLTE_ERROR; + + for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) { + + compute_r(q, nof_prb, ns, q->pusch_cfg.delta_ss); + + // Add cyclic prefix alpha + float alpha = pusch_alpha(q, &q->pusch_cfg, cyclic_shift_for_dmrs, ns); + + // Do complex exponential and adjust amplitude + for (int i=0;itmp_arg[i] + alpha*i)); + } + } + ret = 0; + } + return ret; +} + +/* Number of PUCCH demodulation reference symbols per slot N_rs_pucch tABLE 5.5.2.2.1-1 36.211 */ +uint32_t srslte_refsignal_dmrs_N_rs(srslte_pucch_format_t format, srslte_cp_t cp) { + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + if (SRSLTE_CP_ISNORM(cp)) { + return 3; + } else { + return 2; + } + case SRSLTE_PUCCH_FORMAT_2: + if (SRSLTE_CP_ISNORM(cp)) { + return 2; + } else { + return 1; + } + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + return 2; + default: + fprintf(stderr, "Unsupported format %d\n", format); + return 0; + } + return 0; +} + +/* Table 5.5.2.2.2-1: Demodulation reference signal location for different PUCCH formats. 36.211 */ +uint32_t srslte_refsignal_dmrs_pucch_symbol(uint32_t m, srslte_pucch_format_t format, srslte_cp_t cp) { + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + if (SRSLTE_CP_ISNORM(cp)) { + if (m < 3) { + return pucch_dmrs_symbol_format1_cpnorm[m]; + } + } else { + if (m < 2) { + return pucch_dmrs_symbol_format1_cpext[m]; + } + } + break; + case SRSLTE_PUCCH_FORMAT_2: + if (SRSLTE_CP_ISNORM(cp)) { + if (m < 2) { + return pucch_dmrs_symbol_format2_cpnorm[m]; + } + } else { + if (m < 1) { + return pucch_dmrs_symbol_format2_cpext[m]; + } + } + break; + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + if (m < 2) { + return pucch_dmrs_symbol_format2_cpnorm[m]; + } + break; + default: + fprintf(stderr, "Unsupported format %d\n", format); + return 0; + } + return 0; +} + +/* Generates DMRS for PUCCH according to 5.5.2.2 in 36.211 */ +int srslte_refsignal_dmrs_pucch_gen(srslte_refsignal_ul_t *q, srslte_pucch_format_t format, uint32_t n_pucch, + uint32_t sf_idx, uint8_t pucch_bits[2], cf_t *r_pucch) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q && r_pucch) { + ret = SRSLTE_ERROR; + + uint32_t N_rs=srslte_refsignal_dmrs_N_rs(format, q->cell.cp); + + cf_t z_m_1 = 1.0; + if (format == SRSLTE_PUCCH_FORMAT_2A || format == SRSLTE_PUCCH_FORMAT_2B) { + srslte_pucch_format2ab_mod_bits(format, pucch_bits, &z_m_1); + } + + for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) { + // Get group hopping number u + uint32_t f_gh=0; + if (q->pusch_cfg.group_hopping_en) { + f_gh = q->f_gh[ns]; + } + uint32_t u = (f_gh + (q->cell.id%30))%30; + + srslte_refsignal_r_uv_arg_1prb(q->tmp_arg, u); + + for (uint32_t m=0;mcell.cp); + // Add cyclic prefix alpha + float alpha = 0.0; + if (format < SRSLTE_PUCCH_FORMAT_2) { + alpha = srslte_pucch_alpha_format1(q->n_cs_cell, &q->pucch_cfg, n_pucch, q->cell.cp, true, ns, l, &n_oc, NULL); + } else { + alpha = srslte_pucch_alpha_format2(q->n_cs_cell, &q->pucch_cfg, n_pucch, ns, l); + } + + // Choose number of symbols and orthogonal sequence from Tables 5.5.2.2.1-1 to -3 + float *w=NULL; + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + if (SRSLTE_CP_ISNORM(q->cell.cp)) { + w=w_arg_pucch_format1_cpnorm[n_oc]; + } else { + w=w_arg_pucch_format1_cpext[n_oc]; + } + break; + case SRSLTE_PUCCH_FORMAT_2: + if (SRSLTE_CP_ISNORM(q->cell.cp)) { + w=w_arg_pucch_format2_cpnorm; + } else { + w=w_arg_pucch_format2_cpext; + } + break; + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + w=w_arg_pucch_format2_cpnorm; + break; + default: + fprintf(stderr, "Unsupported format %d\n", format); + return SRSLTE_ERROR; + } + cf_t z_m = 1.0; + if (m == 1) { + z_m = z_m_1; + } + for (uint32_t n=0;ntmp_arg[n]+alpha*n)); + } + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_refsignal_dmrs_pucch_cp(srslte_refsignal_ul_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *source, cf_t *dest, bool source_is_grid) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q && source && dest) { + ret = SRSLTE_ERROR; + uint32_t nsymbols = SRSLTE_CP_ISNORM(q->cell.cp)?SRSLTE_CP_NORM_NSYMB:SRSLTE_CP_EXT_NSYMB; + + uint32_t N_rs = srslte_refsignal_dmrs_N_rs(format, q->cell.cp); + for (uint32_t ns=0;ns<2;ns++) { + + // Determine n_prb + uint32_t n_prb = srslte_pucch_n_prb(&q->pucch_cfg, format, n_pucch, q->cell.nof_prb, q->cell.cp, ns); + + for (uint32_t i=0;icell.cp); + if (!source_is_grid) { + memcpy(&dest[SRSLTE_RE_IDX(q->cell.nof_prb, l+ns*nsymbols, n_prb*SRSLTE_NRE)], + &source[ns*N_rs*SRSLTE_NRE+i*SRSLTE_NRE], + SRSLTE_NRE*sizeof(cf_t)); + } else { + memcpy(&dest[ns*N_rs*SRSLTE_NRE+i*SRSLTE_NRE], + &source[SRSLTE_RE_IDX(q->cell.nof_prb, l+ns*nsymbols, n_prb*SRSLTE_NRE)], + SRSLTE_NRE*sizeof(cf_t)); + } + } + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Maps PUCCH DMRS to the physical resources as defined in 5.5.2.2.2 in 36.211 */ +int srslte_refsignal_dmrs_pucch_put(srslte_refsignal_ul_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *r_pucch, cf_t *output) +{ + return srslte_refsignal_dmrs_pucch_cp(q, format, n_pucch, r_pucch, output, false); +} + +/* Gets PUCCH DMRS from the physical resources as defined in 5.5.2.2.2 in 36.211 */ +int srslte_refsignal_dmrs_pucch_get(srslte_refsignal_ul_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *input, cf_t *r_pucch) +{ + return srslte_refsignal_dmrs_pucch_cp(q, format, n_pucch, input, r_pucch, true); +} + + +uint32_t T_srs_table(uint32_t I_srs) { + uint32_t T_srs; + /* This is Table 8.2-1 */ + if (I_srs < 2) { + T_srs = 2; + } else if (I_srs < 7) { + T_srs = 5; + } else if (I_srs < 17) { + T_srs = 10; + } else if (I_srs < 37) { + T_srs = 20; + } else if (I_srs < 77) { + T_srs = 40; + } else if (I_srs < 157) { + T_srs = 80; + } else if (I_srs < 317) { + T_srs = 160; + } else if (I_srs < 637) { + T_srs = 320; + } else { + T_srs = 0; + } + return T_srs; +} + +/* Returns 1 if tti is a valid subframe for SRS transmission according to I_srs (UE-specific + * configuration index), as defined in Section 8.1 of 36.213. + * Returns 0 if no SRS shall be transmitted or a negative number if error. + */ +int srslte_refsignal_srs_send_ue(uint32_t I_srs, uint32_t tti) { + if (I_srs < 1024 && tti < 10240) { + uint32_t Toffset = 0; + /* This is Table 8.2-1 */ + if (I_srs < 2) { + Toffset = I_srs; + } else if (I_srs < 7) { + Toffset = I_srs-2; + } else if (I_srs < 17) { + Toffset = I_srs-7; + } else if (I_srs < 37) { + Toffset = I_srs-17; + } else if (I_srs < 77) { + Toffset = I_srs-37; + } else if (I_srs < 157) { + Toffset = I_srs-77; + } else if (I_srs < 317) { + Toffset = I_srs-157; + } else if (I_srs < 637) { + Toffset = I_srs-317; + } else { + return 0; + } + if (((tti-Toffset)%T_srs_table(I_srs)) == 0) { + return 1; + } else { + return 0; + } + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/* Returns 1 if sf_idx is a valid subframe for SRS transmission according to subframe_config (cell-specific), + * as defined in Section 5.5.3.3 of 36.211. Returns 0 if no SRS shall be transmitted or a negative + * number if error. + */ +int srslte_refsignal_srs_send_cs(uint32_t subframe_config, uint32_t sf_idx) { + if (subframe_config < 15 && sf_idx < 10) { + uint32_t tsfc = T_sfc[subframe_config]; + if (subframe_config < 7) { + if ((sf_idx%tsfc)==Delta_sfc1[subframe_config]) { + return 1; + } else { + return 0; + } + } else if (subframe_config == 7) { + if (((sf_idx%tsfc)==0) || ((sf_idx%tsfc)==1)){ + return 1; + } else { + return 0; + } + } else if (subframe_config == 8) { + if (((sf_idx%tsfc)==2) || ((sf_idx%tsfc)==3)){ + return 1; + } else { + return 0; + } + } else if (subframe_config < 13) { + if ((sf_idx%tsfc)==Delta_sfc2[subframe_config-9]) { + return 1; + } else { + return 0; + } + } else if (subframe_config == 13) { + if (((sf_idx%tsfc)==5) || ((sf_idx%tsfc)==7) || ((sf_idx%tsfc)==9)){ + return 0; + } else { + return 1; + } + } else if (subframe_config == 14) { + if (((sf_idx%tsfc)==7) || ((sf_idx%tsfc)==9)) { + return 0; + } else { + return 1; + } + } else { + return 0; + } + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +uint32_t srsbwtable_idx(uint32_t nof_prb) { + if (nof_prb <= 40) { + return 0; + } else if (nof_prb <= 60) { + return 1; + } else if (nof_prb <= 80) { + return 2; + } else { + return 3; + } +} + +/* Returns start of common SRS BW region */ +uint32_t srslte_refsignal_srs_rb_start_cs(uint32_t bw_cfg, uint32_t nof_prb) { + if (bw_cfg < 8) { + return nof_prb/2-m_srs_b[srsbwtable_idx(nof_prb)][0][bw_cfg]/2; + } + return 0; +} + +/* Returns number of RB defined for the cell-specific SRS */ +uint32_t srslte_refsignal_srs_rb_L_cs(uint32_t bw_cfg, uint32_t nof_prb) { + if (bw_cfg < 8) { + return m_srs_b[srsbwtable_idx(nof_prb)][0][bw_cfg]; + } + return 0; +} + +uint32_t srs_Fb(srslte_refsignal_srs_cfg_t *cfg, uint32_t b, uint32_t nof_prb, uint32_t tti) { + uint32_t Fb = 0; + uint32_t T = T_srs_table(cfg->I_srs); + if (T) { + uint32_t n_srs = tti/T; + uint32_t N_b = Nb[srsbwtable_idx(nof_prb)][b][cfg->bw_cfg]; + + uint32_t prod_1=1; + for (uint32_t bp=cfg->b_hop+1;bpbw_cfg]; + } + uint32_t prod_2 = prod_1*Nb[srsbwtable_idx(nof_prb)][b][cfg->bw_cfg]; + if ((N_b%2) == 0) { + Fb = (N_b/2)*((n_srs%prod_2)/prod_1)+((n_srs%prod_2)/prod_1/2); + } else { + Fb = (N_b/2)*(n_srs/prod_1); + } + } + return Fb; +} + +/* Returns k0: frequency-domain starting position for ue-specific SRS */ +uint32_t srs_k0_ue(srslte_refsignal_srs_cfg_t *cfg, uint32_t nof_prb, uint32_t tti) { + + if (cfg->bw_cfg < 8 && cfg->B < 4 && cfg->k_tc < 2) { + uint32_t k0p = srslte_refsignal_srs_rb_start_cs(cfg->bw_cfg, nof_prb)*SRSLTE_NRE + cfg->k_tc; + uint32_t k0 = k0p; + uint32_t nb = 0; + for (int b=0;b<=cfg->B;b++) { + uint32_t m_srs = m_srs_b[srsbwtable_idx(nof_prb)][b][cfg->bw_cfg]; + uint32_t m_sc = m_srs*SRSLTE_NRE/2; + if (b <= cfg->b_hop) { + nb = (4*cfg->n_rrc/m_srs)%Nb[srsbwtable_idx(nof_prb)][b][cfg->bw_cfg]; + } else { + uint32_t Fb=srs_Fb(cfg, b, nof_prb, tti); + nb = ((4*cfg->n_rrc/m_srs)+Fb)%Nb[srsbwtable_idx(nof_prb)][b][cfg->bw_cfg]; + } + k0 += 2*m_sc*nb; + + } + return k0; + } + return 0; +} + +uint32_t srslte_refsignal_srs_M_sc(srslte_refsignal_ul_t *q) { + return m_srs_b[srsbwtable_idx(q->cell.nof_prb)][q->srs_cfg.B][q->srs_cfg.bw_cfg]*SRSLTE_NRE/2; +} + +int srslte_refsignal_srs_pregen(srslte_refsignal_ul_t *q, srslte_refsignal_srs_pregen_t *pregen) +{ + uint32_t M_sc = srslte_refsignal_srs_M_sc(q); + for (uint32_t sf_idx=0;sf_idxr[sf_idx] = srslte_vec_malloc(2*M_sc*sizeof(cf_t)); + if (pregen->r[sf_idx]) { + if (srslte_refsignal_srs_gen(q, sf_idx, pregen->r[sf_idx])) { + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } + } + return SRSLTE_SUCCESS; +} + +void srslte_refsignal_srs_pregen_free(srslte_refsignal_ul_t *q, srslte_refsignal_srs_pregen_t *pregen) +{ + for (uint32_t sf_idx=0;sf_idxr[sf_idx]) { + free(pregen->r[sf_idx]); + } + } +} + +int srslte_refsignal_srs_pregen_put(srslte_refsignal_ul_t *q, srslte_refsignal_srs_pregen_t *pregen, + uint32_t tti, cf_t *sf_symbols) +{ + return srslte_refsignal_srs_put(q, tti, pregen->r[tti%SRSLTE_NSUBFRAMES_X_FRAME], sf_symbols); +} + + +/* Genearte SRS signal as defined in Section 5.5.3.1 */ +int srslte_refsignal_srs_gen(srslte_refsignal_ul_t *q, uint32_t sf_idx, cf_t *r_srs) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (r_srs && q) { + ret = SRSLTE_ERROR; + + uint32_t M_sc = srslte_refsignal_srs_M_sc(q); + for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) { + + compute_r(q, M_sc/SRSLTE_NRE, ns, 0); + float alpha = 2*M_PI*q->srs_cfg.n_srs/8; + + // Do complex exponential and adjust amplitude + for (int i=0;itmp_arg[i] + alpha*i)); + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_refsignal_srs_put(srslte_refsignal_ul_t *q, uint32_t tti, cf_t *r_srs, cf_t *sf_symbols) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (r_srs && q) { + ret = SRSLTE_ERROR; + + uint32_t M_sc = srslte_refsignal_srs_M_sc(q); + uint32_t k0 = srs_k0_ue(&q->srs_cfg, q->cell.nof_prb, tti); + for (int i=0;icell.nof_prb, 2*SRSLTE_CP_NSYMB(q->cell.cp)-1, k0 + 2*i)] = r_srs[i]; + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + diff --git a/lib/src/phy/ch_estimation/test/CMakeLists.txt b/lib/src/phy/ch_estimation/test/CMakeLists.txt new file mode 100644 index 0000000..c02c390 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/CMakeLists.txt @@ -0,0 +1,54 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# Downlink Channel Estimation TEST +######################################################################## + +add_executable(chest_test_dl chest_test_dl.c) +target_link_libraries(chest_test_dl srslte_phy) + +add_test(chest_test_dl_cellid0 chest_test_dl -c 0) +add_test(chest_test_dl_cellid1 chest_test_dl -c 1) +add_test(chest_test_dl_cellid2 chest_test_dl -c 2) + +add_test(chest_test_dl_cellid0 chest_test_dl -c 0 -r 50) +add_test(chest_test_dl_cellid1 chest_test_dl -c 1 -r 50) +add_test(chest_test_dl_cellid2 chest_test_dl -c 2 -r 50) + + +######################################################################## +# Uplink Channel Estimation TEST +######################################################################## + +add_executable(chest_test_ul chest_test_ul.c) +target_link_libraries(chest_test_ul srslte_phy srslte_common) + +add_executable(refsignal_ul_test_all refsignal_ul_test.c) +target_link_libraries(refsignal_ul_test_all srslte_phy srslte_common) + +add_test(chest_test_ul_cellid0 chest_test_ul -c 0 -r 50) +add_test(chest_test_ul_cellid1 chest_test_ul -c 1 -r 50) +add_test(chest_test_ul_cellid1 chest_test_ul -c 2 -r 50) + + + + + diff --git a/lib/src/phy/ch_estimation/test/chest_test_dl.c b/lib/src/phy/ch_estimation/test/chest_test_dl.c new file mode 100644 index 0000000..65cc8ce --- /dev/null +++ b/lib/src/phy/ch_estimation/test/chest_test_dl.c @@ -0,0 +1,253 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 1000, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, + SRSLTE_PHICH_R_1_6 +}; + +char *output_matlab = NULL; + +void usage(char *prog) { + printf("Usage: %s [recov]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id); + + printf("\t-o output matlab file [Default %s]\n",output_matlab?output_matlab:"None"); + printf("\t-v increase verbosity\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "recov")) != -1) { + switch(opt) { + case 'r': + cell.nof_prb = atoi(argv[optind]); + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'o': + output_matlab = argv[optind]; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + srslte_chest_dl_t est; + cf_t *input = NULL, *ce = NULL, *h = NULL, *output = NULL; + int i, j, n_port=0, sf_idx=0, cid=0, num_re; + int ret = -1; + int max_cid; + FILE *fmatlab = NULL; + + parse_args(argc,argv); + + if (output_matlab) { + fmatlab=fopen(output_matlab, "w"); + if (!fmatlab) { + perror("fopen"); + goto do_exit; + } + } + + num_re = 2 * cell.nof_prb * SRSLTE_NRE * SRSLTE_CP_NSYMB(cell.cp); + + input = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!input) { + perror("srslte_vec_malloc"); + goto do_exit; + } + output = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!output) { + perror("srslte_vec_malloc"); + goto do_exit; + } + h = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!h) { + perror("srslte_vec_malloc"); + goto do_exit; + } + ce = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!ce) { + perror("srslte_vec_malloc"); + goto do_exit; + } + + if (cell.id == 1000) { + cid = 0; + max_cid = 504; + } else { + cid = cell.id; + max_cid = cell.id; + } + if (srslte_chest_dl_init(&est, cell.nof_prb)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + while(cid <= max_cid) { + cell.id = cid; + if (srslte_chest_dl_set_cell(&est, cell)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + + for (sf_idx=0;sf_idx<1;sf_idx++) { + for (n_port=0;n_port 2.0) { + goto do_exit; + } + + if (fmatlab) { + fprintf(fmatlab, "input="); + srslte_vec_fprint_c(fmatlab, input, num_re); + fprintf(fmatlab, ";\n"); + fprintf(fmatlab, "h="); + srslte_vec_fprint_c(fmatlab, h, num_re); + fprintf(fmatlab, ";\n"); + fprintf(fmatlab, "ce="); + srslte_vec_fprint_c(fmatlab, ce, num_re); + fprintf(fmatlab, ";\n"); + } + } + } + cid+=10; + INFO("cid=%d\n", cid); + } + srslte_chest_dl_free(&est); + + + ret = 0; + +do_exit: + + if (output) { + free(output); + } + if (ce) { + free(ce); + } + if (input) { + free(input); + } + if (h) { + free(h); + } + + if (!ret) { + printf("OK\n"); + } else { + printf("Error at cid=%d, slot=%d, port=%d\n",cid, sf_idx, n_port); + } + + exit(ret); +} diff --git a/lib/src/phy/ch_estimation/test/chest_test_dl_mex.c b/lib/src/phy/ch_estimation/test/chest_test_dl_mex.c new file mode 100644 index 0000000..0465ba6 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/chest_test_dl_mex.c @@ -0,0 +1,159 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("[estChannel, noiseEst, eq_output] = srslte_chest_dl(enb, inputSignal, [w_coeff])\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + int i; + srslte_cell_t cell; + srslte_chest_dl_t chest; + + cf_t *input_signal = NULL, *output_signal = NULL, *tmp_x[SRSLTE_MAX_LAYERS]; + cf_t *ce[SRSLTE_MAX_PORTS]; + + for (int i=0;i NOF_INPUTS) { + float w = (float) mxGetScalar(prhs[NOF_INPUTS]); + srslte_chest_dl_set_smooth_filter3_coeff(&chest, w); + } else { + srslte_chest_dl_set_smooth_filter(&chest, NULL, 0); + } + + // Perform channel estimation + if (srslte_chest_dl_estimate(&chest, input_signal, ce, sf_idx)) { + mexErrMsgTxt("Error running channel estimator\n"); + return; + } + + // Get noise power estimation + float noise_power = srslte_chest_dl_get_noise_estimate(&chest); + + // Perform channel equalization + if (cell.nof_ports == 1) { + srslte_predecoding_single(input_signal, ce[0], output_signal, nof_re, noise_power); + } else { + srslte_predecoding_diversity(input_signal, ce, tmp_x, cell.nof_ports, nof_re); + srslte_layerdemap_diversity(tmp_x, output_signal, cell.nof_ports, nof_re/cell.nof_ports); + } + + /* Write output values */ + if (nlhs >= 1) { + mexutils_write_cf(ce[0], &plhs[0], mxGetM(INPUT), mxGetN(INPUT)); + } + if (nlhs >= 2) { + plhs[1] = mxCreateLogicalScalar(noise_power); + } + if (nlhs >= 3) { + mexutils_write_cf(output_signal, &plhs[2], mxGetM(INPUT), mxGetN(INPUT)); + } + + // Free all memory + srslte_chest_dl_free(&chest); + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 0, + 1000, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM +}; + +char *output_matlab = NULL; + +void usage(char *prog) { + printf("Usage: %s [recov]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id); + + printf("\t-o output matlab file [Default %s]\n",output_matlab?output_matlab:"None"); + printf("\t-v increase verbosity\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "recov")) != -1) { + switch(opt) { + case 'r': + cell.nof_prb = atoi(argv[optind]); + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'o': + output_matlab = argv[optind]; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + srslte_chest_ul_t est; + cf_t *input = NULL, *ce = NULL, *h = NULL; + int i, j, n_port=0, sf_idx=0, cid=0, num_re; + int ret = -1; + int max_cid; + FILE *fmatlab = NULL; + + parse_args(argc,argv); + + if (output_matlab) { + fmatlab=fopen(output_matlab, "w"); + if (!fmatlab) { + perror("fopen"); + goto do_exit; + } + } + + num_re = 2 * cell.nof_prb * SRSLTE_NRE * SRSLTE_CP_NSYMB(cell.cp); + + input = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!input) { + perror("srslte_vec_malloc"); + goto do_exit; + } + h = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!h) { + perror("srslte_vec_malloc"); + goto do_exit; + } + ce = srslte_vec_malloc(num_re * sizeof(cf_t)); + if (!ce) { + perror("srslte_vec_malloc"); + goto do_exit; + } + bzero(ce, num_re*sizeof(cf_t)); + + if (cell.id == 1000) { + cid = 0; + max_cid = 504; + } else { + cid = cell.id; + max_cid = cell.id; + } + printf("max_cid=%d, cid=%d, cell.id=%d\n", max_cid, cid, cell.id); + if (srslte_chest_ul_init(&est, cell.nof_prb)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + while(cid <= max_cid) { + cell.id = cid; + if (srslte_chest_ul_set_cell(&est, cell)) { + fprintf(stderr, "Error initializing equalizer\n"); + goto do_exit; + } + + for (int n=6;n<=cell.nof_prb;n+=5) { + if (srslte_dft_precoding_valid_prb(n)) { + for (int delta_ss=29;delta_ss 4) { + goto do_exit; + } + } + } + } + } + } + } + } + cid+=10; + printf("cid=%d\n", cid); + } + + srslte_chest_ul_free(&est); + + if (fmatlab) { + fprintf(fmatlab, "input="); + srslte_vec_fprint_c(fmatlab, input, num_re); + fprintf(fmatlab, ";\n"); + fprintf(fmatlab, "h="); + srslte_vec_fprint_c(fmatlab, h, num_re); + fprintf(fmatlab, ";\n"); + fprintf(fmatlab, "ce="); + srslte_vec_fprint_c(fmatlab, ce, num_re); + fprintf(fmatlab, ";\n"); + } + + ret = 0; + +do_exit: + + if (ce) { + free(ce); + } + if (input) { + free(input); + } + if (h) { + free(h); + } + + if (!ret) { + printf("OK\n"); + } else { + printf("Error at cid=%d, slot=%d, port=%d\n",cid, sf_idx, n_port); + } + + exit(ret); +} diff --git a/lib/src/phy/ch_estimation/test/chest_test_ul_mex.c b/lib/src/phy/ch_estimation/test/chest_test_ul_mex.c new file mode 100644 index 0000000..9cd6f8a --- /dev/null +++ b/lib/src/phy/ch_estimation/test/chest_test_ul_mex.c @@ -0,0 +1,181 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define INPUT prhs[2] +#define NOF_INPUTS 3 + +void help() +{ + mexErrMsgTxt + ("[estChannel, noiseEst, eq_output] = srslte_chest_ul(ue_cfg, pusch_cfg, inputSignal, [w_coeff])\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_cell_t cell; + srslte_chest_ul_t chest; + + cf_t *input_signal = NULL, *output_signal = NULL; + cf_t *ce = NULL; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + cell.cp = SRSLTE_CP_NORM; + cell.nof_ports = 1; + + uint32_t sf_idx=0; + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) { + help(); + return; + } + + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + pusch_cfg.group_hopping_en = false; + pusch_cfg.sequence_hopping_en = false; + char *tmp = mexutils_get_char_struct(UECFG, "Hopping"); + if (tmp) { + if (!strcmp(tmp, "Group")) { + pusch_cfg.group_hopping_en = true; + } else if (!strcmp(tmp, "Sequence")) { + pusch_cfg.sequence_hopping_en = true; + } + mxFree(tmp); + } + + + if (mexutils_read_uint32_struct(UECFG, "SeqGroup", &pusch_cfg.delta_ss)) { + pusch_cfg.delta_ss = 0; + } + if (mexutils_read_uint32_struct(UECFG, "CyclicShift", &pusch_cfg.cyclic_shift)) { + pusch_cfg.cyclic_shift = 0; + } + float *prbset; + mxArray *p; + p = mxGetField(PUSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found in PUSCH config\n"); + return; + } + uint32_t nof_prb = mexutils_read_f(p, &prbset); + uint32_t n_prb[2]; + n_prb[0] = prbset[0]; + n_prb[1] = prbset[0]; + + + uint32_t cyclic_shift_for_dmrs = 0; + if (mexutils_read_uint32_struct(PUSCHCFG, "DynCyclicShift", &cyclic_shift_for_dmrs)) { + cyclic_shift_for_dmrs = 0; + } + + if (srslte_chest_ul_init(&chest, cell)) { + mexErrMsgTxt("Error initiating channel estimator\n"); + return; + } + + srslte_chest_ul_set_cfg(&chest, &pusch_cfg, NULL, NULL); + + /** Allocate input buffers */ + int nof_re = 2*SRSLTE_CP_NSYMB(cell.cp)*cell.nof_prb*SRSLTE_NRE; + ce = srslte_vec_malloc(nof_re * sizeof(cf_t)); + output_signal = srslte_vec_malloc(nof_re * sizeof(cf_t)); + + // Read input signal + int insignal_len = mexutils_read_cf(INPUT, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + + // Read optional value smooth filter coefficient + if (nrhs > NOF_INPUTS) { + float w = (float) mxGetScalar(prhs[NOF_INPUTS]); + srslte_chest_ul_set_smooth_filter3_coeff(&chest, w); + } else { + srslte_chest_ul_set_smooth_filter(&chest, NULL, 0); + } + + // Perform channel estimation + if (srslte_chest_ul_estimate(&chest, input_signal, ce, nof_prb, sf_idx, cyclic_shift_for_dmrs, n_prb)) { + mexErrMsgTxt("Error running channel estimator\n"); + return; + } + + // Get noise power estimation + float noise_power = srslte_chest_ul_get_noise_estimate(&chest); + + // Perform channel equalization + srslte_predecoding_single(input_signal, ce, output_signal, nof_re, noise_power); + + /* Write output values */ + if (nlhs >= 1) { + mexutils_write_cf(ce, &plhs[0], mxGetM(INPUT), mxGetN(INPUT)); + } + if (nlhs >= 2) { + plhs[1] = mxCreateDoubleScalar(noise_power); + } + if (nlhs >= 3) { + mexutils_write_cf(output_signal, &plhs[2], mxGetM(INPUT), mxGetN(INPUT)); + } + + // Free all memory + srslte_chest_ul_free(&chest); + + if (ce) { + free(ce); + } + if (input_signal) { + free(input_signal); + } + if (output_signal) { + free(output_signal); + } + + return; +} + diff --git a/lib/src/phy/ch_estimation/test/refsignal_pusch_mex.c b/lib/src/phy/ch_estimation/test/refsignal_pusch_mex.c new file mode 100644 index 0000000..2564d1f --- /dev/null +++ b/lib/src/phy/ch_estimation/test/refsignal_pusch_mex.c @@ -0,0 +1,151 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("[seq] = srslte_refsignal_pusch(ueConfig, puschConfig)\n\n"); +} + +extern int indices[2048]; + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + srslte_cell_t cell; + srslte_refsignal_ul_t refs; + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + uint32_t sf_idx; + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + cell.cp = SRSLTE_CP_NORM; + cell.nof_ports = 1; + + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + + bzero(&pusch_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + + + pusch_cfg.group_hopping_en = false; + pusch_cfg.sequence_hopping_en = false; + char *tmp = mexutils_get_char_struct(UECFG, "Hopping"); + if (tmp) { + if (!strcmp(tmp, "Group")) { + pusch_cfg.group_hopping_en = true; + } else if (!strcmp(tmp, "Sequence")) { + pusch_cfg.sequence_hopping_en = true; + } + mxFree(tmp); + } + + + if (mexutils_read_uint32_struct(UECFG, "SeqGroup", &pusch_cfg.delta_ss)) { + pusch_cfg.delta_ss = 0; + } + if (mexutils_read_uint32_struct(UECFG, "CyclicShift", &pusch_cfg.cyclic_shift)) { + pusch_cfg.cyclic_shift = 0; + } + float *prbset; + mxArray *p; + p = mxGetField(PUSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found in PUSCH config\n"); + return; + } + uint32_t nof_prb = mexutils_read_f(p, &prbset); + + uint32_t cyclic_shift_for_dmrs = 0; + if (mexutils_read_uint32_struct(PUSCHCFG, "DynCyclicShift", &cyclic_shift_for_dmrs)) { + cyclic_shift_for_dmrs = 0; + } + + if (srslte_refsignal_ul_init(&refs, cell)) { + mexErrMsgTxt("Error initiating srslte_refsignal_ul\n"); + return; + } + + mexPrintf("nof_prb: %d, ",nof_prb); + mexPrintf("cyclic_shift: %d, ",pusch_cfg.cyclic_shift); + mexPrintf("cyclic_shift_for_dmrs: %d, ", cyclic_shift_for_dmrs); + mexPrintf("delta_ss: %d, ",pusch_cfg.delta_ss); + + cf_t *signal = srslte_vec_malloc(2*SRSLTE_NRE*nof_prb*sizeof(cf_t)); + if (!signal) { + perror("malloc"); + return; + } + cf_t *sf_symbols = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)*sizeof(cf_t)); + if (!sf_symbols) { + perror("malloc"); + return; + } + bzero(sf_symbols, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)*sizeof(cf_t)); + + srslte_refsignal_ul_set_cfg(&refs, &pusch_cfg, NULL, NULL); + + //mexPrintf("Generating DRMS for ns=%d, nof_prb=%d\n", 2*sf_idx+i,pusch_cfg.nof_prb); + srslte_refsignal_dmrs_pusch_gen(&refs, nof_prb, sf_idx, cyclic_shift_for_dmrs, signal); + uint32_t n_prb[2]; + n_prb[0] = prbset[0]; + n_prb[1] = prbset[0]; + srslte_refsignal_dmrs_pusch_put(&refs, signal, nof_prb, n_prb, sf_symbols); + if (nlhs >= 1) { + mexutils_write_cf(sf_symbols, &plhs[0], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1); + } + + srslte_refsignal_ul_free(&refs); + free(signal); + free(prbset); + + return; +} + diff --git a/lib/src/phy/ch_estimation/test/refsignal_srs_mex.c b/lib/src/phy/ch_estimation/test/refsignal_srs_mex.c new file mode 100644 index 0000000..29e93b9 --- /dev/null +++ b/lib/src/phy/ch_estimation/test/refsignal_srs_mex.c @@ -0,0 +1,165 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define SRSCFG prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("[sym, subframe]=srslte_refsignal_srs(ue, chs)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + srslte_cell_t cell; + bzero(&cell, sizeof(srslte_cell_t)); + cell.nof_ports = 1; + cell.cp = SRSLTE_CP_NORM; + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + + uint32_t sf_idx = 0; + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + uint32_t nf = 0; + if (mexutils_read_uint32_struct(UECFG, "NFrame", &nf)) { + mexErrMsgTxt("Field NFrame not found in UE config\n"); + return; + } + uint32_t tti = nf*10+sf_idx; + + srslte_refsignal_srs_cfg_t srs_cfg; + bzero(&srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + + if (mexutils_read_uint32_struct(SRSCFG, "BWConfig", &srs_cfg.bw_cfg)) { + mexErrMsgTxt("Field BWConfig not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "BW", &srs_cfg.B)) { + mexErrMsgTxt("Field BW not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "ConfigIdx", &srs_cfg.I_srs)) { + mexErrMsgTxt("Field ConfigIdx not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "FreqPosition", &srs_cfg.n_rrc)) { + mexErrMsgTxt("Field FreqPosition not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "HoppingBW", &srs_cfg.b_hop)) { + mexErrMsgTxt("Field HoppingBW not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "TxComb", &srs_cfg.k_tc)) { + mexErrMsgTxt("Field TxComb not found in SRSCFG\n"); + return; + } + if (mexutils_read_uint32_struct(SRSCFG, "CyclicShift", &srs_cfg.n_srs)) { + mexErrMsgTxt("Field CyclicShift not found in SRSCFG\n"); + return; + } + bool group_hopping_en = false; + char *hop = mexutils_get_char_struct(UECFG, "Hopping"); + if (hop) { + if (!strcmp(hop, "Group")) { + group_hopping_en = true; + } + mxFree(hop); + } + + cf_t *r_srs = srslte_vec_malloc(sizeof(cf_t) * cell.nof_prb * 12); + if (!r_srs) { + return; + } + bzero(r_srs, cell.nof_prb * 12 * sizeof(cf_t)); + + cf_t *sf_symbols = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)); + if (!sf_symbols) { + return; + } + bzero(sf_symbols, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + + srslte_refsignal_ul_t refsignal; + if (srslte_refsignal_ul_init(&refsignal, cell)) { + mexErrMsgTxt("Error initiating UL refsignal\n"); + return; + } + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + pusch_cfg.group_hopping_en = group_hopping_en; + pusch_cfg.sequence_hopping_en = false; + srslte_refsignal_ul_set_cfg(&refsignal, &pusch_cfg, NULL, &srs_cfg); + + if (srslte_refsignal_srs_gen(&refsignal, sf_idx, r_srs)) { + mexErrMsgTxt("Error generating SRS\n"); + return; + } + + if (srslte_refsignal_srs_put(&refsignal, tti, r_srs, sf_symbols)) { + mexErrMsgTxt("Error allocating SRS\n"); + return; + } + + if (nlhs >= 1) { + uint32_t M_sc = srslte_refsignal_srs_M_sc(&refsignal); ; + mexutils_write_cf(r_srs, &plhs[0], M_sc, 1); + } + + if (nlhs >= 2) { + mexutils_write_cf(sf_symbols, &plhs[1], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1); + } + + srslte_refsignal_ul_free(&refsignal); + free(sf_symbols); + free(r_srs); + + return; +} + diff --git a/lib/src/phy/ch_estimation/test/refsignal_ul_test.c b/lib/src/phy/ch_estimation/test/refsignal_ul_test.c new file mode 100644 index 0000000..5ca18cd --- /dev/null +++ b/lib/src/phy/ch_estimation/test/refsignal_ul_test.c @@ -0,0 +1,169 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + 100, // nof_prb + SRSLTE_MAX_PORTS, // nof_ports + 1, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, + SRSLTE_PHICH_R_1_6 +}; + +void usage(char *prog) { + printf("Usage: %s [recv]\n", prog); + + printf("\t-r nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e extended cyclic prefix [Default normal]\n"); + + printf("\t-c cell_id (1000 tests all). [Default %d]\n", cell.id); + + printf("\t-v increase verbosity\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "recv")) != -1) { + switch(opt) { + case 'r': + cell.nof_prb = atoi(argv[optind]); + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + srslte_refsignal_ul_t refs; + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + cf_t *signal = NULL; + int ret = -1; + + parse_args(argc,argv); + + if (srslte_refsignal_ul_init(&refs, cell.nof_prb)) { + fprintf(stderr, "Error initializing UL reference signal\n"); + goto do_exit; + } + + if (srslte_refsignal_ul_set_cell(&refs, cell)) { + fprintf(stderr, "Error initializing UL reference signal\n"); + goto do_exit; + } + + signal = malloc(2 * SRSLTE_NRE * cell.nof_prb * sizeof(cf_t)); + if (!signal) { + perror("malloc"); + goto do_exit; + } + printf("Running tests for %d PRB\n", cell.nof_prb); + + for (int n=6;n + +// Phi values for M_sc=12 Table 5.5.1.2-1 in 36.211 +int phi_M_sc_12[30][12] = {{-1, 1, 3,-3, 3, 3, 1, 1, 3, 1,-3, 3}, + { 1, 1, 3, 3, 3,-1, 1,-3,-3, 1,-3, 3}, + { 1, 1,-3,-3,-3,-1,-3,-3, 1,-3, 1,-1}, + {-1, 1, 1, 1, 1,-1,-3,-3, 1,-3, 3,-1}, + {-1, 3, 1,-1, 1,-1,-3,-1, 1,-1, 1, 3}, + { 1,-3, 3,-1,-1, 1, 1,-1,-1, 3,-3, 1}, + {-1, 3,-3,-3,-3, 3, 1,-1, 3, 3,-3, 1}, + {-3,-1,-1,-1, 1,-3, 3,-1, 1,-3, 3, 1}, + { 1,-3, 3, 1,-1,-1,-1, 1, 1, 3,-1, 1}, + { 1,-3,-1, 3, 3,-1,-3, 1, 1, 1, 1, 1}, + {-1, 3,-1, 1, 1,-3,-3,-1,-3,-3, 3,-1}, + { 3, 1,-1,-1, 3, 3,-3, 1, 3, 1, 3, 3}, + { 1,-3, 1, 1,-3, 1, 1, 1,-3,-3,-3, 1}, + { 3, 3,-3, 3,-3, 1, 1, 3,-1,-3, 3, 3}, + {-3, 1,-1,-3,-1, 3, 1, 3, 3, 3,-1, 1}, + { 3,-1, 1,-3,-1,-1, 1, 1, 3, 1,-1,-3}, + { 1, 3, 1,-1, 1, 3, 3, 3,-1,-1, 3,-1}, + {-3, 1, 1, 3,-3, 3,-3,-3, 3, 1, 3,-1}, + {-3, 3, 1, 1,-3, 1,-3,-3,-1,-1, 1,-3}, + {-1, 3, 1, 3, 1,-1,-1, 3,-3,-1,-3,-1}, + {-1,-3, 1, 1, 1, 1, 3, 1,-1, 1,-3,-1}, + {-1, 3,-1, 1,-3,-3,-3,-3,-3, 1,-1,-3}, + { 1, 1,-3,-3,-3,-3,-1, 3,-3, 1,-3, 3}, + { 1, 1,-1,-3,-1,-3, 1,-1, 1, 3,-1, 1}, + { 1, 1, 3, 1, 3, 3,-1, 1,-1,-3,-3, 1}, + { 1,-3, 3, 3, 1, 3, 3, 1,-3,-1,-1, 3}, + { 1, 3,-3,-3, 3,-3, 1,-1,-1, 3,-1,-3}, + {-3,-1,-3,-1,-3, 3, 1,-1, 1, 3,-3,-3}, + {-1, 3,-3, 3,-1, 3, 3,-3, 3, 3,-1,-1}, + { 3,-3,-3,-1,-1,-3,-1, 3,-3, 3, 1,-1}}; + +// Phi values for M_sc=24 Table 5.5.1.2-2 in 36.211 +int phi_M_sc_24[30][24] = {{-1, 3, 1,-3, 3,-1, 1, 3,-3, 3, 1, 3,-3, 3, 1, 1,-1, 1, 3,-3, 3,-3,-1,-3}, + {-3, 3,-3,-3,-3, 1,-3,-3, 3,-1, 1, 1, 1, 3, 1,-1, 3,-3,-3, 1, 3, 1, 1,-3}, + { 3,-1, 3, 3, 1, 1,-3, 3, 3, 3, 3, 1,-1, 3,-1, 1, 1,-1,-3,-1,-1, 1, 3, 3}, + {-1,-3, 1, 1, 3,-3, 1, 1,-3,-1,-1, 1, 3, 1, 3, 1,-1, 3, 1, 1,-3,-1,-3,-1}, + {-1,-1,-1,-3,-3,-1, 1, 1, 3, 3,-1, 3,-1, 1,-1,-3, 1,-1,-3,-3, 1,-3,-1,-1}, + {-3, 1, 1, 3,-1, 1, 3, 1,-3, 1,-3, 1, 1,-1,-1, 3,-1,-3, 3,-3,-3,-3, 1, 1}, + { 1, 1,-1,-1, 3,-3,-3, 3,-3, 1,-1,-1, 1,-1, 1, 1,-1,-3,-1, 1,-1, 3,-1,-3}, + {-3, 3, 3,-1,-1,-3,-1, 3, 1, 3, 1, 3, 1, 1,-1, 3, 1,-1, 1, 3,-3,-1,-1, 1}, + {-3, 1, 3,-3, 1,-1,-3, 3,-3, 3,-1,-1,-1,-1, 1,-3,-3,-3, 1,-3,-3,-3, 1,-3}, + { 1, 1,-3, 3, 3,-1,-3,-1, 3,-3, 3, 3, 3,-1, 1, 1,-3, 1,-1, 1, 1,-3, 1, 1}, + {-1, 1,-3,-3, 3,-1, 3,-1,-1,-3,-3,-3,-1,-3,-3, 1,-1, 1, 3, 3,-1, 1,-1, 3}, + { 1, 3, 3,-3,-3, 1, 3, 1,-1,-3,-3,-3, 3, 3,-3, 3, 3,-1,-3, 3,-1, 1,-3, 1}, + { 1, 3, 3, 1, 1, 1,-1,-1, 1,-3, 3,-1, 1, 1,-3, 3, 3,-1,-3, 3,-3,-1,-3,-1}, + { 3,-1,-1,-1,-1,-3,-1, 3, 3, 1,-1, 1, 3, 3, 3,-1, 1, 1,-3, 1, 3,-1,-3, 3}, + {-3,-3, 3, 1, 3, 1,-3, 3, 1, 3, 1, 1, 3, 3,-1,-1,-3, 1,-3,-1, 3, 1, 1, 3}, + {-1,-1, 1,-3, 1, 3,-3, 1,-1,-3,-1, 3, 1, 3, 1,-1,-3,-3,-1,-1,-3,-3,-3,-1}, + {-1,-3, 3,-1,-1,-1,-1, 1, 1,-3, 3, 1, 3, 3, 1,-1, 1,-3, 1,-3, 1, 1,-3,-1}, + { 1, 3,-1, 3, 3,-1,-3, 1,-1,-3, 3, 3, 3,-1, 1, 1, 3,-1,-3,-1, 3,-1,-1,-1}, + { 1, 1, 1, 1, 1,-1, 3,-1,-3, 1, 1, 3,-3, 1,-3,-1, 1, 1,-3,-3, 3, 1, 1,-3}, + { 1, 3, 3, 1,-1,-3, 3,-1, 3, 3, 3,-3, 1,-1, 1,-1,-3,-1, 1, 3,-1, 3,-3,-3}, + {-1,-3, 3,-3,-3,-3,-1,-1,-3,-1,-3, 3, 1, 3,-3,-1, 3,-1, 1,-1, 3,-3, 1,-1}, + {-3,-3, 1, 1,-1, 1,-1, 1,-1, 3, 1,-3,-1, 1,-1, 1,-1,-1, 3, 3,-3,-1, 1,-3}, + {-3,-1,-3, 3, 1,-1,-3,-1,-3,-3, 3,-3, 3,-3,-1, 1, 3, 1,-3, 1, 3, 3,-1,-3}, + {-1,-1,-1,-1, 3, 3, 3, 1, 3, 3,-3, 1, 3,-1, 3,-1, 3, 3,-3, 3, 1,-1, 3, 3}, + { 1,-1, 3, 3,-1,-3, 3,-3,-1,-1, 3,-1, 3,-1,-1, 1, 1, 1, 1,-1,-1,-3,-1, 3}, + { 1,-1, 1,-1, 3,-1, 3, 1, 1,-1,-1,-3, 1, 1,-3, 1, 3,-3, 1, 1,-3,-3,-1,-1}, + {-3,-1, 1, 3, 1, 1,-3,-1,-1,-3, 3,-3, 3, 1,-3, 3,-3, 1,-1, 1,-3, 1, 1, 1}, + {-1,-3, 3, 3, 1, 1, 3,-1,-3,-1,-1,-1, 3, 1,-3,-3,-1, 3,-3,-1,-3,-1,-3,-1}, + {-1,-3,-1,-1, 1,-3,-1,-1, 1,-1,-3, 1, 1,-3, 1,-3,-3, 3, 1, 1,-1, 3,-1,-1}, + { 1, 1,-1,-1,-3,-1, 3,-1, 3,-1, 1, 3, 1,-1, 3, 1, 3,-3,-3, 1,-1,-1, 1, 3}}; + +// Prime numbers used for Section 5.5.1.1 of 36.211 +#define NOF_PRIME_NUMBERS 196 +uint32_t prime_numbers[NOF_PRIME_NUMBERS] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997,1009,1013, + 1019,1021,1031,1033,1039,1049,1051,1061,1063,1069, + 1087,1091,1093,1097,1103,1109,1117,1123,1129,1151, + 1153,1163,1171,1181,1187,1193}; + diff --git a/lib/src/phy/channel/CMakeLists.txt b/lib/src/phy/channel/CMakeLists.txt new file mode 100644 index 0000000..0ea8799 --- /dev/null +++ b/lib/src/phy/channel/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_channel OBJECT ${SOURCES}) diff --git a/lib/src/phy/channel/ch_awgn.c b/lib/src/phy/channel/ch_awgn.c new file mode 100644 index 0000000..d88a91a --- /dev/null +++ b/lib/src/phy/channel/ch_awgn.c @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "gauss.h" +#include "srslte/phy/channel/ch_awgn.h" + +float srslte_ch_awgn_get_variance(float ebno_db, float rate) { + float esno_db = ebno_db + 10 * log10f(rate); + return sqrtf(1 / (powf(10, esno_db / 10))); +} + +void srslte_ch_awgn_c(const cf_t* x, cf_t* y, float variance, uint32_t len) { + cf_t tmp; + uint32_t i; + + for (i=0;i +#include +#include + +float rand_gauss (void) { + float v1,v2,s; + + do { + v1 = 2.0 * ((float) rand()/RAND_MAX) - 1; + v2 = 2.0 * ((float) rand()/RAND_MAX) - 1; + + s = v1*v1 + v2*v2; + } while ( s >= 1.0 || s == 0.0); + + return (v1*sqrt(-2.0 * log(s) / s)); +} diff --git a/lib/src/phy/channel/gauss.h b/lib/src/phy/channel/gauss.h new file mode 100644 index 0000000..412df47 --- /dev/null +++ b/lib/src/phy/channel/gauss.h @@ -0,0 +1,28 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +float rand_gauss (void); diff --git a/lib/src/phy/common/CMakeLists.txt b/lib/src/phy/common/CMakeLists.txt new file mode 100644 index 0000000..ba2fc13 --- /dev/null +++ b/lib/src/phy/common/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_phy_common OBJECT ${SOURCES}) diff --git a/lib/src/phy/common/phy_common.c b/lib/src/phy/common/phy_common.c new file mode 100644 index 0000000..489d6b3 --- /dev/null +++ b/lib/src/phy/common/phy_common.c @@ -0,0 +1,615 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/common/sequence.h" + +#ifdef FORCE_STANDARD_RATE +static bool use_standard_rates = true; +#else +static bool use_standard_rates = false; +#endif + +/* Returns true if the structure pointed by cell has valid parameters + */ + +bool srslte_cellid_isvalid(uint32_t cell_id) { + if (cell_id < 504) { + return true; + } else { + return false; + } +} + +bool srslte_nofprb_isvalid(uint32_t nof_prb) { + if (nof_prb >= 6 && nof_prb <= SRSLTE_MAX_PRB) { + return true; + } else { + return false; + } +} + +bool srslte_cell_isvalid(srslte_cell_t *cell) { + return srslte_cellid_isvalid(cell->id) && + srslte_portid_isvalid(cell->nof_ports) && + srslte_nofprb_isvalid(cell->nof_prb); +} + +void srslte_cell_fprint(FILE *stream, srslte_cell_t *cell, uint32_t sfn) { + fprintf(stream, " - PCI: %d\n", cell->id); + fprintf(stream, " - Nof ports: %d\n", cell->nof_ports); + fprintf(stream, " - CP: %s\n", srslte_cp_string(cell->cp)); + fprintf(stream, " - PRB: %d\n", cell->nof_prb); + fprintf(stream, " - PHICH Length: %s\n", + cell->phich_length == SRSLTE_PHICH_EXT ? "Extended" : "Normal"); + fprintf(stream, " - PHICH Resources: "); + switch (cell->phich_resources) { + case SRSLTE_PHICH_R_1_6: + fprintf(stream, "1/6"); + break; + case SRSLTE_PHICH_R_1_2: + fprintf(stream, "1/2"); + break; + case SRSLTE_PHICH_R_1: + fprintf(stream, "1"); + break; + case SRSLTE_PHICH_R_2: + fprintf(stream, "2"); + break; + } + fprintf(stream, "\n"); + fprintf(stream, " - SFN: %d\n", sfn); + +} + +bool srslte_sfidx_isvalid(uint32_t sf_idx) { + if (sf_idx <= SRSLTE_NSUBFRAMES_X_FRAME) { + return true; + } else { + return false; + } +} + +bool srslte_portid_isvalid(uint32_t port_id) { + if (port_id <= SRSLTE_MAX_PORTS) { + return true; + } else { + return false; + } +} + +bool srslte_N_id_2_isvalid(uint32_t N_id_2) { + if (N_id_2 < 3) { + return true; + } else { + return false; + } +} + +bool srslte_N_id_1_isvalid(uint32_t N_id_1) { + if (N_id_1 < 169) { + return true; + } else { + return false; + } +} + +srslte_mod_t srslte_str2mod (char * mod_str) { + int i = 0; + + /* Upper case */ + while (mod_str[i] &= (~' '), mod_str[++i]); + + if (!strcmp(mod_str, "QPSK")) { + return SRSLTE_MOD_QPSK; + } else if (!strcmp(mod_str, "16QAM")) { + return SRSLTE_MOD_16QAM; + } else if (!strcmp(mod_str, "64QAM")) { + return SRSLTE_MOD_64QAM; + } else { + return (srslte_mod_t) SRSLTE_ERROR_INVALID_INPUTS; + } +}; + + +char *srslte_mod_string(srslte_mod_t mod) { + switch (mod) { + case SRSLTE_MOD_BPSK: + return "BPSK"; + case SRSLTE_MOD_QPSK: + return "QPSK"; + case SRSLTE_MOD_16QAM: + return "16QAM"; + case SRSLTE_MOD_64QAM: + return "64QAM"; + default: + return "N/A"; + } +} + +uint32_t srslte_mod_bits_x_symbol(srslte_mod_t mod) { + switch (mod) { + case SRSLTE_MOD_BPSK: + return 1; + case SRSLTE_MOD_QPSK: + return 2; + case SRSLTE_MOD_16QAM: + return 4; + case SRSLTE_MOD_64QAM: + return 6; + default: + return 0; + } +} + +char *srslte_cp_string(srslte_cp_t cp) { + if (cp == SRSLTE_CP_NORM) { + return "Normal "; + } else { + return "Extended"; + } +} + +/* Returns the new time advance N_ta_new as specified in Section 4.2.3 of 36.213 */ +uint32_t srslte_N_ta_new(uint32_t N_ta_old, uint32_t ta) { + + ta &= 63; + int n_ta_new = N_ta_old + ((float) ta - 31) * 16; + if (n_ta_new < 0) { + return 0; + } else { + if (n_ta_new < 20512) { + return (uint32_t) n_ta_new; + } else { + return 20512; + } + } +} + +float srslte_coderate(uint32_t tbs, uint32_t nof_re) +{ + return (float) (tbs + 24)/(nof_re); +} + +/* Returns the new time advance as indicated by the random access response + * as specified in Section 4.2.3 of 36.213 */ +uint32_t srslte_N_ta_new_rar(uint32_t ta) { + if (ta > 1282) { + ta = 1282; + } + return ta*16; +} + + +void srslte_use_standard_symbol_size(bool enabled) { + use_standard_rates = enabled; +} + +int srslte_sampling_freq_hz(uint32_t nof_prb) { + int n = srslte_symbol_sz(nof_prb); + if (n == -1) { + return SRSLTE_ERROR; + } else { + return 15000 * n; + } +} + +int srslte_symbol_sz_power2(uint32_t nof_prb) { + if (nof_prb<=6) { + return 128; + } else if (nof_prb<=15) { + return 256; + } else if (nof_prb<=25) { + return 512; + } else if (nof_prb<=50) { + return 1024; + } else if (nof_prb<=75) { + return 1536; + } else if (nof_prb<=110) { + return 2048; + } else { + return -1; + } +} + +int srslte_symbol_sz(uint32_t nof_prb) { + if (nof_prb<=0) { + return SRSLTE_ERROR; + } + if (!use_standard_rates) { + if (nof_prb<=6) { + return 128; + } else if (nof_prb<=15) { + return 256; + } else if (nof_prb<=25) { + return 384; + } else if (nof_prb<=50) { + return 768; + } else if (nof_prb<=75) { + return 1024; + } else if (nof_prb<=110) { + return 1536; + } else { + return SRSLTE_ERROR; + } + } else { + return srslte_symbol_sz_power2(nof_prb); + } +} + +int srslte_nof_prb(uint32_t symbol_sz) +{ + if (!use_standard_rates) { + switch(symbol_sz) { + case 128: + return 6; + case 256: + return 15; + case 384: + return 25; + case 768: + return 50; + case 1024: + return 75; + case 1536: + return 100; + } + } else { + switch(symbol_sz) { + case 128: + return 6; + case 256: + return 15; + case 512: + return 25; + case 1024: + return 50; + case 1536: + return 75; + case 2048: + return 100; + } + } + return SRSLTE_ERROR; +} + +bool srslte_symbol_sz_isvalid(uint32_t symbol_sz) { + if (!use_standard_rates) { + if (symbol_sz == 128 || + symbol_sz == 256 || + symbol_sz == 384 || + symbol_sz == 768 || + symbol_sz == 1024 || + symbol_sz == 1536) { + return true; + } else { + return false; + } + } else { + if (symbol_sz == 128 || + symbol_sz == 256 || + symbol_sz == 512 || + symbol_sz == 1024 || + symbol_sz == 1536 || + symbol_sz == 2048) { + return true; + } else { + return false; + } + } +} + +uint32_t srslte_voffset(uint32_t symbol_id, uint32_t cell_id, uint32_t nof_ports) { + if (nof_ports == 1 && symbol_id==0) { + return (cell_id+3) % 6; + } else { + return cell_id % 6; + } +} + + +/** Computes sequence-group pattern f_gh according to 5.5.1.3 of 36.211 */ +int srslte_group_hopping_f_gh(uint32_t f_gh[SRSLTE_NSLOTS_X_FRAME], uint32_t cell_id) { + srslte_sequence_t seq; + bzero(&seq, sizeof(srslte_sequence_t)); + + if (srslte_sequence_LTE_pr(&seq, 160, cell_id / 30)) { + return SRSLTE_ERROR; + } + + for (uint32_t ns=0;ns= band->dl_earfcn_offset) { + return band->fd_low_mhz + 0.1*(dl_earfcn - band->dl_earfcn_offset); + } else { + return 0.0; + } +} + +float get_fu(struct lte_band *band, uint32_t ul_earfcn) { + if (ul_earfcn >= band->ul_earfcn_offset) { + return band->fd_low_mhz - band->duplex_mhz + 0.1*(ul_earfcn - band->ul_earfcn_offset); + } else { + return 0.0; + } +} + +int srslte_band_get_band(uint32_t dl_earfcn) { + uint32_t i = SRSLTE_NOF_LTE_BANDS-1; + if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { + fprintf(stderr, "Invalid DL_EARFCN=%d\n", dl_earfcn); + } + i--; + while(i > 0 && lte_bands[i].dl_earfcn_offset>dl_earfcn) { + i--; + } + return lte_bands[i].band; +} + +float srslte_band_fd(uint32_t dl_earfcn) { + uint32_t i = SRSLTE_NOF_LTE_BANDS-1; + if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { + fprintf(stderr, "Invalid DL_EARFCN=%d\n", dl_earfcn); + } + i--; + while(i > 0 && lte_bands[i].dl_earfcn_offset>dl_earfcn) { + i--; + } + return get_fd(<e_bands[i], dl_earfcn); +} + + +float srslte_band_fu(uint32_t ul_earfcn) { + uint32_t i = SRSLTE_NOF_LTE_BANDS-1; + if (ul_earfcn > lte_bands[i].ul_earfcn_offset) { + fprintf(stderr, "Invalid UL_EARFCN=%d\n", ul_earfcn); + } + i--; + while(i > 0 && (lte_bands[i].ul_earfcn_offset>ul_earfcn || lte_bands[i].ul_earfcn_offset == 0)) { + i--; + } + return get_fu(<e_bands[i], ul_earfcn); +} + +uint32_t srslte_band_ul_earfcn(uint32_t dl_earfcn) { + uint32_t i = SRSLTE_NOF_LTE_BANDS-1; + if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { + fprintf(stderr, "Invalid DL_EARFCN=%d\n", dl_earfcn); + } + i--; + while(i > 0 && lte_bands[i].dl_earfcn_offset>dl_earfcn) { + i--; + } + return lte_bands[i].ul_earfcn_offset + (dl_earfcn-lte_bands[i].dl_earfcn_offset); +} + +int srslte_band_get_fd_band_all(uint32_t band, srslte_earfcn_t *earfcn, uint32_t max_elems) { + return srslte_band_get_fd_band(band, earfcn, -1, -1, max_elems); +} + +int srslte_band_get_fd_band(uint32_t band, srslte_earfcn_t *earfcn, int start_earfcn, int end_earfcn, uint32_t max_elems) { + uint32_t i, j; + uint32_t nof_earfcn; + i=0; + while(i < SRSLTE_NOF_LTE_BANDS && lte_bands[i].band != band) { + i++; + } + if (i >= SRSLTE_NOF_LTE_BANDS - 1) { + fprintf(stderr, "Error: Invalid band %d\n", band); + return SRSLTE_ERROR; + } + if (end_earfcn == -1) { + end_earfcn = lte_bands[i+1].dl_earfcn_offset-1; + } else { + if (end_earfcn > lte_bands[i+1].dl_earfcn_offset-1) { + fprintf(stderr, "Error: Invalid end earfcn %d. Max is %d\n", end_earfcn, lte_bands[i+1].dl_earfcn_offset-1); + return SRSLTE_ERROR; + } + } + if (start_earfcn == -1) { + start_earfcn = lte_bands[i].dl_earfcn_offset; + } else { + if (start_earfcn < lte_bands[i].dl_earfcn_offset) { + fprintf(stderr, "Error: Invalid start earfcn %d. Min is %d\n", start_earfcn, lte_bands[i].dl_earfcn_offset); + return SRSLTE_ERROR; + } + } + nof_earfcn = end_earfcn - start_earfcn; + + if (nof_earfcn > max_elems) { + nof_earfcn = max_elems; + } + for (j=0;j 0;i++) { + if (lte_bands[i].area == region) { + n = srslte_band_get_fd_band(i, &earfcn[nof_fd], -1, -1, max_elems); + if (n != -1) { + nof_fd += n; + max_elems -= n; + } else { + return SRSLTE_ERROR; + } + } + } + return nof_fd; +} + + +/* Returns the interval tti1-tti2 mod 10240 */ +uint32_t srslte_tti_interval(uint32_t tti1, uint32_t tti2) { + if (tti1 >= tti2) { + return tti1-tti2; + } else { + return 10240-tti2+tti1; + } +} + + diff --git a/lib/src/phy/common/phy_logger.c b/lib/src/phy/common/phy_logger.c new file mode 100644 index 0000000..5a44e1a --- /dev/null +++ b/lib/src/phy/common/phy_logger.c @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include +#include +#include "srslte/srslte.h" +#include "srslte/phy/common/phy_logger.h" +/********************************************************************* + Functions for external logging +*********************************************************************/ +static phy_log_handler_t phy_log_handler; +static void *callback_ctx = NULL; + +void srslte_phy_log_register_handler(void *ctx, phy_log_handler_t handler) { + phy_log_handler = handler; + callback_ctx = ctx; + handler_registered++; +} + + void srslte_phy_log_print(phy_logger_level_t log_level, const char *format, ...) { + char tmp[256]; + va_list args; + va_start(args, format); + if (phy_log_handler) { + if(vsnprintf(tmp, 256, format, args) > 0) { + phy_log_handler(log_level, callback_ctx, tmp); + } + } + va_end(args); +} \ No newline at end of file diff --git a/lib/src/phy/common/sequence.c b/lib/src/phy/common/sequence.c new file mode 100644 index 0000000..0ecf9f1 --- /dev/null +++ b/lib/src/phy/common/sequence.c @@ -0,0 +1,188 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/bit.h" + +#define Nc 1600 + +#define MAX_SEQ_LEN (128*1024) + +#define static_memory + +/* + * Pseudo Random Sequence generation. + * It follows the 3GPP Release 8 (LTE) 36.211 + * Section 7.2 + */ +#ifdef static_memory +static uint8_t x1[Nc+MAX_SEQ_LEN+31]; +static uint8_t x2[Nc+MAX_SEQ_LEN+31]; + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +int srslte_sequence_set_LTE_pr(srslte_sequence_t *q, uint32_t len, uint32_t seed) { + int n; + + if (len > q->max_len) { + fprintf(stderr, "Error generating pseudo-random sequence: len %d exceeds maximum len %d\n", + len, MAX_SEQ_LEN); + return -1; + } + + if (len > q->max_len) { + fprintf(stderr, "Error generating pseudo-random sequence: len %d is greater than allocated len %d\n", + len, q->max_len); + return -1; + } + pthread_mutex_lock(&mutex); + + for (n = 0; n < 31; n++) { + x2[n] = (seed >> n) & 0x1; + } + x1[0] = 1; + + for (n = 0; n < Nc + len; n++) { + x1[n + 31] = (x1[n + 3] + x1[n]) & 0x1; + x2[n + 31] = (x2[n + 3] + x2[n + 2] + x2[n+1] + x2[n]) & 0x1; + } + + for (n = 0; n < len; n++) { + q->c[n] = (x1[n + Nc] + x2[n + Nc]) & 0x1; + } + pthread_mutex_unlock(&mutex); + + return 0; +} + +#else +int srslte_sequence_set_LTE_pr(srslte_sequence_t *q, uint32_t len, uint32_t seed) { + int n; + uint32_t *x1, *x2; + + if (len > q->max_len) { + fprintf(stderr, "Error generating pseudo-random sequence: len %d is greater than allocated len %d\n", + len, q->max_len); + return -1; + } + + x1 = calloc(Nc + len + 31, sizeof(uint32_t)); + if (!x1) { + perror("calloc"); + return -1; + } + x2 = calloc(Nc + len + 31, sizeof(uint32_t)); + if (!x2) { + free(x1); + perror("calloc"); + return -1; + } + + for (n = 0; n < 31; n++) { + x2[n] = (seed >> n) & 0x1; + } + x1[0] = 1; + + for (n = 0; n < Nc + len; n++) { + x1[n + 31] = (x1[n + 3] + x1[n]) & 0x1; + x2[n + 31] = (x2[n + 3] + x2[n + 2] + +x2[n+1] + x2[n]) & 0x1; + } + + for (n = 0; n < len; n++) { + q->c[n] = (x1[n + Nc] + x2[n + Nc]) & 0x1; + } + + free(x1); + free(x2); + + return 0; +} + +#endif + +int srslte_sequence_LTE_pr(srslte_sequence_t *q, uint32_t len, uint32_t seed) { + if (srslte_sequence_init(q, len)) { + return SRSLTE_ERROR; + } + q->cur_len = len; + srslte_sequence_set_LTE_pr(q, len, seed); + srslte_bit_pack_vector(q->c, q->c_bytes, len); + for (int i=0;ic_float[i] = (1-2*q->c[i]); + q->c_short[i] = (int16_t) q->c_float[i]; + } + return SRSLTE_SUCCESS; +} + +int srslte_sequence_init(srslte_sequence_t *q, uint32_t len) { + if (q->c && len > q->max_len) { + srslte_sequence_free(q); + } + if (!q->c) { + q->c = srslte_vec_malloc(len * sizeof(uint8_t)); + if (!q->c) { + return SRSLTE_ERROR; + } + q->c_bytes = srslte_vec_malloc(len * sizeof(uint8_t)/8+8); + if (!q->c_bytes) { + return SRSLTE_ERROR; + } + q->c_float = srslte_vec_malloc(len * sizeof(float)); + if (!q->c_float) { + return SRSLTE_ERROR; + } + q->c_short = srslte_vec_malloc(len * sizeof(short)); + if (!q->c_short) { + return SRSLTE_ERROR; + } + q->max_len = len; + } + return SRSLTE_SUCCESS; +} + +void srslte_sequence_free(srslte_sequence_t *q) { + if (q->c) { + free(q->c); + } + if (q->c_bytes) { + free(q->c_bytes); + } + if (q->c_float) { + free(q->c_float); + } + if (q->c_short) { + free(q->c_short); + } + bzero(q, sizeof(srslte_sequence_t)); +} + + diff --git a/lib/src/phy/common/timestamp.c b/lib/src/phy/common/timestamp.c new file mode 100644 index 0000000..ea8b611 --- /dev/null +++ b/lib/src/phy/common/timestamp.c @@ -0,0 +1,86 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/phy/common/timestamp.h" +#include "math.h" + +int srslte_timestamp_init(srslte_timestamp_t *t, time_t full_secs, double frac_secs){ + int ret = SRSLTE_ERROR; + if(t != NULL && frac_secs >= 0.0){ + t->full_secs = full_secs; + t->frac_secs = frac_secs; + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_timestamp_copy(srslte_timestamp_t *dest, srslte_timestamp_t *src){ + int ret = SRSLTE_ERROR; + if(dest != NULL && src != NULL){ + dest->full_secs = src->full_secs; + dest->frac_secs = src->frac_secs; + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_timestamp_add(srslte_timestamp_t *t, time_t full_secs, double frac_secs){ + int ret = SRSLTE_ERROR; + if(t != NULL && frac_secs >= 0.0){ + t->frac_secs += frac_secs; + t->full_secs += full_secs; + double r = floor(t->frac_secs); + t->full_secs += r; + t->frac_secs -= r; + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_timestamp_sub(srslte_timestamp_t *t, time_t full_secs, double frac_secs){ + int ret = SRSLTE_ERROR; + if(t != NULL && frac_secs >= 0.0){ + t->frac_secs -= frac_secs; + t->full_secs -= full_secs; + if(t->frac_secs < 0){ + t->frac_secs = t->frac_secs + 1; + t->full_secs--; + } + if(t->full_secs < 0) + return SRSLTE_ERROR; + ret = SRSLTE_SUCCESS; + } + return ret; +} + +double srslte_timestamp_real(srslte_timestamp_t *t){ + return t->frac_secs + t->full_secs; +} + +uint32_t srslte_timestamp_uint32(srslte_timestamp_t *t){ + uint32_t x = t->full_secs*1e6 + (uint32_t) (t->frac_secs*1e6); + return x; +} diff --git a/lib/src/phy/dft/CMakeLists.txt b/lib/src/phy/dft/CMakeLists.txt new file mode 100644 index 0000000..3af59bf --- /dev/null +++ b/lib/src/phy/dft/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +set(SRCS dft_fftw.c dft_precoding.c ofdm.c) +add_library(srslte_dft OBJECT ${SRCS}) +add_subdirectory(test) diff --git a/lib/src/phy/dft/dft_fftw.c b/lib/src/phy/dft/dft_fftw.c new file mode 100644 index 0000000..d9318fa --- /dev/null +++ b/lib/src/phy/dft/dft_fftw.c @@ -0,0 +1,355 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/utils/vector.h" + +#define dft_ceil(a,b) ((a-1)/b+1) +#define dft_floor(a,b) (a/b) + +#define FFTW_WISDOM_FILE ".fftw_wisdom" + +#ifdef FFTW_WISDOM_FILE +#define FFTW_TYPE FFTW_MEASURE +#else +#define FFTW_TYPE 0 +#endif + +pthread_mutex_t fft_mutex = PTHREAD_MUTEX_INITIALIZER; + +void srslte_dft_load() { +#ifdef FFTW_WISDOM_FILE + fftwf_import_wisdom_from_filename(FFTW_WISDOM_FILE); +#else + printf("Warning: FFTW Wisdom file not defined\n"); +#endif +} + +void srslte_dft_exit() { +#ifdef FFTW_WISDOM_FILE + fftwf_export_wisdom_to_filename(FFTW_WISDOM_FILE); +#endif + fftwf_cleanup(); +} + +int srslte_dft_plan(srslte_dft_plan_t *plan, const int dft_points, srslte_dft_dir_t dir, + srslte_dft_mode_t mode) { + bzero(plan, sizeof(srslte_dft_plan_t)); + if(mode == SRSLTE_DFT_COMPLEX){ + return srslte_dft_plan_c(plan,dft_points,dir); + } else { + return srslte_dft_plan_r(plan,dft_points,dir); + } + return 0; +} + +int srslte_dft_replan(srslte_dft_plan_t *plan, const int new_dft_points) { + if (new_dft_points <= plan->init_size) { + if(plan->mode == SRSLTE_DFT_COMPLEX){ + return srslte_dft_replan_c(plan,new_dft_points); + } else { + return srslte_dft_replan_r(plan,new_dft_points); + } + } else { + fprintf(stderr, "DFT: Error calling replan: new_dft_points (%d) must be lower or equal " + "dft_size passed initially (%d)\n", new_dft_points, plan->init_size); + return -1; + } +} + + + +static void allocate(srslte_dft_plan_t *plan, int size_in, int size_out, int len) { + plan->in = fftwf_malloc(size_in*len); + plan->out = fftwf_malloc(size_out*len); +} + +int srslte_dft_replan_guru_c(srslte_dft_plan_t *plan, const int new_dft_points, cf_t *in_buffer, + cf_t *out_buffer, int istride, int ostride, int how_many, + int idist, int odist) { + int sign = (plan->forward) ? FFTW_FORWARD : FFTW_BACKWARD; + + const fftwf_iodim iodim = {new_dft_points, istride, ostride}; + const fftwf_iodim howmany_dims = {how_many, idist, odist}; + + pthread_mutex_lock(&fft_mutex); + + /* Destroy current plan */ + fftwf_destroy_plan(plan->p); + + plan->p = fftwf_plan_guru_dft(1, &iodim, 1, &howmany_dims, in_buffer, out_buffer, sign, FFTW_TYPE); + + pthread_mutex_unlock(&fft_mutex); + + if (!plan->p) { + return -1; + } + plan->size = new_dft_points; + plan->init_size = plan->size; + + return 0; +} + +int srslte_dft_replan_c(srslte_dft_plan_t *plan, const int new_dft_points) { + int sign = (plan->dir == SRSLTE_DFT_FORWARD) ? FFTW_FORWARD : FFTW_BACKWARD; + + pthread_mutex_lock(&fft_mutex); + if (plan->p) { + fftwf_destroy_plan(plan->p); + plan->p = NULL; + } + plan->p = fftwf_plan_dft_1d(new_dft_points, plan->in, plan->out, sign, FFTW_TYPE); + pthread_mutex_unlock(&fft_mutex); + + if (!plan->p) { + return -1; + } + plan->size = new_dft_points; + return 0; +} + +int srslte_dft_plan_guru_c(srslte_dft_plan_t *plan, const int dft_points, srslte_dft_dir_t dir, cf_t *in_buffer, + cf_t *out_buffer, int istride, int ostride, int how_many, + int idist, int odist) { + int sign = (dir == SRSLTE_DFT_FORWARD) ? FFTW_FORWARD : FFTW_BACKWARD; + + const fftwf_iodim iodim = {dft_points, istride, ostride}; + const fftwf_iodim howmany_dims = {how_many, idist, odist}; + + pthread_mutex_lock(&fft_mutex); + + plan->p = fftwf_plan_guru_dft(1, &iodim, 1, &howmany_dims, in_buffer, out_buffer, sign, FFTW_TYPE); + if (!plan->p) { + return -1; + } + pthread_mutex_unlock(&fft_mutex); + + plan->size = dft_points; + plan->init_size = plan->size; + plan->mode = SRSLTE_DFT_COMPLEX; + plan->dir = dir; + plan->forward = (dir==SRSLTE_DFT_FORWARD)?true:false; + plan->mirror = false; + plan->db = false; + plan->norm = false; + plan->dc = false; + plan->is_guru = true; + + return 0; +} + +int srslte_dft_plan_c(srslte_dft_plan_t *plan, const int dft_points, srslte_dft_dir_t dir) { + allocate(plan,sizeof(fftwf_complex),sizeof(fftwf_complex), dft_points); + + pthread_mutex_lock(&fft_mutex); + + int sign = (dir == SRSLTE_DFT_FORWARD) ? FFTW_FORWARD : FFTW_BACKWARD; + plan->p = fftwf_plan_dft_1d(dft_points, plan->in, plan->out, sign, FFTW_TYPE); + + pthread_mutex_unlock(&fft_mutex); + + if (!plan->p) { + return -1; + } + plan->size = dft_points; + plan->init_size = plan->size; + plan->mode = SRSLTE_DFT_COMPLEX; + plan->dir = dir; + plan->forward = (dir==SRSLTE_DFT_FORWARD)?true:false; + plan->mirror = false; + plan->db = false; + plan->norm = false; + plan->dc = false; + plan->is_guru = false; + + return 0; +} + +int srslte_dft_replan_r(srslte_dft_plan_t *plan, const int new_dft_points) { + int sign = (plan->dir == SRSLTE_DFT_FORWARD) ? FFTW_R2HC : FFTW_HC2R; + + pthread_mutex_lock(&fft_mutex); + if (plan->p) { + fftwf_destroy_plan(plan->p); + plan->p = NULL; + } + plan->p = fftwf_plan_r2r_1d(new_dft_points, plan->in, plan->out, sign, FFTW_TYPE); + pthread_mutex_unlock(&fft_mutex); + + if (!plan->p) { + return -1; + } + plan->size = new_dft_points; + return 0; +} + +int srslte_dft_plan_r(srslte_dft_plan_t *plan, const int dft_points, srslte_dft_dir_t dir) { + allocate(plan,sizeof(float),sizeof(float), dft_points); + int sign = (dir == SRSLTE_DFT_FORWARD) ? FFTW_R2HC : FFTW_HC2R; + + pthread_mutex_lock(&fft_mutex); + plan->p = fftwf_plan_r2r_1d(dft_points, plan->in, plan->out, sign, FFTW_TYPE); + pthread_mutex_unlock(&fft_mutex); + + if (!plan->p) { + return -1; + } + plan->size = dft_points; + plan->init_size = plan->size; + plan->mode = SRSLTE_REAL; + plan->dir = dir; + plan->forward = (dir==SRSLTE_DFT_FORWARD)?true:false; + plan->mirror = false; + plan->db = false; + plan->norm = false; + plan->dc = false; + + return 0; +} + +void srslte_dft_plan_set_mirror(srslte_dft_plan_t *plan, bool val){ + plan->mirror = val; +} +void srslte_dft_plan_set_db(srslte_dft_plan_t *plan, bool val){ + plan->db = val; +} +void srslte_dft_plan_set_norm(srslte_dft_plan_t *plan, bool val){ + plan->norm = val; +} +void srslte_dft_plan_set_dc(srslte_dft_plan_t *plan, bool val){ + plan->dc = val; +} + +static void copy_pre(uint8_t *dst, uint8_t *src, int size_d, int len, + bool forward, bool mirror, bool dc) { + int offset = dc?1:0; + if(mirror && !forward){ + int hlen = dft_floor(len,2); + memset(dst,0,size_d*offset); + memcpy(&dst[size_d*offset], &src[size_d*hlen], size_d*(len-hlen-offset)); + memcpy(&dst[(len-hlen)*size_d], src, size_d*hlen); + } else { + memcpy(dst,src,size_d*len); + } +} + +static void copy_post(uint8_t *dst, uint8_t *src, int size_d, int len, + bool forward, bool mirror, bool dc) { + int offset = dc?1:0; + if(mirror && forward){ + int hlen = dft_ceil(len,2); + memcpy(dst, &src[size_d*hlen], size_d*(len-hlen)); + memcpy(&dst[(len-hlen)*size_d], &src[size_d*offset], size_d*(hlen-offset)); + } else { + memcpy(dst,src,size_d*len); + } +} + +void srslte_dft_run(srslte_dft_plan_t *plan, const void *in, void *out) { + if(plan->mode == SRSLTE_DFT_COMPLEX) { + srslte_dft_run_c(plan,in,out); + } else { + srslte_dft_run_r(plan,in,out); + } +} + +void srslte_dft_run_c_zerocopy(srslte_dft_plan_t *plan, const cf_t *in, cf_t *out) { + fftwf_execute_dft(plan->p, (cf_t*) in, out); +} + +void srslte_dft_run_c(srslte_dft_plan_t *plan, const cf_t *in, cf_t *out) { + float norm; + int i; + fftwf_complex *f_out = plan->out; + + copy_pre((uint8_t*)plan->in, (uint8_t*)in, sizeof(cf_t), plan->size, + plan->forward, plan->mirror, plan->dc); + fftwf_execute(plan->p); + if (plan->norm) { + norm = 1.0/sqrtf(plan->size); + srslte_vec_sc_prod_cfc(f_out, norm, f_out, plan->size); + } + if (plan->db) { + for (i=0;isize;i++) { + f_out[i] = 10*log10(f_out[i]); + } + } + copy_post((uint8_t*)out, (uint8_t*)plan->out, sizeof(cf_t), plan->size, + plan->forward, plan->mirror, plan->dc); +} + +void srslte_dft_run_guru_c(srslte_dft_plan_t *plan) { + if (plan->is_guru == true) { + fftwf_execute(plan->p); + } else { + fprintf(stderr, "srslte_dft_run_guru_c: the selected plan is not guru!\n"); + } +} + +void srslte_dft_run_r(srslte_dft_plan_t *plan, const float *in, float *out) { + float norm; + int i; + int len = plan->size; + float *f_out = plan->out; + + memcpy(plan->in,in,sizeof(float)*plan->size); + fftwf_execute(plan->p); + if (plan->norm) { + norm = 1.0/plan->size; + srslte_vec_sc_prod_fff(f_out, norm, f_out, plan->size); + } + if (plan->db) { + for (i=0;iout,sizeof(float)*plan->size); +} + +void srslte_dft_plan_free(srslte_dft_plan_t *plan) { + if (!plan) return; + if (!plan->size) return; + + pthread_mutex_lock(&fft_mutex); + if (!plan->is_guru) { + if (plan->in) fftwf_free(plan->in); + if (plan->out) fftwf_free(plan->out); + } + if (plan->p) fftwf_destroy_plan(plan->p); + pthread_mutex_unlock(&fft_mutex); + + bzero(plan, sizeof(srslte_dft_plan_t)); +} + + + diff --git a/lib/src/phy/dft/dft_precoding.c b/lib/src/phy/dft/dft_precoding.c new file mode 100644 index 0000000..a43d794 --- /dev/null +++ b/lib/src/phy/dft/dft_precoding.c @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/dft/dft_precoding.h" + +/* Create DFT plans for transform precoding */ + +int srslte_dft_precoding_init(srslte_dft_precoding_t *q, uint32_t max_prb, bool is_tx) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + bzero(q, sizeof(srslte_dft_precoding_t)); + + if (max_prb <= SRSLTE_MAX_PRB) { + ret = SRSLTE_ERROR; + for (uint32_t i=1;i<=max_prb;i++) { + if(srslte_dft_precoding_valid_prb(i)) { + DEBUG("Initiating DFT precoding plan for %d PRBs\n", i); + if (srslte_dft_plan_c(&q->dft_plan[i], i*SRSLTE_NRE, is_tx?SRSLTE_DFT_FORWARD:SRSLTE_DFT_BACKWARD)) { + fprintf(stderr, "Error: Creating DFT plan %d\n",i); + goto clean_exit; + } + srslte_dft_plan_set_norm(&q->dft_plan[i], true); + } + } + q->max_prb = max_prb; + ret = SRSLTE_SUCCESS; + } + + clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_dft_precoding_free(q); + } + return ret; +} + +int srslte_dft_precoding_init_rx(srslte_dft_precoding_t *q, uint32_t max_prb) +{ + return srslte_dft_precoding_init(q, max_prb, false); +} + +int srslte_dft_precoding_init_tx(srslte_dft_precoding_t *q, uint32_t max_prb) { + return srslte_dft_precoding_init(q, max_prb, true); +} + +/* Free DFT plans for transform precoding */ +void srslte_dft_precoding_free(srslte_dft_precoding_t *q) +{ + for (uint32_t i=1;i<=q->max_prb;i++) { + if(srslte_dft_precoding_valid_prb(i)) { + srslte_dft_plan_free(&q->dft_plan[i]); + } + } + bzero(q, sizeof(srslte_dft_precoding_t)); +} + +static bool valid_prb[101]={true,true,true,true,true,true,true,false,true,true,true,false,true,false,false,true,true,false,true,false,true,false,false,false, + true,true,false,true,false,false,true,false,true,false,false,false,true,false,false,false,true,false,false,false,false,true,false,false,true,false, + true,false,false,false,true,false,false,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,true,false,false,true, + false,false,false,false,true,true,false,false,false,false,false,false,false,false,true,false,false,false,false,false,true,false,false,false,true}; + +bool srslte_dft_precoding_valid_prb(uint32_t nof_prb) { + if (nof_prb <= 100) { + return valid_prb[nof_prb]; + } + return false; +} + +int srslte_dft_precoding(srslte_dft_precoding_t *q, cf_t *input, cf_t *output, + uint32_t nof_prb, uint32_t nof_symbols) +{ + + if (!srslte_dft_precoding_valid_prb(nof_prb) && nof_prb <= q->max_prb) { + fprintf(stderr, "Error invalid number of PRB (%d)\n", nof_prb); + return SRSLTE_ERROR; + } + + for (uint32_t i=0;idft_plan[nof_prb], &input[i*SRSLTE_NRE*nof_prb], &output[i*SRSLTE_NRE*nof_prb]); + } + + return SRSLTE_SUCCESS; +} diff --git a/lib/src/phy/dft/ofdm.c b/lib/src/phy/dft/ofdm.c new file mode 100644 index 0000000..06da6b4 --- /dev/null +++ b/lib/src/phy/dft/ofdm.c @@ -0,0 +1,599 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/dft/ofdm.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +/* Uncomment next line for avoiding Guru DFT call */ +//#define AVOID_GURU + +int srslte_ofdm_init_(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, int symbol_sz, int nof_prb, srslte_dft_dir_t dir) { + return srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, nof_prb, dir, SRSLTE_SF_NORM); +} + +int srslte_ofdm_init_mbsfn_(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, int symbol_sz, int nof_prb, srslte_dft_dir_t dir, srslte_sf_t sf_type) { + + /* Set OFDM object attributes */ + q->symbol_sz = (uint32_t) symbol_sz; + q->nof_symbols = SRSLTE_CP_NSYMB(cp); + q->nof_symbols_mbsfn = SRSLTE_CP_NSYMB(SRSLTE_CP_EXT); + q->cp = cp; + q->freq_shift = false; + q->nof_re = (uint32_t) nof_prb * SRSLTE_NRE; + q->nof_guards = ((symbol_sz - q->nof_re) / 2); + q->slot_sz = (uint32_t) SRSLTE_SLOT_LEN(symbol_sz); + q->sf_sz = (uint32_t) SRSLTE_SF_LEN(symbol_sz); + q->in_buffer = in_buffer; + q->out_buffer= out_buffer; + + if (srslte_dft_plan_c(&q->fft_plan, symbol_sz, dir)) { + fprintf(stderr, "Error: Creating DFT plan\n"); + return -1; + } + +#ifdef AVOID_GURU + q->tmp = srslte_vec_malloc((uint32_t) symbol_sz * sizeof(cf_t)); + if (!q->tmp) { + perror("malloc"); + return -1; + } + bzero(q->tmp, sizeof(cf_t) * symbol_sz); +#else + int cp1 = SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN_NORM(0, symbol_sz):SRSLTE_CP_LEN_EXT(symbol_sz); + int cp2 = SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN_NORM(1, symbol_sz):SRSLTE_CP_LEN_EXT(symbol_sz); + + q->tmp = srslte_vec_malloc(sizeof(cf_t) * q->sf_sz); + if (!q->tmp) { + perror("malloc"); + return -1; + } + bzero(q->tmp, sizeof(cf_t) * q->sf_sz); + + if (dir == SRSLTE_DFT_BACKWARD) { + bzero(in_buffer, sizeof(cf_t) * SRSLTE_SF_LEN_RE(nof_prb, cp)); + }else { + bzero(in_buffer, sizeof(cf_t) * q->sf_sz); + } + + for (int slot = 0; slot < 2; slot++) { + //bzero(&q->fft_plan_sf[slot], sizeof(srslte_dft_plan_t)); + //bzero(q->tmp + SRSLTE_CP_NSYMB(cp)*symbol_sz*slot, sizeof(cf_t) * (cp1 + (SRSLTE_CP_NSYMB(cp) - 1)*cp2 + SRSLTE_CP_NSYMB(cp)*symbol_sz)); + if (dir == SRSLTE_DFT_FORWARD) { + if (srslte_dft_plan_guru_c(&q->fft_plan_sf[slot], symbol_sz, dir, + in_buffer + cp1 + q->slot_sz * slot, + q->tmp + q->nof_symbols * q->symbol_sz * slot, + 1, 1, SRSLTE_CP_NSYMB(cp), symbol_sz + cp2, symbol_sz)) { + fprintf(stderr, "Error: Creating DFT plan (1)\n"); + return -1; + } + } else { + if (srslte_dft_plan_guru_c(&q->fft_plan_sf[slot], symbol_sz, dir, + q->tmp + q->nof_symbols * q->symbol_sz * slot, + out_buffer + cp1 + q->slot_sz * slot, + 1, 1, SRSLTE_CP_NSYMB(cp), symbol_sz, symbol_sz + cp2)) { + fprintf(stderr, "Error: Creating DFT plan (1)\n"); + return -1; + } + } + } +#endif + + q->shift_buffer = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN(symbol_sz)); + if (!q->shift_buffer) { + perror("malloc"); + return -1; + } + + srslte_dft_plan_set_mirror(&q->fft_plan, true); + srslte_dft_plan_set_dc(&q->fft_plan, true); + + DEBUG("Init %s symbol_sz=%d, nof_symbols=%d, cp=%s, nof_re=%d, nof_guards=%d\n", + dir==SRSLTE_DFT_FORWARD?"FFT":"iFFT", q->symbol_sz, q->nof_symbols, + q->cp==SRSLTE_CP_NORM?"Normal":"Extended", q->nof_re, q->nof_guards); + + // MBSFN logic + if (sf_type == SRSLTE_SF_MBSFN) { + q->mbsfn_subframe = true; + q->non_mbsfn_region = 2; // default set to 2 + } else { + q->mbsfn_subframe = false; + } + + return SRSLTE_SUCCESS; +} + +void srslte_ofdm_set_non_mbsfn_region(srslte_ofdm_t *q, uint8_t non_mbsfn_region) +{ + q->non_mbsfn_region = non_mbsfn_region; +} + +int srslte_ofdm_replan_(srslte_ofdm_t *q, srslte_cp_t cp, int symbol_sz, int nof_prb) { + + if (srslte_dft_replan_c(&q->fft_plan, symbol_sz)) { + fprintf(stderr, "Error: Creating DFT plan\n"); + return -1; + } + + q->symbol_sz = (uint32_t) symbol_sz; + q->nof_symbols = SRSLTE_CP_NSYMB(cp); + q->nof_symbols_mbsfn = SRSLTE_CP_NSYMB(SRSLTE_CP_EXT); + q->cp = cp; + q->nof_re = (uint32_t) nof_prb * SRSLTE_NRE; + q->nof_guards = ((symbol_sz - q->nof_re) / 2); + q->slot_sz = (uint32_t) SRSLTE_SLOT_LEN(symbol_sz); + q->sf_sz = (uint32_t) SRSLTE_SF_LEN(symbol_sz); + +#ifndef AVOID_GURU + cf_t *in_buffer = q->in_buffer; + cf_t *out_buffer = q->out_buffer; + + int cp1 = SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN_NORM(0, symbol_sz):SRSLTE_CP_LEN_EXT(symbol_sz); + int cp2 = SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN_NORM(1, symbol_sz):SRSLTE_CP_LEN_EXT(symbol_sz); + + srslte_dft_dir_t dir = q->fft_plan_sf[0].dir; + + if (q->tmp) { + free(q->tmp); + } + + q->tmp = srslte_vec_malloc(sizeof(cf_t) * q->sf_sz); + if (!q->tmp) { + perror("malloc"); + return -1; + } + bzero(q->tmp, sizeof(cf_t) * q->sf_sz); + + if (dir == SRSLTE_DFT_BACKWARD) { + bzero(in_buffer, sizeof(cf_t) * SRSLTE_SF_LEN_RE(nof_prb, cp)); + }else { + bzero(in_buffer, sizeof(cf_t) * q->sf_sz); + } + + for (int slot = 0; slot < 2; slot++) { + srslte_dft_plan_free(&q->fft_plan_sf[slot]); + + if (dir == SRSLTE_DFT_FORWARD) { + if (srslte_dft_plan_guru_c(&q->fft_plan_sf[slot], symbol_sz, dir, + in_buffer + cp1 + q->slot_sz * slot, + q->tmp + q->nof_symbols * q->symbol_sz * slot, + 1, 1, SRSLTE_CP_NSYMB(cp), symbol_sz + cp2, symbol_sz)) { + fprintf(stderr, "Error: Creating DFT plan (1)\n"); + return -1; + } + } else { + if (srslte_dft_plan_guru_c(&q->fft_plan_sf[slot], symbol_sz, dir, + q->tmp + q->nof_symbols * q->symbol_sz * slot, + out_buffer + cp1 + q->slot_sz * slot, + 1, 1, SRSLTE_CP_NSYMB(cp), symbol_sz, symbol_sz + cp2)) { + fprintf(stderr, "Error: Creating DFT plan (1)\n"); + return -1; + } + } + } +#endif /* AVOID_GURU */ + + + if (q->freq_shift) { + srslte_ofdm_set_freq_shift(q, q->freq_shift_f); + } + + DEBUG("Replan symbol_sz=%d, nof_symbols=%d, cp=%s, nof_re=%d, nof_guards=%d\n", + q->symbol_sz, q->nof_symbols, + q->cp==SRSLTE_CP_NORM?"Normal":"Extended", q->nof_re, q->nof_guards); + + return SRSLTE_SUCCESS; +} + +void srslte_ofdm_free_(srslte_ofdm_t *q) { + srslte_dft_plan_free(&q->fft_plan); + +#ifndef AVOID_GURU + for (int slot = 0; slot < 2; slot++) { + if (q->fft_plan_sf[slot].init_size) { + srslte_dft_plan_free(&q->fft_plan_sf[slot]); + } + } +#endif + + if (q->tmp) { + free(q->tmp); + } + if (q->shift_buffer) { + free(q->shift_buffer); + } + bzero(q, sizeof(srslte_ofdm_t)); +} + +int srslte_ofdm_rx_init(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t max_prb) { + int symbol_sz = srslte_symbol_sz(max_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", max_prb); + return -1; + } + q->max_prb = max_prb; + return srslte_ofdm_init_(q, cp, in_buffer, out_buffer, symbol_sz, max_prb, SRSLTE_DFT_FORWARD); +} + +int srslte_ofdm_rx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t max_prb) +{ + int symbol_sz = srslte_symbol_sz(max_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", max_prb); + return -1; + } + q->max_prb = max_prb; + return srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, max_prb, SRSLTE_DFT_FORWARD, SRSLTE_SF_MBSFN); +} + + +int srslte_ofdm_tx_init(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t max_prb) { + uint32_t i; + int ret; + + int symbol_sz = srslte_symbol_sz(max_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", max_prb); + return -1; + } + q->max_prb = max_prb; + ret = srslte_ofdm_init_(q, cp, in_buffer, out_buffer, symbol_sz, max_prb, SRSLTE_DFT_BACKWARD); + + + if (ret == SRSLTE_SUCCESS) { + srslte_dft_plan_set_norm(&q->fft_plan, false); + + /* set now zeros at CP */ + for (i=0;inof_symbols;i++) { + bzero(q->tmp, q->nof_guards * sizeof(cf_t)); + bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); + } + } + return ret; +} + +int srslte_ofdm_tx_init_mbsfn(srslte_ofdm_t *q, srslte_cp_t cp, cf_t *in_buffer, cf_t *out_buffer, uint32_t nof_prb) +{ + uint32_t i; + int ret; + + int symbol_sz = srslte_symbol_sz(nof_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", nof_prb); + return -1; + } + q->max_prb = nof_prb; + ret = srslte_ofdm_init_mbsfn_(q, cp, in_buffer, out_buffer, symbol_sz, nof_prb, SRSLTE_DFT_BACKWARD, SRSLTE_SF_MBSFN); + + if (ret == SRSLTE_SUCCESS) { + srslte_dft_plan_set_norm(&q->fft_plan, false); + + /* set now zeros at CP */ + for (i=0;inof_symbols;i++) { + bzero(q->tmp, q->nof_guards * sizeof(cf_t)); + bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); + } + } + return ret; +} + +int srslte_ofdm_rx_set_prb(srslte_ofdm_t *q, srslte_cp_t cp, uint32_t nof_prb) { + if (nof_prb <= q->max_prb) { + int symbol_sz = srslte_symbol_sz(nof_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", nof_prb); + return -1; + } + return srslte_ofdm_replan_(q, cp, symbol_sz, nof_prb); + } else { + fprintf(stderr, "OFDM (Rx): Error calling set_prb: nof_prb (%d) must be equal or lower initialized max_prb (%d)\n", + nof_prb, q->max_prb); + return -1; + } +} + +int srslte_ofdm_tx_set_prb(srslte_ofdm_t *q, srslte_cp_t cp, uint32_t nof_prb) { + uint32_t i; + int ret; + + if (nof_prb <= q->max_prb) { + int symbol_sz = srslte_symbol_sz(nof_prb); + if (symbol_sz < 0) { + fprintf(stderr, "Error: Invalid nof_prb=%d\n", nof_prb); + return -1; + } + + ret = srslte_ofdm_replan_(q, cp, symbol_sz, nof_prb); + + if (ret == SRSLTE_SUCCESS) { + /* set now zeros at CP */ + for (i=0;inof_symbols;i++) { + bzero(q->tmp, q->nof_guards * sizeof(cf_t)); + bzero(&q->tmp[q->nof_re + q->nof_guards], q->nof_guards * sizeof(cf_t)); + } + } + return ret; + } else { + fprintf(stderr, "OFDM (Tx): Error calling set_prb: nof_prb (%d) must be equal or lower initialized max_prb (%d)\n", + nof_prb, q->max_prb); + return -1; + } +} + + +void srslte_ofdm_rx_free(srslte_ofdm_t *q) { + srslte_ofdm_free_(q); +} +/* Shifts the signal after the iFFT or before the FFT. + * Freq_shift is relative to inter-carrier spacing. + * Caution: This function shall not be called during run-time + */ +int srslte_ofdm_set_freq_shift(srslte_ofdm_t *q, float freq_shift) { + cf_t *ptr = q->shift_buffer; + for (uint32_t n=0;n<2;n++) { + for (uint32_t i=0;inof_symbols;i++) { + uint32_t cplen = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); + for (uint32_t t=0;tsymbol_sz+cplen;t++) { + ptr[t] = cexpf(I*2*M_PI*((float) t-(float)cplen)*freq_shift/q->symbol_sz); + } + ptr += q->symbol_sz+cplen; + } + } + + /* Disable DC carrier addition */ + srslte_dft_plan_set_dc(&q->fft_plan, false); + + q->freq_shift = true; + q->freq_shift_f = freq_shift; + return SRSLTE_SUCCESS; +} + +void srslte_ofdm_tx_free(srslte_ofdm_t *q) { + srslte_ofdm_free_(q); +} + +void srslte_ofdm_rx_slot_ng(srslte_ofdm_t *q, cf_t *input, cf_t *output) { + uint32_t i; + for (i=0;inof_symbols;i++) { + input += SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); + srslte_dft_run_c(&q->fft_plan, input, q->tmp); + memcpy(output, &q->tmp[q->nof_guards], q->nof_re * sizeof(cf_t)); + input += q->symbol_sz; + output += q->nof_re; + } +} + +/* Transforms input samples into output OFDM symbols. + * Performs FFT on a each symbol and removes CP. + */ +void srslte_ofdm_rx_slot(srslte_ofdm_t *q, int slot_in_sf) { + cf_t *output = q->out_buffer + slot_in_sf * q->nof_re * q->nof_symbols; + +#ifdef AVOID_GURU + srslte_ofdm_rx_slot_ng(q, q->in_buffer + slot_in_sf * q->slot_sz, q->out_buffer + slot_in_sf * q->nof_re * q->nof_symbols); +#else + float norm = 1.0f/sqrtf(q->fft_plan.size); + cf_t *tmp = q->tmp + slot_in_sf * q->symbol_sz * q->nof_symbols; + uint32_t dc = (q->fft_plan.dc) ? 1:0; + + srslte_dft_run_guru_c(&q->fft_plan_sf[slot_in_sf]); + + for (int i = 0; i < q->nof_symbols; i++) { + memcpy(output, tmp + q->symbol_sz - q->nof_re / 2, sizeof(cf_t) * q->nof_re / 2); + memcpy(output + q->nof_re / 2, &tmp[dc], sizeof(cf_t) * q->nof_re / 2); + + if (q->fft_plan.norm) { + srslte_vec_sc_prod_cfc(output, norm, output, q->nof_re); + } + + tmp += q->symbol_sz; + output += q->nof_re; + } +#endif +} + +void srslte_ofdm_rx_slot_mbsfn(srslte_ofdm_t *q, cf_t *input, cf_t *output) +{ + uint32_t i; + for(i = 0; i < q->nof_symbols_mbsfn; i++){ + if(i == q->non_mbsfn_region) { + input += SRSLTE_NON_MBSFN_REGION_GUARD_LENGTH(q->non_mbsfn_region,q->symbol_sz); + } + input += (i>=q->non_mbsfn_region)?SRSLTE_CP_LEN_EXT(q->symbol_sz):SRSLTE_CP_LEN_NORM(i, q->symbol_sz); + srslte_dft_run_c(&q->fft_plan, input, q->tmp); + memcpy(output, &q->tmp[q->nof_guards], q->nof_re * sizeof(cf_t)); + input += q->symbol_sz; + output += q->nof_re; + } +} + + + +void srslte_ofdm_rx_slot_zerocopy(srslte_ofdm_t *q, cf_t *input, cf_t *output) { + uint32_t i; + for (i=0;inof_symbols;i++) { + input += SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); + srslte_dft_run_c_zerocopy(&q->fft_plan, input, q->tmp); + memcpy(output, &q->tmp[q->symbol_sz/2+q->nof_guards], sizeof(cf_t)*q->nof_re/2); + memcpy(&output[q->nof_re/2], &q->tmp[1], sizeof(cf_t)*q->nof_re/2); + input += q->symbol_sz; + output += q->nof_re; + } +} + +void srslte_ofdm_rx_sf(srslte_ofdm_t *q) { + uint32_t n; + if (q->freq_shift) { + srslte_vec_prod_ccc(q->in_buffer, q->shift_buffer, q->in_buffer, 2*q->slot_sz); + } + if(!q->mbsfn_subframe){ + for (n=0;n<2;n++) { + srslte_ofdm_rx_slot(q, n); + } + } + else{ + srslte_ofdm_rx_slot_mbsfn(q, &q->in_buffer[0*q->slot_sz], &q->out_buffer[0*q->nof_re*q->nof_symbols]); + srslte_ofdm_rx_slot(q, 1); + } +} + +void srslte_ofdm_rx_sf_ng(srslte_ofdm_t *q, cf_t *input, cf_t *output) { + uint32_t n; + if (q->freq_shift) { + srslte_vec_prod_ccc(q->in_buffer, q->shift_buffer, q->in_buffer, 2*q->slot_sz); + } + if(!q->mbsfn_subframe){ + for (n=0;n<2;n++) { + srslte_ofdm_rx_slot_ng(q, &input[n*q->slot_sz], &output[n*q->nof_re*q->nof_symbols]); + } + } + else{ + srslte_ofdm_rx_slot_mbsfn(q, &q->in_buffer[0*q->slot_sz], &q->out_buffer[0*q->nof_re*q->nof_symbols]); + srslte_ofdm_rx_slot(q, 1); + } +} + +/* Transforms input OFDM symbols into output samples. + * Performs FFT on a each symbol and adds CP. + */ +void srslte_ofdm_tx_slot(srslte_ofdm_t *q, int slot_in_sf) { + cf_t *input = q->in_buffer + slot_in_sf * q->nof_re * q->nof_symbols; + cf_t *output = q->out_buffer + slot_in_sf * q->slot_sz; + +#ifdef AVOID_GURU + for (int i=0;inof_symbols;i++) { + int cp_len = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); + memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); + srslte_dft_run_c(&q->fft_plan, q->tmp, &output[cp_len]); + input += q->nof_re; + /* add CP */ + memcpy(output, &output[q->symbol_sz], cp_len * sizeof(cf_t)); + output += q->symbol_sz + cp_len; + } +#else + float norm = 1.0f/sqrtf(q->symbol_sz); + cf_t *tmp = q->tmp + slot_in_sf * q->symbol_sz * q->nof_symbols; + + bzero(tmp, q->slot_sz); + uint32_t dc = (q->fft_plan.dc) ? 1:0; + + for (int i = 0; i < q->nof_symbols; i++) { + memcpy(&tmp[dc], &input[q->nof_re / 2], q->nof_re / 2 * sizeof(cf_t)); + memcpy(&tmp[q->symbol_sz - q->nof_re / 2], &input[0], q->nof_re / 2 * sizeof(cf_t)); + + input += q->nof_re; + tmp += q->symbol_sz; + } + + srslte_dft_run_guru_c(&q->fft_plan_sf[slot_in_sf]); + + for (int i=0;inof_symbols;i++) { + int cp_len = SRSLTE_CP_ISNORM(q->cp) ? SRSLTE_CP_LEN_NORM(i, q->symbol_sz) : SRSLTE_CP_LEN_EXT(q->symbol_sz); + + if (q->fft_plan.norm) { + srslte_vec_sc_prod_cfc(&output[cp_len], norm, &output[cp_len], q->symbol_sz); + } + + /* add CP */ + memcpy(output, &output[q->symbol_sz], cp_len * sizeof(cf_t)); + output += q->symbol_sz + cp_len; + } +#endif + + /*input = q->in_buffer + slot_in_sf * q->nof_re * q->nof_symbols; + cf_t *output2 = srslte_vec_malloc(sizeof(cf_t) * q->slot_sz); + cf_t *o2 = output2; + bzero(q->tmp, sizeof(cf_t)*q->symbol_sz); + //bzero(output2, sizeof(cf_t)*q->slot_sz); + for (int i=0;inof_symbols;i++) { + int cp_len = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); + memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); + srslte_dft_run_c(&q->fft_plan, q->tmp, &o2[cp_len]); + input += q->nof_re; + memcpy(o2, &o2[q->symbol_sz], cp_len * sizeof(cf_t)); + o2 += q->symbol_sz + cp_len; + } + cf_t *output1 = q->out_buffer + slot_in_sf * q->slot_sz;//srslte_vec_malloc(sizeof(cf_t) * q->slot_sz); + + for (int i = 0; i < q->slot_sz; i++) { + float error = cabsf(output1[i] - output2[i])/cabsf(output2[i]); + cf_t k = output1[i]/output2[i]; + if (error > 0.1) printf("%d/%05d error=%f output=%+f%+fi gold=%+f%+fi k=%+f%+fi\n", slot_in_sf, i, error, + __real__ output1[i], __imag__ output1[i], + __real__ output2[i], __imag__ output2[i], + __real__ k, __imag__ k); + } + free(output2);/**/ +} + +void srslte_ofdm_tx_slot_mbsfn(srslte_ofdm_t *q, cf_t *input, cf_t *output) +{ + uint32_t i, cp_len; + for(i=0;inof_symbols_mbsfn;i++) { + cp_len = ( i>(q->non_mbsfn_region-1) )?SRSLTE_CP_LEN_EXT(q->symbol_sz):SRSLTE_CP_LEN_NORM(i, q->symbol_sz); + memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); + srslte_dft_run_c(&q->fft_plan, q->tmp, &output[cp_len]); + input += q->nof_re; + /* add CP */ + memcpy(output, &output[q->symbol_sz], cp_len * sizeof(cf_t)); + output += q->symbol_sz + cp_len; + + /*skip the small section between the non mbms region and the mbms region*/ + if(i == (q->non_mbsfn_region - 1)) + output += SRSLTE_NON_MBSFN_REGION_GUARD_LENGTH(q->non_mbsfn_region,q->symbol_sz); + } +} + +void srslte_ofdm_set_normalize(srslte_ofdm_t *q, bool normalize_enable) { + srslte_dft_plan_set_norm(&q->fft_plan, normalize_enable); +} + +void srslte_ofdm_tx_sf(srslte_ofdm_t *q) +{ + uint32_t n; + if(!q->mbsfn_subframe){ + for (n=0;n<2;n++) { + srslte_ofdm_tx_slot(q, n); + } + } + else{ + srslte_ofdm_tx_slot_mbsfn(q, &q->in_buffer[0*q->nof_re*q->nof_symbols], &q->out_buffer[0*q->slot_sz]); + srslte_ofdm_tx_slot(q, 1); + } + if (q->freq_shift) { + srslte_vec_prod_ccc(q->out_buffer, q->shift_buffer, q->out_buffer, 2*q->slot_sz); + } +} + diff --git a/lib/src/phy/dft/test/CMakeLists.txt b/lib/src/phy/dft/test/CMakeLists.txt new file mode 100644 index 0000000..f781ded --- /dev/null +++ b/lib/src/phy/dft/test/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# FFT TEST +######################################################################## + +add_executable(ofdm_test ofdm_test.c) +target_link_libraries(ofdm_test srslte_phy) + +add_test(ofdm_normal ofdm_test) +add_test(ofdm_extended ofdm_test -e) + +add_test(ofdm_normal_single ofdm_test -n 6) +add_test(ofdm_extended_single ofdm_test -e -n 6) + diff --git a/lib/src/phy/dft/test/ofdm_test.c b/lib/src/phy/dft/test/ofdm_test.c new file mode 100644 index 0000000..c40be4d --- /dev/null +++ b/lib/src/phy/dft/test/ofdm_test.c @@ -0,0 +1,178 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +int nof_prb = -1; +srslte_cp_t cp = SRSLTE_CP_NORM; +int nof_repetitions = 128; + +static double elapsed_us(struct timeval *ts_start, struct timeval *ts_end) { + if (ts_end->tv_usec > ts_start->tv_usec) { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec) * 1000000 + + (double) ts_end->tv_usec - (double) ts_start->tv_usec; + } else { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec - 1) * 1000000 + + ((double) ts_end->tv_usec + 1000000) - (double) ts_start->tv_usec; + } +} + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-n nof_prb [Default All]\n"); + printf("\t-e extended cyclic prefix [Default Normal]\n"); + printf("\t-r nof_repetitions [Default %d]\n", nof_repetitions); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "ner")) != -1) { + switch (opt) { + case 'n': + nof_prb = atoi(argv[optind]); + break; + case 'e': + cp = SRSLTE_CP_EXT; + break; + case 'r': + nof_repetitions = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + struct timeval start, end; + srslte_ofdm_t fft, ifft; + cf_t *input, *outfft, *outifft; + float mse; + int n_prb, max_prb, n_re; + int i; + + parse_args(argc, argv); + + if (nof_prb == -1) { + n_prb = 6; + max_prb = 100; + } else { + n_prb = nof_prb; + max_prb = nof_prb; + } + while(n_prb <= max_prb) { + n_re = SRSLTE_CP_NSYMB(cp) * n_prb * SRSLTE_NRE; + + printf("Running test for %d PRB, %d RE... ", n_prb, n_re);fflush(stdout); + + input = srslte_vec_malloc(sizeof(cf_t) * n_re * 2); + if (!input) { + perror("malloc"); + exit(-1); + } + outfft = srslte_vec_malloc(sizeof(cf_t) * n_re * 2); + if (!outfft) { + perror("malloc"); + exit(-1); + } + outifft = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SLOT_LEN(srslte_symbol_sz(n_prb)) * 2); + if (!outifft) { + perror("malloc"); + exit(-1); + } + bzero(outifft, sizeof(cf_t) * SRSLTE_SLOT_LEN(srslte_symbol_sz(n_prb)) * 2); + + if (srslte_ofdm_rx_init(&fft, cp, outifft, outfft, n_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + exit(-1); + } + srslte_ofdm_set_normalize(&fft, true); + + if (srslte_ofdm_tx_init(&ifft, cp, input, outifft, n_prb)) { + fprintf(stderr, "Error initializing iFFT\n"); + exit(-1); + } + srslte_ofdm_set_normalize(&ifft, true); + + for (i=0;i 1.0f) printf("%04d. %+.1f%+.1fi Vs. %+.1f%+.1f %+.1f%+.1f (mse=%f)\n", i, __real__ input[i], __imag__ input[i], __real__ outifft[i], __imag__ outifft[i], __real__ outfft[i], __imag__ outfft[i], mse); + } + /*for (i=0;i= 0.07) { + printf("MSE too large\n"); + exit(-1); + } + + srslte_ofdm_rx_free(&fft); + srslte_ofdm_tx_free(&ifft); + + free(input); + free(outfft); + free(outifft); + + n_prb++; + } + + srslte_dft_exit(); + + exit(0); +} diff --git a/lib/src/phy/enb/CMakeLists.txt b/lib/src/phy/enb/CMakeLists.txt new file mode 100644 index 0000000..e15fae9 --- /dev/null +++ b/lib/src/phy/enb/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_enb OBJECT ${SOURCES}) diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c new file mode 100644 index 0000000..0040b65 --- /dev/null +++ b/lib/src/phy/enb/enb_dl.c @@ -0,0 +1,545 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/phy/enb/enb_dl.h" + +#include +#include +#include +#include +#include + + +#define CURRENT_FFTSIZE srslte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SRSLTE_SF_LEN(CURRENT_FFTSIZE) + +#define CURRENT_SLOTLEN_RE SRSLTE_SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + +#define SRSLTE_ENB_RF_AMP 0.1 + +int srslte_enb_dl_init(srslte_enb_dl_t *q, cf_t *out_buffer[SRSLTE_MAX_PORTS], uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_enb_dl_t)); + + q->cfi = 3; + q->tx_amp = SRSLTE_ENB_RF_AMP; + + for (int i=0;isf_symbols[i] = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->sf_symbols[i]) { + perror("malloc"); + goto clean_exit; + } + q->slot1_symbols[i] = &q->sf_symbols[i][SRSLTE_SLOT_LEN_RE(max_prb, SRSLTE_CP_NORM)]; + } + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (srslte_ofdm_tx_init(&q->ifft[i], SRSLTE_CP_NORM, q->sf_symbols[i], out_buffer[i], max_prb)) { + fprintf(stderr, "Error initiating FFT (%d)\n", i); + goto clean_exit; + } + } + + if (srslte_ofdm_tx_init_mbsfn(&q->ifft_mbsfn, SRSLTE_CP_EXT, q->sf_symbols[0], out_buffer[0], max_prb)) { + fprintf(stderr, "Error initiating FFT \n"); + goto clean_exit; + } + + if (srslte_pbch_init(&q->pbch)) { + fprintf(stderr, "Error creating PBCH object\n"); + goto clean_exit; + } + if (srslte_pcfich_init(&q->pcfich, 0)) { + fprintf(stderr, "Error creating PCFICH object\n"); + goto clean_exit; + } + if (srslte_phich_init(&q->phich, 0)) { + fprintf(stderr, "Error creating PHICH object\n"); + goto clean_exit; + } + int mbsfn_area_id = 1; + + + if (srslte_pmch_init(&q->pmch, max_prb)) { + fprintf(stderr, "Error creating PMCH object\n"); + } + srslte_pmch_set_area_id(&q->pmch, mbsfn_area_id); + + + if (srslte_pdcch_init_enb(&q->pdcch, max_prb)) { + fprintf(stderr, "Error creating PDCCH object\n"); + goto clean_exit; + } + + if (srslte_pdsch_init_enb(&q->pdsch, max_prb)) { + fprintf(stderr, "Error creating PDSCH object\n"); + goto clean_exit; + } + + if (srslte_refsignal_cs_init(&q->csr_signal, max_prb)) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + goto clean_exit; + } + + if (srslte_refsignal_mbsfn_init(&q->mbsfnr_signal, max_prb)) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + goto clean_exit; + } + ret = SRSLTE_SUCCESS; + + } else { + fprintf(stderr, "Invalid parameters\n"); + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_enb_dl_free(q); + } + return ret; +} + +void srslte_enb_dl_free(srslte_enb_dl_t *q) +{ + if (q) { + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + srslte_ofdm_tx_free(&q->ifft[i]); + } + srslte_ofdm_tx_free(&q->ifft_mbsfn); + srslte_regs_free(&q->regs); + srslte_pbch_free(&q->pbch); + srslte_pcfich_free(&q->pcfich); + srslte_phich_free(&q->phich); + srslte_pdcch_free(&q->pdcch); + srslte_pdsch_free(&q->pdsch); + srslte_pmch_free(&q->pmch); + srslte_refsignal_free(&q->csr_signal); + srslte_refsignal_free(&q->mbsfnr_signal); + for (int i=0;isf_symbols[i]) { + free(q->sf_symbols[i]); + } + } + bzero(q, sizeof(srslte_enb_dl_t)); + } +} + +int srslte_enb_dl_set_cell(srslte_enb_dl_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + q->tx_amp = SRSLTE_ENB_RF_AMP; + + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + if (q->cell.nof_prb != 0) { + srslte_regs_free(&q->regs); + } + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + if (srslte_regs_init(&q->regs, q->cell)) { + fprintf(stderr, "Error resizing REGs\n"); + return SRSLTE_ERROR; + } + for (int i = 0; i < q->cell.nof_ports; i++) { + + q->slot1_symbols[i] = &q->sf_symbols[i][SRSLTE_SLOT_LEN_RE(q->cell.nof_prb, SRSLTE_CP_NORM)]; + + if (srslte_ofdm_tx_set_prb(&q->ifft[i], q->cell.cp, q->cell.nof_prb)) { + fprintf(stderr, "Error re-planning iFFT (%d)\n", i); + return SRSLTE_ERROR; + } + } + + if (srslte_ofdm_tx_set_prb(&q->ifft_mbsfn, SRSLTE_CP_EXT, q->cell.nof_prb)) { + fprintf(stderr, "Error re-planning ifft_mbsfn\n"); + return SRSLTE_ERROR; + } + + srslte_ofdm_set_non_mbsfn_region(&q->ifft_mbsfn, 2); + //srslte_ofdm_set_normalize(&q->ifft_mbsfn, true); + + if (srslte_pbch_set_cell(&q->pbch, q->cell)) { + fprintf(stderr, "Error creating PBCH object\n"); + return SRSLTE_ERROR; + } + if (srslte_pcfich_set_cell(&q->pcfich, &q->regs, q->cell)) { + fprintf(stderr, "Error creating PCFICH object\n"); + return SRSLTE_ERROR; + } + if (srslte_phich_set_cell(&q->phich, &q->regs, q->cell)) { + fprintf(stderr, "Error creating PHICH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pdcch_set_cell(&q->pdcch, &q->regs, q->cell)) { + fprintf(stderr, "Error creating PDCCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pdsch_set_cell(&q->pdsch, q->cell)) { + fprintf(stderr, "Error creating PDSCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pmch_set_cell(&q->pmch, q->cell)) { + fprintf(stderr, "Error creating PMCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_refsignal_cs_set_cell(&q->csr_signal, q->cell)) { + fprintf(stderr, "Error initializing CSR signal (%d)\n",ret); + return SRSLTE_ERROR; + } + int mbsfn_area_id = 1; + if (srslte_refsignal_mbsfn_set_cell(&q->mbsfnr_signal, q->cell, mbsfn_area_id)) { + fprintf(stderr, "Error initializing MBSFNR signal (%d)\n",ret); + return SRSLTE_ERROR; + } + /* Generate PSS/SSS signals */ + srslte_pss_generate(q->pss_signal, cell.id%3); + srslte_sss_generate(q->sss_signal0, q->sss_signal5, cell.id); + } + ret = SRSLTE_SUCCESS; + + } else { + fprintf(stderr, "Invalid cell properties: Id=%d, Ports=%d, PRBs=%d\n", + cell.id, cell.nof_ports, cell.nof_prb); + } + return ret; +} + + + +void srslte_enb_dl_set_non_mbsfn_region(srslte_enb_dl_t *q, uint8_t non_mbsfn_region) +{ + srslte_ofdm_set_non_mbsfn_region(&q->ifft_mbsfn, non_mbsfn_region); +} + +void srslte_enb_dl_set_amp(srslte_enb_dl_t *q, float amp) +{ + q->tx_amp = amp; +} + +void srslte_enb_dl_set_cfi(srslte_enb_dl_t *q, uint32_t cfi) +{ + q->cfi = cfi; +} + +void srslte_enb_dl_set_power_allocation(srslte_enb_dl_t *q, float rho_a, float rho_b) +{ + if (q) { + q->rho_b = rho_b; + srslte_pdsch_set_power_allocation(&q->pdsch, rho_a); + } +} + +void srslte_enb_dl_apply_power_allocation(srslte_enb_dl_t *q) +{ + uint32_t nof_symbols_slot = SRSLTE_CP_NSYMB(q->cell.cp); + uint32_t nof_re_symbol = SRSLTE_NRE * q->cell.nof_prb; + + if (q->rho_b != 0.0f && q->rho_b != 1.0f) { + float scaling = q->rho_b; + for (uint32_t i = 0; i < q->cell.nof_ports; i++) { + for (uint32_t j = 0; j < 2; j++) { + cf_t *ptr; + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 0); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + if (q->cell.cp == SRSLTE_CP_NORM) { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 4); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } else { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 3); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + if (q->cell.nof_ports == 4) { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 1); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + } + } + } +} + +void srslte_enb_dl_prepare_power_allocation(srslte_enb_dl_t *q) +{ + uint32_t nof_symbols_slot = SRSLTE_CP_NSYMB(q->cell.cp); + uint32_t nof_re_symbol = SRSLTE_NRE * q->cell.nof_prb; + + if (q->rho_b != 0.0f && q->rho_b != 1.0f) { + float scaling = 1.0f / q->rho_b; + for (uint32_t i = 0; i < q->cell.nof_ports; i++) { + for (uint32_t j = 0; j < 2; j++) { + cf_t *ptr; + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 0); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + if (q->cell.cp == SRSLTE_CP_NORM) { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 4); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } else { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 3); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + if (q->cell.nof_ports == 4) { + ptr = q->sf_symbols[i] + nof_re_symbol * (j * nof_symbols_slot + 1); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + } + } + } +} + +void srslte_enb_dl_clear_sf(srslte_enb_dl_t *q) +{ + for (int i=0;icell.nof_ports;i++) { + bzero(q->sf_symbols[i], CURRENT_SFLEN_RE * sizeof(cf_t)); + } +} + +void srslte_enb_dl_put_sync(srslte_enb_dl_t *q, uint32_t sf_idx) +{ + if (sf_idx == 0 || sf_idx == 5) { + for (int p = 0; p < q->cell.nof_ports; p++) { + srslte_pss_put_slot(q->pss_signal, q->sf_symbols[p], q->cell.nof_prb, q->cell.cp); + srslte_sss_put_slot(sf_idx ? q->sss_signal5 : q->sss_signal0, q->sf_symbols[p], + q->cell.nof_prb, SRSLTE_CP_NORM); + } + } +} + +void srslte_enb_dl_put_refs(srslte_enb_dl_t *q, uint32_t sf_idx) +{ + for (int p = 0; p < q->cell.nof_ports; p++) { + srslte_refsignal_cs_put_sf(q->cell, (uint32_t) p, q->csr_signal.pilots[p / 2][sf_idx], q->sf_symbols[p]); + } +} + +void srslte_enb_dl_put_mib(srslte_enb_dl_t *q, uint32_t tti) +{ + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + + if ((tti%10) == 0) { + srslte_pbch_mib_pack(&q->cell, tti/10, bch_payload); + srslte_pbch_encode(&q->pbch, bch_payload, q->slot1_symbols, ((tti/10)%4)); + } +} + +void srslte_enb_dl_put_pcfich(srslte_enb_dl_t *q, uint32_t sf_idx) +{ + srslte_pcfich_encode(&q->pcfich, q->cfi, q->sf_symbols, sf_idx); +} + +void srslte_enb_dl_put_phich(srslte_enb_dl_t *q, uint8_t ack, uint32_t n_prb_lowest, + uint32_t n_dmrs, uint32_t sf_idx) +{ + uint32_t ngroup, nseq; + srslte_phich_calc(&q->phich, n_prb_lowest, n_dmrs, &ngroup, &nseq); + srslte_phich_encode(&q->phich, ack, ngroup, nseq, sf_idx, q->sf_symbols); +} + +void srslte_enb_dl_put_base(srslte_enb_dl_t *q, uint32_t tti) +{ + uint32_t sf_idx = tti%10; + + srslte_enb_dl_put_sync(q, sf_idx); + srslte_enb_dl_put_refs(q, sf_idx); + srslte_enb_dl_put_mib(q, tti); + srslte_enb_dl_put_pcfich(q, sf_idx); + +} + +void srslte_enb_dl_put_mbsfn_base(srslte_enb_dl_t *q, uint32_t tti) +{ + uint32_t sf_idx1 = tti%10; + srslte_enb_dl_put_pcfich(q, sf_idx1); + srslte_refsignal_mbsfn_put_sf(q->cell, 0,q->csr_signal.pilots[0][sf_idx1], q->mbsfnr_signal.pilots[0][sf_idx1], q->sf_symbols[0]); +} + +void srslte_enb_dl_gen_signal(srslte_enb_dl_t *q) +{ + // TODO: PAPR control + float norm_factor = 0.05f / sqrt(q->cell.nof_prb); + for (int i = 0; i < q->cell.nof_ports; i++) { + srslte_ofdm_tx_sf(&q->ifft[i]); + srslte_vec_sc_prod_cfc(q->ifft[i].out_buffer, norm_factor, q->ifft[i].out_buffer, (uint32_t) SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + } +} + +void srslte_enb_dl_gen_signal_mbsfn(srslte_enb_dl_t *q) +{ + float norm_factor = 0.05f / sqrt(q->cell.nof_prb); + srslte_ofdm_tx_sf(&q->ifft_mbsfn); + srslte_vec_sc_prod_cfc(q->ifft_mbsfn.out_buffer, norm_factor, q->ifft_mbsfn.out_buffer, (uint32_t) SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); +} + +int srslte_enb_dl_add_rnti(srslte_enb_dl_t *q, uint16_t rnti) +{ + return srslte_pdsch_set_rnti(&q->pdsch, rnti); +} + +void srslte_enb_dl_rem_rnti(srslte_enb_dl_t *q, uint16_t rnti) +{ + srslte_pdsch_free_rnti(&q->pdsch, rnti); +} + +int srslte_enb_dl_put_pdcch_dl(srslte_enb_dl_t *q, srslte_ra_dl_dci_t *grant, + srslte_dci_format_t format, srslte_dci_location_t location, + uint16_t rnti, uint32_t sf_idx) +{ + srslte_dci_msg_t dci_msg = {}; + + bool rnti_is_user = true; + if (rnti == SRSLTE_SIRNTI || rnti == SRSLTE_PRNTI || (rnti >= SRSLTE_RARNTI_START && rnti <= SRSLTE_RARNTI_END)) { + rnti_is_user = false; + } + + if (srslte_dci_msg_pack_pdsch(grant, format, &dci_msg, q->cell.nof_prb, q->cell.nof_ports, rnti_is_user)) { + fprintf(stderr, "Error packing DCI grant\n"); + } + if (srslte_pdcch_encode(&q->pdcch, &dci_msg, location, rnti, q->sf_symbols, sf_idx, q->cfi)) { + fprintf(stderr, "Error encoding DCI message\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int srslte_enb_dl_put_pdcch_ul(srslte_enb_dl_t *q, srslte_ra_ul_dci_t *grant, + srslte_dci_location_t location, + uint16_t rnti, uint32_t sf_idx) +{ + srslte_dci_msg_t dci_msg = {}; + + srslte_dci_msg_pack_pusch(grant, &dci_msg, q->cell.nof_prb); + if (srslte_pdcch_encode(&q->pdcch, &dci_msg, location, rnti, q->sf_symbols, sf_idx, q->cfi)) { + fprintf(stderr, "Error encoding DCI message\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int srslte_enb_dl_put_pdsch(srslte_enb_dl_t *q, srslte_ra_dl_grant_t *grant, srslte_softbuffer_tx_t *softbuffer[SRSLTE_MAX_CODEWORDS], + uint16_t rnti, int rv_idx[SRSLTE_MAX_CODEWORDS], uint32_t sf_idx, + uint8_t *data[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type) +{ + uint32_t pmi = 0; + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(grant); + + /* Translates Precoding Information (pinfo) to Precoding matrix Index (pmi) as 3GPP 36.212 Table 5.3.3.1.5-4 */ + if (mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + switch(nof_tb) { + case 1: + if (grant->pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else if (grant->pinfo > 0 && grant->pinfo < 5) { + pmi = grant->pinfo - 1; + } else { + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + break; + case 2: + if (grant->pinfo < 2) { + pmi = grant->pinfo; + } else { + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + break; + default: + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + return SRSLTE_ERROR; + } + } + + /* Configure pdsch_cfg parameters */ + if (srslte_pdsch_cfg_mimo(&q->pdsch_cfg, q->cell, grant, q->cfi, sf_idx, rv_idx, mimo_type, pmi)) { + ERROR("Error configuring PDSCH (rnti=0x%04x)", rnti); + return SRSLTE_ERROR; + } + + /* Encode PDSCH */ + if (srslte_pdsch_encode(&q->pdsch, &q->pdsch_cfg, softbuffer, data, rnti, q->sf_symbols)) { + fprintf(stderr, "Error encoding PDSCH\n"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +int srslte_enb_dl_put_pmch(srslte_enb_dl_t *q, srslte_ra_dl_grant_t *grant, srslte_softbuffer_tx_t *softbuffer, uint32_t sf_idx, uint8_t *data_mbms) +{ + /* Encode PMCH */ + + int mbsfn_area_id = 1; + if (srslte_pmch_cfg(&q->pmch_cfg, q->cell, grant, q->cfi, sf_idx)) { + fprintf(stderr, "Error configuring PMCH\n"); + return SRSLTE_ERROR; + } + /* Encode PMCH */ + if (srslte_pmch_encode(&q->pmch, &q->pmch_cfg, softbuffer, data_mbms, mbsfn_area_id, q->sf_symbols)) { + fprintf(stderr, "Error encoding PDSCH\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +void srslte_enb_dl_save_signal(srslte_enb_dl_t *q, srslte_softbuffer_tx_t *softbuffer, uint8_t *data, uint32_t tti, uint32_t rv_idx, uint16_t rnti, uint32_t cfi) +{ + char tmpstr[64]; + + snprintf(tmpstr,64,"output/sf_symbols_%d",tti); + srslte_vec_save_file(tmpstr, q->sf_symbols[0], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + + snprintf(tmpstr,64,"output/e_%d",tti); + srslte_bit_unpack_vector(q->pdsch.e[0], q->tmp, q->pdsch_cfg.nbits[0].nof_bits); + srslte_vec_save_file(tmpstr, q->tmp, q->pdsch_cfg.nbits[0].nof_bits*sizeof(uint8_t)); + + /* + int cb_len = q->pdsch_cfg.cb_segm[0].K1; + for (int i=0;ipdsch_cfg.cb_segm[0].C;i++) { + snprintf(tmpstr,64,"output/rmout_%d_%d",i,tti); + srslte_bit_unpack_vector(softbuffer->buffer_b[i], q->tmp, (3*cb_len+12)); + srslte_vec_save_file(tmpstr, q->tmp, (3*cb_len+12)*sizeof(uint8_t)); + }*/ + + snprintf(tmpstr,64,"output/data_%d",tti); + srslte_bit_unpack_vector(data, q->tmp, q->pdsch_cfg.grant.mcs[0].tbs); + srslte_vec_save_file(tmpstr, q->tmp, q->pdsch_cfg.grant.mcs[0].tbs*sizeof(uint8_t)); + + //printf("Saved files for tti=%d, sf=%d, cfi=%d, mcs=%d, tbs=%d, rv=%d, rnti=0x%x\n", tti, tti%10, cfi, + // q->pdsch_cfg.grant.mcs[0].idx, q->pdsch_cfg.grant.mcs[0].tbs, rv_idx, rnti); +} + diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c new file mode 100644 index 0000000..0c561ef --- /dev/null +++ b/lib/src/phy/enb/enb_ul.c @@ -0,0 +1,415 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/phy/enb/enb_ul.h" + +#include +#include +#include + + +#define CURRENT_FFTSIZE srslte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SRSLTE_SF_LEN(CURRENT_FFTSIZE) + +#define CURRENT_SLOTLEN_RE SRSLTE_SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + +#define MAX_CANDIDATES 16 + +int srslte_enb_ul_init(srslte_enb_ul_t *q, + cf_t *in_buffer, + uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_enb_ul_t)); + + q->users = calloc(sizeof(srslte_enb_ul_user_t*), (1+SRSLTE_SIRNTI)); + if (!q->users) { + perror("malloc"); + goto clean_exit; + } + + q->sf_symbols = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->sf_symbols) { + perror("malloc"); + goto clean_exit; + } + + q->ce = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->ce) { + perror("malloc"); + goto clean_exit; + } + + if (srslte_ofdm_rx_init(&q->fft, SRSLTE_CP_NORM, in_buffer, q->sf_symbols, max_prb)) { + fprintf(stderr, "Error initiating FFT\n"); + goto clean_exit; + } + srslte_ofdm_set_normalize(&q->fft, false); + srslte_ofdm_set_freq_shift(&q->fft, -0.5); + + if (srslte_pucch_init(&q->pucch)) { + fprintf(stderr, "Error creating PUCCH object\n"); + goto clean_exit; + } + + if (srslte_pusch_init_enb(&q->pusch, max_prb)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto clean_exit; + } + + srslte_pucch_set_threshold(&q->pucch, 0.8); + + if (srslte_chest_ul_init(&q->chest, max_prb)) { + fprintf(stderr, "Error initiating channel estimator\n"); + goto clean_exit; + } + + ret = SRSLTE_SUCCESS; + + } else { + fprintf(stderr, "Invalid parameters\n"); + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_enb_ul_free(q); + } + return ret; +} + +void srslte_enb_ul_free(srslte_enb_ul_t *q) +{ + if (q) { + + if (q->users) { + for (int i=0;i<=SRSLTE_SIRNTI;i++) { + if (q->users[i]) { + free(q->users[i]); + } + } + free(q->users); + } + + srslte_prach_free(&q->prach); + srslte_ofdm_rx_free(&q->fft); + srslte_pucch_free(&q->pucch); + srslte_pusch_free(&q->pusch); + srslte_chest_ul_free(&q->chest); + if (q->sf_symbols) { + free(q->sf_symbols); + } + if (q->ce) { + free(q->ce); + } + bzero(q, sizeof(srslte_enb_ul_t)); + } +} + +int srslte_enb_ul_set_cell(srslte_enb_ul_t *q, srslte_cell_t cell, + srslte_prach_cfg_t *prach_cfg, + srslte_refsignal_dmrs_pusch_cfg_t *pusch_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_pucch_cfg_t *pucch_cfg) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + if (hopping_cfg) { + memcpy(&q->hopping_cfg, hopping_cfg, sizeof(srslte_pusch_hopping_cfg_t)); + } + + if (srslte_ofdm_rx_set_prb(&q->fft, q->cell.cp, q->cell.nof_prb)) { + fprintf(stderr, "Error initiating FFT\n"); + return SRSLTE_ERROR; + } + + if (srslte_pucch_set_cell(&q->pucch, q->cell)) { + fprintf(stderr, "Error creating PUCCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pusch_set_cell(&q->pusch, q->cell)) { + fprintf(stderr, "Error creating PUSCH object\n"); + return SRSLTE_ERROR; + } + + if (prach_cfg) { + if (srslte_prach_init_cfg(&q->prach, prach_cfg, q->cell.nof_prb)) { + fprintf(stderr, "Error initiating PRACH\n"); + return SRSLTE_ERROR; + } + srslte_prach_set_detect_factor(&q->prach, 60); + } + + if (srslte_chest_ul_set_cell(&q->chest, cell)) { + fprintf(stderr, "Error initiating channel estimator\n"); + return SRSLTE_ERROR; + } + + // Configure common PUCCH configuration + srslte_pucch_set_cfg(&q->pucch, pucch_cfg, pusch_cfg->group_hopping_en); + + // SRS is a dedicated configuration + srslte_chest_ul_set_cfg(&q->chest, pusch_cfg, pucch_cfg, NULL); + + ret = SRSLTE_SUCCESS; + } + } else { + fprintf(stderr, "Invalid cell properties: Id=%d, Ports=%d, PRBs=%d\n", + cell.id, cell.nof_ports, cell.nof_prb); + } + return ret; +} + + +int srslte_enb_ul_add_rnti(srslte_enb_ul_t *q, uint16_t rnti) +{ + if (!q->users[rnti]) { + q->users[rnti] = calloc(1, sizeof(srslte_enb_ul_user_t)); + + if (srslte_pucch_set_crnti(&q->pucch, rnti)) { + fprintf(stderr, "Error setting PUCCH rnti\n"); + return -1; + } + if (srslte_pusch_set_rnti(&q->pusch, rnti)) { + fprintf(stderr, "Error setting PUSCH rnti\n"); + return -1; + } + return 0; + } else { + fprintf(stderr, "Error adding rnti=0x%x, already exists\n", rnti); + return -1; + } +} + +void srslte_enb_ul_rem_rnti(srslte_enb_ul_t *q, uint16_t rnti) +{ + if (q->users[rnti]) { + free(q->users[rnti]); + q->users[rnti] = NULL; + srslte_pusch_free_rnti(&q->pusch, rnti); + } +} + +int srslte_enb_ul_cfg_ue(srslte_enb_ul_t *q, uint16_t rnti, + srslte_uci_cfg_t *uci_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_refsignal_srs_cfg_t *srs_cfg) +{ + if (q->users[rnti]) { + if (uci_cfg) { + memcpy(&q->users[rnti]->uci_cfg, uci_cfg, sizeof(srslte_uci_cfg_t)); + q->users[rnti]->uci_cfg_en = true; + } else { + q->users[rnti]->uci_cfg_en = false; + } + if (pucch_sched) { + memcpy(&q->users[rnti]->pucch_sched, pucch_sched, sizeof(srslte_pucch_sched_t)); + } + if (srs_cfg) { + memcpy(&q->users[rnti]->srs_cfg, srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + q->users[rnti]->srs_cfg_en = true; + } else { + q->users[rnti]->srs_cfg_en = false; + } + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Error configuring UE: rnti=0x%x not found\n", rnti); + return SRSLTE_ERROR; + } +} + +void srslte_enb_ul_fft(srslte_enb_ul_t *q) +{ + srslte_ofdm_rx_sf(&q->fft); +} + +int get_pucch(srslte_enb_ul_t *q, uint16_t rnti, + uint32_t pdcch_n_cce, uint32_t sf_rx, + srslte_uci_data_t *uci_data, uint8_t bits[SRSLTE_PUCCH_MAX_BITS], uint32_t nof_bits) +{ + float noise_power = srslte_chest_ul_get_noise_estimate(&q->chest); + + srslte_pucch_format_t format = srslte_pucch_get_format(uci_data, q->cell.cp); + if (format == SRSLTE_PUCCH_FORMAT_ERROR) { + fprintf(stderr,"Error getting format\n"); + return SRSLTE_ERROR; + } + + uint32_t n_pucch = srslte_pucch_get_npucch(pdcch_n_cce, format, uci_data->scheduling_request, &q->users[rnti]->pucch_sched); + + if (srslte_chest_ul_estimate_pucch(&q->chest, q->sf_symbols, q->ce, format, n_pucch, sf_rx, &bits[20])) { + fprintf(stderr,"Error estimating PUCCH DMRS\n"); + return SRSLTE_ERROR; + } + + int ret_val = srslte_pucch_decode(&q->pucch, format, n_pucch, sf_rx, rnti, q->sf_symbols, q->ce, noise_power, bits, nof_bits); + if (ret_val < 0) { + fprintf(stderr,"Error decoding PUCCH\n"); + return SRSLTE_ERROR; + } + return ret_val; +} + +int srslte_enb_ul_get_pucch(srslte_enb_ul_t *q, uint16_t rnti, + uint32_t pdcch_n_cce, uint32_t sf_rx, + srslte_uci_data_t *uci_data) +{ + uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS]; + + if (q->users[rnti]) { + uint32_t nof_uci_bits = uci_data->ri_periodic_report ? uci_data->uci_ri_len : (uci_data->uci_cqi_len); + int ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits, nof_uci_bits); + + // If we are looking for SR and ACK at the same time and ret=0, means there is no SR. + // try again to decode ACK only + if (uci_data->scheduling_request && uci_data->uci_ack_len && ret_val != 1) { + uci_data->scheduling_request = false; + ret_val = get_pucch(q, rnti, pdcch_n_cce, sf_rx, uci_data, pucch_bits, nof_uci_bits); + } + + // update schedulign request + if (uci_data->scheduling_request) { + uci_data->scheduling_request = (ret_val==1); + } + + // Save ACK bits + if (uci_data->uci_ack_len > 0) { + uci_data->uci_ack = pucch_bits[0]; + } + + if (uci_data->uci_ack_len > 1) { + uci_data->uci_ack_2 = pucch_bits[1]; + } + + // PUCCH2 CQI bits are decoded inside srslte_pucch_decode() + if (uci_data->uci_cqi_len) { + memcpy(uci_data->uci_cqi, pucch_bits, uci_data->uci_cqi_len*sizeof(uint8_t)); + } + + if (uci_data->uci_ri_len) { + uci_data->uci_ri = pucch_bits[0]; /* Assume only one bit of RI */ + } + + if (uci_data->uci_cqi_len || uci_data->uci_ri_len) { + if (uci_data->uci_ack_len >= 1) { + uci_data->uci_ack = pucch_bits[20]; + } + if (uci_data->uci_ack_len == 2) { + uci_data->uci_ack_2 = pucch_bits[21]; + } + } + + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Error getting PUCCH: rnti=0x%x not found\n", rnti); + return SRSLTE_ERROR; + } +} + +int srslte_enb_ul_get_pusch(srslte_enb_ul_t *q, srslte_ra_ul_grant_t *grant, srslte_softbuffer_rx_t *softbuffer, + uint16_t rnti, uint32_t rv_idx, uint32_t current_tx_nb, + uint8_t *data, srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data, uint32_t tti) +{ + if (q->users[rnti]) { + if (srslte_pusch_cfg(&q->pusch, + &q->pusch_cfg, + grant, + q->users[rnti]->uci_cfg_en?&q->users[rnti]->uci_cfg:NULL, + &q->hopping_cfg, + q->users[rnti]->srs_cfg_en?&q->users[rnti]->srs_cfg:NULL, + tti, rv_idx, current_tx_nb)) { + fprintf(stderr, "Error configuring PDSCH\n"); + return SRSLTE_ERROR; + } + } else { + if (srslte_pusch_cfg(&q->pusch, + &q->pusch_cfg, + grant, + NULL, + &q->hopping_cfg, + NULL, + tti, rv_idx, current_tx_nb)) { + fprintf(stderr, "Error configuring PDSCH\n"); + return SRSLTE_ERROR; + } + } + + uint32_t cyclic_shift_for_dmrs = 0; + + srslte_chest_ul_estimate(&q->chest, q->sf_symbols, q->ce, grant->L_prb, tti%10, cyclic_shift_for_dmrs, grant->n_prb); + + float noise_power = srslte_chest_ul_get_noise_estimate(&q->chest); + + return srslte_pusch_decode(&q->pusch, &q->pusch_cfg, + softbuffer, q->sf_symbols, + q->ce, noise_power, + rnti, data, + cqi_value, + uci_data); +} + + +int srslte_enb_ul_detect_prach(srslte_enb_ul_t *q, uint32_t tti, + uint32_t freq_offset, cf_t *signal, + uint32_t *indices, float *offsets, float *peak2avg) +{ + uint32_t nof_detected_prach = 0; + // consider the number of subframes the transmission must be anticipated + if (srslte_prach_tti_opportunity(&q->prach, tti, -1)) + { + + if (srslte_prach_detect_offset(&q->prach, + freq_offset, + &signal[q->prach.N_cp], + SRSLTE_SF_LEN_PRB(q->cell.nof_prb), + indices, + offsets, + peak2avg, + &nof_detected_prach)) + { + fprintf(stderr, "Error detecting PRACH\n"); + return SRSLTE_ERROR; + } + } + return (int) nof_detected_prach; +} + + + + + diff --git a/lib/src/phy/fec/CMakeLists.txt b/lib/src/phy/fec/CMakeLists.txt new file mode 100644 index 0000000..86ed081 --- /dev/null +++ b/lib/src/phy/fec/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_fec OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/fec/cbsegm.c b/lib/src/phy/fec/cbsegm.c new file mode 100644 index 0000000..dc4c192 --- /dev/null +++ b/lib/src/phy/fec/cbsegm.c @@ -0,0 +1,155 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "srslte/phy/fec/turbodecoder_gen.h" +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/utils/debug.h" + + +const uint32_t tc_cb_sizes[SRSLTE_NOF_TC_CB_SIZES] = { 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, + 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, + 240, 248, 256, 264, 272, 280, 288, 296, 304, 312, 320, 328, 336, 344, + 352, 360, 368, 376, 384, 392, 400, 408, 416, 424, 432, 440, 448, 456, + 464, 472, 480, 488, 496, 504, 512, 528, 544, 560, 576, 592, 608, 624, + 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816, 832, 848, + 864, 880, 896, 912, 928, 944, 960, 976, 992, 1008, 1024, 1056, 1088, + 1120, 1152, 1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408, 1440, 1472, + 1504, 1536, 1568, 1600, 1632, 1664, 1696, 1728, 1760, 1792, 1824, 1856, + 1888, 1920, 1952, 1984, 2016, 2048, 2112, 2176, 2240, 2304, 2368, 2432, + 2496, 2560, 2624, 2688, 2752, 2816, 2880, 2944, 3008, 3072, 3136, 3200, + 3264, 3328, 3392, 3456, 3520, 3584, 3648, 3712, 3776, 3840, 3904, 3968, + 4032, 4096, 4160, 4224, 4288, 4352, 4416, 4480, 4544, 4608, 4672, 4736, + 4800, 4864, 4928, 4992, 5056, 5120, 5184, 5248, 5312, 5376, 5440, 5504, + 5568, 5632, 5696, 5760, 5824, 5888, 5952, 6016, 6080, 6144 }; + +/** + * Calculate Codeblock Segmentation parameters as in Section 5.1.2 of 36.212 + * + * @param[out] s Output of code block segmentation calculation + * @param[in] tbs Input Transport Block Size in bits. CRC's will be added to this + * @return Error code + */ +int srslte_cbsegm(srslte_cbsegm_t *s, uint32_t tbs) { + uint32_t Bp, B, idx1; + int ret; + + if (tbs == 0) { + bzero(s, sizeof(srslte_cbsegm_t)); + ret = SRSLTE_SUCCESS; + } else { + B = tbs + 24; + s->tbs = tbs; + + /* Calculate CB sizes */ + if (B <= SRSLTE_TCOD_MAX_LEN_CB) { + s->C = 1; + Bp = B; + } else { + s->C = (uint32_t) ceilf((float) B / (SRSLTE_TCOD_MAX_LEN_CB - 24)); + Bp = B + 24 * s->C; + } + ret = srslte_cbsegm_cbindex((Bp-1) / s->C + 1); + if (ret != SRSLTE_ERROR) { + idx1 = (uint32_t) ret; + ret = srslte_cbsegm_cbsize(idx1); + if (ret != SRSLTE_ERROR) { + s->K1 = (uint32_t) ret; + s->K1_idx = idx1; + if (idx1 > 0) { + ret = srslte_cbsegm_cbsize(idx1 - 1); + } + if (ret != SRSLTE_ERROR) { + if (s->C == 1) { + s->K2 = 0; + s->K2_idx = 0; + s->C2 = 0; + s->C1 = 1; + } else { + s->K2 = (uint32_t) ret; + s->K2_idx = idx1-1; + s->C2 = (s->C * s->K1 - Bp) / (s->K1 - s->K2); + s->C1 = s->C - s->C2; + } + s->F = s->C1 * s->K1 + s->C2 * s->K2 - Bp; + INFO("CB Segmentation: TBS: %d, C=%d, C+=%d K+=%d, C-=%d, K-=%d, F=%d, Bp=%d\n", + tbs, s->C, s->C1, s->K1, s->C2, s->K2, s->F, Bp); + ret = SRSLTE_SUCCESS; + } + } + } + + } + return ret; +} + +/* + * Finds index of minimum K>=long_cb in Table 5.1.3-3 of 36.212 + * + * @return I_TBS or error code + */ +int srslte_cbsegm_cbindex(uint32_t long_cb) { + int j = 0; + while (j < SRSLTE_NOF_TC_CB_SIZES && tc_cb_sizes[j] < long_cb) { + j++; + } + + if (j == SRSLTE_NOF_TC_CB_SIZES) { + return SRSLTE_ERROR; + } else { + return j; + } +} + +/* + * Returns Turbo coder interleaver size for Table 5.1.3-3 (36.212) index + * + * @return Code block size in bits or error code + */ +int srslte_cbsegm_cbsize(uint32_t index) { + if (index < SRSLTE_NOF_TC_CB_SIZES) { + return (int) tc_cb_sizes[index]; + } else { + return SRSLTE_ERROR; + } +} + +/** + * Check is code block size is valid for LTE Turbo Code + * + * @param[in] size Size of code block in bits + * @return true if Code Block size is allowed + */ +bool srslte_cbsegm_cbsize_isvalid(uint32_t size) { + for (int i=0;i +#include +#include +#include + +#include "srslte/phy/fec/convcoder.h" +#include "parity.h" + +/** + * Convolution encodes according to given parameters. + * + * q->R is rate + * q->tail_biting enables tail biting + * q->K is a parameter for tail biting + * + * @param[in] q Convolution coder parameters + * @param[in] input Unpacked bit array. Size frame_length + * @param[out] output Unpacked bit array. Size q->R*frame_length if q->tail_biting, else q->R*(frame_length + q->K - 1) + * @param[in] frame_length Number of bits in input_array + * @return Number of bits in output + */ +int srslte_convcoder_encode(srslte_convcoder_t *q, uint8_t *input, uint8_t *output, uint32_t frame_length) { + uint32_t sr; + uint32_t i,j; + + if (q != NULL && + input != NULL && + output != NULL && + frame_length > q->K + 1) + { + uint32_t len = q->tail_biting ? frame_length : (frame_length + q->K - 1); + if (q->tail_biting) { + sr = 0; + for (i=frame_length - q->K + 1; iR;j++) { + output[q->R * i + j] = parity(sr & q->poly[j]); + } + } + return q->R*len; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + diff --git a/lib/src/phy/fec/crc.c b/lib/src/phy/fec/crc.c new file mode 100644 index 0000000..a2f6d79 --- /dev/null +++ b/lib/src/phy/fec/crc.c @@ -0,0 +1,190 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/fec/crc.h" + +void gen_crc_table(srslte_crc_t *h) { + + int i, j, ord = (h->order - 8); + uint64_t bit, crc; + + for (i = 0; i < 256; i++) { + crc = ((uint64_t) i) << ord; + for (j = 0; j < 8; j++) { + bit = crc & h->crchighbit; + crc <<= 1; + if (bit) + crc ^= h->polynom; + } + h->table[i] = crc & h->crcmask; + } +} + +uint64_t crctable(srslte_crc_t *h, uint8_t byte) { + + // Polynom order 8, 16, 24 or 32 only. + int ord = h->order - 8; + uint64_t crc = h->crcinit; + + crc = (crc << 8) ^ h->table[((crc >> (ord)) & 0xff) ^ byte]; + h->crcinit = crc; + return (crc & h->crcmask); +} + +uint64_t reversecrcbit(uint32_t crc, int nbits, srslte_crc_t *h) { + + uint64_t m, rmask = 0x1; + + for (m = 0; m < nbits; m++) { + if ((rmask & crc) == 0x01) + crc = (crc ^ h->polynom) >> 1; + else + crc = crc >> 1; + } + return (crc & h->crcmask); +} + +int srslte_crc_set_init(srslte_crc_t *crc_par, uint64_t crc_init_value) { + + crc_par->crcinit = crc_init_value; + if (crc_par->crcinit != (crc_par->crcinit & crc_par->crcmask)) { + printf("ERROR, invalid crcinit in crc_set_init().\n"); + return -1; + } + return 0; +} + +int srslte_crc_init(srslte_crc_t *h, uint32_t crc_poly, int crc_order) { + + // Set crc working default parameters + h->polynom = crc_poly; + h->order = crc_order; + h->crcinit = 0x00000000; + + // Compute bit masks for whole CRC and CRC high bit + h->crcmask = ((((uint64_t) 1 << (h->order - 1)) - 1) << 1) | 1; + h->crchighbit = (uint64_t) 1 << (h->order - 1); + + // check parameters + if (h->order % 8 != 0) { + fprintf(stderr, "ERROR, invalid order=%d, it must be 8, 16, 24 or 32.\n", + h->order); + return -1; + } + + if (srslte_crc_set_init(h, h->crcinit)) { + fprintf(stderr, "Error setting CRC init word\n"); + return -1; + } + + // generate lookup table + gen_crc_table(h); + + return 0; +} + +uint32_t srslte_crc_checksum(srslte_crc_t *h, uint8_t *data, int len) { + int i, k, len8, res8, a = 0; + uint32_t crc = 0; + uint8_t *pter; + + srslte_crc_set_init(h, 0); + + // Pack bits into bytes + len8 = (len >> 3); + res8 = (len - (len8 << 3)); + if (res8 > 0) { + a = 1; + } + + // Calculate CRC + for (i = 0; i < len8 + a; i++) { + pter = (uint8_t *) (data + 8 * i); + uint8_t byte; + if (i == len8) { + byte = 0x00; + for (k = 0; k < res8; k++) { + byte |= ((uint8_t) *(pter + k)) << (7 - k); + } + } else { + byte = (uint8_t) (srslte_bit_pack(&pter, 8) & 0xFF); + } + crc = crctable(h, byte); + } + + // Reverse CRC res8 positions + if (a == 1) { + crc = reversecrcbit(crc, 8 - res8, h); + } + + //Return CRC value + return crc; + +} + +// len is multiple of 8 +uint32_t srslte_crc_checksum_byte(srslte_crc_t *h, uint8_t *data, int len) { + int i; + uint32_t crc = 0; + + srslte_crc_set_init(h, 0); + + // Calculate CRC + for (i = 0; i < len/8; i++) { + crc = crctable(h, data[i]); + } + + return crc; + +} + +uint32_t srslte_crc_attach_byte(srslte_crc_t *h, uint8_t *data, int len) { + uint32_t checksum = srslte_crc_checksum_byte(h, data, len); + + // Add CRC + for (int i=0;iorder/8;i++) { + data[len/8+(h->order/8-i-1)] = (checksum&(0xff<<(8*i)))>>(8*i); + } + return checksum; +} + +/** Appends crc_order checksum bits to the buffer data. + * The buffer data must be len + crc_order bytes + */ +uint32_t srslte_crc_attach(srslte_crc_t *h, uint8_t *data, int len) { + uint32_t checksum = srslte_crc_checksum(h, data, len); + + // Add CRC + uint8_t *ptr = &data[len]; + srslte_bit_unpack(checksum, &ptr, h->order); + return checksum; +} + diff --git a/lib/src/phy/fec/parity.c b/lib/src/phy/fec/parity.c new file mode 100644 index 0000000..145ad81 --- /dev/null +++ b/lib/src/phy/fec/parity.c @@ -0,0 +1,30 @@ +/* + * Copyright 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Affero General Public License (LGPL) + */ + +#include +#include + +uint8_t Partab[256]; +uint32_t P_init; + +/* Create 256-entry odd-parity lookup table + * Needed only on non-ia32 machines + */ +void partab_init(void) { + uint32_t i, cnt, ti; + + /* Initialize parity lookup table */ + for (i = 0; i < 256; i++) { + cnt = 0; + ti = i; + while (ti) { + if (ti & 1) + cnt++; + ti >>= 1; + } + Partab[i] = cnt & 1; + } + P_init = 1; +} diff --git a/lib/src/phy/fec/parity.h b/lib/src/phy/fec/parity.h new file mode 100644 index 0000000..247a6fa --- /dev/null +++ b/lib/src/phy/fec/parity.h @@ -0,0 +1,36 @@ +/* + * Copyright 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Affero General Public License (LGPL) + */ + + +#ifdef __x86_64__ +#define __i386__ +#endif + +/* Determine parity of argument: 1 = odd, 0 = even */ +#ifdef __i386__ +static inline uint32_t parityb(uint8_t x){ + __asm__ __volatile__ ("test %1,%1;setpo %0" : "=q" (x) : "q" (x)); + return x; +} +#else +void partab_init(); + +static inline uint32_t parityb(uint8_t x){ + extern uint8_t Partab[256]; + extern uint32_t P_init; + if(!P_init){ + partab_init(); + } + return Partab[x]; +} +#endif + + +static inline uint32_t parity(int x){ + /* Fold down to one byte */ + x ^= (x >> 16); + x ^= (x >> 8); + return parityb(x); +} diff --git a/lib/src/phy/fec/rm_conv.c b/lib/src/phy/fec/rm_conv.c new file mode 100644 index 0000000..9c02d97 --- /dev/null +++ b/lib/src/phy/fec/rm_conv.c @@ -0,0 +1,227 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/fec/rm_conv.h" + +#define NCOLS 32 +#define NROWS_MAX NCOLS + +uint8_t RM_PERM_CC[NCOLS] = { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, + 7, 23, 15, 31, 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30 }; +uint8_t RM_PERM_CC_INV[NCOLS] = + { 16, 0, 24, 8, 20, 4, 28, 12, 18, 2, 26, 10, 22, 6, 30, 14, 17, 1, 25, 9, + 21, 5, 29, 13, 19, 3, 27, 11, 23, 7, 31, 15 }; + +/** + * Rate matching for convolution encoder + * + * @param[in] input Unpacked bit array. Size in_len + * @param[output] output Unpacked bit array. Size out_len <= in_len + */ +int srslte_rm_conv_tx(uint8_t *input, uint32_t in_len, uint8_t *output, uint32_t out_len) { + + uint8_t tmp[3 * NCOLS * NROWS_MAX]; + int nrows, ndummy, K_p; + + int i, j, k, s; + + nrows = (uint32_t) (in_len / 3 - 1) / NCOLS + 1; + if (nrows > NROWS_MAX) { + fprintf(stderr, "Input too large. Max input length is %d\n", + 3 * NCOLS * NROWS_MAX); + return -1; + } + K_p = nrows * NCOLS; + ndummy = K_p - in_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + /* Sub-block interleaver 5.1.4.2.1 */ + k = 0; + for (s = 0; s < 3; s++) { + for (j = 0; j < NCOLS; j++) { + for (i = 0; i < nrows; i++) { + if (i * NCOLS + RM_PERM_CC[j] < ndummy) { + tmp[k] = SRSLTE_TX_NULL; + } else { + tmp[k] = input[(i * NCOLS + RM_PERM_CC[j] - ndummy) * 3 + s]; + } + k++; + } + } + } + /* Bit collection, selection and transmission 5.1.4.2.2 */ + k = 0; + j = 0; + while (k < out_len) { + if (tmp[j] != SRSLTE_TX_NULL) { + output[k] = tmp[j]; + k++; + } + j++; + if (j == 3 * K_p) { + j = 0; + } + } + return 0; +} + +/* Undoes Convolutional Code Rate Matching. + * 3GPP TS 36.212 v10.1.0 section 5.1.4.2 + */ +int srslte_rm_conv_rx(float *input, uint32_t in_len, float *output, uint32_t out_len) { + + int nrows, ndummy, K_p; + int i, j, k; + int d_i, d_j; + + float tmp[3 * NCOLS * NROWS_MAX]; + + nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1; + if (nrows > NROWS_MAX) { + fprintf(stderr, "Output too large. Max output length is %d\n", + 3 * NCOLS * NROWS_MAX); + return -1; + } + K_p = nrows * NCOLS; + + ndummy = K_p - out_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + + for (i = 0; i < 3 * K_p; i++) { + tmp[i] = SRSLTE_RX_NULL; + } + + /* Undo bit collection. Account for dummy bits */ + k = 0; + j = 0; + while (k < in_len) { + d_i = (j % K_p) / nrows; + d_j = (j % K_p) % nrows; + + if (d_j * NCOLS + RM_PERM_CC[d_i] >= ndummy) { + if (tmp[j] == SRSLTE_RX_NULL) { + tmp[j] = input[k]; + } else if (input[k] != SRSLTE_RX_NULL) { + tmp[j] += input[k]; /* soft combine LLRs */ + } + k++; + } + j++; + if (j == 3 * K_p) { + j = 0; + } + } + + /* interleaving and bit selection */ + for (i = 0; i < out_len / 3; i++) { + d_i = (i + ndummy) / NCOLS; + d_j = (i + ndummy) % NCOLS; + for (j = 0; j < 3; j++) { + float o = tmp[K_p * j + RM_PERM_CC_INV[d_j] * nrows + d_i]; + if (o != SRSLTE_RX_NULL) { + output[i * 3 + j] = o; + } else { + output[i * 3 + j] = 0; + } + } + } + return 0; +} + +/************* FIX THIS. MOVE ALL PROCESSING TO INT16 AND HAVE ONLY 1 IMPLEMENTATION ******/ + +/* Undoes Convolutional Code Rate Matching. + * 3GPP TS 36.212 v10.1.0 section 5.1.4.2 + */ +int srslte_rm_conv_rx_s(int16_t *input, uint32_t in_len, int16_t *output, uint32_t out_len) { + + int nrows, ndummy, K_p; + int i, j, k; + int d_i, d_j; + + int16_t tmp[3 * NCOLS * NROWS_MAX]; + + nrows = (uint32_t) (out_len / 3 - 1) / NCOLS + 1; + if (nrows > NROWS_MAX) { + fprintf(stderr, "Output too large. Max output length is %d\n", + 3 * NCOLS * NROWS_MAX); + return -1; + } + K_p = nrows * NCOLS; + + ndummy = K_p - out_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + + for (i = 0; i < 3 * K_p; i++) { + tmp[i] = SRSLTE_RX_NULL; + } + + /* Undo bit collection. Account for dummy bits */ + k = 0; + j = 0; + while (k < in_len) { + d_i = (j % K_p) / nrows; + d_j = (j % K_p) % nrows; + + if (d_j * NCOLS + RM_PERM_CC[d_i] >= ndummy) { + if (tmp[j] == SRSLTE_RX_NULL) { + tmp[j] = input[k]; + } else if (input[k] != SRSLTE_RX_NULL) { + tmp[j] += input[k]; /* soft combine LLRs */ + } + k++; + } + j++; + if (j == 3 * K_p) { + j = 0; + } + } + + /* interleaving and bit selection */ + for (i = 0; i < out_len / 3; i++) { + d_i = (i + ndummy) / NCOLS; + d_j = (i + ndummy) % NCOLS; + for (j = 0; j < 3; j++) { + int16_t o = tmp[K_p * j + RM_PERM_CC_INV[d_j] * nrows + d_i]; + if (o != SRSLTE_RX_NULL) { + output[i * 3 + j] = o; + } else { + output[i * 3 + j] = 0; + } + } + } + return 0; +} + diff --git a/lib/src/phy/fec/rm_turbo.c b/lib/src/phy/fec/rm_turbo.c new file mode 100644 index 0000000..b327500 --- /dev/null +++ b/lib/src/phy/fec/rm_turbo.c @@ -0,0 +1,761 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/rm_turbo.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/fec/cbsegm.h" + +#ifdef DEBUG_MODE +#warning FIXME: Disabling SSE/AVX turbo rate matching +#undef LV_HAVE_SSE +#undef LV_HAVE_AVX +#endif + +#ifdef LV_HAVE_SSE +#include +int srslte_rm_turbo_rx_lut_sse(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx); +#endif + +#ifdef LV_HAVE_AVX +#include +int srslte_rm_turbo_rx_lut_avx(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx); +#endif + +#define NCOLS 32 +#define NROWS_MAX NCOLS + +static uint8_t RM_PERM_TC[NCOLS] = { 0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, + 6, 22, 14, 30, 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31 }; + +/* Align tables to 16-byte boundary */ + +static uint16_t interleaver_systematic_bits[192][6160]; // 4 tail bits +static srslte_bit_interleaver_t bit_interleavers_systematic_bits[192]; +static uint16_t interleaver_parity_bits[192][2*6160]; +static srslte_bit_interleaver_t bit_interleavers_parity_bits[192]; +static uint16_t deinterleaver[192][4][18448]; +static int k0_vec[SRSLTE_NOF_TC_CB_SIZES][4][2]; +static bool rm_turbo_tables_generated = false; + + +static uint16_t temp_table1[3*6176], temp_table2[3*6176]; + +void srslte_rm_turbo_gentable_systematic(uint16_t *table_bits, int k0_vec[4][2], uint32_t nrows, int ndummy) { + + bool last_is_null=true; + int k_b=0, buff_idx=0; + for (int j = 0; j < NCOLS; j++) { + for (int i = 0; i < nrows; i++) { + if (i * NCOLS + RM_PERM_TC[j] >= ndummy) { + table_bits[k_b] = i * NCOLS + RM_PERM_TC[j] - ndummy; + k_b++; + last_is_null=false; + } else { + last_is_null=true; + } + for (int i=0;i<4;i++) { + if (k0_vec[i][1] == -1) { + if (k0_vec[i][0]%(3*nrows*NCOLS) <= buff_idx && !last_is_null) { + k0_vec[i][1] = k_b-1; + } + } + } + buff_idx++; + } + } +} + +void srslte_rm_turbo_gentable_parity(uint16_t *table_parity, int k0_vec[4][2], int offset, uint16_t nrows, int ndummy) { + + bool last_is_null=true; + int k_b=0, buff_idx0=0; + int K_p = nrows*NCOLS; + int buff_idx1=0; + for (int j = 0; j < NCOLS; j++) { + for (int i = 0; i < nrows; i++) { + if (i * NCOLS + RM_PERM_TC[j] >= ndummy) { + table_parity[k_b] = i * NCOLS + RM_PERM_TC[j] - ndummy; + k_b++; + last_is_null=false; + } else { + last_is_null=true; + } + for (int i=0;i<4;i++) { + if (k0_vec[i][1] == -1) { + if (k0_vec[i][0]%(3*K_p) <= 2*buff_idx0+K_p && !last_is_null) { + k0_vec[i][1] = offset+k_b-1; + } + } + } + buff_idx0++; + + int kidx = (RM_PERM_TC[buff_idx1 / nrows] + NCOLS * (buff_idx1 % nrows) + 1) % K_p; + if ((kidx - ndummy) >= 0) { + table_parity[k_b] = kidx-ndummy+offset; + k_b++; + last_is_null=false; + } else { + last_is_null=true; + } + for (int i=0;i<4;i++) { + if (k0_vec[i][1] == -1) { + if (k0_vec[i][0]%(3*K_p) <= 2*buff_idx1+1+K_p && !last_is_null) { + k0_vec[i][1] = offset+k_b-1; + } + } + } + buff_idx1++; + } + } +} + + + +void srslte_rm_turbo_gentable_receive(uint16_t *table, uint32_t cb_len, uint32_t rv_idx) +{ + + int nrows = (uint32_t) (cb_len / 3 - 1) / NCOLS + 1; + int ndummy = nrows*NCOLS - cb_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + + /* Undo bit collection. Account for dummy bits */ + int N_cb = 3*nrows*NCOLS; + int k0 = nrows*(2*(uint16_t) ceilf((float) N_cb/(float) (8*nrows))*rv_idx+2); + + int kidx; + int K_p = nrows * NCOLS; + int k = 0, jp=0, j=0; + bool isdummy = false; + int d_i, d_j; + while (k < cb_len) { + jp = (k0 + j) % N_cb; + + if (jp < K_p || !(jp % 2)) { + if (jp >= K_p) { + d_i = ((jp - K_p) / 2) / nrows; + d_j = ((jp - K_p) / 2) % nrows; + } else { + d_i = jp / nrows; + d_j = jp % nrows; + } + if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { + isdummy = false; + if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < 0) { + isdummy = true; + } + } else { + isdummy = true; + } + + } else { + uint32_t jpp = (jp - K_p - 1) / 2; + kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; + if ((kidx - ndummy) < 0) { + isdummy = true; + } else { + isdummy = false; + } + } + + if (!isdummy) { + temp_table1[k] = jp%(3*nrows*NCOLS); + k++; + } + j++; + } + + for (int i = 0; i < cb_len / 3; i++) { + d_i = (i + ndummy) / NCOLS; + d_j = (i + ndummy) % NCOLS; + for (j = 0; j < 3; j++) { + if (j != 2) { + kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); + } else { + k = (i + ndummy - 1) % K_p; + if (k < 0) + k += K_p; + kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; + kidx = 2 * kidx + K_p + 1; + } + temp_table2[kidx] = 3*i+j; + } + } + for (int i=0;i= in_len) { + cp_len = in_len - r_ptr; + } + srslte_bit_copy(output, w_len+w_offset, w_buff, r_ptr, cp_len); + r_ptr += cp_len; + if (r_ptr >= in_len) { + r_ptr -= in_len; + } + w_len += cp_len; + } + + return 0; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/** + * Undoes rate matching for LTE Turbo Coder. Expands rate matched buffer to full size buffer. + * + * @param[in] input Input buffer of size in_len + * @param[out] output Output buffer of size 3*srslte_cbsegm_cbsize(cb_idx)+12 + * @param[in] cb_idx Code block table index + * @param[in] rv_idx Redundancy Version from DCI control message + * @return Error code + */ +int srslte_rm_turbo_rx_lut(int16_t *input, int16_t *output, uint32_t in_len, uint32_t cb_idx, uint32_t rv_idx) +{ + +#ifdef LV_HAVE_AVX + return srslte_rm_turbo_rx_lut_avx(input, output, in_len, cb_idx, rv_idx); +#else + #ifdef LV_HAVE_SSE + return srslte_rm_turbo_rx_lut_sse(input, output, in_len, cb_idx, rv_idx); + #else + if (rv_idx < 4 && cb_idx < SRSLTE_NOF_TC_CB_SIZES) { + uint32_t out_len = 3*srslte_cbsegm_cbsize(cb_idx)+12; + uint16_t *deinter = deinterleaver[cb_idx][rv_idx]; + for (int i=0;i= out_len && inputCnt < in_len - 8) { + /* Copy last elements */ + for (int j=(nwrapps+1)*out_len-4;j<(nwrapps+1)*out_len;j++) { + output[deinter[j%out_len]] += input[j]; + inputCnt++; + } + /* And wrap pointers */ + nwrapps++; + intCnt = 8; + xPtr = (const __m128i*) &input[nwrapps*out_len]; + lutPtr = (const __m128i*) deinter; + } + } + for (int i=inputCnt;i= out_len && inputCnt < in_len - 16) { + /* Copy last elements */ + if ((out_len%16) == 12) { + for (int j=(nwrapps+1)*out_len-12;j<(nwrapps+1)*out_len;j++) { + output[deinter[j%out_len]] += input[j]; + inputCnt++; + } + } else { + for (int j=(nwrapps+1)*out_len-4;j<(nwrapps+1)*out_len;j++) { + output[deinter[j%out_len]] += input[j]; + inputCnt++; + } + } + /* And wrap pointers */ + nwrapps++; + intCnt = 16; + xPtr = (const __m256i*) &input[nwrapps*out_len]; + lutPtr = (const __m256i*) deinter; + } + } + for (int i=inputCnt;i w_buff_len) { + fprintf(stderr, + "Input too large. Max input length including dummy bits is %d (3x%dx32, in_len %d, Kp=%d)\n", + w_buff_len, nrows, in_len, K_p); + return -1; + } + + ndummy = K_p - in_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + + if (rv_idx == 0) { + /* Sub-block interleaver (5.1.4.1.1) and bit collection */ + k = 0; + for (s = 0; s < 2; s++) { + for (j = 0; j < NCOLS; j++) { + for (i = 0; i < nrows; i++) { + if (s == 0) { + kidx = k % K_p; + } else { + kidx = K_p + 2 * (k % K_p); + } + if (i * NCOLS + RM_PERM_TC[j] < ndummy) { + w_buff[kidx] = SRSLTE_TX_NULL; + } else { + w_buff[kidx] = input[(i * NCOLS + RM_PERM_TC[j] - ndummy) * 3 + s]; + } + k++; + } + } + } + + // d_k^(2) goes through special permutation + for (k = 0; k < K_p; k++) { + kidx = (RM_PERM_TC[k / nrows] + NCOLS * (k % nrows) + 1) % K_p; + if ((kidx - ndummy) < 0) { + w_buff[K_p + 2 * k + 1] = SRSLTE_TX_NULL; + } else { + w_buff[K_p + 2 * k + 1] = input[3 * (kidx - ndummy) + 2]; + } + } + } + + /* Bit selection and transmission 5.1.4.1.2 */ + N_cb = 3 * K_p; // TODO: Soft buffer size limitation + + k0 = nrows + * (2 * (uint16_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); + k = 0; + j = 0; + + while (k < out_len) { + if (w_buff[(k0 + j) % N_cb] != SRSLTE_TX_NULL) { + output[k] = w_buff[(k0 + j) % N_cb]; + k++; + } + j++; + } + return 0; +} + +/* Undoes Turbo Code Rate Matching. + * 3GPP TS 36.212 v10.1.0 section 5.1.4.1 + * + * Soft-combines the data available in w_buff + */ +int srslte_rm_turbo_rx(float *w_buff, uint32_t w_buff_len, float *input, uint32_t in_len, float *output, + uint32_t out_len, uint32_t rv_idx, uint32_t nof_filler_bits) { + + int nrows, ndummy, K_p, k0, N_cb, jp, kidx; + int i, j, k; + int d_i, d_j; + bool isdummy; + + + + nrows = (uint16_t) (out_len / 3 - 1) / NCOLS + 1; + K_p = nrows * NCOLS; + if (3 * K_p > w_buff_len) { + fprintf(stderr, + "Output too large. Max output length including dummy bits is %d (3x%dx32, in_len %d)\n", + w_buff_len, nrows, out_len); + return -1; + } + + if (out_len < 3) { + fprintf(stderr, "Error minimum input length for rate matching is 3\n"); + return -1; + } + + + ndummy = K_p - out_len / 3; + if (ndummy < 0) { + ndummy = 0; + } + + /* Undo bit collection. Account for dummy bits */ + N_cb = 3 * K_p; // TODO: Soft buffer size limitation + k0 = nrows + * (2 * (uint16_t) ceilf((float) N_cb / (float) (8 * nrows)) * rv_idx + 2); + + k = 0; + j = 0; + while (k < in_len) { + jp = (k0 + j) % N_cb; + + if (jp < K_p || !(jp % 2)) { + if (jp >= K_p) { + d_i = ((jp - K_p) / 2) / nrows; + d_j = ((jp - K_p) / 2) % nrows; + } else { + d_i = jp / nrows; + d_j = jp % nrows; + } + if (d_j * NCOLS + RM_PERM_TC[d_i] >= ndummy) { + isdummy = false; + if (d_j * NCOLS + RM_PERM_TC[d_i] - ndummy < nof_filler_bits) { + isdummy = true; + } + } else { + isdummy = true; + } + + } else { + uint16_t jpp = (jp - K_p - 1) / 2; + kidx = (RM_PERM_TC[jpp / nrows] + NCOLS * (jpp % nrows) + 1) % K_p; + if ((kidx - ndummy) < 0) { + isdummy = true; + } else { + isdummy = false; + } + } + + if (!isdummy) { + if (w_buff[jp] == SRSLTE_RX_NULL) { + w_buff[jp] = input[k]; + } else if (input[k] != SRSLTE_RX_NULL) { + w_buff[jp] += input[k]; /* soft combine LLRs */ + } + k++; + } + j++; + } + + //printf("wbuff:\n"); + //srslte_vec_fprint_f(stdout, w_buff, out_len); + + /* interleaving and bit selection */ + for (i = 0; i < out_len / 3; i++) { + d_i = (i + ndummy) / NCOLS; + d_j = (i + ndummy) % NCOLS; + for (j = 0; j < 3; j++) { + if (j != 2) { + kidx = K_p * j + (j + 1) * (RM_PERM_TC[d_j] * nrows + d_i); + } else { + k = (i + ndummy - 1) % K_p; + if (k < 0) + k += K_p; + kidx = (k / NCOLS + nrows * RM_PERM_TC[k % NCOLS]) % K_p; + kidx = 2 * kidx + K_p + 1; + } + if (w_buff[kidx] != SRSLTE_RX_NULL) { + output[i * 3 + j] = w_buff[kidx]; + } else { + output[i * 3 + j] = 0; + } + } + } + + return 0; +} diff --git a/lib/src/phy/fec/softbuffer.c b/lib/src/phy/fec/softbuffer.c new file mode 100644 index 0000000..9ed5268 --- /dev/null +++ b/lib/src/phy/fec/softbuffer.c @@ -0,0 +1,224 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/fec/turbodecoder_gen.h" +#include "srslte/phy/fec/rm_turbo.h" +#include "srslte/phy/fec/softbuffer.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#define MAX_PDSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12) + +int srslte_softbuffer_rx_init(srslte_softbuffer_rx_t *q, uint32_t nof_prb) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + bzero(q, sizeof(srslte_softbuffer_rx_t)); + + ret = srslte_ra_tbs_from_idx(26, nof_prb); + if (ret != SRSLTE_ERROR) { + q->max_cb = (uint32_t) ret / (SRSLTE_TCOD_MAX_LEN_CB - 24) + 1; + ret = SRSLTE_ERROR; + + q->buffer_f = srslte_vec_malloc(sizeof(int16_t*) * q->max_cb); + if (!q->buffer_f) { + perror("malloc"); + goto clean_exit; + } + + q->data = srslte_vec_malloc(sizeof(uint8_t*) * q->max_cb); + if (!q->data) { + perror("malloc"); + goto clean_exit; + } + + q->cb_crc = srslte_vec_malloc(sizeof(bool) * q->max_cb); + if (!q->cb_crc) { + perror("malloc"); + goto clean_exit; + } + bzero(q->cb_crc, sizeof(bool) * q->max_cb); + + // FIXME: Use HARQ buffer limitation based on UE category + for (uint32_t i=0;imax_cb;i++) { + q->buffer_f[i] = srslte_vec_malloc(sizeof(int16_t) * SOFTBUFFER_SIZE); + if (!q->buffer_f[i]) { + perror("malloc"); + goto clean_exit; + } + + q->data[i] = srslte_vec_malloc(sizeof(uint8_t) * 6144/8); + if (!q->data[i]) { + perror("malloc"); + goto clean_exit; + } + } + //srslte_softbuffer_rx_reset(q); + ret = SRSLTE_SUCCESS; + } + } + + clean_exit: + if (ret != SRSLTE_SUCCESS) { + srslte_softbuffer_rx_free(q); + } + + return ret; +} + +void srslte_softbuffer_rx_free(srslte_softbuffer_rx_t *q) { + if (q) { + if (q->buffer_f) { + for (uint32_t i=0;imax_cb;i++) { + if (q->buffer_f[i]) { + free(q->buffer_f[i]); + } + } + free(q->buffer_f); + } + if (q->data) { + for (uint32_t i=0;imax_cb;i++) { + if (q->data[i]) { + free(q->data[i]); + } + } + free(q->data); + } + if (q->cb_crc) { + free(q->cb_crc); + } + bzero(q, sizeof(srslte_softbuffer_rx_t)); + } +} + +void srslte_softbuffer_rx_reset_tbs(srslte_softbuffer_rx_t *q, uint32_t tbs) { + uint32_t nof_cb = (tbs + 24)/(SRSLTE_TCOD_MAX_LEN_CB - 24) + 1; + srslte_softbuffer_rx_reset_cb(q, nof_cb); +} + +void srslte_softbuffer_rx_reset(srslte_softbuffer_rx_t *q) { + srslte_softbuffer_rx_reset_cb(q, q->max_cb); +} + +void srslte_softbuffer_rx_reset_cb(srslte_softbuffer_rx_t *q, uint32_t nof_cb) { + if (q->buffer_f) { + if (nof_cb > q->max_cb) { + nof_cb = q->max_cb; + } + for (uint32_t i=0;ibuffer_f[i]) { + bzero(q->buffer_f[i], SOFTBUFFER_SIZE*sizeof(int16_t)); + } + } + } + if (q->cb_crc) { + bzero(q->cb_crc, sizeof(bool) * q->max_cb); + } +} + + + +int srslte_softbuffer_tx_init(srslte_softbuffer_tx_t *q, uint32_t nof_prb) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_softbuffer_tx_t)); + + ret = srslte_ra_tbs_from_idx(26, nof_prb); + if (ret != SRSLTE_ERROR) { + q->max_cb = (uint32_t) ret / (SRSLTE_TCOD_MAX_LEN_CB - 24) + 1; + + q->buffer_b = srslte_vec_malloc(sizeof(uint8_t*) * q->max_cb); + if (!q->buffer_b) { + perror("malloc"); + return SRSLTE_ERROR; + } + + // FIXME: Use HARQ buffer limitation based on UE category + for (uint32_t i=0;imax_cb;i++) { + q->buffer_b[i] = srslte_vec_malloc(sizeof(float) * SOFTBUFFER_SIZE); + if (!q->buffer_b[i]) { + perror("malloc"); + return SRSLTE_ERROR; + } + } + srslte_softbuffer_tx_reset(q); + ret = SRSLTE_SUCCESS; + } + } + return ret; +} + +void srslte_softbuffer_tx_free(srslte_softbuffer_tx_t *q) { + if (q) { + if (q->buffer_b) { + for (uint32_t i=0;imax_cb;i++) { + if (q->buffer_b[i]) { + free(q->buffer_b[i]); + } + } + free(q->buffer_b); + } + bzero(q, sizeof(srslte_softbuffer_tx_t)); + } +} + + +void srslte_softbuffer_tx_reset_tbs(srslte_softbuffer_tx_t *q, uint32_t tbs) { + uint32_t nof_cb = (tbs + 24)/(SRSLTE_TCOD_MAX_LEN_CB - 24) + 1; + srslte_softbuffer_tx_reset_cb(q, nof_cb); +} + +void srslte_softbuffer_tx_reset(srslte_softbuffer_tx_t *q) { + srslte_softbuffer_tx_reset_cb(q, q->max_cb); +} + +void srslte_softbuffer_tx_reset_cb(srslte_softbuffer_tx_t *q, uint32_t nof_cb) { + int i; + if (q->buffer_b) { + if (nof_cb > q->max_cb) { + nof_cb = q->max_cb; + } + for (i=0;ibuffer_b[i]) { + bzero(q->buffer_b[i], sizeof(uint8_t) * SOFTBUFFER_SIZE); + } + } + } +} diff --git a/lib/src/phy/fec/tc_interl_lte.c b/lib/src/phy/fec/tc_interl_lte.c new file mode 100644 index 0000000..06ba7a7 --- /dev/null +++ b/lib/src/phy/fec/tc_interl_lte.c @@ -0,0 +1,98 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/turbocoder.h" +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/utils/debug.h" + +/************************************************ + * + * LTE TURBO CODE INTERLEAVER + * + ************************************************/ + +const uint32_t f1_list[SRSLTE_NOF_TC_CB_SIZES] = { 3, 7, 19, 7, 7, 11, 5, 11, 7, 41, 103, + 15, 9, 17, 9, 21, 101, 21, 57, 23, 13, 27, 11, 27, 85, 29, 33, 15, 17, 33, + 103, 19, 19, 37, 19, 21, 21, 115, 193, 21, 133, 81, 45, 23, 243, 151, 155, + 25, 51, 47, 91, 29, 29, 247, 29, 89, 91, 157, 55, 31, 17, 35, 227, 65, 19, + 37, 41, 39, 185, 43, 21, 155, 79, 139, 23, 217, 25, 17, 127, 25, 239, 17, + 137, 215, 29, 15, 147, 29, 59, 65, 55, 31, 17, 171, 67, 35, 19, 39, 19, 199, + 21, 211, 21, 43, 149, 45, 49, 71, 13, 17, 25, 183, 55, 127, 27, 29, 29, 57, + 45, 31, 59, 185, 113, 31, 17, 171, 209, 253, 367, 265, 181, 39, 27, 127, + 143, 43, 29, 45, 157, 47, 13, 111, 443, 51, 51, 451, 257, 57, 313, 271, 179, + 331, 363, 375, 127, 31, 33, 43, 33, 477, 35, 233, 357, 337, 37, 71, 71, 37, + 39, 127, 39, 39, 31, 113, 41, 251, 43, 21, 43, 45, 45, 161, 89, 323, 47, 23, + 47, 263 }; + +const uint32_t f2_list[SRSLTE_NOF_TC_CB_SIZES] = { 10, 12, 42, 16, 18, 20, 22, 24, 26, 84, + 90, 32, 34, 108, 38, 120, 84, 44, 46, 48, 50, 52, 36, 56, 58, 60, 62, 32, + 198, 68, 210, 36, 74, 76, 78, 120, 82, 84, 86, 44, 90, 46, 94, 48, 98, 40, + 102, 52, 106, 72, 110, 168, 114, 58, 118, 180, 122, 62, 84, 64, 66, 68, 420, + 96, 74, 76, 234, 80, 82, 252, 86, 44, 120, 92, 94, 48, 98, 80, 102, 52, 106, + 48, 110, 112, 114, 58, 118, 60, 122, 124, 84, 64, 66, 204, 140, 72, 74, 76, + 78, 240, 82, 252, 86, 88, 60, 92, 846, 48, 28, 80, 102, 104, 954, 96, 110, + 112, 114, 116, 354, 120, 610, 124, 420, 64, 66, 136, 420, 216, 444, 456, + 468, 80, 164, 504, 172, 88, 300, 92, 188, 96, 28, 240, 204, 104, 212, 192, + 220, 336, 228, 232, 236, 120, 244, 248, 168, 64, 130, 264, 134, 408, 138, + 280, 142, 480, 146, 444, 120, 152, 462, 234, 158, 80, 96, 902, 166, 336, + 170, 86, 174, 176, 178, 120, 182, 184, 186, 94, 190, 480 }; + +int srslte_tc_interl_LTE_gen(srslte_tc_interl_t *h, uint32_t long_cb) { + uint32_t cb_table_idx, f1, f2; + uint64_t i, j; + + if (long_cb > h->max_long_cb) { + fprintf(stderr, "Interleaver initiated for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + + cb_table_idx = srslte_cbsegm_cbindex(long_cb); + if (cb_table_idx == -1) { + fprintf(stderr, "Can't find long_cb=%d in valid TC CB table\n", long_cb); + return -1; + } + + f1 = f1_list[cb_table_idx]; + f2 = f2_list[cb_table_idx]; + + h->forward[0] = 0; + h->reverse[0] = 0; + for (i = 1; i < long_cb; i++) { + j = (f1 * i + f2 * i * i) % (long_cb); + h->forward[i] = (uint32_t) j; + h->reverse[j] = (uint32_t) i; + } + return 0; + +} + diff --git a/lib/src/phy/fec/tc_interl_umts.c b/lib/src/phy/fec/tc_interl_umts.c new file mode 100644 index 0000000..d7f9ecd --- /dev/null +++ b/lib/src/phy/fec/tc_interl_umts.c @@ -0,0 +1,272 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/fec/tc_interl.h" +#include "srslte/phy/fec/turbocoder.h" + +#define TURBO_SRSLTE_TCOD_RATE 3 + +uint32_t mcd(uint32_t x, uint32_t y); + +/************************************************ + * + * UMTS TURBO CODE INTERLEAVER + * + ************************************************/ + +#define MAX_ROWS 20 +#define MAX_COLS 256 + +const unsigned short table_p[52] = { 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, 257 }; +const uint8_t table_v[52] = { 3, 2, 2, 3, 2, 5, 2, 3, 2, 6, 3, 5, 2, 2, 2, + 2, 7, 5, 3, 2, 3, 5, 2, 5, 2, 6, 3, 3, 2, 3, 2, 2, 6, 5, 2, 5, 2, 2, 2, 19, + 5, 2, 3, 2, 3, 2, 6, 3, 7, 7, 6, 3 }; + +int srslte_tc_interl_init(srslte_tc_interl_t *h, uint32_t max_long_cb) { + int ret = -1; + h->forward = malloc(sizeof(uint32_t) * max_long_cb); + if (!h->forward) { + perror("malloc"); + goto clean_exit; + } + h->reverse = malloc(sizeof(uint32_t) * max_long_cb); + if (!h->reverse) { + perror("malloc"); + goto clean_exit; + } + h->max_long_cb = max_long_cb; + ret = 0; + clean_exit: if (ret == -1) { + srslte_tc_interl_free(h); + } + return ret; +} + +void srslte_tc_interl_free(srslte_tc_interl_t *h) { + if (h->forward) { + free(h->forward); + } + if (h->reverse) { + free(h->reverse); + } + bzero(h, sizeof(srslte_tc_interl_t)); +} + +int srslte_tc_interl_UMTS_gen(srslte_tc_interl_t *h, uint32_t long_cb) { + + uint32_t i, j; + uint32_t res, prim, aux; + uint32_t kp, k; + uint16_t *per, *desper; + uint8_t v; + uint16_t p; + uint16_t s[MAX_COLS], q[MAX_ROWS], r[MAX_ROWS], T[MAX_ROWS]; + uint16_t U[MAX_COLS * MAX_ROWS]; + uint32_t M_Rows, M_Cols, M_long; + + M_long = long_cb; + + if (long_cb > h->max_long_cb) { + fprintf(stderr, "Interleaver initiated for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + + /* Find R*/ + if ((40 <= M_long) && (M_long <= 159)) + M_Rows = 5; + else if (((160 <= M_long) && (M_long <= 200)) + || ((481 <= M_long) && (M_long <= 530))) + M_Rows = 10; + else + M_Rows = 20; + + /* Find p i v*/ + if ((481 <= M_long) && (M_long <= 530)) { + p = 53; + v = 2; + M_Cols = p; + } else { + i = 0; + do { + p = table_p[i]; + v = table_v[i]; + i++; + } while (M_long > (M_Rows * (p + 1))); + + } + + /* Find C*/ + if ((M_long) <= (M_Rows) * ((p) - 1)) + M_Cols = (p) - 1; + else if (((M_Rows) * (p - 1) < M_long) && (M_long <= (M_Rows) * (p))) + M_Cols = p; + else if ((M_Rows) * (p) < M_long) + M_Cols = (p) + 1; + + q[0] = 1; + prim = 6; + + for (i = 1; i < M_Rows; i++) { + do { + prim++; + res = mcd(prim, p - 1); + } while (res != 1); + q[i] = prim; + } + + s[0] = 1; + for (i = 1; i < p - 1; i++) { + s[i] = (v * s[i - 1]) % p; + } + + if (M_long <= 159 && M_long >= 40) { + T[0] = 4; + T[1] = 3; + T[2] = 2; + T[3] = 1; + T[4] = 0; + } else if ((M_long <= 200 && M_long >= 160) + || (M_long <= 530 && M_long >= 481)) { + T[0] = 9; + T[1] = 8; + T[2] = 7; + T[3] = 6; + T[4] = 5; + T[5] = 4; + T[6] = 3; + T[7] = 2; + T[8] = 1; + T[9] = 0; + } else if ((M_long <= 2480 && M_long >= 2281) + || (M_long <= 3210 && M_long >= 3161)) { + T[0] = 19; + T[1] = 9; + T[2] = 14; + T[3] = 4; + T[4] = 0; + T[5] = 2; + T[6] = 5; + T[7] = 7; + T[8] = 12; + T[9] = 18; + T[10] = 16; + T[11] = 13; + T[12] = 17; + T[13] = 15; + T[14] = 3; + T[15] = 1; + T[16] = 6; + T[17] = 11; + T[18] = 8; + T[19] = 10; + } else { + T[0] = 19; + T[1] = 9; + T[2] = 14; + T[3] = 4; + T[4] = 0; + T[5] = 2; + T[6] = 5; + T[7] = 7; + T[8] = 12; + T[9] = 18; + T[10] = 10; + T[11] = 8; + T[12] = 13; + T[13] = 17; + T[14] = 3; + T[15] = 1; + T[16] = 16; + T[17] = 6; + T[18] = 15; + T[19] = 11; + } + + for (i = 0; i < M_Rows; i++) { + r[T[i]] = q[i]; + } + + for (i = 0; i < M_Rows; i++) { + for (j = 0; j < p - 1; j++) { + U[i * M_Cols + j] = s[(j * r[i]) % (p - 1)]; + if (M_Cols == (p - 1)) + U[i * M_Cols + j] -= 1; + } + } + + if (M_Cols == p) { + for (i = 0; i < M_Rows; i++) + U[i * M_Cols + p - 1] = 0; + } else if (M_Cols == p + 1) { + for (i = 0; i < M_Rows; i++) { + U[i * M_Cols + p - 1] = 0; + U[i * M_Cols + p] = p; + } + if (M_long == M_Cols * M_Rows) { + aux = U[(M_Rows - 1) * M_Cols + p]; + U[(M_Rows - 1) * M_Cols + p] = U[(M_Rows - 1) * M_Cols + 0]; + U[(M_Rows - 1) * M_Cols + 0] = aux; + } + } + + per = h->forward; + desper = h->reverse; + + k = 0; + for (j = 0; j < M_Cols; j++) { + for (i = 0; i < M_Rows; i++) { + kp = T[i] * M_Cols + U[i * M_Cols + j]; + if (kp < M_long) { + desper[kp] = k; + per[k] = kp; + k++; + } + } + } + + return 0; + +} + +uint32_t mcd(uint32_t x, uint32_t y) { + uint32_t r = 1; + + while (r) { + r = x % y; + x = y; + y = r; + } + return x; +} diff --git a/lib/src/phy/fec/test/CMakeLists.txt b/lib/src/phy/fec/test/CMakeLists.txt new file mode 100644 index 0000000..b8046c3 --- /dev/null +++ b/lib/src/phy/fec/test/CMakeLists.txt @@ -0,0 +1,82 @@ +# +# Copyright 2013-2017 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/. +# + + +######################################################################## +# RATEMATCHING TEST +######################################################################## + +add_executable(rm_conv_test rm_conv_test.c) +target_link_libraries(rm_conv_test srslte_phy) + +add_executable(rm_turbo_test rm_turbo_test.c) +target_link_libraries(rm_turbo_test srslte_phy) + +add_test(rm_conv_test_1 rm_conv_test -t 480 -r 1920) +add_test(rm_conv_test_2 rm_conv_test -t 1920 -r 480) + +add_test(rm_turbo_test_1 rm_turbo_test -e 1920) +add_test(rm_turbo_test_2 rm_turbo_test -e 8192) + +######################################################################## +# Turbo Coder TEST +######################################################################## +add_executable(turbodecoder_test turbodecoder_test.c) +target_link_libraries(turbodecoder_test srslte_phy) + +add_test(turbodecoder_test_504_1 turbodecoder_test -n 100 -s 1 -l 504 -e 1.0 -t) +add_test(turbodecoder_test_504_2 turbodecoder_test -n 100 -s 1 -l 504 -e 2.0 -t) +add_test(turbodecoder_test_6114_1_5 turbodecoder_test -n 100 -s 1 -l 6144 -e 1.5 -t) +add_test(turbodecoder_test_known turbodecoder_test -n 1 -s 1 -k -e 0.5) + +add_executable(turbocoder_test turbocoder_test.c) +target_link_libraries(turbocoder_test srslte_phy) +add_test(turbocoder_test_all turbocoder_test) + +######################################################################## +# Viterbi TEST +######################################################################## + +add_executable(viterbi_test viterbi_test.c) +target_link_libraries(viterbi_test srslte_phy) + +add_test(viterbi_40_0 viterbi_test -n 1000 -s 1 -l 40 -t -e 0.0) +add_test(viterbi_40_2 viterbi_test -n 1000 -s 1 -l 40 -t -e 2.0) +add_test(viterbi_40_3 viterbi_test -n 1000 -s 1 -l 40 -t -e 3.0) +add_test(viterbi_40_4 viterbi_test -n 1000 -s 1 -l 40 -t -e 4.5) + +add_test(viterbi_1000_0 viterbi_test -n 100 -s 1 -l 1000 -t -e 0.0) +add_test(viterbi_1000_2 viterbi_test -n 100 -s 1 -l 1000 -t -e 2.0) +add_test(viterbi_1000_3 viterbi_test -n 100 -s 1 -l 1000 -t -e 3.0) +add_test(viterbi_1000_4 viterbi_test -n 100 -s 1 -l 1000 -t -e 4.5) + +######################################################################## +# CRC TEST +######################################################################## + +add_executable(crc_test crc_test.c) +target_link_libraries(crc_test srslte_phy) + +add_test(crc_24A crc_test -n 5001 -l 24 -p 0x1864CFB -s 1) +add_test(crc_24B crc_test -n 5001 -l 24 -p 0x1800063 -s 1) +add_test(crc_16 crc_test -n 5001 -l 16 -p 0x11021 -s 1) +add_test(crc_8 crc_test -n 5001 -l 8 -p 0x19B -s 1) + + diff --git a/lib/src/phy/fec/test/crc_test.c b/lib/src/phy/fec/test/crc_test.c new file mode 100644 index 0000000..2d1577f --- /dev/null +++ b/lib/src/phy/fec/test/crc_test.c @@ -0,0 +1,114 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "crc_test.h" + +int num_bits = 5001, crc_length = 24; +uint32_t crc_poly = 0x1864CFB; +uint32_t seed = 1; + +void usage(char *prog) { + printf("Usage: %s [nlps]\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-l crc_length [Default %d]\n", crc_length); + printf("\t-p crc_poly (Hex) [Default 0x%x]\n", crc_poly); + printf("\t-s seed [Default 0=time]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nlps")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 'l': + crc_length = atoi(argv[optind]); + break; + case 'p': + crc_poly = (uint32_t) strtoul(argv[optind], NULL, 16); + break; + case 's': + seed = (uint32_t) strtoul(argv[optind], NULL, 0); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int i; + uint8_t *data; + uint32_t crc_word, expected_word; + srslte_crc_t crc_p; + + parse_args(argc, argv); + + data = malloc(sizeof(uint8_t) * (num_bits + crc_length * 2)); + if (!data) { + perror("malloc"); + exit(-1); + } + + if (!seed) { + seed = time(NULL); + } + srand(seed); + + // Generate data + for (i = 0; i < num_bits; i++) { + data[i] = rand() % 2; + } + + //Initialize CRC params and tables + if (srslte_crc_init(&crc_p, crc_poly, crc_length)) { + exit(-1); + } + + // generate CRC word + crc_word = srslte_crc_checksum(&crc_p, data, num_bits); + + free(data); + + // check if generated word is as expected + if (get_expected_word(num_bits, crc_length, crc_poly, seed, + &expected_word)) { + fprintf(stderr, "Test parameters not defined in test_results.h\n"); + exit(-1); + } + exit(expected_word != crc_word); +} diff --git a/lib/src/phy/fec/test/crc_test.h b/lib/src/phy/fec/test/crc_test.h new file mode 100644 index 0000000..3123133 --- /dev/null +++ b/lib/src/phy/fec/test/crc_test.h @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 + +#include "srslte/phy/fec/crc.h" + +typedef struct { + int n; + int l; + uint32_t p; + uint32_t s; + uint32_t word; +}expected_word_t; + + +static expected_word_t expected_words[] = { + + {5001, 24, SRSLTE_LTE_CRC24A, 1, 0x1C5C97}, // LTE CRC24A (36.212 Sec 5.1.1) + {5001, 24, SRSLTE_LTE_CRC24B, 1, 0x36D1F0}, // LTE CRC24B + {5001, 16, SRSLTE_LTE_CRC16, 1, 0x7FF4}, // LTE CRC16: 0x7FF4 + {5001, 8, SRSLTE_LTE_CRC8, 1, 0xF0}, // LTE CRC8 0xF8 + + {-1, -1, 0, 0, 0} +}; + +int get_expected_word(int n, int l, uint32_t p, unsigned int s, unsigned int *word) { + int i; + i=0; + while(expected_words[i].n != -1) { + if (expected_words[i].l == l + && expected_words[i].p == p + && expected_words[i].s == s) { + break; + } else { + i++; + } + } + if (expected_words[i].n == -1) { + return -1; + } else { + if (word) { + *word = expected_words[i].word; + } + return 0; + } +} diff --git a/lib/src/phy/fec/test/rm_conv_test.c b/lib/src/phy/fec/test/rm_conv_test.c new file mode 100644 index 0000000..73f3fb6 --- /dev/null +++ b/lib/src/phy/fec/test/rm_conv_test.c @@ -0,0 +1,134 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +int nof_tx_bits=-1, nof_rx_bits=-1; + +void usage(char *prog) { + printf("Usage: %s -t nof_tx_bits -r nof_rx_bits\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "tr")) != -1) { + switch (opt) { + case 't': + nof_tx_bits = atoi(argv[optind]); + break; + case 'r': + nof_rx_bits = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (nof_tx_bits == -1) { + usage(argv[0]); + exit(-1); + } + if (nof_rx_bits == -1) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int i; + uint8_t *bits, *rm_bits; + float *rm_symbols, *unrm_symbols; + int nof_errors; + + parse_args(argc, argv); + + bits = malloc(sizeof(uint8_t) * nof_tx_bits); + if (!bits) { + perror("malloc"); + exit(-1); + } + rm_bits = malloc(sizeof(uint8_t) * nof_rx_bits); + if (!rm_bits) { + perror("malloc"); + exit(-1); + } + rm_symbols = malloc(sizeof(float) * nof_rx_bits); + if (!rm_symbols) { + perror("malloc"); + exit(-1); + } + unrm_symbols = malloc(sizeof(float) * nof_tx_bits); + if (!unrm_symbols) { + perror("malloc"); + exit(-1); + } + + for (i=0;i 0) != bits[i]) { + nof_errors++; + } + } + if (nof_rx_bits > nof_tx_bits) { + if (nof_errors) { + printf("nof_errors=%d\n", nof_errors); + exit(-1); + } + } + + free(bits); + free(rm_bits); + free(rm_symbols); + free(unrm_symbols); + + printf("Ok\n"); + exit(0); +} diff --git a/lib/src/phy/fec/test/rm_turbo_rx_mex.c b/lib/src/phy/fec/test/rm_turbo_rx_mex.c new file mode 100644 index 0000000..3d5597e --- /dev/null +++ b/lib/src/phy/fec/test/rm_turbo_rx_mex.c @@ -0,0 +1,99 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define INPUT prhs[0] +#define TRBLKLEN prhs[1] +#define RV prhs[2] +#define NOF_INPUTS 3 + + +void help() +{ + mexErrMsgTxt + ("[out] = srslte_rm_turbo_rx(in, trblkin, rv)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + float *input; + float *output; + uint32_t in_len, trblklen, cblen, rvidx; + float *w_buff_f; + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + // Read input symbols + in_len = mexutils_read_f(INPUT, &input); + if (in_len < 0) { + mexErrMsgTxt("Error reading input bits\n"); + return; + } + + trblklen = (uint32_t) mxGetScalar(TRBLKLEN); + rvidx = (uint32_t) mxGetScalar(RV); + + srslte_cbsegm_t cbsegm; + srslte_cbsegm(&cbsegm, trblklen); + cblen = 3*cbsegm.K1+12; + + w_buff_f = calloc(1,sizeof(float) * cblen * 10); + if (!w_buff_f) { + perror("malloc"); + exit(-1); + } + + // allocate memory for output bits + output = srslte_vec_malloc(cblen * sizeof(float)); + + srslte_rm_turbo_rx(w_buff_f, cblen * 10, input, in_len, output, cblen, + rvidx,cbsegm.F); + + if (nlhs >= 1) { + mexutils_write_f(output, &plhs[0], cblen, 1); + } + if (nlhs >= 2) { + mexutils_write_f(input, &plhs[1], in_len, 1); + } + + free(input); + free(output); + free(w_buff_f); + + return; +} + diff --git a/lib/src/phy/fec/test/rm_turbo_test.c b/lib/src/phy/fec/test/rm_turbo_test.c new file mode 100644 index 0000000..c8a0a95 --- /dev/null +++ b/lib/src/phy/fec/test/rm_turbo_test.c @@ -0,0 +1,208 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + + +int nof_e_bits = -1; +int rv_idx = -1; +int cb_idx = -1; + +uint8_t systematic[6148], parity[2*6148]; +uint8_t systematic_bytes[6148/8+1], parity_bytes[2*6148/8+1]; + +#define BUFFSZ 6176*3 + +uint8_t bits[3*6144+12]; +uint8_t buff_b[BUFFSZ]; +float buff_f[BUFFSZ]; +float bits_f[3*6144+12]; +short bits2_s[3*6144+12]; + +void usage(char *prog) { + printf("Usage: %s -c cb_idx -e nof_e_bits [-i rv_idx]\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cei")) != -1) { + switch (opt) { + case 'c': + cb_idx = atoi(argv[optind]); + break; + case 'e': + nof_e_bits = atoi(argv[optind]); + break; + case 'i': + rv_idx = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (nof_e_bits == -1) { + usage(argv[0]); + exit(-1); + } +} + + +int main(int argc, char **argv) { + int i; + uint8_t *rm_bits, *rm_bits2, *rm_bits2_bytes; + short *rm_bits_s; + float *rm_bits_f; + + parse_args(argc, argv); + + srslte_rm_turbo_gentables(); + + rm_bits_s = srslte_vec_malloc(sizeof(short) * nof_e_bits); + if (!rm_bits_s) { + perror("malloc"); + exit(-1); + } + rm_bits_f = srslte_vec_malloc(sizeof(float) * nof_e_bits); + if (!rm_bits_f) { + perror("malloc"); + exit(-1); + } + rm_bits = srslte_vec_malloc(sizeof(uint8_t) * nof_e_bits); + if (!rm_bits) { + perror("malloc"); + exit(-1); + } + rm_bits2 = malloc(sizeof(uint8_t) * nof_e_bits); + if (!rm_bits2) { + perror("malloc"); + exit(-1); + } + rm_bits2_bytes = malloc(sizeof(uint8_t) * nof_e_bits/8 + 1); + if (!rm_bits2_bytes) { + perror("malloc"); + exit(-1); + } + + uint32_t st=0, end=188; + if (cb_idx != -1) { + st=cb_idx; + end=cb_idx+1; + } + uint32_t rv_st=0, rv_end=4; + if (rv_idx != -1) { + rv_st=rv_idx; + rv_end=rv_idx+1; + } + + for (cb_idx=st;cb_idx 0) { + srslte_rm_turbo_tx(buff_b, BUFFSZ, bits, long_cb_enc, rm_bits, nof_e_bits, rv_idx); + } + + for (int i=0;i 0) { + bzero(rm_bits2_bytes, nof_e_bits/8); + srslte_rm_turbo_tx_lut(buff_b, systematic_bytes, parity_bytes, rm_bits2_bytes, cb_idx, nof_e_bits, 0, rv_idx); + } + + srslte_bit_unpack_vector(rm_bits2_bytes, rm_bits2, nof_e_bits); + + for (int i=0;i +#include +#include +#include +#include +#include +#include + +#include +#include +#include "srslte/srslte.h" + +uint32_t long_cb = 0; + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-l long_cb [Default %u]\n", long_cb); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "lv")) != -1) { + switch (opt) { + case 'l': + long_cb = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +uint8_t input_bytes[6144/8]; +uint8_t input_bits[6144]; +uint8_t parity[3*6144+12]; +uint8_t parity_bits[3*6144+12]; +uint8_t output_bits[3*6144+12]; +uint8_t output_bits2[3*6144+12]; + +int main(int argc, char **argv) { + + parse_args(argc, argv); + + srslte_tcod_t tcod; + srslte_tcod_init(&tcod, 6144); + + uint32_t st=0, end=187; + if (long_cb) { + st=srslte_cbsegm_cbindex(long_cb); + end=st; + } + + for (uint32_t len=st;len<=end;len++) { + long_cb = srslte_cbsegm_cbsize(len); + printf("Checking long_cb=%d\n", long_cb); + for (int i=0;i +#include +#include +#include +#include +#include +#include + +#include +#include +#include "srslte/srslte.h" + +#include "turbodecoder_test.h" + + + +uint32_t frame_length = 1000, nof_frames = 100; +float ebno_db = 100.0; +uint32_t seed = 0; +int K = -1; + +#define MAX_ITERATIONS 10 +int nof_cb = 1; +int nof_iterations = MAX_ITERATIONS; +int test_known_data = 0; +int test_errors = 0; +int nof_repetitions = 1; + +#define SNR_POINTS 4 +#define SNR_MIN 1.0 +#define SNR_MAX 8.0 + +void usage(char *prog) { + printf("Usage: %s [nlesv]\n", prog); + printf( + "\t-k Test with known data (ignores frame_length) [Default disabled]\n"); + printf("\t-c nof_cb in parallel [Default %d]\n", nof_cb); + printf("\t-i nof_iterations [Default %d]\n", nof_iterations); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-N nof_repetitions [Default %d]\n", nof_repetitions); + printf("\t-l frame_length [Default %d]\n", frame_length); + printf("\t-e ebno in dB [Default scan]\n"); + printf("\t-t test: check errors on exit [Default disabled]\n"); + printf("\t-s seed [Default 0=time]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cinNlstvekt")) != -1) { + switch (opt) { + case 'c': + nof_cb = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'N': + nof_repetitions = atoi(argv[optind]); + break; + case 'k': + test_known_data = 1; + break; + case 't': + test_errors = 1; + break; + case 'i': + nof_iterations = atoi(argv[optind]); + break; + case 'l': + frame_length = atoi(argv[optind]); + break; + case 'e': + ebno_db = atof(argv[optind]); + break; + case 's': + seed = (uint32_t) strtoul(argv[optind], NULL, 0); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + uint32_t frame_cnt; + float *llr; + short *llr_s; + uint8_t *llr_c; + uint8_t *data_tx, *data_rx, *data_rx_bytes[SRSLTE_TDEC_MAX_NPAR], *symbols; + uint32_t i, j; + float var[SNR_POINTS]; + uint32_t snr_points; + uint32_t errors; + uint32_t coded_length; + struct timeval tdata[3]; + float mean_usec; + srslte_tdec_t tdec; + srslte_tcod_t tcod; + + parse_args(argc, argv); + + if (!seed) { + seed = time(NULL); + } + srand(seed); + + if (test_known_data) { + frame_length = KNOWN_DATA_LEN; + } else { + frame_length = srslte_cbsegm_cbsize(srslte_cbsegm_cbindex(frame_length)); + } + + coded_length = 3 * (frame_length) + SRSLTE_TCOD_TOTALTAIL; + + printf(" Frame length: %d\n", frame_length); + if (ebno_db < 100.0) { + printf(" EbNo: %.2f\n", ebno_db); + } + + data_tx = srslte_vec_malloc(frame_length * sizeof(uint8_t)); + if (!data_tx) { + perror("malloc"); + exit(-1); + } + + data_rx = srslte_vec_malloc(frame_length * sizeof(uint8_t)); + if (!data_rx) { + perror("malloc"); + exit(-1); + } + for (int cb=0;cb + +typedef struct { + int n; + uint32_t s; + int iterations; + int len; + float ebno; + int errors; +} expected_errors_t; + +static expected_errors_t expected_errors[] = { + { 100, 1, 1, 504, 1.0, 3989 }, + { 100, 1, 2, 504, 1.0, 1922 }, + { 100, 1, 3, 504, 1.0, 1096 }, + { 100, 1, 4, 504, 1.0, 957 }, + + { 100, 1, 1, 504, 2.0, 803 }, + { 100, 1, 2, 504, 2.0, 47 }, + { 100, 1, 3, 504, 2.0, 7 }, + { 100, 1, 4, 504, 2.0, 0 }, + + { 100, 1, 1, 6144, 1.5, 24719 }, + { 100, 1, 2, 6144, 1.5, 897 }, + { 100, 1, 3, 6144, 1.5, 2 }, + { 100, 1, 4, 6144, 1.5, 0 }, + { -1, 0, -1, -1, -1.0, -1} +}; + + +int get_expected_errors(int n, uint32_t s, int iterations, int len, float ebno) { + int i; + i = 0; + while (expected_errors[i].n != -1) { + if (expected_errors[i].n == n + && expected_errors[i].s == s + && expected_errors[i].len == len + && expected_errors[i].iterations == iterations + && expected_errors[i].ebno == ebno) { + break; + } else { + i++; + } + } + return expected_errors[i].errors; +} + +#define KNOWN_DATA_NFRAMES 1 +#define KNOWN_DATA_SEED 1 +#define KNOWN_DATA_EBNO 0.5 +const int known_data_errors[4] = {47, 18, 0, 0}; + +#define KNOWN_DATA_LEN 504 +const uint8_t known_data[KNOWN_DATA_LEN] = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, + 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, + 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, + 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1 }; + +const uint8_t known_data_encoded[3 * KNOWN_DATA_LEN + 12] = { 0, 0, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, + 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, + 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, + 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, + 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, + 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, + 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, + 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, + 1, 0, 1, 1, 1 }; diff --git a/lib/src/phy/fec/test/turbodecoder_test_mex.c b/lib/src/phy/fec/test/turbodecoder_test_mex.c new file mode 100644 index 0000000..f52e122 --- /dev/null +++ b/lib/src/phy/fec/test/turbodecoder_test_mex.c @@ -0,0 +1,106 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define INPUT prhs[0] +#define NITERS prhs[1] +#define NOF_INPUTS 1 + + +void help() +{ + mexErrMsgTxt + ("[decoded_bits] = srslte_turbodecoder(input_llr, nof_iterations)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_tdec_gen_t tdec; + float *input_llr; + uint8_t *output_data; + uint32_t nof_bits; + uint32_t nof_iterations; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + // Read input symbols + uint32_t nof_symbols = mexutils_read_f(INPUT, &input_llr); + if (nof_symbols < 40) { + mexErrMsgTxt("Minimum block size is 40\n"); + return; + } + nof_bits = (nof_symbols-12)/3; + + if (!srslte_cbsegm_cbsize_isvalid(nof_bits)) { + mexErrMsgTxt("Invalid codeblock size\n"); + return; + } + + + // read number of iterations + if (nrhs > NOF_INPUTS) { + nof_iterations = (uint32_t) mxGetScalar(prhs[1]); + if (nof_iterations > 50) { + mexErrMsgTxt("Maximum number of iterations is 50\n"); + return; + } + } else { + nof_iterations = 5; // set the default nof iterations to 5 as in matlab + } + + // allocate memory for output bits + output_data = srslte_vec_malloc(nof_bits * sizeof(uint8_t)); + + if (srslte_tdec_gen_init(&tdec, nof_bits)) { + mexErrMsgTxt("Error initiating Turbo decoder\n"); + return; + } + + srslte_tdec_gen_run_all(&tdec, input_llr, output_data, nof_iterations, nof_bits); + + if (nlhs >= 1) { + mexutils_write_uint8(output_data, &plhs[0], nof_bits, 1); + } + + srslte_tdec_gen_free(&tdec); + + free(input_llr); + free(output_data); + + return; +} + diff --git a/lib/src/phy/fec/test/viterbi_test.c b/lib/src/phy/fec/test/viterbi_test.c new file mode 100644 index 0000000..8899f45 --- /dev/null +++ b/lib/src/phy/fec/test/viterbi_test.c @@ -0,0 +1,299 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +#include "viterbi_test.h" + +#define VITERBI_16 + +#ifndef LV_HAVE_AVX2 +#undef VITERBI_16 +#endif + +int frame_length = 1000, nof_frames = 256; +float ebno_db = 100.0; +uint32_t seed = 0; +bool tail_biting = false; + +#define SNR_POINTS 10 +#define SNR_MIN 0.0 +#define SNR_MAX 5.0 + +void usage(char *prog) { + printf("Usage: %s [nlest]\n", prog); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l frame_length [Default %d]\n", frame_length); + printf("\t-e ebno in dB [Default scan]\n"); + printf("\t-s seed [Default 0=time]\n"); + printf("\t-t tail_bitting [Default %s]\n", tail_biting ? "yes" : "no"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nlste")) != -1) { + switch (opt) { + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'l': + frame_length = atoi(argv[optind]); + break; + case 'e': + ebno_db = atof(argv[optind]); + break; + case 's': + seed = (uint32_t) strtoul(argv[optind], NULL, 0); + break; + case 't': + tail_biting = true; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int frame_cnt; + float *llr; + uint16_t *llr_s; + uint8_t *llr_c; + uint8_t *data_tx, *data_rx, *data_rx2, *symbols; + int i, j; + float var[SNR_POINTS], varunc[SNR_POINTS]; + int snr_points; + uint32_t errors; +#ifdef TEST_SSE + uint32_t errors2; + srslte_viterbi_t dec_sse; +#endif + srslte_viterbi_t dec; + srslte_convcoder_t cod; + int coded_length; + + + parse_args(argc, argv); + + if (!seed) { + seed = time(NULL); + } + srand(seed); + + cod.poly[0] = 0x6D; + cod.poly[1] = 0x4F; + cod.poly[2] = 0x57; + cod.K = 7; + cod.tail_biting = tail_biting; + + cod.R = 3; + coded_length = cod.R * (frame_length + ((cod.tail_biting) ? 0 : cod.K - 1)); + srslte_viterbi_init(&dec, SRSLTE_VITERBI_37, cod.poly, frame_length, cod.tail_biting); + printf("Convolutional Code 1/3 K=%d Tail bitting: %s\n", cod.K, cod.tail_biting ? "yes" : "no"); + +#ifdef TEST_SSE + srslte_viterbi_init_sse(&dec_sse, SRSLTE_VITERBI_37, cod.poly, frame_length, cod.tail_biting); +#endif + + printf(" Frame length: %d\n", frame_length); + if (ebno_db < 100.0) { + printf(" EbNo: %.2f\n", ebno_db); + } + + data_tx = malloc(frame_length * sizeof(uint8_t)); + if (!data_tx) { + perror("malloc"); + exit(-1); + } + + data_rx = malloc(frame_length * sizeof(uint8_t)); + if (!data_rx) { + perror("malloc"); + exit(-1); + } + + data_rx2 = malloc(frame_length * sizeof(uint8_t)); + if (!data_rx2) { + perror("malloc"); + exit(-1); + } + + symbols = malloc(coded_length * sizeof(uint8_t)); + if (!symbols) { + perror("malloc"); + exit(-1); + } + llr = malloc(coded_length * sizeof(float)); + if (!llr) { + perror("malloc"); + exit(-1); + } + llr_s = malloc(2 * coded_length * sizeof(uint16_t)); + if (!llr_s) { + perror("malloc"); + exit(-1); + } + llr_c = malloc(2 * coded_length * sizeof(uint8_t)); + if (!llr_c) { + perror("malloc"); + exit(-1); + } + + float ebno_inc, esno_db; + ebno_inc = (SNR_MAX - SNR_MIN) / SNR_POINTS; + if (ebno_db == 100.0) { + snr_points = SNR_POINTS; + for (i = 0; i < snr_points; i++) { + ebno_db = SNR_MIN + i * ebno_inc; + esno_db = ebno_db + 10 * log10((double) 1 / 3); + var[i] = sqrt(1 / (pow(10, esno_db / 10))); + varunc[i] = sqrt(1 / (pow(10, ebno_db / 10))); + } + } else { + esno_db = ebno_db + 10 * log10((double) 1 / 3); + var[0] = sqrt(1 / (pow(10, esno_db / 10))); + varunc[0] = sqrt(1 / (pow(10, ebno_db / 10))); + snr_points = 1; + } + + for (i = 0; i < snr_points; i++) { + frame_cnt = 0; + errors = 0; +#ifdef TEST_SSE + errors2 = 0; +#endif + while (frame_cnt < nof_frames) { + + /* generate data_tx */ + for (j = 0; j < frame_length; j++) { + data_tx[j] = rand() % 2; + } + + /* uncoded BER */ + for (j = 0; j < frame_length; j++) { + llr[j] = data_tx[j] ? sqrt(2) : -sqrt(2); + } + srslte_ch_awgn_f(llr, llr, varunc[i], frame_length); + + /* coded BER */ + srslte_convcoder_encode(&cod, data_tx, symbols, frame_length); + + for (j = 0; j < coded_length; j++) { + llr[j] = symbols[j] ? sqrt(2) : -sqrt(2); + } + + srslte_ch_awgn_f(llr, llr, var[i], coded_length); + //srslte_vec_fprint_f(stdout, llr, 100); + + srslte_vec_quant_fuc(llr, llr_c, 32, 127.5, 255, coded_length); + srslte_vec_quant_fus(llr, llr_s, 8192, 32767.5, 65535, coded_length); + + struct timeval t[3]; + gettimeofday(&t[1], NULL); + int M = 1; + + + for (int i=0;i expected_errors); + } + } else { + printf("\n"); + printf("Done\n"); + exit(0); + } +} diff --git a/lib/src/phy/fec/test/viterbi_test.h b/lib/src/phy/fec/test/viterbi_test.h new file mode 100644 index 0000000..3866530 --- /dev/null +++ b/lib/src/phy/fec/test/viterbi_test.h @@ -0,0 +1,104 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 + +typedef struct { + int n; + uint32_t s; + int len; + bool tail; + float ebno; + int errors; +}expected_errors_t; + +/* The SSE implementation uses 5-bit metrics and has 0.75 dB loss approximation */ +#ifdef LV_HAVE_SSE + +static expected_errors_t expected_errors[] = { + {1000, 1, 40, true, 0.0, 7282}, + {1000, 1, 40, true, 2.0, 725}, + {1000, 1, 40, true, 3.0, 176}, + {1000, 1, 40, true, 4.5, 24}, + + {100, 1, 1000, true, 0.0, 13208}, + {100, 1, 1000, true, 2.0, 939}, + {100, 1, 1000, true, 3.0, 110}, + {100, 1, 1000, true, 4.5, 5}, + + {-1, -1, -1, true, -1.0, -1} +}; + +#elif HAVE_NEON + +static expected_errors_t expected_errors[] = { + {1000, 1, 40, true, 0.0, 7282}, + {1000, 1, 40, true, 2.0, 725}, + {1000, 1, 40, true, 3.0, 176}, + {1000, 1, 40, true, 4.5, 24}, + + {100, 1, 1000, true, 0.0, 13208}, + {100, 1, 1000, true, 2.0, 939}, + {100, 1, 1000, true, 3.0, 110}, + {100, 1, 1000, true, 4.5, 5}, + + {-1, -1, -1, true, -1.0, -1} +}; + + +#else + +static expected_errors_t expected_errors[] = { + {1000, 1, 40, true, 0.0, 5363}, + {1000, 1, 40, true, 2.0, 356}, + {1000, 1, 40, true, 3.0, 48}, + {1000, 1, 40, true, 4.5, 0}, + + {100, 1, 1000, true, 0.0, 8753}, + {100, 1, 1000, true, 2.0, 350}, + {100, 1, 1000, true, 3.0, 33}, + {100, 1, 1000, true, 4.5, 0}, + + {-1, -1, -1, true, -1.0, -1} +}; + +#endif +int get_expected_errors(int n, uint32_t s, int len, bool tail, float ebno) { + int i; + i=0; + while(expected_errors[i].n != -1) { + if (expected_errors[i].n == n + && expected_errors[i].s == s + && expected_errors[i].len == len + && expected_errors[i].tail == tail + && expected_errors[i].ebno == ebno) { + break; + } else { + i++; + } + } + return expected_errors[i].errors; +} diff --git a/lib/src/phy/fec/test/viterbi_test_mex.c b/lib/src/phy/fec/test/viterbi_test_mex.c new file mode 100644 index 0000000..9d1862a --- /dev/null +++ b/lib/src/phy/fec/test/viterbi_test_mex.c @@ -0,0 +1,89 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define INPUT prhs[0] +#define NOF_INPUTS 1 + + +void help() +{ + mexErrMsgTxt + ("[decoded_bits] = srslte_viterbi(input_llr, type)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_viterbi_t viterbi; + float *input_llr; + uint8_t *output_data; + int nof_bits; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + // Read input symbols + nof_bits = mexutils_read_f(INPUT, &input_llr); + + output_data = srslte_vec_malloc(nof_bits * sizeof(uint8_t)); + + int poly[3] = { 0x6D, 0x4F, 0x57 }; + if (srslte_viterbi_init(&viterbi, SRSLTE_VITERBI_37, poly, nof_bits/3, true)) { + return; + } + + if (nrhs >= 2) { + float gain_quant = mxGetScalar(prhs[1]); + srslte_viterbi_set_gain_quant(&viterbi, gain_quant); + } + + srslte_viterbi_decode_f(&viterbi, input_llr, output_data, nof_bits/3); + + if (nlhs >= 1) { + mexutils_write_uint8(output_data, &plhs[0], nof_bits/3, 1); + } + if (nlhs >= 2) { + mexutils_write_uint8(viterbi.symbols_uc, &plhs[1], nof_bits/3, 1); + } + + srslte_viterbi_free(&viterbi); + + free(input_llr); + free(output_data); + + return; +} + diff --git a/lib/src/phy/fec/turbocoder.c b/lib/src/phy/fec/turbocoder.c new file mode 100644 index 0000000..69f40dd --- /dev/null +++ b/lib/src/phy/fec/turbocoder.c @@ -0,0 +1,342 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/fec/turbocoder.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" + +#define NOF_REGS 3 + +#define RATE 3 +#define TOTALTAIL 12 + +uint8_t tcod_lut_next_state[188][8][256]; +uint8_t tcod_lut_output[188][8][256]; +uint16_t tcod_per_fw[188][6144]; +static srslte_bit_interleaver_t tcod_interleavers[188]; + +static bool table_initiated = false; + +int srslte_tcod_init(srslte_tcod_t *h, uint32_t max_long_cb) { + + h->max_long_cb = max_long_cb; + h->temp = srslte_vec_malloc(max_long_cb/8); + + if (!table_initiated) { + table_initiated = true; + srslte_tcod_gentable(); + } + return 0; +} + +void srslte_tcod_free(srslte_tcod_t *h) { + if (table_initiated) { + h->max_long_cb = 0; + if (h->temp) { + free(h->temp); + } + for (int i = 0; i < 188; i++) { + srslte_bit_interleaver_free(&tcod_interleavers[i]); + } + table_initiated = false; + } +} + +/* Expects bits (1 byte = 1 bit) and produces bits. The systematic and parity bits are interlaced in the output */ +int srslte_tcod_encode(srslte_tcod_t *h, uint8_t *input, uint8_t *output, uint32_t long_cb) +{ + + uint8_t reg1_0, reg1_1, reg1_2, reg2_0, reg2_1, reg2_2; + uint32_t i, k = 0, j; + uint8_t bit; + uint8_t in, out; + uint16_t *per; + + if (long_cb > h->max_long_cb) { + fprintf(stderr, "Turbo coder initiated for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + + int longcb_idx = srslte_cbsegm_cbindex(long_cb); + if (longcb_idx < 0) { + fprintf(stderr, "Invalid CB size %d\n", long_cb); + return -1; + } + + per = tcod_per_fw[longcb_idx]; + + reg1_0 = 0; + reg1_1 = 0; + reg1_2 = 0; + + reg2_0 = 0; + reg2_1 = 0; + reg2_2 = 0; + + k = 0; + for (i = 0; i < long_cb; i++) { + if (input[i] == SRSLTE_TX_NULL) { + bit = 0; + } else { + bit = input[i]; + } + output[k] = input[i]; + + k++; + + in = bit ^ (reg1_2 ^ reg1_1); + out = reg1_2 ^ (reg1_0 ^ in); + + reg1_2 = reg1_1; + reg1_1 = reg1_0; + reg1_0 = in; + + if (input[i] == SRSLTE_TX_NULL) { + output[k] = SRSLTE_TX_NULL; + } else { + output[k] = out; + } + k++; + + bit = input[per[i]]; + if (bit == SRSLTE_TX_NULL) { + bit = 0; + } + + in = bit ^ (reg2_2 ^ reg2_1); + out = reg2_2 ^ (reg2_0 ^ in); + + reg2_2 = reg2_1; + reg2_1 = reg2_0; + reg2_0 = in; + + output[k] = out; + k++; + + } + + k = 3 * long_cb; + + /* TAILING CODER #1 */ + for (j = 0; j < NOF_REGS; j++) { + bit = reg1_2 ^ reg1_1; + + output[k] = bit; + k++; + + in = bit ^ (reg1_2 ^ reg1_1); + out = reg1_2 ^ (reg1_0 ^ in); + + reg1_2 = reg1_1; + reg1_1 = reg1_0; + reg1_0 = in; + + output[k] = out; + k++; + } + + /* TAILING CODER #2 */ + for (j = 0; j < NOF_REGS; j++) { + bit = reg2_2 ^ reg2_1; + + output[k] = bit; + k++; + + in = bit ^ (reg2_2 ^ reg2_1); + out = reg2_2 ^ (reg2_0 ^ in); + + reg2_2 = reg2_1; + reg2_1 = reg2_0; + reg2_0 = in; + + output[k] = out; + k++; + } + return 0; +} + +/* Expects bytes and produces bytes. The systematic and parity bits are interlaced in the output */ +int srslte_tcod_encode_lut(srslte_tcod_t *h, uint8_t *input, uint8_t *parity, uint32_t cblen_idx) +{ + if (cblen_idx < 188) { + uint32_t long_cb = srslte_cbsegm_cbsize(cblen_idx); + + if (long_cb % 8) { + fprintf(stderr, "Turbo coder LUT implementation long_cb must be multiple of 8\n"); + return -1; + } + + /* Parity bits for the 1st constituent encoders */ + uint8_t state0 = 0; + for (uint32_t i=0;itemp, 0); + //srslte_bit_interleave(input, h->temp, tcod_per_fw[cblen_idx], long_cb); + + /* Parity bits for the 2nd constituent encoders */ + uint8_t state1 = 0; + for (uint32_t i=0;itemp[i]]; + parity[long_cb/8+i] |= (out&0xf0)>>4; + parity[long_cb/8+i+1] = (out&0xf)<<4; + state1 = tcod_lut_next_state[cblen_idx][state1][h->temp[i]] % 8; + } + + /* Tail bits */ + uint8_t reg1_0, reg1_1, reg1_2, reg2_0, reg2_1, reg2_2; + uint8_t bit, in, out; + uint8_t k=0; + uint8_t tail[12]; + + reg2_0 = (state1&4)>>2; + reg2_1 = (state1&2)>>1; + reg2_2 = state1&1; + + reg1_0 = (state0&4)>>2; + reg1_1 = (state0&2)>>1; + reg1_2 = state0&1; + + /* TAILING CODER #1 */ + for (uint32_t j = 0; j < NOF_REGS; j++) { + bit = reg1_2 ^ reg1_1; + + tail[k] = bit; + k++; + + in = bit ^ (reg1_2 ^ reg1_1); + out = reg1_2 ^ (reg1_0 ^ in); + + reg1_2 = reg1_1; + reg1_1 = reg1_0; + reg1_0 = in; + + tail[k] = out; + k++; + } + + /* TAILING CODER #2 */ + for (uint32_t j = 0; j < NOF_REGS; j++) { + bit = reg2_2 ^ reg2_1; + + tail[k] = bit; + k++; + + in = bit ^ (reg2_2 ^ reg2_1); + out = reg2_2 ^ (reg2_0 ^ in); + + reg2_2 = reg2_1; + reg2_1 = reg2_0; + reg2_0 = in; + + tail[k] = out; + k++; + } + + uint8_t tailv[3][4]; + for (int i=0;i<4;i++) { + for (int j=0;j<3;j++) { + tailv[j][i] = tail[3*i+j]; + } + } + uint8_t *x = tailv[0]; + input[long_cb/8] = (srslte_bit_pack(&x, 4)<<4); + x = tailv[1]; + parity[long_cb/8] |= (srslte_bit_pack(&x, 4)<<4); + x = tailv[2]; + parity[2*long_cb/8] |= (srslte_bit_pack(&x, 4)&0xf); + + return 3*long_cb+TOTALTAIL; + } else { + return -1; + } +} + +void srslte_tcod_gentable() { + srslte_tc_interl_t interl; + + if (srslte_tc_interl_init(&interl, 6144)) { + fprintf(stderr, "Error initiating interleave\n"); + return; + } + + for (uint32_t len=0;len<188;len++) { + uint32_t long_cb = srslte_cbsegm_cbsize(len); + if (srslte_tc_interl_LTE_gen(&interl, long_cb)) { + fprintf(stderr, "Error initiating TC interleaver for long_cb=%d\n", long_cb); + return; + } + // Save fw/bw permutation tables + for (uint32_t i=0;i>2; + reg_1 = (state&2)>>1; + reg_2 = state&1; + + tcod_lut_output[len][state][data] = 0; + uint8_t bit, in, out; + for (uint32_t i = 0; i < 8; i++) { + bit = (data&(1<<(7-i)))?1:0; + + in = bit ^ (reg_2 ^ reg_1); + out = reg_2 ^ (reg_0 ^ in); + + reg_2 = reg_1; + reg_1 = reg_0; + reg_0 = in; + + tcod_lut_output[len][state][data] |= out<<(7-i); + + } + tcod_lut_next_state[len][state][data] = reg_0<<2 | reg_1<<1 | reg_2; + } + } + } + + srslte_tc_interl_free(&interl); +} diff --git a/lib/src/phy/fec/turbodecoder.c b/lib/src/phy/fec/turbodecoder.c new file mode 100644 index 0000000..54a0ed1 --- /dev/null +++ b/lib/src/phy/fec/turbodecoder.c @@ -0,0 +1,174 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder.h" +#include "srslte/phy/fec/turbodecoder_gen.h" + + +#ifdef LV_HAVE_SSE +#include "srslte/phy/fec/turbodecoder_simd.h" +#endif + +#include "srslte/phy/utils/vector.h" + + +int srslte_tdec_init(srslte_tdec_t * h, uint32_t max_long_cb) { +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_init(&h->tdec_simd, SRSLTE_TDEC_MAX_NPAR, max_long_cb); +#else + h->input_conv = srslte_vec_malloc(sizeof(float) * (3*max_long_cb+12)); + if (!h->input_conv) { + perror("malloc"); + return -1; + } + return srslte_tdec_gen_init(&h->tdec_gen, max_long_cb); +#endif +} + +void srslte_tdec_free(srslte_tdec_t * h) { +#ifdef LV_HAVE_SSE + srslte_tdec_simd_free(&h->tdec_simd); +#else + if (h->input_conv) { + free(h->input_conv); + } + srslte_tdec_gen_free(&h->tdec_gen); +#endif + +} + +int srslte_tdec_reset(srslte_tdec_t * h, uint32_t long_cb) { +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_reset(&h->tdec_simd, long_cb); +#else + return srslte_tdec_gen_reset(&h->tdec_gen, long_cb); +#endif +} + +int srslte_tdec_reset_cb(srslte_tdec_t * h, uint32_t cb_idx) { +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_reset_cb(&h->tdec_simd, cb_idx); +#else + return srslte_tdec_gen_reset(&h->tdec_gen, h->tdec_gen.current_cb_len); +#endif +} + +int srslte_tdec_get_nof_iterations_cb(srslte_tdec_t * h, uint32_t cb_idx) +{ +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_get_nof_iterations_cb(&h->tdec_simd, cb_idx); +#else + return h->tdec_gen.n_iter; +#endif +} + +void srslte_tdec_iteration_par(srslte_tdec_t * h, int16_t* input[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) { +#ifdef LV_HAVE_SSE + srslte_tdec_simd_iteration(&h->tdec_simd, input, long_cb); +#else + srslte_vec_convert_if(input[0], 0.01, h->input_conv, 3*long_cb+12); + srslte_tdec_gen_iteration(&h->tdec_gen, h->input_conv, long_cb); +#endif +} + +void srslte_tdec_iteration(srslte_tdec_t * h, int16_t* input, uint32_t long_cb) { + int16_t *input_par[SRSLTE_TDEC_MAX_NPAR]; + input_par[0] = input; + return srslte_tdec_iteration_par(h, input_par, long_cb); +} + +void srslte_tdec_decision_par(srslte_tdec_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) { +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_decision(&h->tdec_simd, output, long_cb); +#else + return srslte_tdec_gen_decision(&h->tdec_gen, output[0], long_cb); +#endif +} + +uint32_t srslte_tdec_get_nof_parallel(srslte_tdec_t *h) { +#ifdef LV_HAVE_AVX2 + return 2; +#else + return 1; +#endif +} + +void srslte_tdec_decision(srslte_tdec_t * h, uint8_t *output, uint32_t long_cb) { + uint8_t *output_par[SRSLTE_TDEC_MAX_NPAR]; + output_par[0] = output; + srslte_tdec_decision_par(h, output_par, long_cb); +} + +void srslte_tdec_decision_byte_par(srslte_tdec_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) { +#ifdef LV_HAVE_SSE + srslte_tdec_simd_decision_byte(&h->tdec_simd, output, long_cb); +#else + srslte_tdec_gen_decision_byte(&h->tdec_gen, output[0], long_cb); +#endif +} + +void srslte_tdec_decision_byte_par_cb(srslte_tdec_t * h, uint8_t *output, uint32_t cb_idx, uint32_t long_cb) { +#ifdef LV_HAVE_SSE + srslte_tdec_simd_decision_byte_cb(&h->tdec_simd, output, cb_idx, long_cb); +#else + srslte_tdec_gen_decision_byte(&h->tdec_gen, output, long_cb); +#endif +} + +void srslte_tdec_decision_byte(srslte_tdec_t * h, uint8_t *output, uint32_t long_cb) { + uint8_t *output_par[SRSLTE_TDEC_MAX_NPAR]; + output_par[0] = output; + srslte_tdec_decision_byte_par(h, output_par, long_cb); +} + +int srslte_tdec_run_all_par(srslte_tdec_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], + uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_iterations, uint32_t long_cb) { +#ifdef LV_HAVE_SSE + return srslte_tdec_simd_run_all(&h->tdec_simd, input, output, nof_iterations, long_cb); +#else + srslte_vec_convert_if(input[0], 0.01, h->input_conv, 3*long_cb+12); + return srslte_tdec_gen_run_all(&h->tdec_gen, h->input_conv, output[0], nof_iterations, long_cb); +#endif +} + +int srslte_tdec_run_all(srslte_tdec_t * h, int16_t * input, uint8_t *output, uint32_t nof_iterations, uint32_t long_cb) +{ + uint8_t *output_par[SRSLTE_TDEC_MAX_NPAR]; + output_par[0] = output; + int16_t *input_par[SRSLTE_TDEC_MAX_NPAR]; + input_par[0] = input; + + return srslte_tdec_run_all_par(h, input_par, output_par, nof_iterations, long_cb); +} + + diff --git a/lib/src/phy/fec/turbodecoder_avx.c b/lib/src/phy/fec/turbodecoder_avx.c new file mode 100644 index 0000000..2e877cb --- /dev/null +++ b/lib/src/phy/fec/turbodecoder_avx.c @@ -0,0 +1,475 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_simd.h" +#include "srslte/phy/utils/vector.h" + +#include + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TAIL 3 +#define TOTALTAIL 12 + +#define INF 10000 +#define ZERO 0 + + +#ifdef LV_HAVE_AVX2 + +#include +#include + + +// Number of CB processed in parllel in AVX +#define NCB 2 + +/* +static void print_256i(__m256i x) { + int16_t *s = (int16_t*) &x; + printf("[%d", s[0]); + for (int i=1;i<16;i++) { + printf(",%d", s[i]); + } + printf("]\n"); +} +*/ + +/* Computes the horizontal MAX from 8 16-bit integers using the minpos_epu16 SSE4.1 instruction */ +static inline int16_t hMax0(__m256i masked_value) +{ + __m128i tmp1 = _mm256_extractf128_si256(masked_value, 0); + __m128i tmp3 = _mm_minpos_epu16(tmp1); + return (int16_t)(_mm_cvtsi128_si32(tmp3)); +} + +static inline int16_t hMax1(__m256i masked_value) +{ + __m128i tmp1 = _mm256_extractf128_si256(masked_value, 1); + __m128i tmp3 = _mm_minpos_epu16(tmp1); + return (int16_t)(_mm_cvtsi128_si32(tmp3)); +} + +/* Computes beta values */ +void map_avx_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) +{ + int k; + uint32_t end = long_cb + 3; + const __m256i *alphaPtr = (const __m256i*) s->alpha; + + __m256i beta_k = _mm256_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0, -INF, -INF, -INF, -INF, -INF, -INF, -INF, 0); + __m256i g, bp, bn, alpha_k; + + /* Define the shuffle constant for the positive beta */ + __m256i shuf_bp = _mm256_set_epi8( + // 1st CB + 15+16, 14+16, // 7 + 7+16, 6+16, // 3 + 5+16, 4+16, // 2 + 13+16, 12+16, // 6 + 11+16, 10+16, // 5 + 3+16, 2+16, // 1 + 1+16, 0+16, // 0 + 9+16, 8+16, // 4 + + // 2nd CB + 15, 14, // 7 + 7, 6, // 3 + 5, 4, // 2 + 13, 12, // 6 + 11, 10, // 5 + 3, 2, // 1 + 1, 0, // 0 + 9, 8 // 4 + ); + + /* Define the shuffle constant for the negative beta */ + __m256i shuf_bn = _mm256_set_epi8( + 7+16, 6+16, // 3 + 15+16, 14+16, // 7 + 13+16, 12+16, // 6 + 5+16, 4+16, // 2 + 3+16, 2+16, // 1 + 11+16, 10+16, // 5 + 9+16, 8+16, // 4 + 1+16, 0+16, // 0 + + 7, 6, // 3 + 15, 14, // 7 + 13, 12, // 6 + 5, 4, // 2 + 3, 2, // 1 + 11, 10, // 5 + 9, 8, // 4 + 1, 0 // 0 + ); + + alphaPtr += long_cb-1; + + /* Define shuffle for branch costs */ + __m256i shuf_g[4]; + shuf_g[3] = _mm256_set_epi8(3+16,2+16,1+16,0+16,1+16,0+16,3+16,2+16,3+16,2+16,1+16,0+16,1+16,0+16,3+16,2+16, + 3,2,1,0,1,0,3,2,3,2,1,0,1,0,3,2); + shuf_g[2] = _mm256_set_epi8(7+16,6+16,5+16,4+16,5+16,4+16,7+16,6+16,7+16,6+16,5+16,4+16,5+16,4+16,7+16,6+16, + 7,6,5,4,5,4,7,6,7,6,5,4,5,4,7,6); + shuf_g[1] = _mm256_set_epi8(11+16,10+16,9+16,8+16,9+16,8+16,11+16,10+16,11+16,10+16,9+16,8+16,9+16,8+16,11+16,10+16, + 11,10,9,8,9,8,11,10,11,10,9,8,9,8,11,10); + shuf_g[0] = _mm256_set_epi8(15+16,14+16,13+16,12+16,13+16,12+16,15+16,14+16,15+16,14+16,13+16,12+16,13+16,12+16,15+16,14+16, + 15,14,13,12,13,12,15,14,15,14,13,12,13,12,15,14); + + /* Define shuffle for beta normalization */ + __m256i shuf_norm = _mm256_set_epi8(17,16,17,16,17,16,17,16,17,16,17,16,17,16,17,16,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0); + + __m256i gv; + int16_t *b = &s->branch[2*NCB*long_cb-16]; + __m256i *gPtr = (__m256i*) b; + + /* This defines a beta computation step: + * Adds and substracts the branch metrics to the previous beta step, + * shuffles the states according to the trellis path and selects maximum state + */ +#define BETA_STEP(g) bp = _mm256_add_epi16(beta_k, g);\ + bn = _mm256_sub_epi16(beta_k, g);\ + bp = _mm256_shuffle_epi8(bp, shuf_bp);\ + bn = _mm256_shuffle_epi8(bn, shuf_bn);\ + beta_k = _mm256_max_epi16(bp, bn); + + /* Loads the alpha metrics from memory and adds them to the temporal bn and bp + * metrics. Then computes horizontal maximum of both metrics and computes difference + */ +#define BETA_STEP_CNT(c,d) g = _mm256_shuffle_epi8(gv, shuf_g[c]);\ + BETA_STEP(g)\ + alpha_k = _mm256_load_si256(alphaPtr);\ + alphaPtr--;\ + bp = _mm256_add_epi16(bp, alpha_k);\ + bn = _mm256_add_epi16(bn, alpha_k);\ + bn = _mm256_sub_epi16(_mm256_set1_epi16(0x7FFF), bn);\ + bp = _mm256_sub_epi16(_mm256_set1_epi16(0x7FFF), bp);\ + output[0][k-d] = hMax0(bn) - hMax0(bp);\ + output[1][k-d] = hMax1(bn) - hMax1(bp); + + /* The tail does not require to load alpha or produce outputs. Only update + * beta metrics accordingly */ + for (k=end-1; k>=long_cb; k--) { + int16_t g0_1 = s->branch[2*NCB*k]; + int16_t g1_1 = s->branch[2*NCB*k+1]; + int16_t g0_2 = s->branch[2*NCB*k+6]; + int16_t g1_2 = s->branch[2*NCB*k+6+1]; + g = _mm256_set_epi16(g1_2, g0_2, g0_2, g1_2, g1_2, g0_2, g0_2, g1_2, g1_1, g0_1, g0_1, g1_1, g1_1, g0_1, g0_1, g1_1); + BETA_STEP(g); + } + + /* We inline 2 trelis steps for each normalization */ + __m256i norm; + for (; k >= 0; k-=8) { + gv = _mm256_load_si256(gPtr); + gPtr--; + BETA_STEP_CNT(0,0); + BETA_STEP_CNT(1,1); + BETA_STEP_CNT(2,2); + BETA_STEP_CNT(3,3); + norm = _mm256_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm256_sub_epi16(beta_k, norm); + gv = _mm256_load_si256(gPtr); + gPtr--; + BETA_STEP_CNT(0,4); + BETA_STEP_CNT(1,5); + BETA_STEP_CNT(2,6); + BETA_STEP_CNT(3,7); + norm = _mm256_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm256_sub_epi16(beta_k, norm); + } +} + +/* Computes alpha metrics */ +void map_avx_alpha(map_gen_t * s, uint32_t long_cb) +{ + uint32_t k; + int16_t *alpha1 = s->alpha; + int16_t *alpha2 = &s->alpha[8]; + uint32_t i; + + alpha1[0] = 0; + alpha2[0] = 0; + for (i = 1; i < 8; i++) { + alpha1[i] = -INF; + alpha2[i] = -INF; + } + + /* Define the shuffle constant for the positive alpha */ + __m256i shuf_ap = _mm256_set_epi8( + + // 1st CB + 31, 30, // 7 + 25, 24, // 4 + 23, 22, // 3 + 17, 16, // 0 + 29, 28, // 6 + 27, 26, // 5 + 21, 20, // 2 + 19, 18, // 1 + + // 2nd CB + 15, 14, // 7 + 9, 8, // 4 + 7, 6, // 3 + 1, 0, // 0 + 13, 12, // 6 + 11, 10, // 5 + 5, 4, // 2 + 3, 2 // 1 + ); + + /* Define the shuffle constant for the negative alpha */ + __m256i shuf_an = _mm256_set_epi8( + + // 1nd CB + 29, 28, // 6 + 27, 26, // 5 + 21, 20, // 2 + 19, 18, // 1 + 31, 30, // 7 + 25, 24, // 4 + 23, 22, // 3 + 17, 16, // 0 + + // 2nd CB + 13, 12, // 6 + 11, 10, // 5 + 5, 4, // 2 + 3, 2, // 1 + 15, 14, // 7 + 9, 8, // 4 + 7, 6, // 3 + 1, 0 // 0 + ); + + /* Define shuffle for branch costs */ + __m256i shuf_g[4]; + shuf_g[0] = _mm256_set_epi8(3+16,2+16,3+16,2+16,1+16,0+16,1+16,0+16,1+16,0+16,1+16,0+16,3+16,2+16,3+16,2+16, + 3,2,3,2,1,0,1,0,1,0,1,0,3,2,3,2); + shuf_g[1] = _mm256_set_epi8(7+16,6+16,7+16,6+16,5+16,4+16,5+16,4+16,5+16,4+16,5+16,4+16,7+16,6+16,7+16,6+16, + 7,6,7,6,5,4,5,4,5,4,5,4,7,6,7,6); + shuf_g[2] = _mm256_set_epi8(11+16,10+16,11+16,10+16,9+16,8+16,9+16,8+16,9+16,8+16,9+16,8+16,11+16,10+16,11+16,10+16, + 11,10,11,10,9,8,9,8,9,8,9,8,11,10,11,10); + shuf_g[3] = _mm256_set_epi8(15+16,14+16,15+16,14+16,13+16,12+16,13+16,12+16,13+16,12+16,13+16,12+16,15+16,14+16,15+16,14+16, + 15,14,15,14,13,12,13,12,13,12,13,12,15,14,15,14); + + __m256i shuf_norm = _mm256_set_epi8(17,16,17,16,17,16,17,16,17,16,17,16,17,16,17,16,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0); + + __m256i* alphaPtr = (__m256i*) s->alpha; + alphaPtr++; + + __m256i gv; + __m256i *gPtr = (__m256i*) s->branch; + __m256i g, ap, an; + + __m256i alpha_k = _mm256_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0, -INF, -INF, -INF, -INF, -INF, -INF, -INF, 0); + + /* This defines a alpha computation step: + * Adds and substracts the branch metrics to the previous alpha step, + * shuffles the states according to the trellis path and selects maximum state + */ +#define ALPHA_STEP(c) g = _mm256_shuffle_epi8(gv, shuf_g[c]); \ + ap = _mm256_add_epi16(alpha_k, g);\ + an = _mm256_sub_epi16(alpha_k, g);\ + ap = _mm256_shuffle_epi8(ap, shuf_ap);\ + an = _mm256_shuffle_epi8(an, shuf_an);\ + alpha_k = _mm256_max_epi16(ap, an);\ + _mm256_store_si256(alphaPtr, alpha_k);\ + alphaPtr++;\ + + + /* In this loop, we compute 8 steps and normalize twice for each branch metrics memory load */ + __m256i norm; + for (k = 0; k < long_cb/8; k++) { + gv = _mm256_load_si256(gPtr); + + gPtr++; + ALPHA_STEP(0); + ALPHA_STEP(1); + ALPHA_STEP(2); + ALPHA_STEP(3); + norm = _mm256_shuffle_epi8(alpha_k, shuf_norm); + alpha_k = _mm256_sub_epi16(alpha_k, norm); + gv = _mm256_load_si256(gPtr); + gPtr++; + ALPHA_STEP(0); + ALPHA_STEP(1); + ALPHA_STEP(2); + ALPHA_STEP(3); + norm = _mm256_shuffle_epi8(alpha_k, shuf_norm); + alpha_k = _mm256_sub_epi16(alpha_k, norm); + } +} + +void map_sse_gamma_single(int16_t *output, int16_t *input, int16_t *app, int16_t *parity) +{ + __m128i res00, res10, res01, res11, res0, res1; + __m128i in, ap, pa, g1, g0; + + __m128i *inPtr = (__m128i*) input; + __m128i *appPtr = (__m128i*) app; + __m128i *paPtr = (__m128i*) parity; + __m128i *resPtr = (__m128i*) output; + + __m128i res00_mask = _mm_set_epi8(0xff,0xff,7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0); + __m128i res10_mask = _mm_set_epi8(0xff,0xff,15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8); + __m128i res01_mask = _mm_set_epi8(7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0,0xff,0xff); + __m128i res11_mask = _mm_set_epi8(15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8,0xff,0xff); + + in = _mm_load_si128(inPtr); + inPtr++; + pa = _mm_load_si128(paPtr); + paPtr++; + + if (appPtr) { + ap = _mm_load_si128(appPtr); + appPtr++; + in = _mm_add_epi16(ap, in); + } + + g1 = _mm_add_epi16(in, pa); + g0 = _mm_sub_epi16(in, pa); + + g1 = _mm_srai_epi16(g1, 1); + g0 = _mm_srai_epi16(g0, 1); + + res00 = _mm_shuffle_epi8(g0, res00_mask); + res10 = _mm_shuffle_epi8(g0, res10_mask); + res01 = _mm_shuffle_epi8(g1, res01_mask); + res11 = _mm_shuffle_epi8(g1, res11_mask); + + res0 = _mm_or_si128(res00, res01); + res1 = _mm_or_si128(res10, res11); + + _mm_store_si128(resPtr, res0); + resPtr++; + _mm_store_si128(resPtr, res1); + resPtr++; +} + + +/* Compute branch metrics (gamma) */ +void map_avx_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t long_cb) +{ + __m128i res10, res20, res11, res21, res1, res2; + __m256i in, ap, pa, g1, g0; + + __m256i *inPtr = (__m256i*) input; + __m256i *appPtr = (__m256i*) app; + __m256i *paPtr = (__m256i*) parity; + __m128i *resPtr = (__m128i*) h->branch; + + if (cbidx) { + resPtr++; + } + + __m128i res10_mask = _mm_set_epi8(0xff,0xff,7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0); + __m128i res11_mask = _mm_set_epi8(7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0,0xff,0xff); + + __m128i res20_mask = _mm_set_epi8(0xff,0xff,15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8); + __m128i res21_mask = _mm_set_epi8(15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8,0xff,0xff); + + for (int i=0;ibranch[2*i*NCB+cbidx*6] = (input[i] - parity[i])/2; + h->branch[2*i*NCB+cbidx*6+1] = (input[i] + parity[i])/2; + } +} + + +#endif + + diff --git a/lib/src/phy/fec/turbodecoder_gen.c b/lib/src/phy/fec/turbodecoder_gen.c new file mode 100644 index 0000000..649c336 --- /dev/null +++ b/lib/src/phy/fec/turbodecoder_gen.c @@ -0,0 +1,400 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_gen.h" +#include "srslte/phy/utils/vector.h" + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TAIL 3 +#define TOTALTAIL 12 + +#define INF 9e4 +#define ZERO 9e-4 + +/************************************************ + * + * MAP_GEN is the MAX-LOG-MAP generic implementation of the + * Decoder + * + ************************************************/ +static void map_gen_beta(srslte_map_gen_vl_t * s, float * input, float * parity, + uint32_t long_cb) +{ + float m_b[8], new[8], old[8]; + float x, y, xy; + int k; + uint32_t end = long_cb + SRSLTE_TCOD_RATE; + float *beta = s->beta; + uint32_t i; + + for (i = 0; i < 8; i++) { + old[i] = beta[8 * (end) + i]; + } + + for (k = end - 1; k >= 0; k--) { + x = input[k]; + y = parity[k]; + + xy = x + y; + + m_b[0] = old[4] + xy; + m_b[1] = old[4]; + m_b[2] = old[5] + y; + m_b[3] = old[5] + x; + m_b[4] = old[6] + x; + m_b[5] = old[6] + y; + m_b[6] = old[7]; + m_b[7] = old[7] + xy; + + new[0] = old[0]; + new[1] = old[0] + xy; + new[2] = old[1] + x; + new[3] = old[1] + y; + new[4] = old[2] + y; + new[5] = old[2] + x; + new[6] = old[3] + xy; + new[7] = old[3]; + + for (i = 0; i < 8; i++) { + if (m_b[i] > new[i]) + new[i] = m_b[i]; + old[i] = new[i]; + beta[8 * k + i] = old[i]; + } + } +} + +static void map_gen_alpha(srslte_map_gen_vl_t * s, float * input, float * parity, float * output, + uint32_t long_cb) +{ + float m_b[8], new[8], old[8], max1[8], max0[8]; + float m1, m0; + float x, y, xy; + float out; + uint32_t k; + uint32_t end = long_cb; + float *beta = s->beta; + uint32_t i; + + old[0] = 0; + for (i = 1; i < 8; i++) { + old[i] = -INF; + } + + for (k = 1; k < end + 1; k++) { + x = input[k - 1]; + y = parity[k - 1]; + + xy = x + y; + + m_b[0] = old[0]; + m_b[1] = old[3] + y; + m_b[2] = old[4] + y; + m_b[3] = old[7]; + m_b[4] = old[1]; + m_b[5] = old[2] + y; + m_b[6] = old[5] + y; + m_b[7] = old[6]; + + new[0] = old[1] + xy; + new[1] = old[2] + x; + new[2] = old[5] + x; + new[3] = old[6] + xy; + new[4] = old[0] + xy; + new[5] = old[3] + x; + new[6] = old[4] + x; + new[7] = old[7] + xy; + + for (i = 0; i < 8; i++) { + max0[i] = m_b[i] + beta[8 * k + i]; + max1[i] = new[i] + beta[8 * k + i]; + } + + m1 = max1[0]; + m0 = max0[0]; + + for (i = 1; i < 8; i++) { + if (max1[i] > m1) + m1 = max1[i]; + if (max0[i] > m0) + m0 = max0[i]; + } + + for (i = 0; i < 8; i++) { + if (m_b[i] > new[i]) + new[i] = m_b[i]; + old[i] = new[i]; + } + + out = m1 - m0; + output[k - 1] = out; + } +} + +static int map_gen_init(srslte_map_gen_vl_t * h, int max_long_cb) +{ + bzero(h, sizeof(srslte_map_gen_vl_t)); + h->beta = srslte_vec_malloc(sizeof(float) * (max_long_cb + SRSLTE_TCOD_TOTALTAIL + 1) * NUMSTATES); + if (!h->beta) { + perror("srslte_vec_malloc"); + return -1; + } + h->max_long_cb = max_long_cb; + return 0; +} + +static void map_gen_free(srslte_map_gen_vl_t * h) +{ + if (h->beta) { + free(h->beta); + } + bzero(h, sizeof(srslte_map_gen_vl_t)); +} + +static void map_gen_dec(srslte_map_gen_vl_t * h, float * input, float * parity, float * output, + uint32_t long_cb) +{ + uint32_t k; + + h->beta[(long_cb + TAIL) * NUMSTATES] = 0; + for (k = 1; k < NUMSTATES; k++) + h->beta[(long_cb + TAIL) * NUMSTATES + k] = -INF; + + map_gen_beta(h, input, parity, long_cb); + map_gen_alpha(h, input, parity, output, long_cb); +} + +/************************************************ + * + * TURBO DECODER INTERFACE + * + ************************************************/ +int srslte_tdec_gen_init(srslte_tdec_gen_t * h, uint32_t max_long_cb) +{ + int ret = -1; + bzero(h, sizeof(srslte_tdec_gen_t)); + uint32_t len = max_long_cb + SRSLTE_TCOD_TOTALTAIL; + + h->max_long_cb = max_long_cb; + + h->llr1 = srslte_vec_malloc(sizeof(float) * len); + if (!h->llr1) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->llr2 = srslte_vec_malloc(sizeof(float) * len); + if (!h->llr2) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->w = srslte_vec_malloc(sizeof(float) * len); + if (!h->w) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->syst = srslte_vec_malloc(sizeof(float) * len); + if (!h->syst) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->parity = srslte_vec_malloc(sizeof(float) * len); + if (!h->parity) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + + if (map_gen_init(&h->dec, h->max_long_cb)) { + goto clean_and_exit; + } + + for (int i=0;iinterleaver[i], srslte_cbsegm_cbsize(i)) < 0) { + goto clean_and_exit; + } + srslte_tc_interl_LTE_gen(&h->interleaver[i], srslte_cbsegm_cbsize(i)); + } + h->current_cbidx = -1; + ret = 0; +clean_and_exit:if (ret == -1) { + srslte_tdec_gen_free(h); + } + return ret; +} + +void srslte_tdec_gen_free(srslte_tdec_gen_t * h) +{ + if (h->llr1) { + free(h->llr1); + } + if (h->llr2) { + free(h->llr2); + } + if (h->w) { + free(h->w); + } + if (h->syst) { + free(h->syst); + } + if (h->parity) { + free(h->parity); + } + + map_gen_free(&h->dec); + + for (int i=0;iinterleaver[i]); + } + + bzero(h, sizeof(srslte_tdec_gen_t)); +} + +void srslte_tdec_gen_iteration(srslte_tdec_gen_t * h, float * input, uint32_t long_cb) +{ + uint32_t i; + + if (h->current_cbidx >= 0) { + + uint16_t *inter = h->interleaver[h->current_cbidx].forward; + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + + // Prepare systematic and parity bits for MAP DEC #1 + for (i = 0; i < long_cb; i++) { + h->syst[i] = input[SRSLTE_TCOD_RATE * i] + h->w[i]; + h->parity[i] = input[SRSLTE_TCOD_RATE * i + 1]; + } + for (i = long_cb; i < long_cb + SRSLTE_TCOD_RATE; i++) { + h->syst[i] = input[SRSLTE_TCOD_RATE * long_cb + NINPUTS * (i - long_cb)]; + h->parity[i] = input[SRSLTE_TCOD_RATE * long_cb + NINPUTS * (i - long_cb) + 1]; + } + + // Run MAP DEC #1 + map_gen_dec(&h->dec, h->syst, h->parity, h->llr1, long_cb); + + // Prepare systematic and parity bits for MAP DEC #1 + for (i = 0; i < long_cb; i++) { + h->syst[i] = h->llr1[inter[i]] + - h->w[inter[i]]; + h->parity[i] = input[SRSLTE_TCOD_RATE * i + 2]; + } + for (i = long_cb; i < long_cb + SRSLTE_TCOD_RATE; i++) { + h->syst[i] = + input[SRSLTE_TCOD_RATE * long_cb + NINPUTS * SRSLTE_TCOD_RATE + NINPUTS * (i - long_cb)]; + h->parity[i] = input[SRSLTE_TCOD_RATE * long_cb + NINPUTS * SRSLTE_TCOD_RATE + + NINPUTS * (i - long_cb) + 1]; + } + + // Run MAP DEC #2 + map_gen_dec(&h->dec, h->syst, h->parity, h->llr2, long_cb); + + //printf("llr2="); + //srslte_vec_fprint_f(stdout, h->llr2, long_cb); + + + // Update a-priori LLR from the last iteration + for (i = 0; i < long_cb; i++) { + h->w[i] += h->llr2[deinter[i]] - h->llr1[i]; + } + } else { + fprintf(stderr, "Error CB index not set (call srslte_tdec_gen_reset() first\n"); + } + + // Increase number of iterations + h->n_iter++; +} + +int srslte_tdec_gen_reset(srslte_tdec_gen_t * h, uint32_t long_cb) +{ + if (long_cb > h->max_long_cb) { + fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + memset(h->w, 0, sizeof(float) * long_cb); + h->current_cbidx = srslte_cbsegm_cbindex(long_cb); + h->current_cb_len = long_cb; + if (h->current_cbidx < 0) { + fprintf(stderr, "Invalid CB length %d\n", long_cb); + return -1; + } + return 0; +} + +void srslte_tdec_gen_decision(srslte_tdec_gen_t * h, uint8_t *output, uint32_t long_cb) +{ + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + uint32_t i; + for (i = 0; i < long_cb; i++) { + output[i] = (h->llr2[deinter[i]] > 0) ? 1 : 0; + } +} + +void srslte_tdec_gen_decision_byte(srslte_tdec_gen_t * h, uint8_t *output, uint32_t long_cb) +{ + uint32_t i; + uint8_t mask[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + + // long_cb is always byte aligned + for (i = 0; i < long_cb/8; i++) { + uint8_t out0 = h->llr2[deinter[8*i+0]]>0?mask[0]:0; + uint8_t out1 = h->llr2[deinter[8*i+1]]>0?mask[1]:0; + uint8_t out2 = h->llr2[deinter[8*i+2]]>0?mask[2]:0; + uint8_t out3 = h->llr2[deinter[8*i+3]]>0?mask[3]:0; + uint8_t out4 = h->llr2[deinter[8*i+4]]>0?mask[4]:0; + uint8_t out5 = h->llr2[deinter[8*i+5]]>0?mask[5]:0; + uint8_t out6 = h->llr2[deinter[8*i+6]]>0?mask[6]:0; + uint8_t out7 = h->llr2[deinter[8*i+7]]>0?mask[7]:0; + + output[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7; + } +} + +int srslte_tdec_gen_run_all(srslte_tdec_gen_t * h, float * input, uint8_t *output, + uint32_t nof_iterations, uint32_t long_cb) +{ + uint32_t iter = 0; + + if (srslte_tdec_gen_reset(h, long_cb)) { + return SRSLTE_ERROR; + } + + do { + srslte_tdec_gen_iteration(h, input, long_cb); + iter++; + } while (iter < nof_iterations); + + srslte_tdec_gen_decision_byte(h, output, long_cb); + + return SRSLTE_SUCCESS; +} diff --git a/lib/src/phy/fec/turbodecoder_simd.c b/lib/src/phy/fec/turbodecoder_simd.c new file mode 100644 index 0000000..f9bfbbd --- /dev/null +++ b/lib/src/phy/fec/turbodecoder_simd.c @@ -0,0 +1,542 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_simd.h" +#include "srslte/phy/utils/vector.h" + +#include + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TAIL 3 +#define TOTALTAIL 12 + +#define INF 10000 +#define ZERO 0 + + +#ifdef LV_HAVE_SSE +#include + +// Define SSE/AVX implementations +void map_sse_beta(map_gen_t * s, int16_t * output, uint32_t long_cb); +void map_sse_alpha(map_gen_t * s, uint32_t long_cb); +void map_sse_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t long_cb); + +#ifdef LV_HAVE_AVX2 +void map_avx_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb); +void map_avx_alpha(map_gen_t * s, uint32_t long_cb); +void map_avx_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t long_cb); +#endif + + +void map_simd_beta(map_gen_t * s, int16_t * output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb) +{ + if (nof_cb == 1) { + map_sse_beta(s, output[0], long_cb); + } +#ifdef LV_HAVE_AVX2 + else if (nof_cb == 2) { + map_avx_beta(s, output, long_cb); + } +#endif +} + +void map_simd_alpha(map_gen_t * s, uint32_t nof_cb, uint32_t long_cb) +{ + if (nof_cb == 1) { + map_sse_alpha(s, long_cb); + } +#ifdef LV_HAVE_AVX2 + else if (nof_cb == 2) { + map_avx_alpha(s, long_cb); + } +#endif +} +void map_simd_gamma(map_gen_t * s, int16_t *input, int16_t *app, int16_t *parity, uint32_t cbidx, uint32_t nof_cb, uint32_t long_cb) +{ + if (nof_cb == 1) { + map_sse_gamma(s, input, app, parity, long_cb); + } +#ifdef LV_HAVE_AVX2 + else if (nof_cb == 2) { + map_avx_gamma(s, input, app, parity, cbidx, long_cb); + } +#endif +} + +/* Inititalizes constituent decoder object */ +int map_simd_init(map_gen_t * h, uint32_t max_par_cb, uint32_t max_long_cb) +{ + bzero(h, sizeof(map_gen_t)); + + h->max_par_cb = max_par_cb; + h->max_long_cb = max_long_cb; + + h->alpha = srslte_vec_malloc(sizeof(int16_t) * (max_long_cb + SRSLTE_TCOD_TOTALTAIL + 1) * NUMSTATES * h->max_par_cb); + if (!h->alpha) { + perror("srslte_vec_malloc"); + return -1; + } + h->branch = srslte_vec_malloc(sizeof(int16_t) * (max_long_cb + SRSLTE_TCOD_TOTALTAIL + 1) * NUMSTATES * h->max_par_cb); + if (!h->branch) { + perror("srslte_vec_malloc"); + return -1; + } + return 0; +} + +void map_simd_free(map_gen_t * h) +{ + if (h->alpha) { + free(h->alpha); + } + if (h->branch) { + free(h->branch); + } + bzero(h, sizeof(map_gen_t)); +} + +/* Runs one instance of a decoder */ +void map_simd_dec(map_gen_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], int16_t *app[SRSLTE_TDEC_MAX_NPAR], int16_t * parity[SRSLTE_TDEC_MAX_NPAR], + int16_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t cb_mask, uint32_t long_cb) +{ + + uint32_t nof_cb = 1; + int16_t *outptr[SRSLTE_TDEC_MAX_NPAR] = { NULL, NULL }; + + // Compute branch metrics + switch(cb_mask) { + case 1: + nof_cb = 1; + outptr[0] = output[0]; + map_simd_gamma(h, input[0], app?app[0]:NULL, parity[0], 0, 1, long_cb); + break; + case 2: + nof_cb = 1; + outptr[0] = output[1]; + map_simd_gamma(h, input[1], app?app[1]:NULL, parity[1], 0, 1, long_cb); + break; + case 3: + nof_cb = 2; + for (int i=0;i<2;i++) { + outptr[i] = output[i]; + map_simd_gamma(h, input[i], app?app[i]:NULL, parity[i], i, 2, long_cb); + } + break; + } + + // Forward recursion + map_simd_alpha(h, nof_cb, long_cb); + + // Backwards recursion + LLR computation + map_simd_beta(h, outptr, nof_cb, long_cb); +} + +/* Initializes the turbo decoder object */ +int srslte_tdec_simd_init(srslte_tdec_simd_t * h, uint32_t max_par_cb, uint32_t max_long_cb) +{ + int ret = -1; + bzero(h, sizeof(srslte_tdec_simd_t)); + uint32_t len = max_long_cb + SRSLTE_TCOD_TOTALTAIL; + + h->max_long_cb = max_long_cb; + h->max_par_cb = max_par_cb; + + for (int i=0;imax_par_cb;i++) { + h->app1[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->app1[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->app2[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->app2[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->ext1[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->ext1[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->ext2[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->ext2[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->syst[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->syst[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->parity0[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->parity0[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->parity1[i] = srslte_vec_malloc(sizeof(int16_t) * len); + if (!h->parity1[i]) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + + } + + if (map_simd_init(&h->dec, h->max_par_cb, h->max_long_cb)) { + goto clean_and_exit; + } + + for (int i=0;iinterleaver[i], srslte_cbsegm_cbsize(i)) < 0) { + goto clean_and_exit; + } + srslte_tc_interl_LTE_gen(&h->interleaver[i], srslte_cbsegm_cbsize(i)); + } + h->current_cbidx = -1; + h->cb_mask = 0; + ret = 0; +clean_and_exit:if (ret == -1) { + srslte_tdec_simd_free(h); + } + return ret; +} + +void srslte_tdec_simd_free(srslte_tdec_simd_t * h) +{ + for (int i=0;imax_par_cb;i++) { + if (h->app1[i]) { + free(h->app1[i]); + } + if (h->app2[i]) { + free(h->app2[i]); + } + if (h->ext1[i]) { + free(h->ext1[i]); + } + if (h->ext2[i]) { + free(h->ext2[i]); + } + if (h->syst[i]) { + free(h->syst[i]); + } + if (h->parity0[i]) { + free(h->parity0[i]); + } + if (h->parity1[i]) { + free(h->parity1[i]); + } + } + + map_simd_free(&h->dec); + + for (int i=0;iinterleaver[i]); + } + + bzero(h, sizeof(srslte_tdec_simd_t)); +} + +/* Deinterleaves the 3 streams from the input (systematic and 2 parity bits) into + * 3 buffers ready to be used by compute_gamma() + */ +void deinterleave_input_simd(srslte_tdec_simd_t *h, int16_t *input, uint32_t cbidx, uint32_t long_cb) { + uint32_t i; + + __m128i *inputPtr = (__m128i*) input; + __m128i in0, in1, in2; + __m128i s0, s1, s2, s; + __m128i p00, p01, p02, p0; + __m128i p10, p11, p12, p1; + + __m128i *sysPtr = (__m128i*) h->syst[cbidx]; + __m128i *pa0Ptr = (__m128i*) h->parity0[cbidx]; + __m128i *pa1Ptr = (__m128i*) h->parity1[cbidx]; + + // pick bits 0, 3, 6 from 1st word + __m128i s0_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,13,12,7,6,1,0); + // pick bits 1, 4, 7 from 2st word + __m128i s1_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,15,14,9,8,3,2,0xff,0xff,0xff,0xff,0xff,0xff); + // pick bits 2, 5 from 3rd word + __m128i s2_mask = _mm_set_epi8(11,10,5,4,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff); + + // pick bits 1, 4, 7 from 1st word + __m128i p00_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,15,14,9,8,3,2); + // pick bits 2, 5, from 2st word + __m128i p01_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,11,10,5,4,0xff,0xff,0xff,0xff,0xff,0xff); + // pick bits 0, 3, 6 from 3rd word + __m128i p02_mask = _mm_set_epi8(13,12,7,6,1,0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff); + + // pick bits 2, 5 from 1st word + __m128i p10_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,11,10,5,4); + // pick bits 0, 3, 6, from 2st word + __m128i p11_mask = _mm_set_epi8(0xff,0xff,0xff,0xff,0xff,0xff,13,12,7,6,1,0,0xff,0xff,0xff,0xff); + // pick bits 1, 4, 7 from 3rd word + __m128i p12_mask = _mm_set_epi8(15,14,9,8,3,2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff); + + // Split systematic and parity bits + for (i = 0; i < long_cb/8; i++) { + + in0 = _mm_load_si128(inputPtr); inputPtr++; + in1 = _mm_load_si128(inputPtr); inputPtr++; + in2 = _mm_load_si128(inputPtr); inputPtr++; + + /* Deinterleave Systematic bits */ + s0 = _mm_shuffle_epi8(in0, s0_mask); + s1 = _mm_shuffle_epi8(in1, s1_mask); + s2 = _mm_shuffle_epi8(in2, s2_mask); + s = _mm_or_si128(s0, s1); + s = _mm_or_si128(s, s2); + + _mm_store_si128(sysPtr, s); + sysPtr++; + + /* Deinterleave parity 0 bits */ + p00 = _mm_shuffle_epi8(in0, p00_mask); + p01 = _mm_shuffle_epi8(in1, p01_mask); + p02 = _mm_shuffle_epi8(in2, p02_mask); + p0 = _mm_or_si128(p00, p01); + p0 = _mm_or_si128(p0, p02); + + _mm_store_si128(pa0Ptr, p0); + pa0Ptr++; + + /* Deinterleave parity 1 bits */ + p10 = _mm_shuffle_epi8(in0, p10_mask); + p11 = _mm_shuffle_epi8(in1, p11_mask); + p12 = _mm_shuffle_epi8(in2, p12_mask); + p1 = _mm_or_si128(p10, p11); + p1 = _mm_or_si128(p1, p12); + + _mm_store_si128(pa1Ptr, p1); + pa1Ptr++; + + } + + for (i = 0; i < 3; i++) { + h->syst[cbidx][i+long_cb] = input[3*long_cb + 2*i]; + h->parity0[cbidx][i+long_cb] = input[3*long_cb + 2*i + 1]; + } + for (i = 0; i < 3; i++) { + h->app2[cbidx][i+long_cb] = input[3*long_cb + 6 + 2*i]; + h->parity1[cbidx][i+long_cb] = input[3*long_cb + 6 + 2*i + 1]; + } + +} + +/* Runs 1 turbo decoder iteration */ +void srslte_tdec_simd_iteration(srslte_tdec_simd_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) +{ + + int16_t *tmp_app[SRSLTE_TDEC_MAX_NPAR]; + + if (h->current_cbidx >= 0) { + uint16_t *inter = h->interleaver[h->current_cbidx].forward; + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + +#ifndef LV_HAVE_AVX2 + input[1] = NULL; +#endif + + h->cb_mask = (input[0]?1:0) | (input[1]?2:0); + + for (int i=0;imax_par_cb;i++) { + if (h->n_iter[i] == 0 && input[i]) { + //printf("deinterleaveing %d\n",i); + deinterleave_input_simd(h, input[i], i, long_cb); + } + } + + // Add apriori information to decoder 1 + for (int i=0;imax_par_cb;i++) { + if (h->n_iter[i] > 0 && input[i]) { + srslte_vec_sub_sss(h->app1[i], h->ext1[i], h->app1[i], long_cb); + } + } + + // Run MAP DEC #1 + for (int i=0;imax_par_cb;i++) { + if (input[i]) { + tmp_app[i] = h->n_iter[i]?h->app1[i]:NULL; + } else { + tmp_app[i] = NULL; + } + } + map_simd_dec(&h->dec, h->syst, tmp_app, h->parity0, h->ext1, h->cb_mask, long_cb); + + // Convert aposteriori information into extrinsic information + for (int i=0;imax_par_cb;i++) { + if (h->n_iter[i] > 0 && input[i]) { + srslte_vec_sub_sss(h->ext1[i], h->app1[i], h->ext1[i], long_cb); + } + } + + // Interleave extrinsic output of DEC1 to form apriori info for decoder 2 + for (int i=0;imax_par_cb;i++) { + if (input[i]) { + srslte_vec_lut_sss(h->ext1[i], deinter, h->app2[i], long_cb); + } + } + + // Run MAP DEC #2. 2nd decoder uses apriori information as systematic bits + map_simd_dec(&h->dec, h->app2, NULL, h->parity1, h->ext2, h->cb_mask, long_cb); + + // Deinterleaved extrinsic bits become apriori info for decoder 1 + for (int i=0;imax_par_cb;i++) { + if (input[i]) { + srslte_vec_lut_sss(h->ext2[i], inter, h->app1[i], long_cb); + } + } + + for (int i=0;imax_par_cb;i++) { + if (input[i]) { + h->n_iter[i]++; + } + } + } else { + fprintf(stderr, "Error CB index not set (call srslte_tdec_simd_reset() first\n"); + } +} + +/* Resets the decoder and sets the codeblock length */ +int srslte_tdec_simd_reset(srslte_tdec_simd_t * h, uint32_t long_cb) +{ + if (long_cb > h->max_long_cb) { + fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + for (int i=0;imax_par_cb;i++) { + h->n_iter[i] = 0; + } + h->cb_mask = 0; + h->current_cbidx = srslte_cbsegm_cbindex(long_cb); + if (h->current_cbidx < 0) { + fprintf(stderr, "Invalid CB length %d\n", long_cb); + return -1; + } + return 0; +} + +int srslte_tdec_simd_reset_cb(srslte_tdec_simd_t * h, uint32_t cb_idx) +{ + h->n_iter[cb_idx] = 0; + return 0; +} + +int srslte_tdec_simd_get_nof_iterations_cb(srslte_tdec_simd_t * h, uint32_t cb_idx) +{ + return h->n_iter[cb_idx]; +} + +void tdec_simd_decision(srslte_tdec_simd_t * h, uint8_t *output, uint32_t cbidx, uint32_t long_cb) +{ + __m128i zero = _mm_set1_epi16(0); + __m128i lsb_mask = _mm_set1_epi16(1); + + __m128i *appPtr = (__m128i*) h->app1[cbidx]; + __m128i *outPtr = (__m128i*) output; + __m128i ap, out, out0, out1; + + for (uint32_t i = 0; i < long_cb/16; i++) { + ap = _mm_load_si128(appPtr); appPtr++; + out0 = _mm_and_si128(_mm_cmpgt_epi16(ap, zero), lsb_mask); + ap = _mm_load_si128(appPtr); appPtr++; + out1 = _mm_and_si128(_mm_cmpgt_epi16(ap, zero), lsb_mask); + + out = _mm_packs_epi16(out0, out1); + _mm_store_si128(outPtr, out); + outPtr++; + } + if (long_cb%16) { + for (int i=0;i<8;i++) { + output[long_cb-8+i] = h->app1[cbidx][long_cb-8+i]>0?1:0; + } + } +} + +void srslte_tdec_simd_decision(srslte_tdec_simd_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) +{ + for (int i=0;imax_par_cb;i++) { + tdec_simd_decision(h, output[i], i, long_cb); + } +} + +void srslte_tdec_simd_decision_byte_cb(srslte_tdec_simd_t * h, uint8_t *output, uint32_t cbidx, uint32_t long_cb) +{ + uint8_t mask[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; + + // long_cb is always byte aligned + for (uint32_t i = 0; i < long_cb/8; i++) { + uint8_t out0 = h->app1[cbidx][8*i+0]>0?mask[0]:0; + uint8_t out1 = h->app1[cbidx][8*i+1]>0?mask[1]:0; + uint8_t out2 = h->app1[cbidx][8*i+2]>0?mask[2]:0; + uint8_t out3 = h->app1[cbidx][8*i+3]>0?mask[3]:0; + uint8_t out4 = h->app1[cbidx][8*i+4]>0?mask[4]:0; + uint8_t out5 = h->app1[cbidx][8*i+5]>0?mask[5]:0; + uint8_t out6 = h->app1[cbidx][8*i+6]>0?mask[6]:0; + uint8_t out7 = h->app1[cbidx][8*i+7]>0?mask[7]:0; + + output[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7; + } +} + +void srslte_tdec_simd_decision_byte(srslte_tdec_simd_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t long_cb) +{ + for (int i=0;imax_par_cb;i++) { + if (output[i]) { + srslte_tdec_simd_decision_byte_cb(h, output[i], i, long_cb); + } + } +} + + +/* Runs nof_iterations iterations and decides the output bits */ +int srslte_tdec_simd_run_all(srslte_tdec_simd_t * h, int16_t * input[SRSLTE_TDEC_MAX_NPAR], uint8_t *output[SRSLTE_TDEC_MAX_NPAR], + uint32_t nof_iterations, uint32_t long_cb) +{ + if (srslte_tdec_simd_reset(h, long_cb)) { + return SRSLTE_ERROR; + } + + do { + srslte_tdec_simd_iteration(h, input, long_cb); + } while (h->n_iter[0] < nof_iterations); + + srslte_tdec_simd_decision_byte(h, output, long_cb); + + return SRSLTE_SUCCESS; +} + +#endif + + diff --git a/lib/src/phy/fec/turbodecoder_simd_inter.c b/lib/src/phy/fec/turbodecoder_simd_inter.c new file mode 100644 index 0000000..3c04e21 --- /dev/null +++ b/lib/src/phy/fec/turbodecoder_simd_inter.c @@ -0,0 +1,299 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_simd_inter.h" +#include "srslte/phy/utils/vector.h" + +#define TOTALTAIL 12 + +#ifdef LV_HAVE_SSE +#include + +void map_see_inter_alpha(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, uint32_t long_cb); +void map_sse_inter_beta(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, int16_t * output, uint32_t long_cb); +void sse_inter_update_w(srslte_tdec_simd_inter_t *h, uint16_t *deinter, uint32_t long_cb); +void sse_inter_extract_syst1(srslte_tdec_simd_inter_t *h, uint16_t *inter, uint32_t long_cb); + + +static void map_sse_inter_dec(srslte_tdec_simd_inter_t * h, int16_t * input, int16_t * parity, int16_t * output, + uint32_t long_cb) +{ + map_see_inter_alpha(h, input, parity, long_cb); + map_sse_inter_beta(h, input, parity, output, long_cb); +} + +/************************************************ + * + * TURBO DECODER INTERFACE + * + ************************************************/ +int srslte_tdec_simd_inter_init(srslte_tdec_simd_inter_t * h, uint32_t max_par_cb, uint32_t max_long_cb) +{ + int ret = -1; + bzero(h, sizeof(srslte_tdec_simd_inter_t)); + uint32_t len = max_long_cb + 12; + + h->max_long_cb = max_long_cb; + h->max_par_cb = max_par_cb; + + h->llr1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->llr1) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->llr2 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->llr2) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->w = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->w) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->syst0 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->syst0) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->syst1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->syst1) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->parity0 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->parity0) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->parity1 = srslte_vec_malloc(sizeof(int16_t) * len * h->max_par_cb); + if (!h->parity1) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + h->alpha = srslte_vec_malloc(sizeof(int16_t) * 8*(len+12) * h->max_par_cb); + if (!h->alpha) { + perror("srslte_vec_malloc"); + goto clean_and_exit; + } + + for (int i=0;iinterleaver[i], srslte_cbsegm_cbsize(i)) < 0) { + goto clean_and_exit; + } + srslte_tc_interl_LTE_gen(&h->interleaver[i], srslte_cbsegm_cbsize(i)); + } + h->current_cbidx = -1; + ret = 0; +clean_and_exit:if (ret == -1) { + srslte_tdec_simd_inter_free(h); + } + return ret; +} + +void srslte_tdec_simd_inter_free(srslte_tdec_simd_inter_t * h) +{ + if (h->llr1) { + free(h->llr1); + } + if (h->llr2) { + free(h->llr2); + } + if (h->w) { + free(h->w); + } + if (h->syst0) { + free(h->syst0); + } + if (h->syst1) { + free(h->syst1); + } + if (h->parity0) { + free(h->parity0); + } + if (h->parity1) { + free(h->parity1); + } + if (h->alpha) { + free(h->alpha); + } + + for (int i=0;iinterleaver[i]); + } + + bzero(h, sizeof(srslte_tdec_simd_inter_t)); +} + + +/* Deinterleave for inter-frame parallelization */ +void extract_input(srslte_tdec_simd_inter_t *h, int16_t *input, uint32_t cbidx, uint32_t long_cb) +{ + for (int i=0;isyst0[h->max_par_cb*i+cbidx] = input[3*i+0]; + h->parity0[h->max_par_cb*i+cbidx] = input[3*i+1]; + h->parity1[h->max_par_cb*i+cbidx] = input[3*i+2]; + } + for (int i = long_cb; i < long_cb + 3; i++) { + h->syst0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb)]; + h->syst1[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb)]; + h->parity0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb) + 1]; + h->parity0[h->max_par_cb*i+cbidx] = input[3*long_cb + 2*(i - long_cb) + 2]; + } +} + +void srslte_tdec_simd_inter_iteration(srslte_tdec_simd_inter_t * h, int16_t *input[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb) +{ + + if (h->current_cbidx >= 0) { + + uint16_t *inter = h->interleaver[h->current_cbidx].forward; + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + + // Prepare systematic and parity bits for MAP DEC #1 + for (int i=0;in_iter[i] == 0) { + extract_input(h, input[i], i, long_cb); + } + srslte_vec_sum_sss(h->syst0, h->w, h->syst0, long_cb*h->max_par_cb); + } + + // Run MAP DEC #1 + map_sse_inter_dec(h, h->syst0, h->parity0, h->llr1, long_cb); + + // Prepare systematic and parity bits for MAP DEC #1 + sse_inter_extract_syst1(h, inter, long_cb); + + // Run MAP DEC #2 + map_sse_inter_dec(h, h->syst1, h->parity1, h->llr2, long_cb); + + // Update a-priori LLR from the last iteration + sse_inter_update_w(h, deinter, long_cb); + + } else { + fprintf(stderr, "Error CB index not set (call srslte_tdec_simd_inter_reset() first\n"); + } +} + +int srslte_tdec_simd_inter_reset_cb(srslte_tdec_simd_inter_t * h, uint32_t cb_idx) +{ + for (int i=0;icurrent_long_cb;i++) { + h->w[h->max_par_cb*i+cb_idx] = 0; + } + return 0; +} + +int srslte_tdec_simd_inter_reset(srslte_tdec_simd_inter_t * h, uint32_t long_cb) +{ + if (long_cb > h->max_long_cb) { + fprintf(stderr, "TDEC was initialized for max_long_cb=%d\n", + h->max_long_cb); + return -1; + } + h->current_long_cb = long_cb; + h->current_cbidx = srslte_cbsegm_cbindex(long_cb); + if (h->current_cbidx < 0) { + fprintf(stderr, "Invalid CB length %d\n", long_cb); + return -1; + } + memset(h->w, 0, sizeof(int16_t) * long_cb * h->max_par_cb); + return 0; +} + +void srslte_tdec_simd_inter_decision_cb(srslte_tdec_simd_inter_t * h, uint8_t *output, uint32_t cb_idx, uint32_t long_cb) +{ + uint16_t *deinter = h->interleaver[h->current_cbidx].reverse; + uint32_t i; + for (i = 0; i < long_cb; i++) { + output[i] = (h->llr2[h->max_par_cb*deinter[i]+cb_idx] > 0) ? 1 : 0; + } +} + +void srslte_tdec_simd_inter_decision(srslte_tdec_simd_inter_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb) +{ + for (int i=0;iinterleaver[h->current_cbidx].reverse; + +#define indexOf_cb(idx, cb) (h->max_par_cb*(deinter[8*i+idx])+cb) + + // long_cb is always byte aligned + for (i = 0; i < long_cb/8; i++) { + uint8_t out0 = h->llr2[indexOf_cb(0, cb_idx)]>0?mask[0]:0; + uint8_t out1 = h->llr2[indexOf_cb(1, cb_idx)]>0?mask[1]:0; + uint8_t out2 = h->llr2[indexOf_cb(2, cb_idx)]>0?mask[2]:0; + uint8_t out3 = h->llr2[indexOf_cb(3, cb_idx)]>0?mask[3]:0; + uint8_t out4 = h->llr2[indexOf_cb(4, cb_idx)]>0?mask[4]:0; + uint8_t out5 = h->llr2[indexOf_cb(5, cb_idx)]>0?mask[5]:0; + uint8_t out6 = h->llr2[indexOf_cb(6, cb_idx)]>0?mask[6]:0; + uint8_t out7 = h->llr2[indexOf_cb(7, cb_idx)]>0?mask[7]:0; + + output[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7; + } +} + +void srslte_tdec_simd_inter_decision_byte(srslte_tdec_simd_inter_t * h, uint8_t *output[SRSLTE_TDEC_MAX_NPAR], uint32_t nof_cb, uint32_t long_cb) +{ + for (int i=0;i +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_simd.h" +#include "srslte/phy/utils/vector.h" + +#include + +#ifdef LV_HAVE_SSE +#include +#endif + + +#define NUMSTATES 8 +#define NINPUTS 2 +#define TAIL 3 +#define TOTALTAIL 12 + +#define INF 10000 +#define ZERO 0 + + +#ifdef LV_HAVE_SSE + +/* +static void print_128i(__m128i x) { + int16_t *s = (int16_t*) &x; + printf("[%d", s[0]); + for (int i=1;i<8;i++) { + printf(",%d", s[i]); + } + printf("]\n"); +} +*/ +//#define use_beta_transposed_max + +#ifndef use_beta_transposed_max + +/* Computes the horizontal MAX from 8 16-bit integers using the minpos_epu16 SSE4.1 instruction */ +static inline int16_t hMax(__m128i buffer) +{ + __m128i tmp1 = _mm_sub_epi16(_mm_set1_epi16(0x7FFF), buffer); + __m128i tmp3 = _mm_minpos_epu16(tmp1); + return (int16_t)(_mm_cvtsi128_si32(tmp3)); +} + +/* Computes beta values */ +void map_sse_beta(map_gen_t * s, int16_t * output, uint32_t long_cb) +{ + int k; + uint32_t end = long_cb + 3; + const __m128i *alphaPtr = (const __m128i*) s->alpha; + + __m128i beta_k = _mm_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0); + __m128i g, bp, bn, alpha_k; + + /* Define the shuffle constant for the positive beta */ + __m128i shuf_bp = _mm_set_epi8( + 15, 14, // 7 + 7, 6, // 3 + 5, 4, // 2 + 13, 12, // 6 + 11, 10, // 5 + 3, 2, // 1 + 1, 0, // 0 + 9, 8 // 4 + ); + + /* Define the shuffle constant for the negative beta */ + __m128i shuf_bn = _mm_set_epi8( + 7, 6, // 3 + 15, 14, // 7 + 13, 12, // 6 + 5, 4, // 2 + 3, 2, // 1 + 11, 10, // 5 + 9, 8, // 4 + 1, 0 // 0 + ); + + alphaPtr += long_cb-1; + + /* Define shuffle for branch costs */ + __m128i shuf_g[4]; + shuf_g[3] = _mm_set_epi8(3,2,1,0,1,0,3,2,3,2,1,0,1,0,3,2); + shuf_g[2] = _mm_set_epi8(7,6,5,4,5,4,7,6,7,6,5,4,5,4,7,6); + shuf_g[1] = _mm_set_epi8(11,10,9,8,9,8,11,10,11,10,9,8,9,8,11,10); + shuf_g[0] = _mm_set_epi8(15,14,13,12,13,12,15,14,15,14,13,12,13,12,15,14); + __m128i gv; + int16_t *b = &s->branch[2*long_cb-8]; + __m128i *gPtr = (__m128i*) b; + /* Define shuffle for beta normalization */ + __m128i shuf_norm = _mm_set_epi8(1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0); + + /* This defines a beta computation step: + * Adds and substracts the branch metrics to the previous beta step, + * shuffles the states according to the trellis path and selects maximum state + */ +#define BETA_STEP(g) bp = _mm_add_epi16(beta_k, g);\ + bn = _mm_sub_epi16(beta_k, g);\ + bp = _mm_shuffle_epi8(bp, shuf_bp);\ + bn = _mm_shuffle_epi8(bn, shuf_bn);\ + beta_k = _mm_max_epi16(bp, bn); + + /* Loads the alpha metrics from memory and adds them to the temporal bn and bp + * metrics. Then computes horizontal maximum of both metrics and computes difference + */ +#define BETA_STEP_CNT(c,d) g = _mm_shuffle_epi8(gv, shuf_g[c]);\ + BETA_STEP(g)\ + alpha_k = _mm_load_si128(alphaPtr);\ + alphaPtr--;\ + bp = _mm_add_epi16(bp, alpha_k);\ + bn = _mm_add_epi16(bn, alpha_k);\ + output[k-d] = hMax(bn)-hMax(bp); + + /* The tail does not require to load alpha or produce outputs. Only update + * beta metrics accordingly */ + for (k=end-1; k>=long_cb; k--) { + int16_t g0 = s->branch[2*k]; + int16_t g1 = s->branch[2*k+1]; + g = _mm_set_epi16(g1, g0, g0, g1, g1, g0, g0, g1); + BETA_STEP(g); + } + + /* We inline 2 trelis steps for each normalization */ + __m128i norm; + for (; k >= 0; k-=8) { + gv = _mm_load_si128(gPtr); + gPtr--; + + BETA_STEP_CNT(0,0); + BETA_STEP_CNT(1,1); + BETA_STEP_CNT(2,2); + BETA_STEP_CNT(3,3); + norm = _mm_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm_sub_epi16(beta_k, norm); + gv = _mm_load_si128(gPtr); + gPtr--; + BETA_STEP_CNT(0,4); + BETA_STEP_CNT(1,5); + BETA_STEP_CNT(2,6); + BETA_STEP_CNT(3,7); + + norm = _mm_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm_sub_epi16(beta_k, norm); + } +} + +#endif + +/* Computes alpha metrics */ +void map_sse_alpha(map_gen_t * s, uint32_t long_cb) +{ + uint32_t k; + int16_t *alpha = s->alpha; + uint32_t i; + + alpha[0] = 0; + for (i = 1; i < 8; i++) { + alpha[i] = -INF; + } + + /* Define the shuffle constant for the positive alpha */ + __m128i shuf_ap = _mm_set_epi8( + 15, 14, // 7 + 9, 8, // 4 + 7, 6, // 3 + 1, 0, // 0 + 13, 12, // 6 + 11, 10, // 5 + 5, 4, // 2 + 3, 2 // 1 + ); + + /* Define the shuffle constant for the negative alpha */ + __m128i shuf_an = _mm_set_epi8( + 13, 12, // 6 + 11, 10, // 5 + 5, 4, // 2 + 3, 2, // 1 + 15, 14, // 7 + 9, 8, // 4 + 7, 6, // 3 + 1, 0 // 0 + ); + + /* Define shuffle for branch costs */ + __m128i shuf_g[4]; + shuf_g[0] = _mm_set_epi8(3,2,3,2,1,0,1,0,1,0,1,0,3,2,3,2); + shuf_g[1] = _mm_set_epi8(7,6,7,6,5,4,5,4,5,4,5,4,7,6,7,6); + shuf_g[2] = _mm_set_epi8(11,10,11,10,9,8,9,8,9,8,9,8,11,10,11,10); + shuf_g[3] = _mm_set_epi8(15,14,15,14,13,12,13,12,13,12,13,12,15,14,15,14); + + __m128i shuf_norm = _mm_set_epi8(1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0); + + __m128i* alphaPtr = (__m128i*) alpha; + alphaPtr++; + + __m128i gv; + __m128i *gPtr = (__m128i*) s->branch; + __m128i g, ap, an; + + __m128i alpha_k = _mm_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0); + + /* This defines a alpha computation step: + * Adds and substracts the branch metrics to the previous alpha step, + * shuffles the states according to the trellis path and selects maximum state + */ +#define ALPHA_STEP(c) g = _mm_shuffle_epi8(gv, shuf_g[c]); \ + ap = _mm_add_epi16(alpha_k, g);\ + an = _mm_sub_epi16(alpha_k, g);\ + ap = _mm_shuffle_epi8(ap, shuf_ap);\ + an = _mm_shuffle_epi8(an, shuf_an);\ + alpha_k = _mm_max_epi16(ap, an);\ + _mm_store_si128(alphaPtr, alpha_k);\ + alphaPtr++; \ + + /* In this loop, we compute 8 steps and normalize twice for each branch metrics memory load */ + __m128i norm; + for (k = 0; k < long_cb/8; k++) { + gv = _mm_load_si128(gPtr); + gPtr++; + ALPHA_STEP(0); + ALPHA_STEP(1); + ALPHA_STEP(2); + ALPHA_STEP(3); + norm = _mm_shuffle_epi8(alpha_k, shuf_norm); + alpha_k = _mm_sub_epi16(alpha_k, norm); + gv = _mm_load_si128(gPtr); + gPtr++; + ALPHA_STEP(0); + ALPHA_STEP(1); + ALPHA_STEP(2); + ALPHA_STEP(3); + norm = _mm_shuffle_epi8(alpha_k, shuf_norm); + alpha_k = _mm_sub_epi16(alpha_k, norm); + } +} + +/* Compute branch metrics (gamma) */ +void map_sse_gamma(map_gen_t * h, int16_t *input, int16_t *app, int16_t *parity, uint32_t long_cb) +{ + __m128i res00, res10, res01, res11, res0, res1; + __m128i in, ap, pa, g1, g0; + + __m128i *inPtr = (__m128i*) input; + __m128i *appPtr = (__m128i*) app; + __m128i *paPtr = (__m128i*) parity; + __m128i *resPtr = (__m128i*) h->branch; + + __m128i res00_mask = _mm_set_epi8(0xff,0xff,7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0); + __m128i res10_mask = _mm_set_epi8(0xff,0xff,15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8); + __m128i res01_mask = _mm_set_epi8(7,6,0xff,0xff,5,4,0xff,0xff,3,2,0xff,0xff,1,0,0xff,0xff); + __m128i res11_mask = _mm_set_epi8(15,14,0xff,0xff,13,12,0xff,0xff,11,10,0xff,0xff,9,8,0xff,0xff); + + for (int i=0;ibranch[2*i] = (input[i] - parity[i])/2; + h->branch[2*i+1] = (input[i] + parity[i])/2; + } +} + + + + + + +/*********************** + * + * This is an attempt to parallelize the horizontal max + * by doing a 8x8 tranpose of the vectors and computing max + * in cascade. However since we need to store 16 registers + * for the positive and negative values the performance is not very good + */ + + +#ifdef use_beta_transposed_max + +static inline __m128i transposed_max(__m128i a, __m128i b, __m128i c, __m128i d, + __m128i e, __m128i f, __m128i g, __m128i h) +{ + // Transpose 8 vectors + __m128i t0 = _mm_unpacklo_epi16(a, b); + __m128i t1 = _mm_unpacklo_epi16(c, d); + __m128i t2 = _mm_unpacklo_epi16(e, f); + __m128i t3 = _mm_unpacklo_epi16(g, h); + __m128i t4 = _mm_unpackhi_epi16(a, b); + __m128i t5 = _mm_unpackhi_epi16(c, d); + __m128i t6 = _mm_unpackhi_epi16(e, f); + __m128i t7 = _mm_unpackhi_epi16(g, h); + + __m128i s0 = _mm_unpacklo_epi32(t0, t1); + __m128i s1 = _mm_unpackhi_epi32(t0, t1); + __m128i s2 = _mm_unpacklo_epi32(t2, t3); + __m128i s3 = _mm_unpackhi_epi32(t2, t3); + __m128i s4 = _mm_unpacklo_epi32(t4, t5); + __m128i s5 = _mm_unpackhi_epi32(t4, t5); + __m128i s6 = _mm_unpacklo_epi32(t6, t7); + __m128i s7 = _mm_unpackhi_epi32(t6, t7); + + __m128i x0 = _mm_unpacklo_epi64(s0, s2); + __m128i x1 = _mm_unpackhi_epi64(s0, s2); + __m128i x2 = _mm_unpacklo_epi64(s1, s3); + __m128i x3 = _mm_unpackhi_epi64(s1, s3); + __m128i x4 = _mm_unpacklo_epi64(s4, s6); + __m128i x5 = _mm_unpackhi_epi64(s4, s6); + __m128i x6 = _mm_unpacklo_epi64(s5, s7); + __m128i x7 = _mm_unpackhi_epi64(s5, s7); + + // Cascade max on the transposed vector + __m128i res = _mm_max_epi16(x0, + _mm_max_epi16(x1, + _mm_max_epi16(x2, + _mm_max_epi16(x3, + _mm_max_epi16(x4, + _mm_max_epi16(x5, + _mm_max_epi16(x6, + x7))))))); + + return res; +} + +void map_sse_beta(map_gen_t * s, int16_t * output, uint32_t long_cb) +{ + int k; + uint32_t end = long_cb + 3; + const __m128i *alphaPtr = (const __m128i*) s->alpha; + + __m128i beta_k = _mm_set_epi16(-INF, -INF, -INF, -INF, -INF, -INF, -INF, 0); + __m128i g, alpha_k; + __m128i bn, bn_0, bn_1, bn_2, bn_3, bn_4, bn_5, bn_6, bn_7; + __m128i bp, bp_0, bp_1, bp_2, bp_3, bp_4, bp_5, bp_6, bp_7; + + /* Define the shuffle constant for the positive beta */ + __m128i shuf_bp = _mm_set_epi8( + 15, 14, // 7 + 7, 6, // 3 + 5, 4, // 2 + 13, 12, // 6 + 11, 10, // 5 + 3, 2, // 1 + 1, 0, // 0 + 9, 8 // 4 + ); + + /* Define the shuffle constant for the negative beta */ + __m128i shuf_bn = _mm_set_epi8( + 7, 6, // 3 + 15, 14, // 7 + 13, 12, // 6 + 5, 4, // 2 + 3, 2, // 1 + 11, 10, // 5 + 9, 8, // 4 + 1, 0 // 0 + ); + + alphaPtr += long_cb-1; + + /* Define shuffle for branch costs */ + __m128i shuf_g[4]; + shuf_g[3] = _mm_set_epi8(3,2,1,0,1,0,3,2,3,2,1,0,1,0,3,2); + shuf_g[2] = _mm_set_epi8(7,6,5,4,5,4,7,6,7,6,5,4,5,4,7,6); + shuf_g[1] = _mm_set_epi8(11,10,9,8,9,8,11,10,11,10,9,8,9,8,11,10); + shuf_g[0] = _mm_set_epi8(15,14,13,12,13,12,15,14,15,14,13,12,13,12,15,14); + __m128i gv; + int16_t *b = &s->branch[2*long_cb-8]; + __m128i *gPtr = (__m128i*) b; + /* Define shuffle for beta normalization */ + __m128i shuf_norm = _mm_set_epi8(1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0); + + /* This defines a beta computation step: + * Adds and substracts the branch metrics to the previous beta step, + * shuffles the states according to the trellis path and selects maximum state + */ +#define BETA_STEP(g) bp = _mm_add_epi16(beta_k, g);\ + bn = _mm_sub_epi16(beta_k, g);\ + bp = _mm_shuffle_epi8(bp, shuf_bp);\ + bn = _mm_shuffle_epi8(bn, shuf_bn);\ + beta_k = _mm_max_epi16(bp, bn); + + /* Loads the alpha metrics from memory and adds them to the temporal bn and bp + * metrics. + */ +#define BETA_STEP_CNT(c,d) g = _mm_shuffle_epi8(gv, shuf_g[c]);\ + BETA_STEP(g)\ + alpha_k = _mm_load_si128(alphaPtr);\ + alphaPtr--;\ + bp_##d = _mm_add_epi16(bp, alpha_k);\ + bn_##d = _mm_add_epi16(bn, alpha_k);\ + + /* The tail does not require to load alpha or produce outputs. Only update + * beta metrics accordingly */ + for (k=end-1; k>=long_cb; k--) { + int16_t g0 = s->branch[2*k]; + int16_t g1 = s->branch[2*k+1]; + g = _mm_set_epi16(g1, g0, g0, g1, g1, g0, g0, g1); + BETA_STEP(g); + } + + /* We inline 2 trelis steps for each normalization */ + __m128i norm; + __m128i *outPtr = (__m128i*) &output[long_cb-8]; + for (; k >= 0; k-=8) { + gv = _mm_load_si128(gPtr); + gPtr--; + + BETA_STEP_CNT(0,0); + BETA_STEP_CNT(1,1); + BETA_STEP_CNT(2,2); + BETA_STEP_CNT(3,3); + norm = _mm_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm_sub_epi16(beta_k, norm); + gv = _mm_load_si128(gPtr); + gPtr--; + BETA_STEP_CNT(0,4); + BETA_STEP_CNT(1,5); + BETA_STEP_CNT(2,6); + BETA_STEP_CNT(3,7); + norm = _mm_shuffle_epi8(beta_k, shuf_norm); + beta_k = _mm_sub_epi16(beta_k, norm); + + __m128i bn_transp = transposed_max(bn_7, bn_6, bn_5, bn_4, bn_3, bn_2, bn_1, bn_0); + __m128i bp_transp = transposed_max(bp_7, bp_6, bp_5, bp_4, bp_3, bp_2, bp_1, bp_0); + __m128i outval = _mm_sub_epi16(bp_transp,bn_transp); + _mm_store_si128(outPtr, outval); + outPtr--; + } +} +#endif + + + + +#endif + + diff --git a/lib/src/phy/fec/turbodecoder_sse_inter.c b/lib/src/phy/fec/turbodecoder_sse_inter.c new file mode 100644 index 0000000..d75c8a6 --- /dev/null +++ b/lib/src/phy/fec/turbodecoder_sse_inter.c @@ -0,0 +1,202 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/fec/turbodecoder_simd_inter.h" +#include "srslte/phy/utils/vector.h" + + +#define NCB 8 + +#define INF 10000 + +#ifdef LV_HAVE_SSE +#include + +void sse_inter_extract_syst1(srslte_tdec_simd_inter_t *h, uint16_t *inter, uint32_t long_cb) +{ + __m128i *llr1Ptr = (__m128i*) h->llr1; + __m128i *wPtr = (__m128i*) h->w; + __m128i *syst1Ptr = (__m128i*) h->syst1; + + for (int i = 0; i < long_cb; i++) { + __m128i llr1 = _mm_load_si128(&llr1Ptr[inter[i]]); + __m128i w = _mm_load_si128(&wPtr[inter[i]]); + _mm_store_si128(syst1Ptr++, _mm_sub_epi16(llr1, w)); + } +} + +void sse_inter_update_w(srslte_tdec_simd_inter_t *h, uint16_t *deinter, uint32_t long_cb) +{ + __m128i *llr1Ptr = (__m128i*) h->llr1; + __m128i *llr2Ptr = (__m128i*) h->llr2; + __m128i *wPtr = (__m128i*) h->w; + __m128i *syst1Ptr = (__m128i*) h->syst1; + + for (int i = 0; i < long_cb; i++) { + __m128i llr1 = _mm_load_si128(llr1Ptr++); + __m128i w = _mm_load_si128(wPtr++); + __m128i llr2 = _mm_load_si128(&llr2Ptr[deinter[i]]); + + _mm_store_si128(syst1Ptr++, _mm_add_epi16(w, _mm_sub_epi16(llr2, llr1))); + } +} + +/* Computes beta values */ +void map_sse_inter_beta(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, int16_t * output, uint32_t long_cb) +{ + __m128i m_b[8], new[8], old[8], max1[8], max0[8]; + __m128i x, y, xy; + __m128i m1, m0; + uint32_t end = long_cb + 3; + uint32_t i; + + __m128i *inputPtr = (__m128i*) input; + __m128i *parityPtr = (__m128i*) parity; + __m128i *outputPtr = (__m128i*) output; + __m128i *alphaPtr = (__m128i*) s->alpha; + + for (int i = 0; i < 8; i++) { + old[i] = _mm_set1_epi16(0); + } + + for (int k = end - 1; k >= 0; k--) { + x = _mm_load_si128(inputPtr++); + y = _mm_load_si128(parityPtr++); + + xy = _mm_add_epi16(x,y); + + m_b[0] = _mm_add_epi16(old[4], xy); + m_b[1] = old[4]; + m_b[2] = _mm_add_epi16(old[5], y); + m_b[3] = _mm_add_epi16(old[5], x); + m_b[4] = _mm_add_epi16(old[6], x); + m_b[5] = _mm_add_epi16(old[6], y); + m_b[6] = old[7]; + m_b[7] = _mm_add_epi16(old[7], xy); + + new[0] = old[0]; + new[1] = _mm_add_epi16(old[0], xy); + new[2] = _mm_add_epi16(old[1], x); + new[3] = _mm_add_epi16(old[1], y); + new[4] = _mm_add_epi16(old[2], y); + new[5] = _mm_add_epi16(old[2], x); + new[6] = _mm_add_epi16(old[3], xy); + new[7] = old[3]; + + for (i = 0; i < 8; i++) { + __m128i alpha = _mm_load_si128(alphaPtr++); + max0[i] = _mm_add_epi16(alpha, m_b[i]); + max1[i] = _mm_add_epi16(alpha, new[i]); + } + + m1 = _mm_max_epi16(max1[0], max1[1]); + m0 = _mm_max_epi16(max0[0], max0[1]); + + for (i = 2; i < 8; i++) { + m1 = _mm_max_epi16(m1, max1[i]); + m0 = _mm_max_epi16(m0, max0[i]); + } + + for (i = 0; i < 8; i++) { + new[i] = _mm_max_epi16(m_b[i], new[i]); + old[i] = new[i]; + } + + __m128i out = _mm_sub_epi16(m1, m0); + _mm_store_si128(outputPtr++, out); + + // normalize + if ((k%4)==0) { + for (int i=1;i<8;i++) { + _mm_sub_epi16(old[i], old[0]); + } + } + } +} + +/* Computes alpha metrics */ +void map_see_inter_alpha(srslte_tdec_simd_inter_t * s, int16_t *input, int16_t *parity, uint32_t long_cb) +{ + __m128i m_b[8], new[8], old[8]; + __m128i x, y, xy; + uint32_t k; + + __m128i *inputPtr = (__m128i*) input; + __m128i *parityPtr = (__m128i*) parity; + __m128i *alphaPtr = (__m128i*) s->alpha; + + old[0] = _mm_set1_epi16(0); + for (int i = 1; i < 8; i++) { + old[i] = _mm_set1_epi16(-INF); + } + + for (k = 0; k < long_cb; k++) { + x = _mm_load_si128(inputPtr++); + y = _mm_load_si128(parityPtr++); + + xy = _mm_add_epi16(x,y); + + m_b[0] = old[0]; + m_b[1] = _mm_add_epi16(old[3], y); + m_b[2] = _mm_add_epi16(old[4], y); + m_b[3] = old[7]; + m_b[4] = old[1]; + m_b[5] = _mm_add_epi16(old[2], y); + m_b[6] = _mm_add_epi16(old[5], y); + m_b[7] = old[6]; + + new[0] = _mm_add_epi16(old[1], xy); + new[1] = _mm_add_epi16(old[2], x); + new[2] = _mm_add_epi16(old[5], x); + new[3] = _mm_add_epi16(old[6], xy); + new[4] = _mm_add_epi16(old[0], xy); + new[5] = _mm_add_epi16(old[3], x); + new[6] = _mm_add_epi16(old[4], x); + new[7] = _mm_add_epi16(old[7], xy); + + for (int i = 0; i < 8; i++) { + new[i] = _mm_max_epi16(m_b[i], new[i]); + old[i] = new[i]; + _mm_store_si128(alphaPtr++, old[i]); + } + + // normalize + if ((k%4)==0) { + for (int i=1;i<8;i++) { + _mm_sub_epi16(old[i], old[0]); + } + } + } +} + +#endif diff --git a/lib/src/phy/fec/viterbi.c b/lib/src/phy/fec/viterbi.c new file mode 100644 index 0000000..4e5801b --- /dev/null +++ b/lib/src/phy/fec/viterbi.c @@ -0,0 +1,598 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include +#include + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/fec/viterbi.h" +#include "parity.h" +#include "viterbi37.h" + +#define DEB 0 + +#define TB_ITER 3 + +#define DEFAULT_GAIN 100 + +#define DEFAULT_GAIN_16 1000 +#define VITERBI_16 + +#ifndef LV_HAVE_AVX2 +#undef VITERBI_16 +#endif + + +//#undef LV_HAVE_SSE + +int decode37(void *o, uint8_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + + /* Initialize Viterbi decoder */ + init_viterbi37_port(q->ptr, q->tail_biting ? -1 : 0); + + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp[i*3*frame_length], symbols, 3*frame_length*sizeof(uint8_t)); + } + update_viterbi37_blk_port(q->ptr, q->tmp, TB_ITER*frame_length, &best_state); + chainback_viterbi37_port(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_port(q->ptr, symbols, frame_length + q->K - 1, NULL); + chainback_viterbi37_port(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + + +#ifdef LV_HAVE_SSE +int decode37_sse(void *o, uint8_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + + /* Initialize Viterbi decoder */ + init_viterbi37_sse(q->ptr, q->tail_biting?-1:0); + + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp[i*3*frame_length], symbols, 3*frame_length*sizeof(uint8_t)); + } + update_viterbi37_blk_sse(q->ptr, q->tmp, TB_ITER*frame_length, &best_state); + chainback_viterbi37_sse(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_sse(q->ptr, symbols, frame_length+q->K-1, NULL); + chainback_viterbi37_sse(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + +void free37_sse(void *o) { + srslte_viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_sse(q->ptr); +} + +#endif + + + +#ifdef LV_HAVE_AVX2 +int decode37_avx2_16bit(void *o, uint16_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + + /* Initialize Viterbi decoder */ + init_viterbi37_avx2_16bit(q->ptr, q->tail_biting?-1:0); + + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp_s[i*3*frame_length], symbols, 3*frame_length*sizeof(uint16_t)); + } + update_viterbi37_blk_avx2_16bit(q->ptr, q->tmp_s, TB_ITER*frame_length, &best_state); + chainback_viterbi37_avx2_16bit(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_avx2_16bit(q->ptr, symbols, frame_length+q->K-1, NULL); + chainback_viterbi37_avx2_16bit(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + +void free37_avx2_16bit(void *o) { + srslte_viterbi_t *q = o; + + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->symbols_us) { + free(q->symbols_us); + } + if (q->tmp) { + free(q->tmp); + } + if (q->tmp_s) { + free(q->tmp_s); + } + delete_viterbi37_avx2_16bit(q->ptr); +} + + +int decode37_avx2(void *o, uint8_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + /* Initialize Viterbi decoder */ + init_viterbi37_avx2(q->ptr, q->tail_biting?-1:0); + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp[i*3*frame_length], symbols, 3*frame_length*sizeof(uint8_t)); + } + update_viterbi37_blk_avx2(q->ptr, q->tmp, TB_ITER*frame_length, &best_state); + chainback_viterbi37_avx2(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_avx2(q->ptr, symbols, frame_length+q->K-1, NULL); + chainback_viterbi37_avx2(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + +void free37_avx2(void *o) { + srslte_viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_avx2(q->ptr); +} + + +#endif + +#ifdef HAVE_NEON +int decode37_neon(void *o, uint8_t *symbols, uint8_t *data, uint32_t frame_length) { + srslte_viterbi_t *q = o; + + uint32_t best_state; + + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + + /* Initialize Viterbi decoder */ + init_viterbi37_neon(q->ptr, q->tail_biting?-1:0); + + /* Decode block */ + if (q->tail_biting) { + for (int i=0;itmp[i*3*frame_length], symbols, 3*frame_length*sizeof(uint8_t)); + } + update_viterbi37_blk_neon(q->ptr, q->tmp, TB_ITER*frame_length, &best_state); + chainback_viterbi37_neon(q->ptr, q->tmp, TB_ITER*frame_length, best_state); + memcpy(data, &q->tmp[((int) (TB_ITER/2))*frame_length], frame_length*sizeof(uint8_t)); + } else { + update_viterbi37_blk_neon(q->ptr, symbols, frame_length+q->K-1, NULL); + chainback_viterbi37_neon(q->ptr, data, frame_length, 0); + } + + return q->framebits; +} + +void free37_neon(void *o) { + srslte_viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_neon(q->ptr); +} + +#endif + +void free37(void *o) { + srslte_viterbi_t *q = o; + if (q->symbols_uc) { + free(q->symbols_uc); + } + if (q->tmp) { + free(q->tmp); + } + delete_viterbi37_port(q->ptr); +} + +int init37(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN; + q->tail_biting = tail_biting; + q->decode = decode37; + q->free = free37; + q->decode_f = NULL; + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER * 3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + bzero(q->tmp, 3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_port(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} + +#ifdef LV_HAVE_SSE +int init37_sse(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN; + q->tail_biting = tail_biting; + q->decode = decode37_sse; + q->free = free37_sse; + q->decode_f = NULL; + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_sse(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} +#endif + +#ifdef HAVE_NEON +int init37_neon(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN; + q->tail_biting = tail_biting; + q->decode = decode37_neon; + q->free = free37_neon; + q->decode_f = NULL; + printf("USING NEON VITERBI***************\n"); + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_neon(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} +#endif + + +#ifdef LV_HAVE_AVX2 +int init37_avx2(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN; + q->tail_biting = tail_biting; + q->decode = decode37_avx2; + q->free = free37_avx2; + q->decode_f = NULL; + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->symbols_uc) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint8_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + + if ((q->ptr = create_viterbi37_avx2(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} + +int init37_avx2_16bit(srslte_viterbi_t *q, int poly[3], uint32_t framebits, bool tail_biting) { + q->K = 7; + q->R = 3; + q->framebits = framebits; + q->gain_quant_s = 4; + q->gain_quant = DEFAULT_GAIN_16; + q->tail_biting = tail_biting; + q->decode_s = decode37_avx2_16bit; + q->free = free37_avx2_16bit; + q->decode_f = NULL; + q->symbols_uc = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint8_t)); + q->symbols_us = srslte_vec_malloc(3 * (q->framebits + q->K - 1) * sizeof(uint16_t)); + if (!q->symbols_uc || !q->symbols_us) { + perror("malloc"); + return -1; + } + if (q->tail_biting) { + q->tmp = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint8_t)); + q->tmp_s = srslte_vec_malloc(TB_ITER*3*(q->framebits + q->K - 1) * sizeof(uint16_t)); + if (!q->tmp) { + perror("malloc"); + free37(q); + return -1; + } + } else { + q->tmp = NULL; + } + //printf("pt0\n"); + if ((q->ptr = create_viterbi37_avx2_16bit(poly, TB_ITER*framebits)) == NULL) { + fprintf(stderr, "create_viterbi37 failed\n"); + free37(q); + return -1; + } else { + return 0; + } +} + +#endif + +void srslte_viterbi_set_gain_quant(srslte_viterbi_t *q, float gain_quant) { + q->gain_quant = gain_quant; +} + +void srslte_viterbi_set_gain_quant_s(srslte_viterbi_t *q, int16_t gain_quant) { + q->gain_quant_s = gain_quant; +} + +int srslte_viterbi_init(srslte_viterbi_t *q, srslte_viterbi_type_t type, int poly[3], uint32_t max_frame_length, bool tail_bitting) +{ + switch (type) { + case SRSLTE_VITERBI_37: +#ifdef LV_HAVE_SSE + + #ifdef LV_HAVE_AVX2 + #ifdef VITERBI_16 + return init37_avx2_16bit(q, poly, max_frame_length, tail_bitting); + #else + return init37_avx2(q, poly, max_frame_length, tail_bitting); + #endif + #else + return init37_sse(q, poly, max_frame_length, tail_bitting); + #endif +#else + #ifdef HAVE_NEON + return init37_neon(q, poly, max_frame_length, tail_bitting); + #else + return init37(q, poly, max_frame_length, tail_bitting); + #endif +#endif + default: + fprintf(stderr, "Decoder not implemented\n"); + return -1; + } +} + +#ifdef LV_HAVE_SSE +int srslte_viterbi_init_sse(srslte_viterbi_t *q, srslte_viterbi_type_t type, int poly[3], uint32_t max_frame_length, bool tail_bitting) +{ + return init37_sse(q, poly, max_frame_length, tail_bitting); +} +#endif + +#ifdef LV_HAVE_AVX2 +int srslte_viterbi_init_avx2(srslte_viterbi_t *q, srslte_viterbi_type_t type, int poly[3], uint32_t max_frame_length, bool tail_bitting) +{ + return init37_avx2(q, poly, max_frame_length, tail_bitting); +} +#endif + +void srslte_viterbi_free(srslte_viterbi_t *q) { + if (q->free) { + q->free(q); + } + bzero(q, sizeof(srslte_viterbi_t)); +} + +/* symbols are real-valued */ +int srslte_viterbi_decode_f(srslte_viterbi_t *q, float *symbols, uint8_t *data, uint32_t frame_length) +{ + uint32_t len; + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + if (q->tail_biting) { + len = 3 * frame_length; + } else { + len = 3 * (frame_length + q->K - 1); + } + if (!q->decode_f) { + + float max = -9e9; + for (int i=0;i max) { + max = fabs(symbols[i]); + } + } +#ifdef VITERBI_16 + srslte_vec_quant_fus(symbols, q->symbols_us, q->gain_quant/max, 32767.5, 65535, len); + return srslte_viterbi_decode_us(q, q->symbols_us, data, frame_length); +#else + srslte_vec_quant_fuc(symbols, q->symbols_uc, q->gain_quant/max, 127.5, 255, len); + return srslte_viterbi_decode_uc(q, q->symbols_uc, data, frame_length); +#endif + } else { + return q->decode_f(q, symbols, data, frame_length); + } +} + +/* symbols are int16 */ +int srslte_viterbi_decode_s(srslte_viterbi_t *q, int16_t *symbols, uint8_t *data, uint32_t frame_length) +{ + uint32_t len; + if (frame_length > q->framebits) { + fprintf(stderr, "Initialized decoder for max frame length %d bits\n", + q->framebits); + return -1; + } + if (q->tail_biting) { + len = 3 * frame_length; + } else { + len = 3 * (frame_length + q->K - 1); + } + + int16_t max = -INT16_MAX; + for (int i=0;i max) { + max = abs(symbols[i]); + } + } +#ifdef VITERBI_16 + srslte_vec_quant_sus(symbols, q->symbols_us, 1, 32767, len); + return srslte_viterbi_decode_us(q, q->symbols_us, data, frame_length); +#else + srslte_vec_quant_suc(symbols, q->symbols_uc, (float) q->gain_quant/max, 127, 255, len); + return srslte_viterbi_decode_uc(q, q->symbols_uc, data, frame_length); +#endif + + +} + +int srslte_viterbi_decode_us(srslte_viterbi_t *q, uint16_t *symbols, uint8_t *data, uint32_t frame_length) +{ + return q->decode_s(q, symbols, data, frame_length); +} + + +int srslte_viterbi_decode_uc(srslte_viterbi_t *q, uint8_t *symbols, uint8_t *data, uint32_t frame_length) +{ + return q->decode(q, symbols, data, frame_length); +} diff --git a/lib/src/phy/fec/viterbi37.h b/lib/src/phy/fec/viterbi37.h new file mode 100644 index 0000000..a771455 --- /dev/null +++ b/lib/src/phy/fec/viterbi37.h @@ -0,0 +1,132 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 + +void *create_viterbi37_port(int polys[3], + uint32_t len); + +int init_viterbi37_port(void *p, + int starting_state); + +int chainback_viterbi37_port(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_port(void *p); + +int update_viterbi37_blk_port(void *p, + uint8_t *syms, + uint32_t nbits, + uint32_t *best_state); + + +void *create_viterbi37_sse(int polys[3], + uint32_t len); + +int init_viterbi37_sse(void *p, + int starting_state); + + +void reset_blk_sse(void *p, int nbits); + +int chainback_viterbi37_sse(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_sse(void *p); + +int update_viterbi37_blk_sse(void *p, + uint8_t *syms, + uint32_t nbits, + uint32_t *best_state); + +void *create_viterbi37_neon(int polys[3], + uint32_t len); + +int init_viterbi37_neon(void *p, + int starting_state); + + +void reset_blk_neon(void *p, int nbits); + +int chainback_viterbi37_neon(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_neon(void *p); + +int update_viterbi37_blk_neon(void *p, + uint8_t *syms, + uint32_t nbits, + uint32_t *best_state); + + +void *create_viterbi37_avx2(int polys[3], + uint32_t len); + +int init_viterbi37_avx2(void *p, + int starting_state); + + +void reset_blk_avx2(void *p, int nbits); + +int chainback_viterbi37_avx2(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_avx2(void *p); + +int update_viterbi37_blk_avx2(void *p, + uint8_t *syms, + uint32_t nbits, + uint32_t *best_state); + + +void *create_viterbi37_avx2_16bit(int polys[3], + uint32_t len); + +int init_viterbi37_avx2_16bit(void *p, + int starting_state); + + +void reset_blk_avx2_16bit(void *p, int nbits); + +int chainback_viterbi37_avx2_16bit(void *p, + uint8_t *data, + uint32_t nbits, + uint32_t endstate); + +void delete_viterbi37_avx2_16bit(void *p); + +int update_viterbi37_blk_avx2_16bit(void *p, + uint16_t *syms, + uint32_t nbits, + uint32_t *best_state); \ No newline at end of file diff --git a/lib/src/phy/fec/viterbi37_avx2.c b/lib/src/phy/fec/viterbi37_avx2.c new file mode 100644 index 0000000..8735bf7 --- /dev/null +++ b/lib/src/phy/fec/viterbi37_avx2.c @@ -0,0 +1,341 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=15 r=1/6 Viterbi decoder for x86 SSE2 + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "parity.h" + +//#define DEBUG + +#ifdef LV_HAVE_AVX2 + +#include +#include +#include +#include +#define _mm256_set_m128i(v0, v1) _mm256_insertf128_si256(_mm256_castsi128_si256(v1), (v0), 1) + +#define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0)) + +typedef union { + unsigned char c[64]; + __m128i v[4]; +} metric_t; +typedef union { + unsigned int w[2]; + unsigned char c[8]; + unsigned short s[4]; + __m64 v; +} decision_t; + +union branchtab27 { + unsigned char c[32]; + __m256i v; +} Branchtab37_sse2[3]; + +int firstGo; +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void set_viterbi37_polynomial_avx2(int polys[3]) { + int state; + + for(state=0;state < 32;state++){ + Branchtab37_sse2[0].c[state] = (polys[0] < 0) ^ parity((2*state) & polys[0]) ? 255:0; + Branchtab37_sse2[1].c[state] = (polys[1] < 0) ^ parity((2*state) & polys[1]) ? 255:0; + Branchtab37_sse2[2].c[state] = (polys[2] < 0) ^ parity((2*state) & polys[2]) ? 255:0; + } +} + +void clear_v37_avx2(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_avx2(void *p, int starting_state) { + struct v37 *vp = p; + uint32_t i; + firstGo = 1; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + clear_v37_avx2(vp); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + + if (starting_state != -1) { + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + } + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_avx2(int polys[3], uint32_t len) { + void *p; + struct v37 *vp; + + set_viterbi37_polynomial_avx2(polys); + + /* Ordinary malloc() only returns 8-byte alignment, we need 16 */ + if(posix_memalign(&p, sizeof(__m128i),sizeof(struct v37))) + return NULL; + + vp = (struct v37 *)p; + if(posix_memalign(&p, sizeof(__m128i),(len+6)*sizeof(decision_t))) { + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + vp->len = len+6; + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi37_avx2( + void *p, + uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + + if (p == NULL) + return -1; + + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits--) { + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + //printf("nbits=%d, endstate=%3d, k=%d, w[0]=%d, w[1]=%d, c=%d\n", nbits, endstate, k, d[nbits].s[1]&1, d[nbits].s[2]&1, d[nbits].c[(endstate>>2)/8]&1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_avx2(void *p){ + struct v37 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} +void printer_256i(char *s, __m256i val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<32;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + +void printer_128i(char *s, __m128i val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<16;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + +void printer_m64(char *s, __m64 val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<8;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + + +void update_viterbi37_blk_avx2(void *p,unsigned char *syms,int nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + if(p == NULL) + return; + +#ifdef DEBUG + printf("["); +#endif + + d = (decision_t *) vp->dp; + + for (int s=0;sold_metrics->v[1], vp->old_metrics->v[0]); + m0 = _mm256_add_epi8(temp,metric); + m2 = _mm256_add_epi8(temp,m_metric); + + temp = _mm256_set_m128i( vp->old_metrics->v[3], vp->old_metrics->v[2]); + m3 = _mm256_add_epi8(temp,metric); + m1 = _mm256_add_epi8(temp,m_metric); + + /* Compare and select, using modulo arithmetic */ + decision0 = _mm256_cmpgt_epi8(_mm256_sub_epi8(m0,m1),_mm256_setzero_si256()); + decision1 =_mm256_cmpgt_epi8(_mm256_sub_epi8(m2,m3),_mm256_setzero_si256()); + survivor0 = _mm256_or_si256(_mm256_and_si256(decision0,m1),_mm256_andnot_si256(decision0,m0)); + survivor1 = _mm256_or_si256(_mm256_and_si256(decision1,m3),_mm256_andnot_si256(decision1,m2)); + + unsigned int x = _mm256_movemask_epi8(_mm256_unpackhi_epi8(decision0,decision1)); + unsigned int y = _mm256_movemask_epi8(_mm256_unpacklo_epi8(decision0,decision1)); + + d->s[0] = (short) y; + d->s[1] = (short) x; + d->s[2] = (short) (y >>16); + d->s[3] = (short) (x >>16); + + + __m256i unpack; + unpack = _mm256_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[0] =_mm256_castsi256_si128(unpack); + + vp->new_metrics->v[1] = _mm256_extractf128_si256(unpack,1); + + unpack = _mm256_unpackhi_epi8(survivor0,survivor1); + + vp->new_metrics->v[2] =_mm256_castsi256_si128(unpack); + vp->new_metrics->v[3] = _mm256_extractf128_si256(unpack,1); + + __m128i temp1 = vp->new_metrics->v[1]; + + vp->new_metrics->v[1] = vp->new_metrics->v[2]; + vp->new_metrics->v[2] = temp1; + + // See if we need to normalize + if (vp->new_metrics->c[0] > 100) { + int i; + uint8_t adjust; + __m128i adjustv; + union { __m128i v; signed short w[8]; } t; + + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4;i++) { + adjustv = _mm_min_epu8(adjustv,vp->new_metrics->v[i]); + } + + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,8)); + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,4)); + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,2)); + + t.v = adjustv; + adjust = t.w[0]; + adjustv = _mm_set1_epi8(adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<4;i++) + vp->new_metrics->v[i] = _mm_sub_epi8(vp->new_metrics->v[i],adjustv); + + } + + firstGo = 0; + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + + if (best_state) { + uint32_t i, bst=0; + uint8_t minmetric=UINT8_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->c[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->c[i]; + } + } + *best_state = bst; + } + + #ifdef DEBUG + printf("];\n===========================================\n"); +#endif + + vp->dp = d; +} + +#endif + + + diff --git a/lib/src/phy/fec/viterbi37_avx2_16bit.c b/lib/src/phy/fec/viterbi37_avx2_16bit.c new file mode 100644 index 0000000..913b6a8 --- /dev/null +++ b/lib/src/phy/fec/viterbi37_avx2_16bit.c @@ -0,0 +1,363 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=15 r=1/6 Viterbi decoder for x86 SSE2 + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "parity.h" + +//#define DEBUG + +#ifdef LV_HAVE_AVX2 + + +#include +#include +#include +#include + +typedef union { + //unsigned char c[64]; + //__m128i v[4]; + unsigned short c[64]; + __m256i v[4]; +} metric_t; + +typedef union { + unsigned int w[2]; + unsigned char c[8]; + unsigned short s[4]; + __m64 v[1]; +} decision_t; + +union branchtab27 { + + //unsigned char c[32]; + //__m128i v[2]; + unsigned short c[32]; + __m256i v[2]; + +} Branchtab37_sse2[3]; + +int firstGo; +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void set_viterbi37_polynomial_avx2_16bit(int polys[3]) { + int state; + for(state=0;state < 32;state++){ + Branchtab37_sse2[0].c[state] = (polys[0] < 0) ^ parity((2*state) & polys[0]) ? 65535:0; + Branchtab37_sse2[1].c[state] = (polys[1] < 0) ^ parity((2*state) & polys[1]) ? 65535:0; + Branchtab37_sse2[2].c[state] = (polys[2] < 0) ^ parity((2*state) & polys[2]) ? 65535:0; + } +} + +void clear_v37_avx2_16bit(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_avx2_16bit(void *p, int starting_state) { + + struct v37 *vp = p; + uint32_t i; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + clear_v37_avx2_16bit(vp); +firstGo = 1; + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + if (starting_state != -1) { + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + } + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_avx2_16bit(int polys[3], uint32_t len) { + void *p; + struct v37 *vp; + + set_viterbi37_polynomial_avx2_16bit(polys); + + /* Ordinary malloc() only returns 8-byte alignment, we need 16 */ + if(posix_memalign(&p, sizeof(__m256i),sizeof(struct v37))) + return NULL; + + vp = (struct v37 *)p; + if(posix_memalign(&p, sizeof(__m256i),(len+6)*sizeof(decision_t))) { + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + vp->len = len+6; + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi37_avx2_16bit( + void *p, + uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + + if (p == NULL) + return -1; + + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits--) { + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + //printf("nbits=%d, endstate=%3d, k=%d, w[0]=%d, w[1]=%d, c=%d\n", nbits, endstate, k, d[nbits].s[1]&1, d[nbits].s[2]&1, d[nbits].c[(endstate>>2)/8]&1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_avx2_16bit(void *p){ + struct v37 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +void print_256i(char *s, __m256i val) { + + printf("%s: ", s); + + uint16_t *x = (uint16_t*) &val; + for (int i=0;i<16;i++) { + printf("%.5f, ", (float)x[i]/65535); + } + printf("\n"); +} +void print_256i_char(char *s, __m256i val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<32;i++) { + printf("%d, ",x[31-i]); + } + printf("\n"); +} + + +inline unsigned short my_mm256_movemask_epi16(__m256i x) { + uint32_t x1 = _mm256_movemask_epi8(x); + uint16_t tmp = 0; + for(int i = 0; i<16;i++){ + tmp |= ((x1 >> ((i*2)+1)) & 0x01) << i; + } + + return (tmp); + } + + +void update_viterbi37_blk_avx2_16bit(void *p, unsigned short *syms, int nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + if(p == NULL) + return; + +#ifdef DEBUG + printf("["); +#endif + + d = (decision_t *) vp->dp; + + for (int s=0;sold_metrics->v[i],metric); + m3 = _mm256_add_epi16(vp->old_metrics->v[2+i],metric); + m1 = _mm256_add_epi16(vp->old_metrics->v[2+i],m_metric); + m2 = _mm256_add_epi16(vp->old_metrics->v[i],m_metric); + + + /* Compare and select, using modulo arithmetic */ + + decision0 = _mm256_cmpgt_epi16(_mm256_sub_epi16(m0,m1),_mm256_setzero_si256()); + decision1 = _mm256_cmpgt_epi16(_mm256_sub_epi16(m2,m3),_mm256_setzero_si256()); + survivor0 = _mm256_or_si256(_mm256_and_si256(decision0,m1),_mm256_andnot_si256(decision0,m0)); + survivor1 = _mm256_or_si256(_mm256_and_si256(decision1,m3),_mm256_andnot_si256(decision1,m2)); + + + + /* Pack each set of decisions into 16 bits */ + + decision0 = _mm256_permute4x64_epi64(decision0,216); + decision1 = _mm256_permute4x64_epi64(decision1,216); + + __m256i packed = _mm256_packus_epi16( _mm256_srli_epi16(_mm256_unpacklo_epi16(decision0,decision1),8),_mm256_srli_epi16(_mm256_unpackhi_epi16(decision0,decision1),8)); + + d->w[i] = _mm256_movemask_epi8(packed); + + unsigned char temp_char1 = d->c[4*i + 1]; + unsigned char temp_char2 = d->c[4*i + 2]; + + d->c[4*i+1] = temp_char2; + d->c[4*i+2] = temp_char1; + + /* Store surviving metrics */ + survivor0 = _mm256_permute4x64_epi64(survivor0,216); + survivor1 = _mm256_permute4x64_epi64(survivor1,216); + + vp->new_metrics->v[2*i] = _mm256_unpacklo_epi16(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm256_unpackhi_epi16(survivor0,survivor1); + + } + + // See if we need to normalize + if (vp->new_metrics->c[0] > 12288) { + int i; + + uint16_t adjust; + __m256i adjustv; + union { __m256i v; signed short w[8]; } t; + + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4;i++) { + adjustv = _mm256_min_epu16(adjustv,vp->new_metrics->v[i]); + } + + adjustv = _mm256_min_epu16(adjustv,_mm256_srli_si256(adjustv,16)); + adjustv = _mm256_min_epu16(adjustv,_mm256_srli_si256(adjustv,8)); + adjustv = _mm256_min_epu16(adjustv,_mm256_srli_si256(adjustv,4)); + + + t.v = adjustv; + adjust = t.w[0]; + adjustv = _mm256_set1_epi16(adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<4;i++) + vp->new_metrics->v[i] = _mm256_sub_epi16(vp->new_metrics->v[i],adjustv); + } + + + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + + if (best_state) { + uint32_t i, bst=0; + + uint16_t minmetric= UINT16_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->c[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->c[i]; + } + } + *best_state = bst; + } + + #ifdef DEBUG + printf("];\n===========================================\n"); +#endif + + vp->dp = d; +} + +#endif + + + diff --git a/lib/src/phy/fec/viterbi37_neon.c b/lib/src/phy/fec/viterbi37_neon.c new file mode 100644 index 0000000..82ed190 --- /dev/null +++ b/lib/src/phy/fec/viterbi37_neon.c @@ -0,0 +1,354 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=15 r=1/6 Viterbi decoder for ARM NEON + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "parity.h" + +//#define DEBUG +//#define HAVE_NEON +#ifdef HAVE_NEON + +#include + +typedef union { + unsigned char c[64]; + uint8x16_t v[4]; +} metric_t; + + +typedef union { + unsigned long w[2]; + unsigned char c[8]; + unsigned short s[4]; + uint8x8_t v[1]; +} decision_t; + + +union branchtab27{ + unsigned char c[32]; + uint8x16_t v[2]; +} Branchtab37_neon[3]; + + int8_t __attribute__((aligned(16))) xr[8]; + uint8x8_t mask_and; + int8x8_t mask_shift; + + +int firstGo; +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void set_viterbi37_polynomial_neon(int polys[3]) { + int state; + + for(state=0;state < 32;state++){ + Branchtab37_neon[0].c[state] = (polys[0] < 0) ^ parity((2*state) & polys[0]) ? 255:0; + Branchtab37_neon[1].c[state] = (polys[1] < 0) ^ parity((2*state) & polys[1]) ? 255:0; + Branchtab37_neon[2].c[state] = (polys[2] < 0) ^ parity((2*state) & polys[2]) ? 255:0; + } +} + +void clear_v37_neon(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_neon(void *p, int starting_state) { + struct v37 *vp = p; + uint32_t i; + firstGo = 1; + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + clear_v37_neon(vp); + for(int i = 0; i <8;i++) + xr[i] = i-7; + + mask_and = vdup_n_u8(0x80); + mask_shift = vld1_s8(xr); + + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + if (starting_state != -1) { + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + } + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_neon(int polys[3], uint32_t len) { + void *p; + struct v37 *vp; + + set_viterbi37_polynomial_neon(polys); + + /* Ordinary malloc() only returns 8-byte alignment, we need 16 */ + if(posix_memalign(&p, sizeof(uint8x16_t),sizeof(struct v37))) + return NULL; + + vp = (struct v37 *)p; + if(posix_memalign(&p, sizeof(uint8x16_t),(len+6)*sizeof(decision_t))) { + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + vp->len = len+6; + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi37_neon( + void *p, + uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + + if (p == NULL) + return -1; + + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits--) { + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + //printf("nbits=%d, endstate=%3d, k=%d, w[0]=%d, w[1]=%d, c=%d\n", nbits, endstate, k, d[nbits].s[1]&1, d[nbits].s[2]&1, d[nbits].c[(endstate>>2)/8]&1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_neon(void *p){ + struct v37 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +void print_uint8x16_t(char *s, uint8x16_t val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<16;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + +static inline int movemask_neon(uint8x16_t movemask_low_in) +{ + uint8x8_t lo = vget_low_u8(movemask_low_in); + uint8x8_t hi = vget_high_u8(movemask_low_in); + lo = vand_u8(lo, mask_and); + lo = vshl_u8(lo, mask_shift); + hi = vand_u8(hi, mask_and); + hi = vshl_u8(hi, mask_shift); + + lo = vpadd_u8(lo, lo); + lo = vpadd_u8(lo, lo); + lo = vpadd_u8(lo, lo); + + hi = vpadd_u8(hi, hi); + hi = vpadd_u8(hi, hi); + hi = vpadd_u8(hi, hi); + + return ((hi[0] << 8) | (lo[0] & 0xFF)); +} + +void update_viterbi37_blk_neon(void *p,unsigned char *syms,int nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + uint8_t thirtyone; + thirtyone = 31; + if(p == NULL) + return; + +#ifdef DEBUG + printf("["); +#endif + + d = (decision_t *) vp->dp; + + for (int s=0;sold_metrics->v[i],metric); + m3 = vaddq_u8(vp->old_metrics->v[2+i],metric); + m1 = vaddq_u8(vp->old_metrics->v[2+i],m_metric); + m2 = vaddq_u8(vp->old_metrics->v[i],m_metric); + + /* Compare and select, using modulo arithmetic */ + + + decision0 = (uint8x16_t)vcgtq_s8(vsubq_s8((int8x16_t)m0,(int8x16_t)m1),vdupq_n_s8(0)); + decision1 = (uint8x16_t)vcgtq_s8(vsubq_s8((int8x16_t)m2,(int8x16_t)m3),vdupq_n_s8(0)); + survivor0 = vorrq_u8(vandq_u8(decision0,m1),vandq_u8(vmvnq_u8(decision0),m0)); + survivor1 = vorrq_u8 (vandq_u8(decision1,m3),vandq_u8(vmvnq_u8(decision1),m2) ); + + ////// equal to _mm_unpacklo_epi8 ////////// + uint8x8_t a1 = vget_low_u8(decision0); + uint8x8_t b1 = vget_low_u8(decision1); + uint8x8x2_t result = vzip_u8(a1, b1); + uint8x16_t movemask_low_in = vcombine_u8(result.val[0], result.val[1]); + ///////////////////////////////////////// + + + ////////equal to _mm_movemask_epi8 //////// + d->s[2*i] = movemask_neon(movemask_low_in); + + ///////equal to _mm_unpackhi_epi8//////////// + a1 = vget_high_u8(decision0); + b1 = vget_high_u8(decision1); + result = vzip_u8(a1, b1); + uint8x16_t movemask_hi_in = vcombine_u8(result.val[0], result.val[1]); + + + + ////////equal to _mm_movemask////////////// + d->s[2*i+1] = movemask_neon(movemask_hi_in); + + + a1 = vget_low_u8(survivor0); + b1 = vget_low_u8(survivor1); + result = vzip_u8(a1, b1); + vp->new_metrics->v[2*i] = vcombine_u8(result.val[0], result.val[1]); + + + a1 = vget_high_u8(survivor0); + b1 = vget_high_u8(survivor1); + result = vzip_u8(a1, b1); + vp->new_metrics->v[2*i+1] = vcombine_u8(result.val[0], result.val[1]); + + + + } + + // See if we need to normalize + if (vp->new_metrics->c[0] > 100) { + int i; + uint8_t adjust; + uint8x16_t adjustv; + + union { uint8x16_t v; signed short w[8]; } t; + + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4;i++) + { + adjustv = vminq_u8(vp->new_metrics->v[i],adjustv); + } + + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (8))); + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (4))); + adjustv = vminq_u8(adjustv,vextq_u8(adjustv, vdupq_n_u8(0), (2))); + t.v = adjustv; + adjust = t.w[0]; + adjustv = vld1q_dup_u8(&adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<4;i++) + { + vp->new_metrics->v[i] = vsubq_u8(vp->new_metrics->v[i],adjustv); + } + + } + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + //firstGo = 0; + } + + if (best_state) { + uint32_t i, bst=0; + uint8_t minmetric=UINT8_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->c[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->c[i]; + } + } + *best_state = bst; + } + + #ifdef DEBUG + printf("];\n===========================================\n"); +#endif + + vp->dp = d; +} + +#endif + + + diff --git a/lib/src/phy/fec/viterbi37_port.c b/lib/src/phy/fec/viterbi37_port.c new file mode 100644 index 0000000..db2acc7 --- /dev/null +++ b/lib/src/phy/fec/viterbi37_port.c @@ -0,0 +1,235 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=9 r=1/3 Viterbi decoder in portable C + * Copyright Aug 2006, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ +#include +#include +#include + +#include +#include "viterbi37.h" +#include "parity.h" +#include + +//#define DEBUG + +typedef union { + uint32_t w[64]; +} metric_t; +typedef union { + unsigned long w[2]; +} decision_t; + +static union { + uint8_t c[32]; +} Branchtab37[3]; + +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics, *new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void clear_v37(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_port(void *p, int starting_state) { + struct v37 *vp = p; + uint32_t i; + + if (p == NULL) + return -1; + + clear_v37(vp); + + for (i = 0; i < 64; i++) + vp->metrics1.w[i] = 63; + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + if (starting_state != -1) { + vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */ + } + return 0; +} + +void set_viterbi37_polynomial_port(int polys[3]) { + int state; + + for (state = 0; state < 32; state++) { + Branchtab37[0].c[state] = + (polys[0] < 0) ^ parity((2 * state) & abs(polys[0])) ? 255 : 0; + Branchtab37[1].c[state] = + (polys[1] < 0) ^ parity((2 * state) & abs(polys[1])) ? 255 : 0; + Branchtab37[2].c[state] = + (polys[2] < 0) ^ parity((2 * state) & abs(polys[2])) ? 255 : 0; + } +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_port(int polys[3], uint32_t len) { + struct v37 *vp; + + set_viterbi37_polynomial_port(polys); + + if ((vp = (struct v37 *) malloc(sizeof(struct v37))) == NULL) + return NULL ; + + if ((vp->decisions = (decision_t *) malloc((len + 6) * sizeof(decision_t))) + == NULL) { + free(vp); + return NULL ; + } + + vp->len = len+6; + + return vp; +} + +/* Viterbi chainback */ +int chainback_viterbi37_port(void *p, uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + decision_t *d; + + if (p == NULL) + return -1; + + d = vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while (nbits-- != 0) { + int k; + + k = (d[nbits].w[(endstate >> 2) / 32] >> ((endstate >> 2) % 32)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; +#ifdef DEBUG + // printf("endstate=%3d, k=%d, w[0]=%d, w[1]=%d\n", endstate, k, d[nbits].w[0]&1, d[nbits].w[1]&1); +#endif + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_port(void *p) { + struct v37 *vp = p; + + if (vp != NULL) { + free(vp->decisions); + free(vp); + } +} + +/* C-language butterfly */ +#define BFLY(i) {\ +uint32_t metric,m0,m1,decision;\ + metric = (Branchtab37[0].c[i] ^ sym0) + (Branchtab37[1].c[i] ^ sym1) + \ + (Branchtab37[2].c[i] ^ sym2);\ + m0 = vp->old_metrics->w[i] + metric;\ + m1 = vp->old_metrics->w[i+32] + (765 - metric);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i)&31);\ + m0 -= (metric+metric-765);\ + m1 += (metric+metric-765);\ + decision = (signed int)(m0-m1) > 0;\ + vp->new_metrics->w[2*i+1] = decision ? m1 : m0;\ + d->w[i/16] |= decision << ((2*i+1)&31);\ +} + +/* Update decoder with a block of demodulated symbols + * Note that nbits is the number of decoded data bits, not the number + * of symbols! + */ + +int update_viterbi37_blk_port(void *p, uint8_t *syms, uint32_t nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + if (p == NULL) + return -1; + uint32_t k=0; + d = (decision_t *) vp->dp; + +#ifdef DEBUG + printf("["); +#endif + + while (nbits--) { + void *tmp; + uint8_t sym0, sym1, sym2; + uint32_t i; + + d->w[0] = d->w[1] = 0; + + sym0 = *syms++; + sym1 = *syms++; + sym2 = *syms++; + + k++; + for (i = 0; i < 32; i++) + BFLY(i); + +#ifdef DEBUG + uint32_t wmin=UINT_MAX; + int minstate = 0; + for (int j=0;j<64;j++) { + if (vp->new_metrics->w[j] <= wmin) { + wmin = vp->new_metrics->w[j]; + minstate = j; + } + } + + printf("%3d, ", minstate); +#endif + + d++; + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + if (best_state) { + uint32_t i, bst=0; + uint32_t minmetric=UINT_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->w[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->w[i]; + } + } + *best_state = bst; + } + vp->dp = d; + +#ifdef DEBUG + printf("];\n"); +#endif + + return 0; +} diff --git a/lib/src/phy/fec/viterbi37_sse.c b/lib/src/phy/fec/viterbi37_sse.c new file mode 100644 index 0000000..d6dc6a9 --- /dev/null +++ b/lib/src/phy/fec/viterbi37_sse.c @@ -0,0 +1,295 @@ +/* Adapted Phil Karn's r=1/3 k=9 viterbi decoder to r=1/3 k=7 + * + * K=15 r=1/6 Viterbi decoder for x86 SSE2 + * Copyright Mar 2004, Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + */ + +#include +#include +#include +#include +#include +#include "parity.h" + +//#define DEBUG + +#ifdef LV_HAVE_SSE + +#include + +typedef union { + unsigned char c[64]; + __m128i v[4]; +} metric_t; +typedef union { + unsigned long w[2]; + unsigned char c[8]; + unsigned short s[4]; + __m64 v[1]; +} decision_t; + +union branchtab27 { + unsigned char c[32]; + __m128i v[2]; +} Branchtab37_sse2[3]; + + +/* State info for instance of Viterbi decoder */ +struct v37 { + metric_t metrics1; /* path metric buffer 1 */ + metric_t metrics2; /* path metric buffer 2 */ + decision_t *dp; /* Pointer to current decision */ + metric_t *old_metrics,*new_metrics; /* Pointers to path metrics, swapped on every bit */ + decision_t *decisions; /* Beginning of decisions for block */ + uint32_t len; +}; + +void set_viterbi37_polynomial_sse(int polys[3]) { + int state; + + for(state=0;state < 32;state++){ + Branchtab37_sse2[0].c[state] = (polys[0] < 0) ^ parity((2*state) & polys[0]) ? 255:0; + Branchtab37_sse2[1].c[state] = (polys[1] < 0) ^ parity((2*state) & polys[1]) ? 255:0; + Branchtab37_sse2[2].c[state] = (polys[2] < 0) ^ parity((2*state) & polys[2]) ? 255:0; + } +} + +void clear_v37_sse(struct v37 *vp) { + bzero(vp->decisions, sizeof(decision_t)*vp->len); + vp->dp = NULL; + bzero(&vp->metrics1, sizeof(metric_t)); + bzero(&vp->metrics2, sizeof(metric_t)); + vp->old_metrics = NULL; + vp->new_metrics = NULL; +} + + +/* Initialize Viterbi decoder for start of new frame */ +int init_viterbi37_sse(void *p, int starting_state) { + struct v37 *vp = p; + uint32_t i; + + for(i=0;i<64;i++) + vp->metrics1.c[i] = 63; + + clear_v37_sse(vp); + + vp->old_metrics = &vp->metrics1; + vp->new_metrics = &vp->metrics2; + vp->dp = vp->decisions; + if (starting_state != -1) { + vp->old_metrics->c[starting_state & 63] = 0; /* Bias known start state */ + } + return 0; +} + +/* Create a new instance of a Viterbi decoder */ +void *create_viterbi37_sse(int polys[3], uint32_t len) { + void *p; + struct v37 *vp; + + set_viterbi37_polynomial_sse(polys); + + /* Ordinary malloc() only returns 8-byte alignment, we need 16 */ + if(posix_memalign(&p, sizeof(__m128i),sizeof(struct v37))) + return NULL; + + vp = (struct v37 *)p; + if(posix_memalign(&p, sizeof(__m128i),(len+6)*sizeof(decision_t))) { + free(vp); + return NULL; + } + vp->decisions = (decision_t *)p; + vp->len = len+6; + return vp; +} + + +/* Viterbi chainback */ +int chainback_viterbi37_sse( + void *p, + uint8_t *data, /* Decoded output data */ + uint32_t nbits, /* Number of data bits */ + uint32_t endstate) { /* Terminal encoder state */ + struct v37 *vp = p; + + if (p == NULL) + return -1; + + decision_t *d = (decision_t *)vp->decisions; + + /* Make room beyond the end of the encoder register so we can + * accumulate a full byte of decoded data + */ + endstate %= 64; + endstate <<= 2; + + /* The store into data[] only needs to be done every 8 bits. + * But this avoids a conditional branch, and the writes will + * combine in the cache anyway + */ + d += 6; /* Look past tail */ + while(nbits--) { + int k; + + k = (d[nbits].c[(endstate>>2)/8] >> ((endstate>>2)%8)) & 1; + endstate = (endstate >> 1) | (k << 7); + data[nbits] = k; + //printf("nbits=%d, endstate=%3d, k=%d, w[0]=%d, w[1]=%d, c=%d\n", nbits, endstate, k, d[nbits].s[1]&1, d[nbits].s[2]&1, d[nbits].c[(endstate>>2)/8]&1); + } + return 0; +} + +/* Delete instance of a Viterbi decoder */ +void delete_viterbi37_sse(void *p){ + struct v37 *vp = p; + + if(vp != NULL){ + free(vp->decisions); + free(vp); + } +} + +void print_128i(char *s, __m128i val) { + + printf("%s: ", s); + + uint8_t *x = (uint8_t*) &val; + for (int i=0;i<16;i++) { + printf("%3d, ", x[i]); + } + printf("\n"); +} + +void update_viterbi37_blk_sse(void *p,unsigned char *syms,int nbits, uint32_t *best_state) { + struct v37 *vp = p; + decision_t *d; + + if(p == NULL) + return; + +#ifdef DEBUG + printf("["); +#endif + + d = (decision_t *) vp->dp; + + for (int s=0;sold_metrics->v[i],metric); + m3 = _mm_add_epi8(vp->old_metrics->v[2+i],metric); + m1 = _mm_add_epi8(vp->old_metrics->v[2+i],m_metric); + m2 = _mm_add_epi8(vp->old_metrics->v[i],m_metric); + + /* Compare and select, using modulo arithmetic */ + decision0 = _mm_cmpgt_epi8(_mm_sub_epi8(m0,m1),_mm_setzero_si128()); + decision1 = _mm_cmpgt_epi8(_mm_sub_epi8(m2,m3),_mm_setzero_si128()); + survivor0 = _mm_or_si128(_mm_and_si128(decision0,m1),_mm_andnot_si128(decision0,m0)); + survivor1 = _mm_or_si128(_mm_and_si128(decision1,m3),_mm_andnot_si128(decision1,m2)); + + /* Pack each set of decisions into 16 bits */ + d->s[2*i] = _mm_movemask_epi8(_mm_unpacklo_epi8(decision0,decision1)); + d->s[2*i+1] = _mm_movemask_epi8(_mm_unpackhi_epi8(decision0,decision1)); + + /* Store surviving metrics */ + vp->new_metrics->v[2*i] = _mm_unpacklo_epi8(survivor0,survivor1); + vp->new_metrics->v[2*i+1] = _mm_unpackhi_epi8(survivor0,survivor1); + + } + + // See if we need to normalize + if (vp->new_metrics->c[0] > 100) { + int i; + uint8_t adjust; + __m128i adjustv; + union { __m128i v; signed short w[8]; } t; + + adjustv = vp->new_metrics->v[0]; + for(i=1;i<4;i++) { + adjustv = _mm_min_epu8(adjustv,vp->new_metrics->v[i]); + } + + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,8)); + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,4)); + adjustv = _mm_min_epu8(adjustv,_mm_srli_si128(adjustv,2)); + + t.v = adjustv; + adjust = t.w[0]; + adjustv = _mm_set1_epi8(adjust); + + /* We cannot use a saturated subtract, because we often have to adjust by more than SHRT_MAX + * This is okay since it can't overflow anyway + */ + for(i=0;i<4;i++) + vp->new_metrics->v[i] = _mm_sub_epi8(vp->new_metrics->v[i],adjustv); + } + + + d++; + /* Swap pointers to old and new metrics */ + tmp = vp->old_metrics; + vp->old_metrics = vp->new_metrics; + vp->new_metrics = tmp; + } + + if (best_state) { + uint32_t i, bst=0; + uint8_t minmetric=UINT8_MAX; + for (i=0;i<64;i++) { + if (vp->old_metrics->c[i] <= minmetric) { + bst = i; + minmetric = vp->old_metrics->c[i]; + } + } + *best_state = bst; + } + + #ifdef DEBUG + printf("];\n===========================================\n"); +#endif + + vp->dp = d; +} + +#endif + + + diff --git a/lib/src/phy/io/CMakeLists.txt b/lib/src/phy/io/CMakeLists.txt new file mode 100644 index 0000000..a56658e --- /dev/null +++ b/lib/src/phy/io/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_io OBJECT ${SOURCES}) diff --git a/lib/src/phy/io/binsource.c b/lib/src/phy/io/binsource.c new file mode 100644 index 0000000..f78bfc4 --- /dev/null +++ b/lib/src/phy/io/binsource.c @@ -0,0 +1,135 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/io/binsource.h" +#include "srslte/phy/utils/bit.h" + +#define DIV(a,b) ((a-1)/b+1) + + +/* Internal functions */ +static int gen_seq_buff(srslte_binsource_t* q, int nwords) { + if (q->seq_buff_nwords != nwords) { + free(q->seq_buff); + q->seq_buff_nwords = 0; + } + if (!q->seq_buff_nwords) { + q->seq_buff = malloc(nwords*sizeof(uint32_t)); + if (!q->seq_buff) { + return -1; + } + q->seq_buff_nwords = nwords; + } + for (int i=0;iseq_buff_nwords;i++) { + q->seq_buff[i] = rand_r(&q->seed); + } + return 0; +} + +/* Low-level API */ + +/** + * Initializes the binsource object. + */ +void srslte_binsource_init(srslte_binsource_t* q) { + bzero((void*) q,sizeof(srslte_binsource_t)); +} + +/** + * Destroys binsource object + */ +void srslte_binsource_free(srslte_binsource_t* q) { + if (q->seq_buff) { + free(q->seq_buff); + } + bzero(q, sizeof(srslte_binsource_t)); +} + +/** + * Sets a new seed + */ +void srslte_binsource_seed_set(srslte_binsource_t* q, uint32_t seed) { + q->seed = seed; +} + +/** + * Sets local time as seed. + */ +void srslte_binsource_seed_time(srslte_binsource_t *q) { + struct timeval t1; + gettimeofday(&t1, NULL); + q->seed = t1.tv_usec * t1.tv_sec; +} + +/** + * Generates a sequence of nbits random bits + */ +int srslte_binsource_cache_gen(srslte_binsource_t* q, int nbits) { + if (gen_seq_buff(q,DIV(nbits,32))) { + return -1; + } + q->seq_cache_nbits = nbits; + q->seq_cache_rp = 0; + return 0; +} + +static int int_2_bits(uint32_t* src, uint8_t* dst, int nbits) { + int n; + n=nbits/32; + for (int i=0;iseq_cache_rp += int_2_bits(&q->seq_buff[q->seq_cache_rp],bits,nbits); +} + +/** + * Stores in the bits buffer a sequence of nbits pseudo-random bits. + * Overwrites the bits generated using srslte_binsource_cache_gen. + */ +int srslte_binsource_generate(srslte_binsource_t* q, uint8_t *bits, int nbits) { + + if (gen_seq_buff(q,DIV(nbits,32))) { + return -1; + } + int_2_bits(q->seq_buff,bits,nbits); + return 0; +} + + + diff --git a/lib/src/phy/io/filesink.c b/lib/src/phy/io/filesink.c new file mode 100644 index 0000000..a61c417 --- /dev/null +++ b/lib/src/phy/io/filesink.c @@ -0,0 +1,158 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + + +#include "srslte/phy/io/filesink.h" + +int srslte_filesink_init(srslte_filesink_t *q, char *filename, srslte_datatype_t type) { + bzero(q, sizeof(srslte_filesink_t)); + q->f = fopen(filename, "w"); + if (!q->f) { + perror("fopen"); + return -1; + } + q->type = type; + return 0; +} + +void srslte_filesink_free(srslte_filesink_t *q) { + if (q->f) { + fclose(q->f); + } + bzero(q, sizeof(srslte_filesink_t)); +} + +int srslte_filesink_write(srslte_filesink_t *q, void *buffer, int nsamples) { + int i = 0; + float *fbuf = (float*) buffer; + _Complex float *cbuf = (_Complex float*) buffer; + _Complex short *sbuf = (_Complex short*) buffer; + int size; + + switch(q->type) { + case SRSLTE_FLOAT: + for (i=0;if,"%g\n",fbuf[i]); + } + break; + case SRSLTE_COMPLEX_FLOAT: + for (i=0;i= 0) + fprintf(q->f,"%g+%gi\n",__real__ cbuf[i],__imag__ cbuf[i]); + else + fprintf(q->f,"%g-%gi\n",__real__ cbuf[i],fabsf(__imag__ cbuf[i])); + } + break; + case SRSLTE_COMPLEX_SHORT: + for (i=0;i= 0) + fprintf(q->f,"%hd+%hdi\n",__real__ sbuf[i],__imag__ sbuf[i]); + else + fprintf(q->f,"%hd-%hdi\n",__real__ sbuf[i],(short) abs(__imag__ sbuf[i])); + } + break; + case SRSLTE_FLOAT_BIN: + case SRSLTE_COMPLEX_FLOAT_BIN: + case SRSLTE_COMPLEX_SHORT_BIN: + if (q->type == SRSLTE_FLOAT_BIN) { + size = sizeof(float); + } else if (q->type == SRSLTE_COMPLEX_FLOAT_BIN) { + size = sizeof(_Complex float); + } else if (q->type == SRSLTE_COMPLEX_SHORT_BIN) { + size = sizeof(_Complex short); + } + return fwrite(buffer, size, nsamples, q->f); + break; + default: + i = -1; + break; + } + return i; +} + +int srslte_filesink_write_multi(srslte_filesink_t *q, void **buffer, int nsamples, int nchannels) { + int i, j; + float **fbuf = (float**) buffer; + _Complex float **cbuf = (_Complex float**) buffer; + _Complex short **sbuf = (_Complex short**) buffer; + int size; + + switch(q->type) { + case SRSLTE_FLOAT: + for (i=0;if, "%g%c", fbuf[j][i], (j!=(nchannels-1))?'\t':'\n'); + } + } + break; + case SRSLTE_COMPLEX_FLOAT: + for (i=0;if, "%g%+gi%c", __real__ cbuf[j][i], __imag__ cbuf[j][i], (j!=(nchannels-1))?'\t':'\n'); + } + } + break; + case SRSLTE_COMPLEX_SHORT: + for (i=0;if, "%hd%+hdi%c", __real__ sbuf[j][i], __imag__ sbuf[j][i], (j!=(nchannels-1))?'\t':'\n'); + } + } + break; + case SRSLTE_FLOAT_BIN: + case SRSLTE_COMPLEX_FLOAT_BIN: + case SRSLTE_COMPLEX_SHORT_BIN: + if (q->type == SRSLTE_FLOAT_BIN) { + size = sizeof(float); + } else if (q->type == SRSLTE_COMPLEX_FLOAT_BIN) { + size = sizeof(_Complex float); + } else if (q->type == SRSLTE_COMPLEX_SHORT_BIN) { + size = sizeof(_Complex short); + } + if (nchannels > 1) { + uint32_t count = 0; + for (i = 0; i < nsamples; i++) { + for (j = 0; j < nchannels; j++) { + count += fwrite(&cbuf[j][i], size, 1, q->f); + } + } + return count; + } else { + return fwrite(buffer[0], size, nsamples, q->f); + } + break; + default: + i = -1; + break; + } + return i; +} \ No newline at end of file diff --git a/lib/src/phy/io/filesource.c b/lib/src/phy/io/filesource.c new file mode 100644 index 0000000..1324bf2 --- /dev/null +++ b/lib/src/phy/io/filesource.c @@ -0,0 +1,146 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/io/filesource.h" + +int srslte_filesource_init(srslte_filesource_t *q, char *filename, srslte_datatype_t type) { + bzero(q, sizeof(srslte_filesource_t)); + q->f = fopen(filename, "r"); + if (!q->f) { + perror("fopen"); + return -1; + } + q->type = type; + return 0; +} + +void srslte_filesource_free(srslte_filesource_t *q) { + if (q->f) { + fclose(q->f); + } + bzero(q, sizeof(srslte_filesource_t)); +} + +void srslte_filesource_seek(srslte_filesource_t *q, int pos) { + if (!fseek(q->f, pos, SEEK_SET)){ + perror("srslte_filesource_seek"); + } +} + +int read_complex_f(FILE *f, _Complex float *y) { + char in_str[64]; + _Complex float x; + if (NULL == fgets(in_str, 64, f)) { + return -1; + } else { + if (index(in_str, 'i') || index(in_str, 'j')) { + sscanf(in_str,"%f%fi",&(__real__ x),&(__imag__ x)); + } else { + __imag__ x = 0; + sscanf(in_str,"%f",&(__real__ x)); + } + *y = x; + return 0; + } +} + +int srslte_filesource_read(srslte_filesource_t *q, void *buffer, int nsamples) { + int i; + float *fbuf = (float*) buffer; + _Complex float *cbuf = (_Complex float*) buffer; + _Complex short *sbuf = (_Complex short*) buffer; + int size; + + switch(q->type) { + case SRSLTE_FLOAT: + for (i=0;if,"%g\n",&fbuf[i])) + break; + } + break; + case SRSLTE_COMPLEX_FLOAT: + for (i=0;if, &cbuf[i])) { + break; + } + } + break; + case SRSLTE_COMPLEX_SHORT: + for (i=0;if,"%hd%hdi\n",&(__real__ sbuf[i]),&(__imag__ sbuf[i]))) + break; + } + break; + case SRSLTE_FLOAT_BIN: + case SRSLTE_COMPLEX_FLOAT_BIN: + case SRSLTE_COMPLEX_SHORT_BIN: + if (q->type == SRSLTE_FLOAT_BIN) { + size = sizeof(float); + } else if (q->type == SRSLTE_COMPLEX_FLOAT_BIN) { + size = sizeof(_Complex float); + } else if (q->type == SRSLTE_COMPLEX_SHORT_BIN) { + size = sizeof(_Complex short); + } + return fread(buffer, size, nsamples, q->f); + break; + default: + i = -1; + break; + } + return i; +} + +int srslte_filesource_read_multi(srslte_filesource_t *q, void **buffer, int nsamples, int nof_channels) { + int i, j, count = 0; + _Complex float **cbuf = (_Complex float **) buffer; + + switch (q->type) { + case SRSLTE_FLOAT: + case SRSLTE_COMPLEX_FLOAT: + case SRSLTE_COMPLEX_SHORT: + case SRSLTE_FLOAT_BIN: + case SRSLTE_COMPLEX_SHORT_BIN: + fprintf(stderr, "%s.%d:Read Mode not implemented\n", __FILE__, __LINE__); + count = SRSLTE_ERROR; + break; + case SRSLTE_COMPLEX_FLOAT_BIN: + for (i = 0; i < nsamples; i++) { + for (j = 0; j < nof_channels; j++) { + count += fread(&cbuf[j][i], sizeof(cf_t), (size_t) 1, q->f); + } + } + break; + default: + count = SRSLTE_ERROR; + break; + } + return count; +} \ No newline at end of file diff --git a/lib/src/phy/io/netsink.c b/lib/src/phy/io/netsink.c new file mode 100644 index 0000000..ac01df9 --- /dev/null +++ b/lib/src/phy/io/netsink.c @@ -0,0 +1,116 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "srslte/phy/io/netsink.h" + +int srslte_netsink_init(srslte_netsink_t *q, const char *address, uint16_t port, srslte_netsink_type_t type) { + bzero(q, sizeof(srslte_netsink_t)); + + q->sockfd=socket(AF_INET, type==SRSLTE_NETSINK_TCP?SOCK_STREAM:SOCK_DGRAM,0); + if (q->sockfd < 0) { + perror("socket"); + return -1; + } + + int enable = 1; +#if defined (SO_REUSEADDR) + if (setsockopt(q->sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + perror("setsockopt(SO_REUSEADDR) failed"); +#endif +#if defined (SO_REUSEPORT) + if (setsockopt(q->sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) + perror("setsockopt(SO_REUSEPORT) failed"); +#endif + + q->servaddr.sin_family = AF_INET; + q->servaddr.sin_addr.s_addr=inet_addr(address); + q->servaddr.sin_port=htons(port); + q->connected = false; + q->type = type; + + return 0; +} + +void srslte_netsink_free(srslte_netsink_t *q) { + if (q->sockfd) { + close(q->sockfd); + } + bzero(q, sizeof(srslte_netsink_t)); +} + +int srslte_netsink_set_nonblocking(srslte_netsink_t *q) { + if (fcntl(q->sockfd, F_SETFL, O_NONBLOCK)) { + perror("fcntl"); + return -1; + } + return 0; +} + +int srslte_netsink_write(srslte_netsink_t *q, void *buffer, int nof_bytes) { + if (!q->connected) { + if (connect(q->sockfd,&q->servaddr,sizeof(q->servaddr)) < 0) { + if (errno == ECONNREFUSED || errno == EINPROGRESS) { + return 0; + } else { + perror("connect"); + exit(-1); + return -1; + } + } else { + q->connected = true; + } + } + int n = 0; + if (q->connected) { + n = write(q->sockfd, buffer, nof_bytes); + if (n < 0) { + if (errno == ECONNRESET) { + close(q->sockfd); + q->sockfd=socket(AF_INET, q->type==SRSLTE_NETSINK_TCP?SOCK_STREAM:SOCK_DGRAM,0); + if (q->sockfd < 0) { + perror("socket"); + return -1; + } + q->connected = false; + return 0; + } + } + } + return n; +} + diff --git a/lib/src/phy/io/netsource.c b/lib/src/phy/io/netsource.c new file mode 100644 index 0000000..62fe673 --- /dev/null +++ b/lib/src/phy/io/netsource.c @@ -0,0 +1,136 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/io/netsource.h" + +int srslte_netsource_init(srslte_netsource_t *q, const char *address, uint16_t port, srslte_netsource_type_t type) { + bzero(q, sizeof(srslte_netsource_t)); + + q->sockfd=socket(AF_INET,type==SRSLTE_NETSOURCE_TCP?SOCK_STREAM:SOCK_DGRAM,0); + + if (q->sockfd < 0) { + perror("socket"); + return -1; + } + + // Make sockets reusable + int enable = 1; +#if defined (SO_REUSEADDR) + if (setsockopt(q->sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + perror("setsockopt(SO_REUSEADDR) failed"); +#endif +#if defined (SO_REUSEPORT) + if (setsockopt(q->sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) + perror("setsockopt(SO_REUSEPORT) failed"); +#endif + q->type = type; + + q->servaddr.sin_family = AF_INET; + q->servaddr.sin_addr.s_addr=inet_addr(address); + q->servaddr.sin_port=htons(port); + + if (bind(q->sockfd,(struct sockaddr *)&q->servaddr,sizeof(struct sockaddr_in))) { + perror("bind"); + return -1; + } + q->connfd = 0; + + return 0; +} + +void srslte_netsource_free(srslte_netsource_t *q) { + if (q->sockfd) { + close(q->sockfd); + } + bzero(q, sizeof(srslte_netsource_t)); +} + +int srslte_netsource_read(srslte_netsource_t *q, void *buffer, int nbytes) { + if (q->type == SRSLTE_NETSOURCE_UDP) { + int n = recv(q->sockfd, buffer, nbytes, 0); + + if (n == -1) { + if (errno == EAGAIN) { + return 0; + } else { + return -1; + } + } else { + return n; + } + } else { + if (q->connfd == 0) { + printf("Waiting for TCP connection\n"); + listen(q->sockfd, 1); + socklen_t clilen = sizeof(q->cliaddr); + q->connfd = accept(q->sockfd, (struct sockaddr *)&q->cliaddr, &clilen); + if (q->connfd < 0) { + perror("accept"); + return -1; + } + } + int n = read(q->connfd, buffer, nbytes); + if (n == 0) { + printf("Connection closed\n"); + close(q->connfd); + q->connfd = 0; + return 0; + } + if (n == -1) { + perror("read"); + } + return n; + } +} + +int srslte_netsource_set_nonblocking(srslte_netsource_t *q) { + if (fcntl(q->sockfd, F_SETFL, O_NONBLOCK)) { + perror("fcntl"); + return -1; + } + return 0; +} + +int srslte_netsource_set_timeout(srslte_netsource_t *q, uint32_t microseconds) { + struct timeval t; + t.tv_sec = 0; + t.tv_usec = microseconds; + if (setsockopt(q->sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval))) { + perror("setsockopt"); + return -1; + } + return 0; +} diff --git a/lib/src/phy/mimo/CMakeLists.txt b/lib/src/phy/mimo/CMakeLists.txt new file mode 100644 index 0000000..f909a60 --- /dev/null +++ b/lib/src/phy/mimo/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_mimo OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/mimo/layermap.c b/lib/src/phy/mimo/layermap.c new file mode 100644 index 0000000..6de30fc --- /dev/null +++ b/lib/src/phy/mimo/layermap.c @@ -0,0 +1,221 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/layermap.h" + + + +int srslte_layermap_single(cf_t *d, cf_t *x, int nof_symbols) { + memcpy(x, d, sizeof(cf_t) * nof_symbols); + return nof_symbols; +} + +int srslte_layermap_diversity(cf_t *d, cf_t *x[SRSLTE_MAX_LAYERS], int nof_layers, int nof_symbols) { + int i, j; + for (i=0;i SRSLTE_MAX_CODEWORDS) { + fprintf(stderr, "Maximum number of codewords is %d (nof_cw=%d)\n", SRSLTE_MAX_CODEWORDS, nof_cw); + return -1; + } + if (nof_layers > SRSLTE_MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", SRSLTE_MAX_LAYERS, nof_layers); + return -1; + } + if (nof_layers < nof_cw) { + fprintf(stderr, "Number of codewords must be lower or equal than number of layers\n"); + return -1; + } + + switch(type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_cw == 1 && nof_layers == 1) { + return srslte_layermap_single(x[0], d[0], nof_symbols[0]); + } else { + fprintf(stderr, "Number of codewords and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_cw == 1) { + if (nof_layers == 2 || nof_layers == 4) { + return srslte_layermap_diversity(d[0], x, nof_layers, nof_symbols[0]); + } else { + fprintf(stderr, "Number of layers must be 2 or 4 for transmit diversity\n"); + return -1; + } + } else { + fprintf(stderr, "Number of codewords must be 1 for transmit diversity\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + case SRSLTE_MIMO_TYPE_CDD: + return srslte_layermap_multiplex(d, x, nof_cw, nof_layers, nof_symbols); + break; + } + return 0; +} + + + + + + + + + +int srslte_layerdemap_single(cf_t *x, cf_t *d, int nof_symbols) { + memcpy(d, x, sizeof(cf_t) * nof_symbols); + return nof_symbols; +} +int srslte_layerdemap_diversity(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *d, int nof_layers, int nof_layer_symbols) { + int i, j; + for (i=0;i SRSLTE_MAX_CODEWORDS) { + fprintf(stderr, "Maximum number of codewords is %d (nof_cw=%d)\n", SRSLTE_MAX_CODEWORDS, nof_cw); + return -1; + } + if (nof_layers > SRSLTE_MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", SRSLTE_MAX_LAYERS, nof_layers); + return -1; + } + if (nof_layers < nof_cw) { + fprintf(stderr, "Number of codewords must be lower or equal than number of layers\n"); + return -1; + } + + switch(type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_cw == 1 && nof_layers == 1) { + nof_symbols[0] = srslte_layerdemap_single(x[0], d[0], nof_layer_symbols); + nof_symbols[1] = 0; + } else { + fprintf(stderr, "Number of codewords and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_cw == 1) { + if (nof_layers == 2 || nof_layers == 4) { + nof_symbols[0] = srslte_layerdemap_diversity(x, d[0], nof_layers, nof_layer_symbols); + nof_symbols[1] = 0; + } else { + fprintf(stderr, "Number of layers must be 2 or 4 for transmit diversity\n"); + return -1; + } + } else { + fprintf(stderr, "Number of codewords must be 1 for transmit diversity\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + case SRSLTE_MIMO_TYPE_CDD: + return srslte_layerdemap_multiplex(x, d, nof_layers, nof_cw, nof_layer_symbols, nof_symbols); + break; + } + return 0; +} diff --git a/lib/src/phy/mimo/precoding.c b/lib/src/phy/mimo/precoding.c new file mode 100644 index 0000000..dbf34ab --- /dev/null +++ b/lib/src/phy/mimo/precoding.c @@ -0,0 +1,2924 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/mat.h" +#include "srslte/phy/utils/simd.h" + +#ifdef LV_HAVE_SSE +#include +int srslte_predecoding_single_sse(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS], cf_t *x, int nof_rxant, int nof_symbols, float scaling, float noise_estimate); +int srslte_predecoding_diversity2_sse(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], int nof_rxant, int nof_symbols, float scaling); +#endif + +#ifdef LV_HAVE_AVX +#include +int srslte_predecoding_single_avx(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS], cf_t *x, int nof_rxant, int nof_symbols, float scaling, float noise_estimate); +#endif +#include "srslte/phy/utils/mat.h" + +static srslte_mimo_decoder_t mimo_decoder = SRSLTE_MIMO_DECODER_MMSE; + +/************************************************ + * + * RECEIVER SIDE FUNCTIONS + * + **************************************************/ + +#ifdef LV_HAVE_SSE + +#define PROD(a,b) _mm_addsub_ps(_mm_mul_ps(a,_mm_moveldup_ps(b)),_mm_mul_ps(_mm_shuffle_ps(a,a,0xB1),_mm_movehdup_ps(b))) + +int srslte_predecoding_single_sse(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS], cf_t *x, int nof_rxant, int nof_symbols, float scaling, float noise_estimate) { + + float *xPtr = (float*) x; + const float *hPtr1 = (const float*) h[0]; + const float *yPtr1 = (const float*) y[0]; + const float *hPtr2 = (const float*) h[1]; + const float *yPtr2 = (const float*) y[1]; + + __m128 conjugator = _mm_setr_ps(0, -0.f, 0, -0.f); + + __m128 noise = _mm_set1_ps(noise_estimate); + __m128 h1Val1, h2Val1, y1Val1, y2Val1; + __m128 h1Val2, h2Val2, y1Val2, y2Val2; + __m128 hsquare, h1square, h2square, h1conj1, h2conj1, x1Val1, x2Val1; + __m128 hsquare2, h1conj2, h2conj2, x1Val2, x2Val2; + + for (int i=0;i 0) { + hsquare = _mm_add_ps(hsquare, noise); + } + + h1square = _mm_shuffle_ps(hsquare, hsquare, _MM_SHUFFLE(1, 1, 0, 0)); + h2square = _mm_shuffle_ps(hsquare, hsquare, _MM_SHUFFLE(3, 3, 2, 2)); + + /* Conjugate channel */ + h1conj1 = _mm_xor_ps(h1Val1, conjugator); + h2conj1 = _mm_xor_ps(h2Val1, conjugator); + + if (nof_rxant == 2) { + h1conj2 = _mm_xor_ps(h1Val2, conjugator); + h2conj2 = _mm_xor_ps(h2Val2, conjugator); + } + + /* Complex product */ + x1Val1 = PROD(y1Val1, h1conj1); + x2Val1 = PROD(y2Val1, h2conj1); + + if (nof_rxant == 2) { + x1Val2 = PROD(y1Val2, h1conj2); + x2Val2 = PROD(y2Val2, h2conj2); + x1Val1 = _mm_add_ps(x1Val1, x1Val2); + x2Val1 = _mm_add_ps(x2Val1, x2Val2); + } + + x1Val1 = _mm_div_ps(x1Val1, h1square); + x2Val1 = _mm_div_ps(x2Val1, h2square); + + x1Val1 = _mm_mul_ps(x1Val1, _mm_set1_ps(1/scaling)); + x2Val1 = _mm_mul_ps(x2Val1, _mm_set1_ps(1/scaling)); + + _mm_store_ps(xPtr, x1Val1); xPtr+=4; + _mm_store_ps(xPtr, x2Val1); xPtr+=4; + + } + for (int i=8*(nof_symbols/8);i 0) { + h12square = _mm256_add_ps(h12square, noise); + } + + h1_p = _mm256_permute_ps(h12square, _MM_SHUFFLE(1, 1, 0, 0)); + h2_p = _mm256_permute_ps(h12square, _MM_SHUFFLE(3, 3, 2, 2)); + h1square = _mm256_permute2f128_ps(h1_p, h2_p, 2<<4); + h2square = _mm256_permute2f128_ps(h1_p, h2_p, 3<<4 | 1); + + /* Conjugate channel */ + h1conj1 = _mm256_xor_ps(h1Val1, conjugator); + h2conj1 = _mm256_xor_ps(h2Val1, conjugator); + + if (nof_rxant == 2) { + h1conj2 = _mm256_xor_ps(h1Val2, conjugator); + h2conj2 = _mm256_xor_ps(h2Val2, conjugator); + } + + /* Complex product */ + x1Val = PROD_AVX(y1Val1, h1conj1); + x2Val = PROD_AVX(y2Val1, h2conj1); + + if (nof_rxant == 2) { + x1Val = _mm256_add_ps(x1Val, PROD_AVX(y1Val2, h1conj2)); + x2Val = _mm256_add_ps(x2Val, PROD_AVX(y2Val2, h2conj2)); + } + + x1Val = _mm256_div_ps(x1Val, h1square); + x2Val = _mm256_div_ps(x2Val, h2square); + + x1Val = _mm256_mul_ps(x1Val, avx_scaling); + x2Val = _mm256_mul_ps(x2Val, avx_scaling); + + _mm256_store_ps(xPtr, x1Val); xPtr+=8; + _mm256_store_ps(xPtr, x2Val); xPtr+=8; + } + for (int i=16*(nof_symbols/16);i 32 && nof_rxant <= 2) { + return srslte_predecoding_single_avx(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } else { + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } +#else + #ifdef LV_HAVE_SSE + if (nof_symbols > 32 && nof_rxant <= 2) { + return srslte_predecoding_single_sse(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } else { + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } + #else + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + #endif +#endif +} + +/* ZF/MMSE SISO equalizer x=y(h'h+no)^(-1)h' (ZF if n0=0.0)*/ +int srslte_predecoding_single_multi(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS], cf_t *x, float *csi[SRSLTE_MAX_CODEWORDS], + int nof_rxant, int nof_symbols, float scaling, float noise_estimate) { + if (csi && csi[0]) { + return srslte_predecoding_single_csi(y, h, x, csi[0], nof_rxant, nof_symbols, scaling, noise_estimate); + } + +#ifdef LV_HAVE_AVX + if (nof_symbols > 32) { + return srslte_predecoding_single_avx(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } else { + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } +#else + #ifdef LV_HAVE_SSE + if (nof_symbols > 32) { + return srslte_predecoding_single_sse(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } else { + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + } + #else + return srslte_predecoding_single_gen(y, h, x, nof_rxant, nof_symbols, scaling, noise_estimate); + #endif +#endif +} + +/* C implementatino of the SFBC equalizer */ +int srslte_predecoding_diversity_gen_(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], + int nof_rxant, int nof_ports, int nof_symbols, int symbol_start, float scaling) +{ + int i; + if (nof_ports == 2) { + cf_t h00, h01, h10, h11, r0, r1; + + for (i = symbol_start/2; i < nof_symbols / 2; i++) { + float hh = 0; + cf_t x0 = 0; + cf_t x1 = 0; + for (int p=0;p 32 && nof_ports == 2) { + return srslte_predecoding_diversity2_sse(y, h, x, nof_rxant, nof_symbols, scaling); + } else { + return srslte_predecoding_diversity_gen(y, h, x, nof_rxant, nof_ports, nof_symbols, scaling); + } +#else + return srslte_predecoding_diversity_gen(y, h, x, nof_rxant, nof_ports, nof_symbols, scaling); +#endif +} + +int srslte_predecoding_diversity_csi(cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + cf_t *x[SRSLTE_MAX_LAYERS], float *csi[SRSLTE_MAX_CODEWORDS], + int nof_rxant, int nof_ports, int nof_symbols, float scaling) { + int i; + if (nof_ports == 2) { + cf_t h00, h01, h10, h11, r0, r1; + + for (i = 0; i < nof_symbols / 2; i++) { + float hh = 0; + cf_t x0 = 0; + cf_t x1 = 0; + for (int p=0;p 32 && nof_ports == 2) { + return srslte_predecoding_diversity2_sse(y, h, x, nof_rxant, nof_symbols, scaling); + } else { + return srslte_predecoding_diversity_gen(y, h, x, nof_rxant, nof_ports, nof_symbols, scaling); + } +#else + return srslte_predecoding_diversity_gen(y, h, x, nof_rxant, nof_ports, nof_symbols, scaling); +#endif + } +} + +int srslte_precoding_mimo_2x2_gen(cf_t W[2][2], cf_t *y[SRSLTE_MAX_PORTS], cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], cf_t *x[SRSLTE_MAX_LAYERS], + int nof_symbols, float scaling, float noise_estimate) +{ + + cf_t G[2][2], Gx[2][2]; + + for (int i=0; i SRSLTE_MAX_PORTS) { + fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", SRSLTE_MAX_PORTS, + nof_ports); + return -1; + } + if (nof_layers > SRSLTE_MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", + SRSLTE_MAX_LAYERS, nof_layers); + return -1; + } + + switch (type) { + case SRSLTE_MIMO_TYPE_CDD: + if (nof_layers >= 2 && nof_layers <= 4) { + switch (mimo_decoder) { + case SRSLTE_MIMO_DECODER_ZF: + return srslte_predecoding_ccd_zf(y, h, x, csi, nof_rxant, nof_ports, nof_layers, nof_symbols, scaling); + break; + case SRSLTE_MIMO_DECODER_MMSE: + return srslte_predecoding_ccd_mmse(y, h, x, csi, nof_rxant, nof_ports, nof_layers, nof_symbols, scaling, noise_estimate); + break; + } + } else { + DEBUG("Invalid number of layers %d\n", nof_layers); + return -1; + } + return -1; + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_ports == 1 && nof_layers == 1) { + return srslte_predecoding_single_multi(y, h[0], x[0], csi, nof_rxant, nof_symbols, scaling, noise_estimate); + } else { + fprintf(stderr, + "Number of ports and layers must be 1 for transmission on single antenna ports (%d, %d)\n", nof_ports, nof_layers); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_ports == nof_layers) { + return srslte_predecoding_diversity_multi(y, h, x, csi, nof_rxant, nof_ports, nof_symbols, scaling); + } else { + fprintf(stderr, + "Error number of layers must equal number of ports in transmit diversity\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + return srslte_predecoding_multiplex(y, h, x, csi, nof_rxant, nof_ports, nof_layers, codebook_idx, nof_symbols, + scaling, noise_estimate); + default: + return SRSLTE_ERROR; + } + return SRSLTE_ERROR; +} + + + + + + +/************************************************ + * + * TRANSMITTER SIDE FUNCTIONS + * + **************************************************/ + +int srslte_precoding_single(cf_t *x, cf_t *y, int nof_symbols, float scaling) { + if (scaling == 1.0f) { + memcpy(y, x, nof_symbols * sizeof(cf_t)); + } else { + srslte_vec_sc_prod_cfc(x, scaling, y, (uint32_t) nof_symbols); + } + return nof_symbols; +} +int srslte_precoding_diversity(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_ports, + int nof_symbols, float scaling) { + int i; + if (nof_ports == 2) { + for (i = 0; i < nof_symbols; i++) { + y[0][2 * i] = x[0][i]; + y[1][2 * i] = -conjf(x[1][i]); + y[0][2 * i + 1] = x[1][i]; + y[1][2 * i + 1] = conjf(x[0][i]); + } + // normalize + srslte_vec_sc_prod_cfc(y[0], scaling/sqrtf(2), y[0], 2*nof_symbols); + srslte_vec_sc_prod_cfc(y[1], scaling/sqrtf(2), y[1], 2*nof_symbols); + return 2 * i; + } else if (nof_ports == 4) { + scaling /= sqrtf(2); + + //int m_ap = (nof_symbols%4)?(nof_symbols*4-2):nof_symbols*4; + int m_ap = 4 * nof_symbols; + for (i = 0; i < m_ap / 4; i++) { + y[0][4 * i] = x[0][i] * scaling; + y[1][4 * i] = 0; + y[2][4 * i] = -conjf(x[1][i]) * scaling; + y[3][4 * i] = 0; + + y[0][4 * i + 1] = x[1][i] * scaling; + y[1][4 * i + 1] = 0; + y[2][4 * i + 1] = conjf(x[0][i]) * scaling; + y[3][4 * i + 1] = 0; + + y[0][4 * i + 2] = 0; + y[1][4 * i + 2] = x[2][i] * scaling; + y[2][4 * i + 2] = 0; + y[3][4 * i + 2] = -conjf(x[3][i]) * scaling; + + y[0][4 * i + 3] = 0; + y[1][4 * i + 3] = x[3][i] * scaling; + y[2][4 * i + 3] = 0; + y[3][4 * i + 3] = conjf(x[2][i]) * scaling; + } + return 4 * i; + } else { + fprintf(stderr, "Number of ports must be 2 or 4 for transmit diversity (nof_ports=%d)\n", nof_ports); + return -1; + } +} + +#ifdef LV_HAVE_AVX + +int srslte_precoding_cdd_2x2_avx(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_symbols, float scaling) +{ + __m256 norm_avx = _mm256_set1_ps(0.5f * scaling); + for (int i = 0; i < nof_symbols - 3; i += 4) { + __m256 x0 = _mm256_load_ps((float*) &x[0][i]); + __m256 x1 = _mm256_load_ps((float*) &x[1][i]); + + __m256 y0 = _mm256_mul_ps(norm_avx, _mm256_add_ps(x0, x1)); + + x0 = _mm256_xor_ps(x0, _mm256_setr_ps(+0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f)); + x1 = _mm256_xor_ps(x1, _mm256_set_ps(+0.0f, +0.0f, -0.0f, -0.0f, +0.0f, +0.0f, -0.0f, -0.0f)); + + __m256 y1 = _mm256_mul_ps(norm_avx, _mm256_add_ps(x0, x1)); + + _mm256_store_ps((float*)&y[0][i], y0); + _mm256_store_ps((float*)&y[1][i], y1); + } + + return 2*nof_symbols; +} + +#endif /* LV_HAVE_AVX */ + +#ifdef LV_HAVE_SSE + +int srslte_precoding_cdd_2x2_sse(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_symbols, float scaling) +{ + __m128 norm_sse = _mm_set1_ps(0.5f * scaling); + for (int i = 0; i < nof_symbols - 1; i += 2) { + __m128 x0 = _mm_load_ps((float*) &x[0][i]); + __m128 x1 = _mm_load_ps((float*) &x[1][i]); + + __m128 y0 = _mm_mul_ps(norm_sse, _mm_add_ps(x0, x1)); + + x0 = _mm_xor_ps(x0, _mm_setr_ps(+0.0f, +0.0f, -0.0f, -0.0f)); + x1 = _mm_xor_ps(x1, _mm_set_ps(+0.0f, +0.0f, -0.0f, -0.0f)); + + __m128 y1 = _mm_mul_ps(norm_sse, _mm_add_ps(x0, x1)); + + _mm_store_ps((float*)&y[0][i], y0); + _mm_store_ps((float*)&y[1][i], y1); + } + + return 2 * nof_symbols; +} + +#endif /* LV_HAVE_SSE */ + + +int srslte_precoding_cdd_2x2_gen(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_symbols, float scaling) +{ + scaling /= 2.0f; + for (int i = 0; i < nof_symbols; i++) { + y[0][i] = (x[0][i]+x[1][i]) * scaling; + y[1][i] = (x[0][i]-x[1][i]) * scaling; + i++; + y[0][i] = (x[0][i]+x[1][i]) * scaling; + y[1][i] = (-x[0][i]+x[1][i]) * scaling; + } + return 2 * nof_symbols; +} + +int srslte_precoding_cdd(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, int nof_ports, int nof_symbols, float scaling) +{ + if (nof_ports == 2) { + if (nof_layers != 2) { + DEBUG("Invalid number of layers %d for 2 ports\n", nof_layers); + return -1; + } +#ifdef LV_HAVE_AVX + return srslte_precoding_cdd_2x2_avx(x, y, nof_symbols, scaling); +#else +#ifdef LV_HAVE_SSE + return srslte_precoding_cdd_2x2_sse(x, y, nof_symbols, scaling); +#else + return srslte_precoding_cdd_2x2_gen(x, y, nof_symbols, scaling); +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX */ + } else if (nof_ports == 4) { + DEBUG("Not implemented\n"); + return -1; + } else { + DEBUG("Number of ports must be 2 or 4 for transmit diversity (nof_ports=%d)\n", nof_ports); + return -1; + } +} + +int srslte_precoding_multiplex(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, int nof_ports, + int codebook_idx, uint32_t nof_symbols, float scaling) +{ + int i = 0; + if (nof_ports == 2) { + if (nof_layers == 1) { + scaling /= sqrtf(2.0f); + switch(codebook_idx) { + case 0: + srslte_vec_sc_prod_cfc(x[0], scaling, y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[0], scaling, y[1], nof_symbols); + break; + case 1: + srslte_vec_sc_prod_cfc(x[0], scaling, y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[0], -scaling, y[1], nof_symbols); + break; + case 2: + srslte_vec_sc_prod_cfc(x[0], scaling, y[0], nof_symbols); + srslte_vec_sc_prod_ccc(x[0], _Complex_I * scaling, y[1], nof_symbols); + break; + case 3: + srslte_vec_sc_prod_cfc(x[0], scaling, y[0], nof_symbols); + srslte_vec_sc_prod_ccc(x[0], -_Complex_I * scaling, y[1], nof_symbols); + break; + default: + fprintf(stderr, "Invalid multiplex combination: codebook_idx=%d, nof_layers=%d, nof_ports=%d\n", + codebook_idx, nof_layers, nof_ports); + return SRSLTE_ERROR; + } + } else if (nof_layers == 2) { + switch(codebook_idx) { + case 0: + scaling /= sqrtf(2.0f); + srslte_vec_sc_prod_cfc(x[0], scaling, y[0], nof_symbols); + srslte_vec_sc_prod_cfc(x[1], scaling, y[1], nof_symbols); + break; + case 1: + scaling /= 2.0f; +#ifdef LV_HAVE_AVX + for (; i < nof_symbols - 3; i += 4) { + __m256 x0 = _mm256_load_ps((float *) &x[0][i]); + __m256 x1 = _mm256_load_ps((float *) &x[1][i]); + + __m256 y0 = _mm256_mul_ps(_mm256_set1_ps(scaling), _mm256_add_ps(x0, x1)); + __m256 y1 = _mm256_mul_ps(_mm256_set1_ps(scaling), _mm256_sub_ps(x0, x1)); + + _mm256_store_ps((float *) &y[0][i], y0); + _mm256_store_ps((float *) &y[1][i], y1); + } +#endif /* LV_HAVE_AVX */ + +#ifdef LV_HAVE_SSE + for (; i < nof_symbols - 1; i += 2) { + __m128 x0 = _mm_load_ps((float *) &x[0][i]); + __m128 x1 = _mm_load_ps((float *) &x[1][i]); + + __m128 y0 = _mm_mul_ps(_mm_set1_ps(scaling), _mm_add_ps(x0, x1)); + __m128 y1 = _mm_mul_ps(_mm_set1_ps(scaling), _mm_sub_ps(x0, x1)); + + _mm_store_ps((float *) &y[0][i], y0); + _mm_store_ps((float *) &y[1][i], y1); + } +#endif /* LV_HAVE_SSE */ + + for (; i < nof_symbols; i++) { + y[0][i] = (x[0][i] + x[1][i]) * scaling; + y[1][i] = (x[0][i] - x[1][i]) * scaling; + } + break; + case 2: + scaling /= 2.0f; +#ifdef LV_HAVE_AVX + for (; i < nof_symbols - 3; i += 4) { + __m256 x0 = _mm256_load_ps((float*)&x[0][i]); + __m256 x1 = _mm256_load_ps((float*)&x[1][i]); + + __m256 y0 = _mm256_mul_ps(_mm256_set1_ps(scaling), _mm256_add_ps(x0, x1)); + __m256 y1 = _mm256_mul_ps(_mm256_set1_ps(scaling), _MM256_MULJ_PS(_mm256_sub_ps(x0, x1))); + + _mm256_store_ps((float*)&y[0][i], y0); + _mm256_store_ps((float*)&y[1][i], y1); + } +#endif /* LV_HAVE_AVX */ + +#ifdef LV_HAVE_SSE + for (; i < nof_symbols - 1; i += 2) { + __m128 x0 = _mm_load_ps((float*)&x[0][i]); + __m128 x1 = _mm_load_ps((float*)&x[1][i]); + + __m128 y0 = _mm_mul_ps(_mm_set1_ps(scaling), _mm_add_ps(x0, x1)); + __m128 y1 = _mm_mul_ps(_mm_set1_ps(scaling), _MM_MULJ_PS(_mm_sub_ps(x0, x1))); + + _mm_store_ps((float*)&y[0][i], y0); + _mm_store_ps((float*)&y[1][i], y1); + } +#endif /* LV_HAVE_SSE */ + + for (; i < nof_symbols; i++) { + y[0][i] = (x[0][i] + x[1][i])*scaling; + y[1][i] = (_Complex_I*x[0][i] - _Complex_I*x[1][i])*scaling; + } + break; + case 3: + default: + fprintf(stderr, "Invalid multiplex combination: codebook_idx=%d, nof_layers=%d, nof_ports=%d\n", + codebook_idx, nof_layers, nof_ports); + return SRSLTE_ERROR; + } + } else { + ERROR("Not implemented"); + } + } else { + ERROR("Not implemented"); + } + return SRSLTE_SUCCESS; +} + +/* 36.211 v10.3.0 Section 6.3.4 */ +int srslte_precoding_type(cf_t *x[SRSLTE_MAX_LAYERS], cf_t *y[SRSLTE_MAX_PORTS], int nof_layers, + int nof_ports, int codebook_idx, int nof_symbols, float scaling, srslte_mimo_type_t type) { + + if (nof_ports > SRSLTE_MAX_PORTS) { + fprintf(stderr, "Maximum number of ports is %d (nof_ports=%d)\n", SRSLTE_MAX_PORTS, + nof_ports); + return -1; + } + if (nof_layers > SRSLTE_MAX_LAYERS) { + fprintf(stderr, "Maximum number of layers is %d (nof_layers=%d)\n", + SRSLTE_MAX_LAYERS, nof_layers); + return -1; + } + + switch (type) { + case SRSLTE_MIMO_TYPE_CDD: + return srslte_precoding_cdd(x, y, nof_layers, nof_ports, nof_symbols, scaling); + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_ports == 1 && nof_layers == 1) { + return srslte_precoding_single(x[0], y[0], nof_symbols, scaling); + } else { + fprintf(stderr, + "Number of ports and layers must be 1 for transmission on single antenna ports\n"); + return -1; + } + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_ports == nof_layers) { + return srslte_precoding_diversity(x, y, nof_ports, nof_symbols, scaling); + } else { + fprintf(stderr, + "Error number of layers must equal number of ports in transmit diversity\n"); + return -1; + } + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + return srslte_precoding_multiplex(x, y, nof_layers, nof_ports, codebook_idx, (uint32_t) nof_symbols, scaling); + default: + return SRSLTE_ERROR; + } + return SRSLTE_ERROR; +} + +#define PMI_SEL_PRECISION 24 + +/* PMI Select for 1 layer */ +int srslte_precoding_pmi_select_1l_gen(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + +#define SQRT1_2 ((float)M_SQRT1_2) + float max_sinr = 0.0; + uint32_t i, count; + + for (i = 0; i < 4; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols; j += PMI_SEL_PRECISION) { + /* 0. Load channel matrix */ + cf_t h00 = h[0][0][j]; + cf_t h01 = h[1][0][j]; + cf_t h10 = h[0][1][j]; + cf_t h11 = h[1][1][j]; + + /* 1. B = W'* H' */ + cf_t a0, a1; + switch (i) { + case 0: + a0 = conjf(h00) + conjf(h01); + a1 = conjf(h10) + conjf(h11); + break; + case 1: + a0 = conjf(h00) - conjf(h01); + a1 = conjf(h10) - conjf(h11); + break; + case 2: + a0 = conjf(h00) - _Complex_I * conjf(h01); + a1 = conjf(h10) - _Complex_I * conjf(h11); + break; + case 3: + a0 = conjf(h00) + _Complex_I * conjf(h01); + a1 = conjf(h10) + _Complex_I * conjf(h11); + break; + } + a0 *= SQRT1_2; + a1 *= SQRT1_2; + + /* 2. B = W' * H' * H = A * H */ + cf_t b0 = a0 * h00 + a1 * h10; + cf_t b1 = a0 * h01 + a1 * h11; + + /* 3. C = W' * H' * H * W' = B * W */ + cf_t c; + switch (i) { + case 0: + c = b0 + b1; + break; + case 1: + c = b0 - b1; + break; + case 2: + c = b0 + _Complex_I * b1; + break; + case 3: + c = b0 - _Complex_I * b1; + break; + default: + return SRSLTE_ERROR; + } + c *= SQRT1_2; + + /* Add for averaging */ + sinr_list[i] += crealf(c); + + count++; + } + + /* Divide average by noise */ + sinr_list[i] /= noise_estimate * count; + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#ifdef LV_HAVE_SSE + +/* PMI Select for 1 layer */ +int srslte_precoding_pmi_select_1l_sse(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + float max_sinr = 0.0; + uint32_t i, count; + __m128 sse_norm = _mm_set1_ps(0.5f); + + for (i = 0; i < 4; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols - PMI_SEL_PRECISION * 2 + 1; j += PMI_SEL_PRECISION * 2) { + /* 0. Load channel matrix */ + __m128 h00 = _mm_set_ps(crealf(h[0][0][j]), + cimagf(h[0][0][j]), + crealf(h[0][0][j + PMI_SEL_PRECISION]), + cimagf(h[0][0][j + PMI_SEL_PRECISION])); + __m128 h01 = _mm_set_ps(crealf(h[1][0][j]), + cimagf(h[1][0][j]), + crealf(h[1][0][j + PMI_SEL_PRECISION]), + cimagf(h[1][0][j + PMI_SEL_PRECISION])); + __m128 h10 = _mm_set_ps(crealf(h[0][1][j]), + cimagf(h[0][1][j]), + crealf(h[0][1][j + PMI_SEL_PRECISION]), + cimagf(h[0][1][j + PMI_SEL_PRECISION])); + __m128 h11 = _mm_set_ps(crealf(h[1][1][j]), + cimagf(h[1][1][j]), + crealf(h[1][1][j + PMI_SEL_PRECISION]), + cimagf(h[1][1][j + PMI_SEL_PRECISION])); + + /* 1. B = W'* H' */ + __m128 a0, a1; + switch (i) { + case 0: + a0 = _mm_add_ps(_MM_CONJ_PS(h00), _MM_CONJ_PS(h01)); + a1 = _mm_add_ps(_MM_CONJ_PS(h10), _MM_CONJ_PS(h11)); + break; + case 1: + a0 = _mm_sub_ps(_MM_CONJ_PS(h00), _MM_CONJ_PS(h01)); + a1 = _mm_sub_ps(_MM_CONJ_PS(h10), _MM_CONJ_PS(h11)); + break; + case 2: + a0 = _mm_add_ps(_MM_CONJ_PS(h00), _MM_MULJ_PS(_MM_CONJ_PS(h01))); + a1 = _mm_add_ps(_MM_CONJ_PS(h10), _MM_MULJ_PS(_MM_CONJ_PS(h11))); + break; + case 3: + a0 = _mm_sub_ps(_MM_CONJ_PS(h00), _MM_MULJ_PS(_MM_CONJ_PS(h01))); + a1 = _mm_sub_ps(_MM_CONJ_PS(h10), _MM_MULJ_PS(_MM_CONJ_PS(h11))); + break; + } + + /* 2. B = W' * H' * H = A * H */ + __m128 b0 = _mm_add_ps(_MM_PROD_PS(a0, h00), _MM_PROD_PS(a1, h10)); + __m128 b1 = _mm_add_ps(_MM_PROD_PS(a0, h01), _MM_PROD_PS(a1, h11)); + + /* 3. C = W' * H' * H * W' = B * W */ + __m128 c; + switch (i) { + case 0: + c = _mm_add_ps(b0, b1); + break; + case 1: + c = _mm_sub_ps(b0, b1); + break; + case 2: + c = _mm_sub_ps(b0, _MM_MULJ_PS(b1)); + break; + case 3: + c = _mm_add_ps(b0, _MM_MULJ_PS(b1)); + break; + default: + return SRSLTE_ERROR; + } + c = _mm_mul_ps(c, sse_norm); + + /* Add for averaging */ + __attribute__((aligned(128))) float gamma[4]; + _mm_store_ps(gamma, c); + sinr_list[i] += gamma[0] + gamma[2]; + + count += 2; + } + + /* Divide average by noise */ + sinr_list[i] /= noise_estimate * count; + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + +/* PMI Select for 1 layer */ +int srslte_precoding_pmi_select_1l_avx(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + float max_sinr = 0.0; + uint32_t i, count; + __m256 avx_norm = _mm256_set1_ps(0.5f); + + for (i = 0; i < 4; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols - PMI_SEL_PRECISION * 4 + 1; j += PMI_SEL_PRECISION * 4) { + /* 0. Load channel matrix */ + __m256 h00 = _mm256_setr_ps(crealf(h[0][0][j]), + cimagf(h[0][0][j]), + crealf(h[0][0][j + PMI_SEL_PRECISION]), + cimagf(h[0][0][j + PMI_SEL_PRECISION]), + crealf(h[0][0][j + PMI_SEL_PRECISION * 2]), + cimagf(h[0][0][j + PMI_SEL_PRECISION * 2]), + crealf(h[0][0][j + PMI_SEL_PRECISION * 3]), + cimagf(h[0][0][j + PMI_SEL_PRECISION * 3])); + __m256 h01 = _mm256_setr_ps(crealf(h[1][0][j]), + cimagf(h[1][0][j]), + crealf(h[1][0][j + PMI_SEL_PRECISION]), + cimagf(h[1][0][j + PMI_SEL_PRECISION]), + crealf(h[1][0][j + PMI_SEL_PRECISION * 2]), + cimagf(h[1][0][j + PMI_SEL_PRECISION * 2]), + crealf(h[1][0][j + PMI_SEL_PRECISION * 3]), + cimagf(h[1][0][j + PMI_SEL_PRECISION * 3])); + __m256 h10 = _mm256_setr_ps(crealf(h[0][1][j]), + cimagf(h[0][1][j]), + crealf(h[0][1][j + PMI_SEL_PRECISION]), + cimagf(h[0][1][j + PMI_SEL_PRECISION]), + crealf(h[0][1][j + PMI_SEL_PRECISION * 2]), + cimagf(h[0][1][j + PMI_SEL_PRECISION * 2]), + crealf(h[0][1][j + PMI_SEL_PRECISION * 3]), + cimagf(h[0][1][j + PMI_SEL_PRECISION * 3])); + __m256 h11 = _mm256_setr_ps(crealf(h[1][1][j]), + cimagf(h[1][1][j]), + crealf(h[1][1][j + PMI_SEL_PRECISION]), + cimagf(h[1][1][j + PMI_SEL_PRECISION]), + crealf(h[1][1][j + PMI_SEL_PRECISION * 2]), + cimagf(h[1][1][j + PMI_SEL_PRECISION * 2]), + crealf(h[1][1][j + PMI_SEL_PRECISION * 3]), + cimagf(h[1][1][j + PMI_SEL_PRECISION * 3])); + + /* 1. B = W'* H' */ + __m256 a0, a1; + switch (i) { + case 0: + a0 = _mm256_add_ps(_MM256_CONJ_PS(h00), _MM256_CONJ_PS(h01)); + a1 = _mm256_add_ps(_MM256_CONJ_PS(h10), _MM256_CONJ_PS(h11)); + break; + case 1: + a0 = _mm256_sub_ps(_MM256_CONJ_PS(h00), _MM256_CONJ_PS(h01)); + a1 = _mm256_sub_ps(_MM256_CONJ_PS(h10), _MM256_CONJ_PS(h11)); + break; + case 2: + a0 = _mm256_sub_ps(_MM256_CONJ_PS(h00), _MM256_MULJ_PS(_MM256_CONJ_PS(h01))); + a1 = _mm256_sub_ps(_MM256_CONJ_PS(h10), _MM256_MULJ_PS(_MM256_CONJ_PS(h11))); + break; + default: + a0 = _mm256_add_ps(_MM256_CONJ_PS(h00), _MM256_MULJ_PS(_MM256_CONJ_PS(h01))); + a1 = _mm256_add_ps(_MM256_CONJ_PS(h10), _MM256_MULJ_PS(_MM256_CONJ_PS(h11))); + break; + } + + /* 2. B = W' * H' * H = A * H */ +#ifdef LV_HAVE_FMA + __m256 b0 = _MM256_PROD_ADD_PS(a0, h00, _MM256_PROD_PS(a1, h10)); + __m256 b1 = _MM256_PROD_ADD_PS(a0, h01, _MM256_PROD_PS(a1, h11)); +#else + __m256 b0 = _mm256_add_ps(_MM256_PROD_PS(a0, h00), _MM256_PROD_PS(a1, h10)); + __m256 b1 = _mm256_add_ps(_MM256_PROD_PS(a0, h01), _MM256_PROD_PS(a1, h11)); +#endif /* LV_HAVE_FMA */ + + /* 3. C = W' * H' * H * W' = B * W */ + __m256 c; + switch (i) { + case 0: + c = _mm256_add_ps(b0, b1); + break; + case 1: + c = _mm256_sub_ps(b0, b1); + break; + case 2: + c = _mm256_add_ps(b0, _MM256_MULJ_PS(b1)); + break; + case 3: + c = _mm256_sub_ps(b0, _MM256_MULJ_PS(b1)); + break; + default: + return SRSLTE_ERROR; + } + c = _mm256_mul_ps(c, avx_norm); + + /* Add for averaging */ + __attribute__((aligned(256))) float gamma[8]; + _mm256_store_ps(gamma, c); + sinr_list[i] += gamma[0] + gamma[2] + gamma[4] + gamma[6]; + + count += 4; + } + + /* Divide average by noise */ + sinr_list[i] /= noise_estimate * count; + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#endif /* LV_HAVE_AVX */ + +int srslte_precoding_pmi_select_1l(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + int ret; +#ifdef LV_HAVE_AVX + ret = srslte_precoding_pmi_select_1l_avx(h, nof_symbols, noise_estimate, pmi, sinr_list); +#else + #ifdef LV_HAVE_SSE + ret = srslte_precoding_pmi_select_1l_sse(h, nof_symbols, noise_estimate, pmi, sinr_list); +#else + ret = srslte_precoding_pmi_select_1l_gen(h, nof_symbols, noise_estimate, pmi, sinr_list); +#endif +#endif + INFO("Precoder PMI Select for 1 layer SINR=[%.1fdB; %.1fdB; %.1fdB; %.1fdB] PMI=%d\n", 10 * log10(sinr_list[0]), + 10 * log10(sinr_list[1]), 10 * log10(sinr_list[2]), 10 * log10(sinr_list[3]), *pmi); + + return ret; +} + +int srslte_precoding_pmi_select_2l_gen(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + + float max_sinr = 0.0; + uint32_t i, count; + + for (i = 0; i < 2; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols; j += PMI_SEL_PRECISION) { + /* 0. Load channel matrix */ + cf_t h00 = h[0][0][j]; + cf_t h01 = h[1][0][j]; + cf_t h10 = h[0][1][j]; + cf_t h11 = h[1][1][j]; + + /* 1. B = W'* H' */ + cf_t a00, a01, a10, a11; + switch (i) { + case 0: + a00 = conjf(h00) + conjf(h01); + a01 = conjf(h10) + conjf(h11); + a10 = conjf(h00) - conjf(h01); + a11 = conjf(h10) - conjf(h11); + break; + case 1: + a00 = conjf(h00) - _Complex_I * conjf(h01); + a01 = conjf(h10) - _Complex_I * conjf(h11); + a10 = conjf(h00) + _Complex_I * conjf(h01); + a11 = conjf(h10) + _Complex_I * conjf(h11); + break; + default: + return SRSLTE_ERROR; + } + + /* 2. B = W' * H' * H = A * H */ + cf_t b00 = a00 * h00 + a01 * h10; + cf_t b01 = a00 * h01 + a01 * h11; + cf_t b10 = a10 * h00 + a11 * h10; + cf_t b11 = a10 * h01 + a11 * h11; + + /* 3. C = W' * H' * H * W' = B * W */ + cf_t c00, c01, c10, c11; + switch (i) { + case 0: + c00 = b00 + b01; + c01 = b00 - b01; + c10 = b10 + b11; + c11 = b10 - b11; + break; + case 1: + c00 = b00 + _Complex_I * b01; + c01 = b00 - _Complex_I * b01; + c10 = b10 + _Complex_I * b11; + c11 = b10 - _Complex_I * b11; + break; + default: + return SRSLTE_ERROR; + } + c00 *= 0.25; + c01 *= 0.25; + c10 *= 0.25; + c11 *= 0.25; + + /* 4. C += noise * I */ + c00 += noise_estimate; + c11 += noise_estimate; + + /* 5. detC */ + cf_t detC = c00 * c11 - c01 * c10; + cf_t inv_detC = conjf(detC) / (crealf(detC) * crealf(detC) + cimagf(detC) * cimagf(detC)); + + cf_t den0 = noise_estimate * c00 * inv_detC; + cf_t den1 = noise_estimate * c11 * inv_detC; + + float gamma0 = crealf((conjf(den0) / (crealf(den0) * crealf(den0) + cimagf(den0) * cimagf(den0))) - 1); + float gamma1 = crealf((conjf(den1) / (crealf(den1) * crealf(den1) + cimagf(den1) * cimagf(den1))) - 1); + + /* Add for averaging */ + sinr_list[i] += (gamma0 + gamma1); + + count++; + } + + /* Divide average by noise */ + if (count) { + sinr_list[i] /= count; + } + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#ifdef LV_HAVE_SSE + +int srslte_precoding_pmi_select_2l_sse(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + + float max_sinr = 0.0; + uint32_t i, count; + + __m128 sse_noise_estimate = _mm_setr_ps(noise_estimate, 0.0f, noise_estimate, 0.0f); + __m128 sse_norm = _mm_set1_ps(0.25f); + __m128 sse_ones = _mm_set1_ps(1.0f); + + for (i = 0; i < 2; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols - PMI_SEL_PRECISION * 2 + 1; j += PMI_SEL_PRECISION * 2) { + /* 0. Load channel matrix */ + __m128 h00 = _mm_setr_ps(crealf(h[0][0][j]), + cimagf(h[0][0][j]), + crealf(h[0][0][j + PMI_SEL_PRECISION]), + cimagf(h[0][0][j + PMI_SEL_PRECISION])); + __m128 h01 = _mm_setr_ps(crealf(h[1][0][j]), + cimagf(h[1][0][j]), + crealf(h[1][0][j + PMI_SEL_PRECISION]), + cimagf(h[1][0][j + PMI_SEL_PRECISION])); + __m128 h10 = _mm_setr_ps(crealf(h[0][1][j]), + cimagf(h[0][1][j]), + crealf(h[0][1][j + PMI_SEL_PRECISION]), + cimagf(h[0][1][j + PMI_SEL_PRECISION])); + __m128 h11 = _mm_setr_ps(crealf(h[1][1][j]), + cimagf(h[1][1][j]), + crealf(h[1][1][j + PMI_SEL_PRECISION]), + cimagf(h[1][1][j + PMI_SEL_PRECISION])); + + /* 1. B = W'* H' */ + __m128 a00, a01, a10, a11; + switch (i) { + case 0: + a00 = _mm_add_ps(_MM_CONJ_PS(h00), _MM_CONJ_PS(h01)); + a01 = _mm_add_ps(_MM_CONJ_PS(h10), _MM_CONJ_PS(h11)); + a10 = _mm_sub_ps(_MM_CONJ_PS(h00), _MM_CONJ_PS(h01)); + a11 = _mm_sub_ps(_MM_CONJ_PS(h10), _MM_CONJ_PS(h11)); + break; + case 1: + a00 = _mm_sub_ps(_MM_CONJ_PS(h00), _MM_MULJ_PS(_MM_CONJ_PS(h01))); + a01 = _mm_sub_ps(_MM_CONJ_PS(h10), _MM_MULJ_PS(_MM_CONJ_PS(h11))); + a10 = _mm_add_ps(_MM_CONJ_PS(h00), _MM_MULJ_PS(_MM_CONJ_PS(h01))); + a11 = _mm_add_ps(_MM_CONJ_PS(h10), _MM_MULJ_PS(_MM_CONJ_PS(h11))); + break; + default: + return SRSLTE_ERROR; + } + + /* 2. B = W' * H' * H = A * H */ + __m128 b00 = _mm_add_ps(_MM_PROD_PS(a00, h00), _MM_PROD_PS(a01, h10)); + __m128 b01 = _mm_add_ps(_MM_PROD_PS(a00, h01), _MM_PROD_PS(a01, h11)); + __m128 b10 = _mm_add_ps(_MM_PROD_PS(a10, h00), _MM_PROD_PS(a11, h10)); + __m128 b11 = _mm_add_ps(_MM_PROD_PS(a10, h01), _MM_PROD_PS(a11, h11)); + + /* 3. C = W' * H' * H * W' = B * W */ + __m128 c00, c01, c10, c11; + switch (i) { + case 0: + c00 = _mm_add_ps(b00, b01); + c01 = _mm_sub_ps(b00, b01); + c10 = _mm_add_ps(b10, b11); + c11 = _mm_sub_ps(b10, b11); + break; + case 1: + c00 = _mm_add_ps(b00, _MM_MULJ_PS(b01)); + c01 = _mm_sub_ps(b00, _MM_MULJ_PS(b01)); + c10 = _mm_add_ps(b10, _MM_MULJ_PS(b11)); + c11 = _mm_sub_ps(b10, _MM_MULJ_PS(b11)); + break; + default: + return SRSLTE_ERROR; + } + c00 = _mm_mul_ps(c00, sse_norm); + c01 = _mm_mul_ps(c01, sse_norm); + c10 = _mm_mul_ps(c10, sse_norm); + c11 = _mm_mul_ps(c11, sse_norm); + + /* 4. C += noise * I */ + c00 = _mm_add_ps(c00, sse_noise_estimate); + c11 = _mm_add_ps(c11, sse_noise_estimate); + + /* 5. detC */ + __m128 detC = srslte_mat_2x2_det_sse(c00, c01, c10, c11); + __m128 inv_detC = srslte_mat_cf_recip_sse(detC); + inv_detC = _mm_mul_ps(sse_noise_estimate, inv_detC); + + __m128 den0 = _MM_PROD_PS(c00, inv_detC); + __m128 den1 = _MM_PROD_PS(c11, inv_detC); + + __m128 gamma0 = _mm_sub_ps(_mm_rcp_ps(den0), sse_ones); + __m128 gamma1 = _mm_sub_ps(_mm_rcp_ps(den1), sse_ones); + + /* Add for averaging */ + __m128 sinr_sse = _mm_add_ps(gamma0, gamma1); + __attribute__((aligned(128))) float sinr[4]; + _mm_store_ps(sinr, sinr_sse); + + sinr_list[i] += sinr[0] + sinr[2]; + + count += 2; + } + + /* Divide average by noise */ + if (count) { + sinr_list[i] /= count; + } + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + +int srslte_precoding_pmi_select_2l_avx(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + + float max_sinr = 0.0; + uint32_t i, count; + + __m256 avx_noise_estimate = _mm256_setr_ps(noise_estimate, 0.0f, noise_estimate, 0.0f, + noise_estimate, 0.0f, noise_estimate, 0.0f); + __m256 avx_norm = _mm256_set1_ps(0.25f); + __m256 avx_ones = _mm256_set1_ps(1.0f); + + for (i = 0; i < 2; i++) { + sinr_list[i] = 0; + count = 0; + + for (uint32_t j = 0; j < nof_symbols - PMI_SEL_PRECISION * 4 + 1; j += PMI_SEL_PRECISION * 4) { + /* 0. Load channel matrix */ + __m256 h00 = _mm256_setr_ps(crealf(h[0][0][j]), + cimagf(h[0][0][j]), + crealf(h[0][0][j + PMI_SEL_PRECISION]), + cimagf(h[0][0][j + PMI_SEL_PRECISION]), + crealf(h[0][0][j + PMI_SEL_PRECISION * 2]), + cimagf(h[0][0][j + PMI_SEL_PRECISION * 2]), + crealf(h[0][0][j + PMI_SEL_PRECISION * 3]), + cimagf(h[0][0][j + PMI_SEL_PRECISION * 3])); + __m256 h01 = _mm256_setr_ps(crealf(h[1][0][j]), + cimagf(h[1][0][j]), + crealf(h[1][0][j + PMI_SEL_PRECISION]), + cimagf(h[1][0][j + PMI_SEL_PRECISION]), + crealf(h[1][0][j + PMI_SEL_PRECISION * 2]), + cimagf(h[1][0][j + PMI_SEL_PRECISION * 2]), + crealf(h[1][0][j + PMI_SEL_PRECISION * 3]), + cimagf(h[1][0][j + PMI_SEL_PRECISION * 3])); + __m256 h10 = _mm256_setr_ps(crealf(h[0][1][j]), + cimagf(h[0][1][j]), + crealf(h[0][1][j + PMI_SEL_PRECISION]), + cimagf(h[0][1][j + PMI_SEL_PRECISION]), + crealf(h[0][1][j + PMI_SEL_PRECISION * 2]), + cimagf(h[0][1][j + PMI_SEL_PRECISION * 2]), + crealf(h[0][1][j + PMI_SEL_PRECISION * 3]), + cimagf(h[0][1][j + PMI_SEL_PRECISION * 3])); + __m256 h11 = _mm256_setr_ps(crealf(h[1][1][j]), + cimagf(h[1][1][j]), + crealf(h[1][1][j + PMI_SEL_PRECISION]), + cimagf(h[1][1][j + PMI_SEL_PRECISION]), + crealf(h[1][1][j + PMI_SEL_PRECISION * 2]), + cimagf(h[1][1][j + PMI_SEL_PRECISION * 2]), + crealf(h[1][1][j + PMI_SEL_PRECISION * 3]), + cimagf(h[1][1][j + PMI_SEL_PRECISION * 3])); + + /* 1. B = W'* H' */ + __m256 a00, a01, a10, a11; + switch (i) { + case 0: + a00 = _mm256_add_ps(_MM256_CONJ_PS(h00), _MM256_CONJ_PS(h01)); + a01 = _mm256_add_ps(_MM256_CONJ_PS(h10), _MM256_CONJ_PS(h11)); + a10 = _mm256_sub_ps(_MM256_CONJ_PS(h00), _MM256_CONJ_PS(h01)); + a11 = _mm256_sub_ps(_MM256_CONJ_PS(h10), _MM256_CONJ_PS(h11)); + break; + case 1: + a00 = _mm256_sub_ps(_MM256_CONJ_PS(h00), _MM256_MULJ_PS(_MM256_CONJ_PS(h01))); + a01 = _mm256_sub_ps(_MM256_CONJ_PS(h10), _MM256_MULJ_PS(_MM256_CONJ_PS(h11))); + a10 = _mm256_add_ps(_MM256_CONJ_PS(h00), _MM256_MULJ_PS(_MM256_CONJ_PS(h01))); + a11 = _mm256_add_ps(_MM256_CONJ_PS(h10), _MM256_MULJ_PS(_MM256_CONJ_PS(h11))); + break; + default: + return SRSLTE_ERROR; + } + + /* 2. B = W' * H' * H = A * H */ +#ifdef LV_HAVE_FMA + __m256 b00 = _MM256_PROD_ADD_PS(a00, h00, _MM256_PROD_PS(a01, h10)); + __m256 b01 = _MM256_PROD_ADD_PS(a00, h01, _MM256_PROD_PS(a01, h11)); + __m256 b10 = _MM256_PROD_ADD_PS(a10, h00, _MM256_PROD_PS(a11, h10)); + __m256 b11 = _MM256_PROD_ADD_PS(a10, h01, _MM256_PROD_PS(a11, h11)); +#else + __m256 b00 = _mm256_add_ps(_MM256_PROD_PS(a00, h00), _MM256_PROD_PS(a01, h10)); + __m256 b01 = _mm256_add_ps(_MM256_PROD_PS(a00, h01), _MM256_PROD_PS(a01, h11)); + __m256 b10 = _mm256_add_ps(_MM256_PROD_PS(a10, h00), _MM256_PROD_PS(a11, h10)); + __m256 b11 = _mm256_add_ps(_MM256_PROD_PS(a10, h01), _MM256_PROD_PS(a11, h11)); +#endif /* LV_HAVE_FMA */ + + /* 3. C = W' * H' * H * W' = B * W */ + __m256 c00, c01, c10, c11; + switch (i) { + case 0: + c00 = _mm256_add_ps(b00, b01); + c01 = _mm256_sub_ps(b00, b01); + c10 = _mm256_add_ps(b10, b11); + c11 = _mm256_sub_ps(b10, b11); + break; + case 1: + c00 = _mm256_add_ps(b00, _MM256_MULJ_PS(b01)); + c01 = _mm256_sub_ps(b00, _MM256_MULJ_PS(b01)); + c10 = _mm256_add_ps(b10, _MM256_MULJ_PS(b11)); + c11 = _mm256_sub_ps(b10, _MM256_MULJ_PS(b11)); + break; + default: + return SRSLTE_ERROR; + } + c00 = _mm256_mul_ps(c00, avx_norm); + c01 = _mm256_mul_ps(c01, avx_norm); + c10 = _mm256_mul_ps(c10, avx_norm); + c11 = _mm256_mul_ps(c11, avx_norm); + + /* 4. C += noise * I */ + c00 = _mm256_add_ps(c00, avx_noise_estimate); + c11 = _mm256_add_ps(c11, avx_noise_estimate); + + /* 5. detC */ + __m256 detC = srslte_mat_2x2_det_avx(c00, c01, c10, c11); + __m256 inv_detC = srslte_mat_cf_recip_avx(detC); + inv_detC = _mm256_mul_ps(avx_noise_estimate, inv_detC); + + __m256 den0 = _MM256_PROD_PS(c00, inv_detC); + __m256 den1 = _MM256_PROD_PS(c11, inv_detC); + + __m256 gamma0 = _mm256_sub_ps(_mm256_rcp_ps(den0), avx_ones); + __m256 gamma1 = _mm256_sub_ps(_mm256_rcp_ps(den1), avx_ones); + + /* Add for averaging */ + __m256 sinr_avx = _mm256_permute_ps(_mm256_add_ps(gamma0, gamma1), 0b00101000); + __attribute__((aligned(256))) float sinr[8]; + _mm256_store_ps(sinr, sinr_avx); + + sinr_list[i] += sinr[0] + sinr[2] + sinr[4] + sinr[6]; + + count += 4; + } + + /* Divide average by noise */ + if (count) { + sinr_list[i] /= count; + } + + if (sinr_list[i] > max_sinr) { + max_sinr = sinr_list[i]; + *pmi = i; + } + } + + return i; +} + +#endif /* LV_HAVE_AVX */ + +/* PMI Select for 2 layers */ +int srslte_precoding_pmi_select_2l(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, uint32_t *pmi, + float sinr_list[SRSLTE_MAX_CODEBOOKS]) { + + int ret; +#ifdef LV_HAVE_AVX + ret = srslte_precoding_pmi_select_2l_avx(h, nof_symbols, noise_estimate, pmi, sinr_list); +#else + #ifdef LV_HAVE_SSE + ret = srslte_precoding_pmi_select_2l_sse(h, nof_symbols, noise_estimate, pmi, sinr_list); +#else + ret = srslte_precoding_pmi_select_2l_gen(h, nof_symbols, noise_estimate, pmi, sinr_list); +#endif /* LV_HAVE_SSE */ +#endif /* LV_HAVE_AVX */ + + INFO("Precoder PMI Select for 2 layers SINR=[%.1fdB; %.1fdB] PMI=%d\n", 10 * log10(sinr_list[0]), + 10 * log10(sinr_list[1]), *pmi); + + return ret; +} + +int srslte_precoding_pmi_select(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols, + float noise_estimate, int nof_layers, uint32_t *pmi, + float sinr[SRSLTE_MAX_CODEBOOKS]) { + int ret; + + if (sinr == NULL || pmi == NULL) { + ERROR("Null pointer"); + ret = SRSLTE_ERROR_INVALID_INPUTS; + } else if (nof_layers == 1) { + ret = srslte_precoding_pmi_select_1l(h, nof_symbols, noise_estimate, pmi, sinr); + } else if (nof_layers == 2) { + ret = srslte_precoding_pmi_select_2l(h, nof_symbols, noise_estimate, pmi, sinr); + } else { + ERROR("Wrong number of layers"); + ret = SRSLTE_ERROR_INVALID_INPUTS; + } + + return ret; +} + +/* PMI Select for 1 layer */ +float srslte_precoding_2x2_cn_gen(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_symbols) { + uint32_t count = 0; + float cn_avg = 0.0f; + + for (uint32_t i = 0; i < nof_symbols; i += PMI_SEL_PRECISION) { + /* 0. Load channel matrix */ + cf_t h00 = h[0][0][i]; + cf_t h01 = h[1][0][i]; + cf_t h10 = h[0][1][i]; + cf_t h11 = h[1][1][i]; + + cn_avg += srslte_mat_2x2_cn(h00, h01, h10, h11); + + count++; + } + + if (count) { + cn_avg /= count; + } + + return cn_avg; +} + +/* Computes the condition number for a given number of antennas, + * stores in the parameter *cn the Condition Number in dB */ +int srslte_precoding_cn(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_tx_antennas, + uint32_t nof_rx_antennas, uint32_t nof_symbols, float *cn) { + if (nof_tx_antennas == 2 && nof_rx_antennas == 2) { + *cn = srslte_precoding_2x2_cn_gen(h, nof_symbols); + return SRSLTE_SUCCESS; + } else { + DEBUG("MIMO Condition Number calculation not implemented for %d×%d", nof_tx_antennas, nof_rx_antennas); + return SRSLTE_ERROR; + } +} + diff --git a/lib/src/phy/mimo/test/CMakeLists.txt b/lib/src/phy/mimo/test/CMakeLists.txt new file mode 100644 index 0000000..cae668d --- /dev/null +++ b/lib/src/phy/mimo/test/CMakeLists.txt @@ -0,0 +1,81 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# LAYER MAPPING TEST +######################################################################## + +add_executable(layermap_test layermap_test.c) +target_link_libraries(layermap_test srslte_phy) + +add_test(layermap_single layermap_test -n 1000 -m single -c 1 -l 1) + +add_test(layermap_diversity_2 layermap_test -n 1000 -m diversity -c 1 -l 2) +add_test(layermap_diversity_4 layermap_test -n 1000 -m diversity -c 1 -l 4) + +add_test(layermap_multiplex_11 layermap_test -n 1000 -m multiplex -c 1 -l 1) +add_test(layermap_multiplex_12 layermap_test -n 1000 -m multiplex -c 1 -l 2) +add_test(layermap_multiplex_13 layermap_test -n 1002 -m multiplex -c 1 -l 3) +add_test(layermap_multiplex_14 layermap_test -n 1000 -m multiplex -c 1 -l 4) + + +add_test(layermap_multiplex_22 layermap_test -n 1000 -m multiplex -c 2 -l 2) +add_test(layermap_multiplex_23 layermap_test -n 1002 -m multiplex -c 2 -l 3) +add_test(layermap_multiplex_24 layermap_test -n 1000 -m multiplex -c 2 -l 4) + + +######################################################################## +# PRECODING MAPPING TEST +######################################################################## + +add_executable(precoding_test precoder_test.c) +target_link_libraries(precoding_test srslte_phy) + +add_test(precoding_single precoding_test -n 1000 -m single) +add_test(precoding_diversity2 precoding_test -n 1000 -m diversity -l 2 -p 2) +add_test(precoding_diversity4 precoding_test -n 1024 -m diversity -l 4 -p 4) + +add_test(precoding_cdd_2x2_zf precoding_test -m cdd -l 2 -p 2 -r 2 -n 14000 -d zf) +add_test(precoding_cdd_2x2_mmse precoding_test -m cdd -l 2 -p 2 -r 2 -n 14000 -d mmse) + +add_test(precoding_multiplex_1l_cb0 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 0) +add_test(precoding_multiplex_1l_cb1 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 1) +add_test(precoding_multiplex_1l_cb2 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 2) +add_test(precoding_multiplex_1l_cb3 precoding_test -m multiplex -l 1 -p 2 -r 2 -n 14000 -c 3) + +add_test(precoding_multiplex_2l_cb0_zf precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 0 -d zf) +add_test(precoding_multiplex_2l_cb1_zf precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 1 -d zf) +add_test(precoding_multiplex_2l_cb2_zf precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 2 -d zf) + + +add_test(precoding_multiplex_2l_cb0_mmse precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 0 -d mmse) +add_test(precoding_multiplex_2l_cb1_mmse precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 1 -d mmse) +add_test(precoding_multiplex_2l_cb2_mmse precoding_test -m multiplex -l 2 -p 2 -r 2 -n 14000 -c 2 -d mmse) + +######################################################################## +# PMI SELECT TEST +######################################################################## + +add_executable(pmi_select_test pmi_select_test.c) +target_link_libraries(pmi_select_test srslte_phy) + +add_test(pmi_select_test pmi_select_test) + + diff --git a/lib/src/phy/mimo/test/layermap_test.c b/lib/src/phy/mimo/test/layermap_test.c new file mode 100644 index 0000000..6f026f9 --- /dev/null +++ b/lib/src/phy/mimo/test/layermap_test.c @@ -0,0 +1,162 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +int nof_symbols = 1000; +int nof_cw = 1, nof_layers = 1; +char *mimo_type_name = NULL; + +void usage(char *prog) { + printf("Usage: %s -m [single|diversity|multiplex|cdd] -c [nof_cw] -l [nof_layers]\n", prog); + printf("\t-n num_symbols [Default %d]\n", nof_symbols); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "mcln")) != -1) { + switch (opt) { + case 'n': + nof_symbols = atoi(argv[optind]); + break; + case 'c': + nof_cw = atoi(argv[optind]); + break; + case 'l': + nof_layers = atoi(argv[optind]); + break; + case 'm': + mimo_type_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!mimo_type_name) { + usage(argv[0]); + exit(-1); + } +} + +int main(int argc, char **argv) { + int i, j, num_errors, symbols_layer; + cf_t *d[SRSLTE_MAX_CODEWORDS], *x[SRSLTE_MAX_LAYERS], *dp[SRSLTE_MAX_CODEWORDS]; + srslte_mimo_type_t type; + int nof_symb_cw[SRSLTE_MAX_CODEWORDS]; + int n[2]; + + parse_args(argc, argv); + + if (srslte_str2mimotype(mimo_type_name, &type)) { + fprintf(stderr, "Invalid MIMO type %s\n", mimo_type_name); + exit(-1); + } + + if (nof_cw > 1) { + n[0] = nof_layers / nof_cw; + n[1] = nof_layers - n[0]; + nof_symb_cw[0] = nof_symbols * n[0]; + nof_symb_cw[1] = nof_symbols * n[1]; + } else { + nof_symb_cw[0] = nof_symbols; + nof_symb_cw[1] = 0; + } + + for (i=0;i +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/mimo/precoding.h" +#include "pmi_select_test.h" +#include "srslte/phy/utils/debug.h" + +int main(int argc, char **argv) { + cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + float noise_estimate; + float sinr_1l[SRSLTE_MAX_CODEBOOKS]; + float sinr_2l[SRSLTE_MAX_CODEBOOKS]; + float cn; + uint32_t pmi[2]; + uint32_t nof_symbols = (uint32_t) SRSLTE_SF_LEN_RE(6, SRSLTE_CP_NORM); + int ret = SRSLTE_ERROR; + + /* Allocate channels */ + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + h[i][j] = srslte_vec_malloc(sizeof(cf_t) * nof_symbols); + if (!h[i][j]) { + goto clean; + } + bzero(h[i][j], sizeof(cf_t) * nof_symbols); + } + } + + for (int c = 0; c < PMI_SELECT_TEST_NOF_CASES; c++) { + pmi_select_test_case_gold_t *gold = &pmi_select_test_case_gold[c]; + + /* Set channel */ + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + cf_t hij = gold->h[i][j]; + + for (int k = 0; k < nof_symbols; k++) { + h[i][j][k] = hij; + } + } + } + + /* Set noise estimate */ + noise_estimate = gold->n; + + /* PMI select for 1 layer */ + ret = srslte_precoding_pmi_select(h, nof_symbols, noise_estimate, 1, &pmi[0], sinr_1l); + if (ret < 0) { + ERROR("During PMI selection for 1 layer"); + goto clean; + } + + /* Check SINR for 1 layer */ + for (int i = 0; i < ret; i++) { + if (fabsf(gold->snri_1l[i] - sinr_1l[i]) > 0.1) { + ERROR("Test case %d failed computing 1 layer SINR for codebook %d (test=%.2f; gold=%.2f)\n", + c + 1, i, sinr_1l[i], gold->snri_1l[i]); + goto clean; + } + } + + /* Check PMI select for 1 layer*/ + if (pmi[0] != gold->pmi[0]) { + ERROR("Test case %d failed computing 1 layer PMI (test=%d; gold=%d)\n", c + 1, pmi[0], gold->pmi[0]); + goto clean; + } + + /* PMI select for 2 layer */ + ret = srslte_precoding_pmi_select(h, nof_symbols, noise_estimate, 2, &pmi[1], sinr_2l); + if (ret < 0) { + ERROR("During PMI selection for 2 layer"); + goto clean; + } + + /* Check SINR for 2 layer */ + for (int i = 0; i < ret; i++) { + if (fabsf(gold->snri_2l[i] - sinr_2l[i]) > 0.1) { + ERROR("Test case %d failed computing 2 layer SINR for codebook %d (test=%.2f; gold=%.2f)\n", + c + 1, i, sinr_2l[i], gold->snri_2l[i]); + goto clean; + } + } + + /* Check PMI select for 2 layer*/ + if (pmi[1] != gold->pmi[1]) { + ERROR("Test case %d failed computing 2 layer PMI (test=%d; gold=%d)\n", c + 1, pmi[1], gold->pmi[1]); + goto clean; + } + + /* Condition number */ + if (srslte_precoding_cn(h, 2, 2, nof_symbols, &cn)) { + ERROR("Test case %d condition number returned error\n", c + 1); + goto clean; + } + + /* Check condition number */ + if (fabsf(gold->k - cn) > 0.1) { + ERROR("Test case %d failed computing condition number (test=%.2f; gold=%.2f)\n", + c + 1, cn, gold->k); + goto clean; + } + } + + /* Test passed */ + ret = SRSLTE_SUCCESS; + + clean: + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + if (h[i][j]) { + free(h[i][j]); + } + } + } + + if (ret) { + printf("Failed!\n"); + } else { + printf("Passed!\n"); + } + + return ret; +} diff --git a/lib/src/phy/mimo/test/pmi_select_test.h b/lib/src/phy/mimo/test/pmi_select_test.h new file mode 100644 index 0000000..c9c2ef5 --- /dev/null +++ b/lib/src/phy/mimo/test/pmi_select_test.h @@ -0,0 +1,237 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +#ifndef PMI_SELECT_TEST_H +#define PMI_SELECT_TEST_H + +#define PMI_SELECT_TEST_NOF_CASES 16 + +typedef struct { + cf_t h[2][2]; /* Channel estimate */ + float n; /* Noise estimation */ + float snri_1l[4]; /* SINR Approximation for 1 layer (linear) */ + float snri_2l[2]; /* SINR Approximation for 2 layers (linear) */ + uint32_t pmi[2]; /* Precoding Matrix Indicator for 1 and 2 layers */ + uint32_t ri; /* Rank indicator */ + float k; /* Condition number (κ) in dB */ +} pmi_select_test_case_gold_t; + +static pmi_select_test_case_gold_t pmi_select_test_case_gold [PMI_SELECT_TEST_NOF_CASES] = { + { /* Test case 1 */ + .h = { + {+0.626226f+0.060103f*_Complex_I, -0.233387f-0.449860f*_Complex_I}, + {+0.234558f-0.502742f*_Complex_I, +0.150990f-0.096722f*_Complex_I} + }, + .n = 0.227713, + .snri_1l = {2.728043f, 1.630673f, 3.226421f, 1.132295f}, + .snri_2l = {1.797660f, 1.982149f}, + .pmi = {2, 1}, + .ri = 1, + .k = 6.4007, + }, + { /* Test case 2 */ + .h = { + {+0.608899f-0.825846f*_Complex_I, +0.972208f+0.604183f*_Complex_I}, + {-0.940016f+0.978290f*_Complex_I, +0.071328f-0.866107f*_Complex_I} + }, + .n = 0.939398, + .snri_1l = {0.686850f, 4.591972f, 3.773925f, 1.504897f}, + .snri_2l = {2.298235f, 1.761859f}, + .pmi = {1, 0}, + .ri = 1, + .k = 11.1305, + }, + { /* Test case 3 */ + .h = { + {-0.963645f+0.770719f*_Complex_I, +0.367677f+0.798010f*_Complex_I}, + {+0.567473f+0.251875f*_Complex_I, +0.068275f-0.724262f*_Complex_I} + }, + .n = 0.217802, + .snri_1l = {3.209674f, 11.525338f, 11.962786f, 2.772226f}, + .snri_2l = {3.226053f, 3.526363f}, + .pmi = {2, 1}, + .ri = 1, + .k = 15.4589, + }, + { /* Test case 4 */ + .h = { + {-0.635718f+0.879322f*_Complex_I, -0.916360f-0.291089f*_Complex_I}, + {-0.786117f-0.178742f*_Complex_I, +0.232887f+0.968699f*_Complex_I} + }, + .n = 0.945579, + .snri_1l = {1.818313f, 2.141519f, 1.995787f, 1.964045f}, + .snri_2l = {1.965011f, 1.958537f}, + .pmi = {1, 0}, + .ri = 2, + .k = 1.2910, + }, + { /* Test case 5 */ + .h = { + {+0.353289f+0.324764f*_Complex_I, +0.976605f-0.511669f*_Complex_I}, + {+0.533663f-0.408985f*_Complex_I, -0.326601f+0.360357f*_Complex_I} + }, + .n = 0.527847, + .snri_1l = {1.173803f, 2.869865f, 2.273783f, 1.769885f}, + .snri_2l = {1.871430f, 1.713879f}, + .pmi = {1, 0}, + .ri = 2, + .k = 5.5388, + }, + { /* Test case 6 */ + .h = { + {-0.176813f+0.103585f*_Complex_I, +0.205276f+0.167141f*_Complex_I}, + {+0.501040f+0.023640f*_Complex_I, +0.167066f-0.834815f*_Complex_I} + }, + .n = 0.719570, + .snri_1l = {0.490387f, 1.022313f, 1.111245f, 0.401456f}, + .snri_2l = {0.578124f, 0.597176f}, + .pmi = {2, 1}, + .ri = 1, + .k = 21.8808, + }, + { /* Test case 7 */ + .h = { + {+0.992312f+0.773088f*_Complex_I, -0.290931f-0.090610f*_Complex_I}, + {+0.942518f-0.173145f*_Complex_I, -0.307102f-0.564536f*_Complex_I} + }, + .n = 0.125655, + .snri_1l = {19.459529f, 4.467420f, 18.044021f, 5.882928f}, + .snri_2l = {8.055238f, 6.832247f}, + .pmi = {0, 0}, + .ri = 1, + .k = 9.9136, + }, + { /* Test case 8 */ + .h = { + {-0.382171f-0.980395f*_Complex_I, +0.452209f+0.686427f*_Complex_I}, + {+0.565744f+0.844664f*_Complex_I, +0.387575f+0.541908f*_Complex_I} + }, + .n = 0.042660, + .snri_1l = {26.560881f, 49.864772f, 33.269985f, 43.155668f}, + .snri_2l = {37.201526f, 34.461078f}, + .pmi = {1, 0}, + .ri = 2, + .k = 3.1172, + }, + { /* Test case 9 */ + .h = { + {-0.243628f-0.461891f*_Complex_I, +0.408679f+0.346062f*_Complex_I}, + {+0.459026f-0.045016f*_Complex_I, -0.551446f+0.247433f*_Complex_I} + }, + .n = 0.236445, + .snri_1l = {1.429443f, 3.381496f, 0.227617f, 4.583322f}, + .snri_2l = {1.272903f, 2.118832f}, + .pmi = {3, 1}, + .ri = 1, + .k = 24.1136, + }, + { /* Test case 10 */ + .h = { + {-0.645752f-0.784222f*_Complex_I, +0.659287f-0.635545f*_Complex_I}, + {+0.533843f-0.801809f*_Complex_I, +0.868957f-0.020472f*_Complex_I} + }, + .n = 0.193245, + .snri_1l = {13.697372f, 4.693597f, 1.561737f, 16.829232f}, + .snri_2l = {2.961344f, 5.773049f}, + .pmi = {3, 1}, + .ri = 1, + .k = 17.5194, + }, + { /* Test case 11 */ + .h = { + {+0.791783f+0.544990f*_Complex_I, -0.801821f-0.376120f*_Complex_I}, + {-0.911669f-0.642035f*_Complex_I, +0.114590f-0.322089f*_Complex_I} + }, + .n = 0.210146, + .snri_1l = {2.340213f, 12.261749f, 5.921675f, 8.680286f}, + .snri_2l = {6.912040f, 4.520201f}, + .pmi = {1, 0}, + .ri = 2, + .k = 7.7819, + }, + { /* Test case 12 */ + .h = { + {+0.020305f-0.218290f*_Complex_I, +0.812729f-0.890767f*_Complex_I}, + {+0.257848f+0.002566f*_Complex_I, -0.796932f-0.136558f*_Complex_I} + }, + .n = 0.997560, + .snri_1l = {0.591218f, 1.636514f, 1.880263f, 0.347469f}, + .snri_2l = {0.869026f, 0.967991f}, + .pmi = {2, 1}, + .ri = 1, + .k = 12.9774, + }, + { /* Test case 13 */ + .h = { + {+0.623205f-0.219990f*_Complex_I, -0.028697f+0.854712f*_Complex_I}, + {+0.788896f+0.834988f*_Complex_I, -0.724907f+0.427148f*_Complex_I} + }, + .n = 0.618337, + .snri_1l = {3.706176f, 1.461946f, 0.479632f, 4.688490f}, + .snri_2l = {1.444336f, 2.102567f}, + .pmi = {3, 1}, + .ri = 1, + .k = 17.0493, + }, + { /* Test case 14 */ + .h = { + {-0.313424f+0.292955f*_Complex_I, +0.872055f+0.666304f*_Complex_I}, + {-0.750452f-0.203436f*_Complex_I, +0.461171f+0.499644f*_Complex_I} + }, + .n = 0.835221, + .snri_1l = {2.560265f, 0.379539f, 0.976562f, 1.963242f}, + .snri_2l = {1.380223f, 1.109300f}, + .pmi = {0, 0}, + .ri = 1, + .k = 10.1729, + }, + { /* Test case 15 */ + .h = { + {-0.355079f-0.339153f*_Complex_I, +0.104523f+0.238943f*_Complex_I}, + {+0.958258f-0.278727f*_Complex_I, +0.098617f+0.513019f*_Complex_I} + }, + .n = 0.413901, + .snri_1l = {1.633620f, 2.178855f, 0.809297f, 3.003178f}, + .snri_2l = {1.250898f, 1.512017f}, + .pmi = {3, 1}, + .ri = 1, + .k = 10.8925, + }, + { /* Test case 16 */ + .h = { + {-0.015310f+0.675606f*_Complex_I, +0.389486f+0.478144f*_Complex_I}, + {+0.945468f+0.908349f*_Complex_I, -0.344490f-0.936155f*_Complex_I} + }, + .n = 0.356869, + .snri_1l = {5.024121f, 4.926495f, 7.364348f, 2.586268f}, + .snri_2l = {3.165416f, 3.851590f}, + .pmi = {2, 1}, + .ri = 2, + .k = 7.7799, + }, +}; + +#endif /* PMI_SELECT_TEST_H */ diff --git a/lib/src/phy/mimo/test/precoder_mex.c b/lib/src/phy/mimo/test/precoder_mex.c new file mode 100644 index 0000000..b021f42 --- /dev/null +++ b/lib/src/phy/mimo/test/precoder_mex.c @@ -0,0 +1,145 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the predecoder + */ + +#define INPUT prhs[0] +#define NLAYERS prhs[1] +#define NPORTS prhs[2] +#define TXSCHEME prhs[3] +#define NOF_INPUTS 3 + + +void help() +{ + mexErrMsgTxt + ("[output] = srslte_decoder(input, NLayers, NCellRefP, TxScheme)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + cf_t *input = NULL; + cf_t *output = NULL; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + // Read input symbols + int nof_symbols = mexutils_read_cf(INPUT, &input); + if (nof_symbols < 0) { + mexErrMsgTxt("Error reading input\n"); + return; + } + uint32_t nof_layers = mxGetScalar(NLAYERS); + uint32_t nof_tx_ports = mxGetScalar(NPORTS); + uint32_t nof_codewords = 1; + + mexPrintf("nof_tx_ports=%d, nof_layers=%d, nof_symbols=%d\n", nof_tx_ports, nof_layers, nof_symbols); + + cf_t *y[SRSLTE_MAX_PORTS]; + cf_t *x[SRSLTE_MAX_LAYERS]; + cf_t *d[SRSLTE_MAX_CODEWORDS]; + + d[0] = input; // Single codeword supported only + + /* Allocate memory */ + for (int i = 0; i < nof_layers; i++) { + x[i] = srslte_vec_malloc(sizeof(cf_t)*nof_symbols/nof_layers); + } + + output = srslte_vec_malloc(sizeof(cf_t)*nof_symbols*nof_tx_ports); + for (int i=0;i= NOF_INPUTS) { + txscheme = mxArrayToString(TXSCHEME); + } + srslte_mimo_type_t type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + if (!strcmp(txscheme, "Port0")) { + type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + } else if (!strcmp(txscheme, "TxDiversity")) { + type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else if (!strcmp(txscheme, "CDD")) { + type = SRSLTE_MIMO_TYPE_CDD; + } else if (!strcmp(txscheme, "SpatialMux")) { + type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } else { + mexPrintf("Unsupported TxScheme=%s\n", txscheme); + return; + } + int symbols_layers[SRSLTE_MAX_LAYERS]; + for (int i=0;i= 1) { + switch (type) { + case SRSLTE_MIMO_TYPE_CDD: + mexutils_write_cf(output, &plhs[0], nof_symbols/nof_layers, nof_tx_ports); + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + default: + mexutils_write_cf(output, &plhs[0], (uint32_t) nof_symbols, nof_tx_ports); + break; + } + } + + if (nlhs >= 2) { + mexutils_write_cf(x[0], &plhs[1], nof_symbols / nof_layers, 1); + } + if (nlhs >= 3) { + mexutils_write_cf(x[1], &plhs[2], nof_symbols / nof_layers, 1); + } + + if (input) { + free(input); + } + if (output) { + free(output); + } + for (int i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "srslte/phy/channel/ch_awgn.h" + +#define MSE_THRESHOLD 0.0005 + +int nof_symbols = 1000; +uint32_t codebook_idx = 0; +int nof_layers = 1, nof_tx_ports = 1, nof_rx_ports = 1, nof_re = 1; +char *mimo_type_name = NULL; +char decoder_type_name [17] = "zf"; +float snr_db = 100.0f; +float scaling = 0.1f; + +void usage(char *prog) { + printf( + "Usage: %s -m [single|diversity|multiplex|cdd] -l [nof_layers] -p [nof_tx_ports]\n" + " -r [nof_rx_ports] -g [scaling]\n", prog); + printf("\t-n num_symbols [Default %d]\n", nof_symbols); + printf("\t-c codebook_idx [Default %d]\n", codebook_idx); + printf("\t-s SNR in dB [Default %.1fdB]*\n", snr_db); + printf("\t-g Scaling [Default %.1f]*\n", scaling); + printf("\t-d decoder type [zf|mmse] [Default %s]\n", decoder_type_name); + printf("\n"); + printf("* Performance test example:\n\t for snr in {0..20..1}; do ./precoding_test -m single -s $snr; done; \n\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "mplnrcdsg")) != -1) { + switch (opt) { + case 'n': + nof_symbols = atoi(argv[optind]); + break; + case 'p': + nof_tx_ports = atoi(argv[optind]); + break; + case 'r': + nof_rx_ports = atoi(argv[optind]); + break; + case 'l': + nof_layers = atoi(argv[optind]); + break; + case 'm': + mimo_type_name = argv[optind]; + break; + case 'c': + codebook_idx = (uint32_t) atoi(argv[optind]); + break; + case 'd': + strncpy(decoder_type_name, argv[optind], 15); + decoder_type_name[15] = 0; + break; + case 's': + snr_db = (float) atof(argv[optind]); + break; + case 'g': + scaling = (float) atof(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!mimo_type_name) { + usage(argv[0]); + exit(-1); + } +} + +void populate_channel_cdd(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t n) { + int i, j, k; + + for (i = 0; i < nof_tx_ports; i++) { + for (j = 0; j < nof_rx_ports; j++) { + for (k = 0; k < n; k++) { + h[i][j][k] = (float) rand() / RAND_MAX + ((float) rand() / RAND_MAX) * _Complex_I; + } + } + } +} + +void populate_channel_diversity(cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t n) { + int i, j, k, l; + + for (i = 0; i < nof_tx_ports; i++) { + for (j = 0; j < nof_rx_ports; j++) { + for (k = 0; k < n / nof_layers; k++) { + cf_t hsymb = (float) rand() / RAND_MAX + ((float) rand() / RAND_MAX) * _Complex_I; + for (l = 0; l < nof_layers; l++) { + // assume the channel is the same for all symbols + h[i][j][k * nof_layers + l] = hsymb; + } + } + } + } +} + +void populate_channel_single(cf_t *h) { + int i; + + for (i = 0; i < nof_re; i++) { + h[i] = (float) rand() / RAND_MAX + ((float) rand() / RAND_MAX) * _Complex_I; + } +} + +void populate_channel(srslte_mimo_type_t type, cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]) { + switch (type) { + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + case SRSLTE_MIMO_TYPE_CDD: + populate_channel_cdd(h, (uint32_t) nof_re); + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + populate_channel_diversity(h, (uint32_t) nof_re); + break; + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + default: + populate_channel_single(h[0][0]); + } +} + +static void awgn(cf_t *y[SRSLTE_MAX_PORTS], uint32_t n, float snr) { + int i; + float std_dev = powf(10, - (snr + 3.0f) / 20.0f) * scaling; + + for (i = 0; i < nof_rx_ports; i++) { + srslte_ch_awgn_c(y[i], y[i], std_dev, n); + } +} + +int main(int argc, char **argv) { + int i, j, k, nof_errors = 0, ret = SRSLTE_SUCCESS; + float mse; + cf_t *x[SRSLTE_MAX_LAYERS], *r[SRSLTE_MAX_PORTS], *y[SRSLTE_MAX_PORTS], *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + *xr[SRSLTE_MAX_LAYERS]; + srslte_mimo_type_t type; + + parse_args(argc, argv); + + /* Check input ranges */ + if (nof_tx_ports > SRSLTE_MAX_PORTS || nof_rx_ports > SRSLTE_MAX_PORTS || nof_layers > SRSLTE_MAX_LAYERS) { + fprintf(stderr, "Invalid number of layers or ports\n"); + exit(-1); + } + + /* Parse MIMO Type */ + if (srslte_str2mimotype(mimo_type_name, &type)) { + fprintf(stderr, "Invalid MIMO type %s\n", mimo_type_name); + exit(-1); + } + + /* Check scenario conditions are OK */ + switch (type) { + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + nof_re = nof_layers*nof_symbols; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + nof_re = nof_symbols; + break; + case SRSLTE_MIMO_TYPE_CDD: + nof_re = nof_symbols*nof_tx_ports/nof_layers; + if (nof_rx_ports != 2 || nof_tx_ports != 2) { + fprintf(stderr, "CDD nof_tx_ports=%d nof_rx_ports=%d is not currently supported\n", nof_tx_ports, nof_rx_ports); + exit(-1); + } + break; + default: + nof_re = nof_symbols*nof_layers; + } + + /* Allocate x and xr (received symbols) in memory for each layer */ + for (i = 0; i < nof_layers; i++) { + /* Source data */ + x[i] = srslte_vec_malloc(sizeof(cf_t) * nof_symbols); + if (!x[i]) { + perror("srslte_vec_malloc"); + exit(-1); + } + + /* Sink data */ + xr[i] = srslte_vec_malloc(sizeof(cf_t) * nof_symbols); + if (!xr[i]) { + perror("srslte_vec_malloc"); + exit(-1); + } + } + + /* Allocate y in memory for tx each port */ + for (i = 0; i < nof_tx_ports; i++) { + y[i] = srslte_vec_malloc(sizeof(cf_t) * nof_re); + if (!y[i]) { + perror("srslte_vec_malloc"); + exit(-1); + } + } + + /* Allocate h in memory for each cross channel and layer */ + for (i = 0; i < nof_tx_ports; i++) { + for (j = 0; j < nof_rx_ports; j++) { + h[i][j] = srslte_vec_malloc(sizeof(cf_t) * nof_re); + if (!h[i][j]) { + perror("srslte_vec_malloc"); + exit(-1); + } + } + } + + /* Allocate r */ + for (i = 0; i < nof_rx_ports; i++) { + r[i] = srslte_vec_malloc(sizeof(cf_t) * nof_re); + if (!r[i]) { + perror("srslte_vec_malloc"); + exit(-1); + } + } + + /* Generate source random data */ + for (i = 0; i < nof_layers; i++) { + for (j = 0; j < nof_symbols; j++) { + x[i][j] = (2 * (rand() % 2) - 1 + (2 * (rand() % 2) - 1) * _Complex_I) / sqrt(2); + } + } + + /* Execute Precoding (Tx) */ + if (srslte_precoding_type(x, y, nof_layers, nof_tx_ports, codebook_idx, nof_symbols, scaling, type) < 0) { + fprintf(stderr, "Error layer mapper encoder\n"); + exit(-1); + } + + /* generate channel */ + populate_channel(type, h); + + /* pass signal through channel + (we are in the frequency domain so it's a multiplication) */ + for (i = 0; i < nof_rx_ports; i++) { + for (k = 0; k < nof_re; k++) { + r[i][k] = (cf_t) (0.0 + 0.0 * _Complex_I); + for (j = 0; j < nof_tx_ports; j++) { + r[i][k] += y[j][k] * h[j][i][k]; + } + } + } + + awgn(r, (uint32_t) nof_re, snr_db); + + /* If CDD or Spatial muliplex choose decoder */ + if (strncmp(decoder_type_name, "zf", 16) == 0) { + srslte_predecoding_set_mimo_decoder(SRSLTE_MIMO_DECODER_ZF); + } else if (strncmp(decoder_type_name, "mmse", 16) == 0) { + srslte_predecoding_set_mimo_decoder(SRSLTE_MIMO_DECODER_MMSE); + } else { + ret = SRSLTE_ERROR; + goto quit; + } + + + /* predecoding / equalization */ + struct timeval t[3]; + gettimeofday(&t[1], NULL); + srslte_predecoding_type(r, h, xr, NULL, nof_rx_ports, nof_tx_ports, nof_layers, + codebook_idx, nof_re, type, scaling, powf(10, -snr_db / 10)); + gettimeofday(&t[2], NULL); + get_time_interval(t); + + /* check errors */ + mse = 0; + for (i = 0; i < nof_layers; i++) { + for (j = 0; j < nof_symbols; j++) { + mse += cabsf(xr[i][j] - x[i][j]); + + if ((crealf(xr[i][j]) > 0) != (crealf(x[i][j]) > 0)) { + nof_errors ++; + } + if ((cimagf(xr[i][j]) > 0) != (cimagf(x[i][j]) > 0)) { + nof_errors ++; + } + } + } + printf("SNR: %5.1fdB;\tExecution time: %5ldus;\tMSE: %.6f;\tBER: %.6f\n", snr_db, t[0].tv_usec, + mse / nof_layers / nof_symbols, (float) nof_errors / (4.0f * nof_re)); + if (mse / nof_layers / nof_symbols > MSE_THRESHOLD) { + ret = SRSLTE_ERROR; + } + + quit: + /* Free all data */ + for (i = 0; i < nof_layers; i++) { + free(x[i]); + free(xr[i]); + } + + for (i = 0; i < nof_rx_ports; i++) { + free(r[i]); + } + + for (i = 0; i < nof_rx_ports; i++) { + for (j = 0; j < nof_tx_ports; j++) { + free(h[j][i]); + } + } + + for (i = 0; i < nof_tx_ports; i++) { + if (y[i]) { + free(y[i]); + } + } + + exit(ret); +} diff --git a/lib/src/phy/mimo/test/predecoder_mex.c b/lib/src/phy/mimo/test/predecoder_mex.c new file mode 100644 index 0000000..0465e1e --- /dev/null +++ b/lib/src/phy/mimo/test/predecoder_mex.c @@ -0,0 +1,213 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the predecoder + */ + +#define INPUT prhs[0] +#define HEST prhs[1] +#define NEST prhs[2] +#define NLAYERS prhs[3] +#define NCW prhs[4] +#define TXSCHEME prhs[5] +#define CODEBOOK prhs[6] +#define NOF_INPUTS 7 + + +void help() +{ + mexErrMsgTxt + ("[output] = srslte_predecoder(input, hest, nest, Nl, TxScheme)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + const mwSize *dims = mxGetDimensions(INPUT); + mwSize ndims; + cf_t *input = NULL; + cf_t *hest = NULL; + cf_t *output = NULL; + uint32_t nof_symbols = 0; + uint32_t nof_rx_ants = 1; + uint32_t nof_layers; + uint32_t nof_tx_ports = 1; + uint32_t nof_codewords = 1; + uint32_t codebook_idx = 0; + float noise_estimate = 0; + cf_t *x[SRSLTE_MAX_LAYERS]; + cf_t *h[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + cf_t *y[SRSLTE_MAX_PORTS]; + int symbols_layers[SRSLTE_MAX_LAYERS]; + int i, j; + srslte_mimo_type_t type; + + /* Print help if number of inputs does not match with expected */ + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + /* Read input symbols */ + if (mexutils_read_cf(INPUT, &input) < 0) { + mexErrMsgTxt("Error reading input\n"); + return; + } + + /* Read number of layers */ + nof_layers = (uint32_t) mxGetScalar(NLAYERS); + + /* Read number of codewords */ + nof_codewords = (uint32_t) mxGetScalar(NCW); + + if (nof_layers > SRSLTE_MAX_LAYERS) { + mexErrMsgTxt("Too many layers\n"); + return; + } + + /* Read number of symbols and Rx antennas */ + ndims = mxGetNumberOfDimensions(INPUT); + nof_symbols = (uint32_t) dims[0]; + + if (ndims >= 2) { + nof_rx_ants = (uint32_t) dims[1]; + } + + /* Read channel estimates */ + if (mexutils_read_cf(HEST, &hest) < 0) { + mexErrMsgTxt("Error reading hest\n"); + return; + } + + /* Get number of tx ports */ + dims = mxGetDimensions(HEST); + ndims = mxGetNumberOfDimensions(HEST); + + if (ndims == 3) { + nof_tx_ports = (uint32_t) dims[2]; + } + + /* Print parameters trace */ + mexPrintf("nof_tx_ports=%d, nof_rx_ants=%d, nof_layers=%d, nof_codewords=%d, codebook_idx=%d, nof_symbols=%d\n", + nof_tx_ports, nof_rx_ants, nof_layers, nof_codewords, codebook_idx, nof_symbols); + + /* Read noise estimate */ + if (nrhs >= NOF_INPUTS) { + noise_estimate = (float) mxGetScalar(NEST); + } + + /* Initialise x, h & y pointers */ + for (i=0;i= NOF_INPUTS) { + mxGetString_700(TXSCHEME, txscheme, 32); + } + + codebook_idx = (uint32_t) mxGetScalar(CODEBOOK); + + if (srslte_str2mimotype(txscheme, &type)) { + mexPrintf("Unsupported TxScheme=%s\n", txscheme); + return; + } + + /* Populate symbols in layers */ + for (i = 0; i < nof_layers; i++) { + symbols_layers[i] = nof_symbols; + } + + /* Set output pointer */ + cf_t *d[SRSLTE_MAX_CODEWORDS]; + for (i = 0; i= 1) { + mexutils_write_cf(output, &plhs[0], nof_symbols, nof_codewords); + } + + /* Free memory */ + if (input) { + free(input); + } + if (hest) { + free(hest); + } + if (output) { + free(output); + } + for (i=0;i +#include + +#include "srslte/phy/modem/demod_hard.h" +#include "hard_demod_lte.h" + + +void srslte_demod_hard_init(srslte_demod_hard_t* q) { + bzero((void*) q, sizeof(srslte_demod_hard_t)); +} + +void srslte_demod_hard_table_set(srslte_demod_hard_t* q, srslte_mod_t mod) { + q->mod = mod; +} + +int srslte_demod_hard_demodulate(srslte_demod_hard_t* q, cf_t* symbols, uint8_t *bits, uint32_t nsymbols) { + + int nbits=-1; + switch(q->mod) { + case SRSLTE_MOD_LAST: + case SRSLTE_MOD_BPSK: + hard_bpsk_demod(symbols,bits,nsymbols); + nbits=nsymbols; + break; + case SRSLTE_MOD_QPSK: + hard_qpsk_demod(symbols,bits,nsymbols); + nbits=nsymbols*2; + break; + case SRSLTE_MOD_16QAM: + hard_qam16_demod(symbols,bits,nsymbols); + nbits=nsymbols*4; + break; + case SRSLTE_MOD_64QAM: + hard_qam64_demod(symbols,bits,nsymbols); + nbits=nsymbols*6; + break; + } + return nbits; +} + + diff --git a/lib/src/phy/modem/demod_soft.c b/lib/src/phy/modem/demod_soft.c new file mode 100644 index 0000000..0ea3ce9 --- /dev/null +++ b/lib/src/phy/modem/demod_soft.c @@ -0,0 +1,283 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/modem/demod_soft.h" + +// AVX implementation not useful for integers. Wait for AVX2 + +#ifdef LV_HAVE_SSE +#include +void demod_16qam_lte_s_sse(const cf_t *symbols, short *llr, int nsymbols); +#endif + + +#define SCALE_SHORT_CONV_QPSK 100 +#define SCALE_SHORT_CONV_QAM16 400 +#define SCALE_SHORT_CONV_QAM64 700 + +void demod_bpsk_lte_s(const cf_t *symbols, short *llr, int nsymbols) { + for (int i=0;i +#include +#include + +#include "srslte/phy/modem/demod_hard.h" +#include "hard_demod_lte.h" + + +/** + * @ingroup Hard BPSK demodulator + * + *LTE-BPSK constellation: + * Q + * | 0 + *---------> I + *1 | + * \param in input symbols (_Complex float) + * \param out output symbols (uint8_ts) + * \param N Number of input symbols + * \param modulation Modulation type + */ +inline void hard_bpsk_demod(const cf_t* in, uint8_t* out, uint32_t N) +{ + uint32_t s; + + for (s=0; s 0) { + if ((__imag__ in[s] > 0) || (__real__ in[s] > -__imag__ in[s])) { + out[s] = 0x0; + } else { + out[s] = 0x1; + } + } else { + if ((__imag__ in[s] < 0) || (__imag__ in[s] < -__real__ in[s])) { + out[s] = 0x1; + } else { + out[s] = 0x0; + } + } + } +} + +/** + * @ingroup Hard QPSK demodulator + * + * LTE-QPSK constellation: + * Q + *10 | 00 + *-----------> I + *11 | 01 + * + * \param in input symbols (_Complex float) + * \param out output symbols (uint8_ts) + * \param N Number of input symbols + * \param modulation Modulation type + */ +inline void hard_qpsk_demod(const cf_t* in, uint8_t* out, uint32_t N) +{ + uint32_t s; + + for (s=0; s 0) { + out[2*s] = 0x0; + } else { + out[2*s] = 0x1; + } + if (__imag__ in[s] > 0) { + out[2*s+1] = 0x0; + } else { + out[2*s+1] = 0x1; + } + } +} + +/** + * @ingroup Hard 16QAM demodulator + * + * LTE-16QAM constellation: + * Q + * 1011 1001 | 0001 0011 + * 1010 1000 | 0000 0010 + *---------------------------------> I + * 1110 1100 | 0100 0110 + * 1111 1101 | 0101 0111 + * + * \param in input symbols (_Complex float) + * \param out output symbols (uint8_ts) + * \param N Number of input symbols + * \param modulation Modulation type + */ +inline void hard_qam16_demod(const cf_t* in, uint8_t* out, uint32_t N) +{ + uint32_t s; + + for (s=0; s 0) { + out[4*s] = 0x0; + } else { + out[4*s] = 0x1; + } + + if ((__real__ in[s] > QAM16_THRESHOLD) || (__real__ in[s] < -QAM16_THRESHOLD)) { + out[4*s+2] = 0x1; + } else { + out[4*s+2] = 0x0; + } + + if (__imag__ in[s] > 0) { + out[4*s+1] = 0x0; + } else { + out[4*s+1] = 0x1; + } + + if ((__imag__ in[s] > QAM16_THRESHOLD) || (__imag__ in[s] < -QAM16_THRESHOLD)) { + out[4*s+3] = 0x1; + } else { + out[4*s+3] = 0x0; + } + } +} + +/** + * @ingroup Hard 64QAM demodulator + * + * LTE-64QAM constellation: + * see [3GPP TS 36.211 version 10.5.0 Release 10, Section 7.1.4] + * + * \param in input symbols (_Complex float) + * \param out output symbols (uint8_ts) + * \param N Number of input symbols + * \param modulation Modulation type + */ +inline void hard_qam64_demod(const cf_t* in, uint8_t* out, uint32_t N) +{ + uint32_t s; + + for (s=0; s 0){ + out[6*s] = 0x0; + } else { + out[6*s] = 0x1; + } + if ((__real__ in[s] > QAM64_THRESHOLD_3) || (__real__ in[s] < -QAM64_THRESHOLD_3)) { + out[6*s+2] = 0x1; + out[6*s+4] = 0x1; + } else if ((__real__ in[s] > QAM64_THRESHOLD_2) || (__real__ in[s] < -QAM64_THRESHOLD_2)) { + out[6*s+2] = 0x1; + out[6*s+4] = 0x0; + } else if ((__real__ in[s] > QAM64_THRESHOLD_1) || (__real__ in[s] < -QAM64_THRESHOLD_1)) { + out[6*s+2] = 0x0; + out[6*s+4] = 0x0; + } else { + out[6*s+2] = 0x0; + out[6*s+4] = 0x1; + } + + /* bits associated with/obtained from quadrature component: b1, b3, b5 */ + if (__imag__ in[s] > 0){ + out[6*s+1] = 0x0; + } else { + out[6*s+1] = 0x1; + } + if ((__imag__ in[s] > QAM64_THRESHOLD_3) || (__imag__ in[s] < -QAM64_THRESHOLD_3)) { + out[6*s+3] = 0x1; + out[6*s+5] = 0x1; + } else if ((__imag__ in[s] > QAM64_THRESHOLD_2) || (__imag__ in[s] < -QAM64_THRESHOLD_2)) { + out[6*s+3] = 0x1; + out[6*s+5] = 0x0; + } else if ((__imag__ in[s] > QAM64_THRESHOLD_1) || (__imag__ in[s] < -QAM64_THRESHOLD_1)) { + out[6*s+3] = 0x0; + out[6*s+5] = 0x0; + } else { + out[6*s+3] = 0x0; + out[6*s+5] = 0x1; + } + } +} diff --git a/lib/src/phy/modem/hard_demod_lte.h b/lib/src/phy/modem/hard_demod_lte.h new file mode 100644 index 0000000..b5ce8fc --- /dev/null +++ b/lib/src/phy/modem/hard_demod_lte.h @@ -0,0 +1,50 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +/* Thresholds for Demodulation */ +/* Assume perfect amplitude and phase alignment. + * Check threshold values for real case + * or implement dynamic threshold adjustent as a function of received symbol amplitudes */ +#define QAM16_THRESHOLD 2/sqrt(10) +#define QAM64_THRESHOLD_1 2/sqrt(42) +#define QAM64_THRESHOLD_2 4/sqrt(42) +#define QAM64_THRESHOLD_3 6/sqrt(42) + +void hard_bpsk_demod(const cf_t* in, + uint8_t* out, + uint32_t N); + +void hard_qpsk_demod(const cf_t* in, + uint8_t* out, + uint32_t N); + +void hard_qam16_demod(const cf_t* in, + uint8_t* out, + uint32_t N); + +void hard_qam64_demod(const cf_t* in, + uint8_t* out, + uint32_t N); diff --git a/lib/src/phy/modem/lte_tables.c b/lib/src/phy/modem/lte_tables.c new file mode 100644 index 0000000..d15fa36 --- /dev/null +++ b/lib/src/phy/modem/lte_tables.c @@ -0,0 +1,166 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/modem/modem_table.h" +#include "lte_tables.h" + +/** + * Set the BPSK modulation table */ +void set_BPSKtable(cf_t* table) +{ + // LTE-BPSK constellation: + // Q + // | 0 + //---------> I + // 1 | + table[0] = BPSK_LEVEL + BPSK_LEVEL*_Complex_I; + table[1] = -BPSK_LEVEL -BPSK_LEVEL*_Complex_I; + +} + +/** + * Set the QPSK modulation table */ +void set_QPSKtable(cf_t* table) +{ + + // LTE-QPSK constellation: + // Q + // 10 | 00 + //-----------> I + // 11 | 01 + table[0] = QPSK_LEVEL + QPSK_LEVEL*_Complex_I; + table[1] = QPSK_LEVEL - QPSK_LEVEL*_Complex_I; + table[2] = -QPSK_LEVEL + QPSK_LEVEL*_Complex_I; + table[3] = -QPSK_LEVEL - QPSK_LEVEL*_Complex_I; +} + +/** + * Set the 16QAM modulation table */ +void set_16QAMtable(cf_t* table) +{ + // LTE-16QAM constellation: + // Q + // 1011 1001 | 0001 0011 + // 1010 1000 | 0000 0010 + //---------------------------------> I + // 1110 1100 | 0100 0110 + // 1111 1101 | 0101 0111 + table[0] = QAM16_LEVEL_1 + QAM16_LEVEL_1*_Complex_I; + table[1] = QAM16_LEVEL_1 + QAM16_LEVEL_2*_Complex_I; + table[2] = QAM16_LEVEL_2 + QAM16_LEVEL_1*_Complex_I; + table[3] = QAM16_LEVEL_2 + QAM16_LEVEL_2*_Complex_I; + table[4] = QAM16_LEVEL_1 - QAM16_LEVEL_1*_Complex_I; + table[5] = QAM16_LEVEL_1 - QAM16_LEVEL_2*_Complex_I; + table[6] = QAM16_LEVEL_2 - QAM16_LEVEL_1*_Complex_I; + table[7] = QAM16_LEVEL_2 - QAM16_LEVEL_2*_Complex_I; + table[8] = -QAM16_LEVEL_1 + QAM16_LEVEL_1*_Complex_I; + table[9] = -QAM16_LEVEL_1 + QAM16_LEVEL_2*_Complex_I; + table[10] = -QAM16_LEVEL_2 + QAM16_LEVEL_1*_Complex_I; + table[11] = -QAM16_LEVEL_2 + QAM16_LEVEL_2*_Complex_I; + table[12] = -QAM16_LEVEL_1 - QAM16_LEVEL_1*_Complex_I; + table[13] = -QAM16_LEVEL_1 - QAM16_LEVEL_2*_Complex_I; + table[14] = -QAM16_LEVEL_2 - QAM16_LEVEL_1*_Complex_I; + table[15] = -QAM16_LEVEL_2 - QAM16_LEVEL_2*_Complex_I; +} + +/** + * Set the 64QAM modulation table */ +void set_64QAMtable(cf_t* table) +{ + // LTE-64QAM constellation: + // see [3GPP TS 36.211 version 10.5.0 Release 10, Section 7.1.4] + table[0] = QAM64_LEVEL_2 + QAM64_LEVEL_2*_Complex_I; + table[1] = QAM64_LEVEL_2 + QAM64_LEVEL_1*_Complex_I; + table[2] = QAM64_LEVEL_1 + QAM64_LEVEL_2*_Complex_I; + table[3] = QAM64_LEVEL_1 + QAM64_LEVEL_1*_Complex_I; + table[4] = QAM64_LEVEL_2 + QAM64_LEVEL_3*_Complex_I; + table[5] = QAM64_LEVEL_2 + QAM64_LEVEL_4*_Complex_I; + table[6] = QAM64_LEVEL_1 + QAM64_LEVEL_3*_Complex_I; + table[7] = QAM64_LEVEL_1 + QAM64_LEVEL_4*_Complex_I; + table[8] = QAM64_LEVEL_3 + QAM64_LEVEL_2*_Complex_I; + table[9] = QAM64_LEVEL_3 + QAM64_LEVEL_1*_Complex_I; + table[10] = QAM64_LEVEL_4 + QAM64_LEVEL_2*_Complex_I; + table[11] = QAM64_LEVEL_4 + QAM64_LEVEL_1*_Complex_I; + table[12] = QAM64_LEVEL_3 + QAM64_LEVEL_3*_Complex_I; + table[13] = QAM64_LEVEL_3 + QAM64_LEVEL_4*_Complex_I; + table[14] = QAM64_LEVEL_4 + QAM64_LEVEL_3*_Complex_I; + table[15] = QAM64_LEVEL_4 + QAM64_LEVEL_4*_Complex_I; + table[16] = QAM64_LEVEL_2 - QAM64_LEVEL_2*_Complex_I; + table[17] = QAM64_LEVEL_2 - QAM64_LEVEL_1*_Complex_I; + table[18] = QAM64_LEVEL_1 - QAM64_LEVEL_2*_Complex_I; + table[19] = QAM64_LEVEL_1 - QAM64_LEVEL_1*_Complex_I; + table[20] = QAM64_LEVEL_2 - QAM64_LEVEL_3*_Complex_I; + table[21] = QAM64_LEVEL_2 - QAM64_LEVEL_4*_Complex_I; + table[22] = QAM64_LEVEL_1 - QAM64_LEVEL_3*_Complex_I; + table[23] = QAM64_LEVEL_1 - QAM64_LEVEL_4*_Complex_I; + table[24] = QAM64_LEVEL_3 - QAM64_LEVEL_2*_Complex_I; + table[25] = QAM64_LEVEL_3 - QAM64_LEVEL_1*_Complex_I; + table[26] = QAM64_LEVEL_4 - QAM64_LEVEL_2*_Complex_I; + table[27] = QAM64_LEVEL_4 - QAM64_LEVEL_1*_Complex_I; + table[28] = QAM64_LEVEL_3 - QAM64_LEVEL_3*_Complex_I; + table[29] = QAM64_LEVEL_3 - QAM64_LEVEL_4*_Complex_I; + table[30] = QAM64_LEVEL_4 - QAM64_LEVEL_3*_Complex_I; + table[31] = QAM64_LEVEL_4 - QAM64_LEVEL_4*_Complex_I; + table[32] = -QAM64_LEVEL_2 + QAM64_LEVEL_2*_Complex_I; + table[33] = -QAM64_LEVEL_2 + QAM64_LEVEL_1*_Complex_I; + table[34] = -QAM64_LEVEL_1 + QAM64_LEVEL_2*_Complex_I; + table[35] = -QAM64_LEVEL_1 + QAM64_LEVEL_1*_Complex_I; + table[36] = -QAM64_LEVEL_2 + QAM64_LEVEL_3*_Complex_I; + table[37] = -QAM64_LEVEL_2 + QAM64_LEVEL_4*_Complex_I; + table[38] = -QAM64_LEVEL_1 + QAM64_LEVEL_3*_Complex_I; + table[39] = -QAM64_LEVEL_1 + QAM64_LEVEL_4*_Complex_I; + table[40] = -QAM64_LEVEL_3 + QAM64_LEVEL_2*_Complex_I; + table[41] = -QAM64_LEVEL_3 + QAM64_LEVEL_1*_Complex_I; + table[42] = -QAM64_LEVEL_4 + QAM64_LEVEL_2*_Complex_I; + table[43] = -QAM64_LEVEL_4 + QAM64_LEVEL_1*_Complex_I; + table[44] = -QAM64_LEVEL_3 + QAM64_LEVEL_3*_Complex_I; + table[45] = -QAM64_LEVEL_3 + QAM64_LEVEL_4*_Complex_I; + table[46] = -QAM64_LEVEL_4 + QAM64_LEVEL_3*_Complex_I; + table[47] = -QAM64_LEVEL_4 + QAM64_LEVEL_4*_Complex_I; + table[48] = -QAM64_LEVEL_2 - QAM64_LEVEL_2*_Complex_I; + table[49] = -QAM64_LEVEL_2 - QAM64_LEVEL_1*_Complex_I; + table[50] = -QAM64_LEVEL_1 - QAM64_LEVEL_2*_Complex_I; + table[51] = -QAM64_LEVEL_1 - QAM64_LEVEL_1*_Complex_I; + table[52] = -QAM64_LEVEL_2 - QAM64_LEVEL_3*_Complex_I; + table[53] = -QAM64_LEVEL_2 - QAM64_LEVEL_4*_Complex_I; + table[54] = -QAM64_LEVEL_1 - QAM64_LEVEL_3*_Complex_I; + table[55] = -QAM64_LEVEL_1 - QAM64_LEVEL_4*_Complex_I; + table[56] = -QAM64_LEVEL_3 - QAM64_LEVEL_2*_Complex_I; + table[57] = -QAM64_LEVEL_3 - QAM64_LEVEL_1*_Complex_I; + table[58] = -QAM64_LEVEL_4 - QAM64_LEVEL_2*_Complex_I; + table[59] = -QAM64_LEVEL_4 - QAM64_LEVEL_1*_Complex_I; + table[60] = -QAM64_LEVEL_3 - QAM64_LEVEL_3*_Complex_I; + table[61] = -QAM64_LEVEL_3 - QAM64_LEVEL_4*_Complex_I; + table[62] = -QAM64_LEVEL_4 - QAM64_LEVEL_3*_Complex_I; + table[63] = -QAM64_LEVEL_4 - QAM64_LEVEL_4*_Complex_I; +} + diff --git a/lib/src/phy/modem/lte_tables.h b/lib/src/phy/modem/lte_tables.h new file mode 100644 index 0000000..cccb0a0 --- /dev/null +++ b/lib/src/phy/modem/lte_tables.h @@ -0,0 +1,60 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +#define BPSK_LEVEL 1/sqrt(2) + +#define QPSK_LEVEL 1/sqrt(2) + +#define QAM16_LEVEL_1 1/sqrt(10) +#define QAM16_LEVEL_2 3/sqrt(10) + +#define QAM64_LEVEL_1 1/sqrt(42) +#define QAM64_LEVEL_2 3/sqrt(42) +#define QAM64_LEVEL_3 5/sqrt(42) +#define QAM64_LEVEL_4 7/sqrt(42) + +/* HARD DEMODULATION Thresholds, necessary for obtaining the zone of received symbol for optimized LLR approx implementation */ +#define QAM16_THRESHOLD 2/sqrt(10) +#define QAM64_THRESHOLD_1 2/sqrt(42) +#define QAM64_THRESHOLD_2 4/sqrt(42) +#define QAM64_THRESHOLD_3 6/sqrt(42) +//=========================================// + +#define QAM64_LEVEL_x 2/sqrt(42) +/* this is not an QAM64 level, but, rather, an auxiliary value that can be used for computing the + * symbol from the bit sequence */ + + + + +void set_BPSKtable(cf_t* table); + +void set_QPSKtable(cf_t* table); + +void set_16QAMtable(cf_t* table); + +void set_64QAMtable(cf_t* table); diff --git a/lib/src/phy/modem/mod.c b/lib/src/phy/modem/mod.c new file mode 100644 index 0000000..b72fdd1 --- /dev/null +++ b/lib/src/phy/modem/mod.c @@ -0,0 +1,157 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/modem/mod.h" + +/** Low-level API */ + +int srslte_mod_modulate(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) { + uint32_t i,j,idx; + uint8_t *b_ptr=(uint8_t*) bits; + j=0; + for (i=0;inbits_x_symbol) { + idx = srslte_bit_pack(&b_ptr,q->nbits_x_symbol); + if (idx < q->nsymbols) { + symbols[j] = q->symbol_table[idx]; + } else { + return SRSLTE_ERROR; + } + j++; + } + return j; +} + +void mod_bpsk_bytes(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) { + uint8_t mask_bpsk[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; + uint8_t shift_bpsk[8] = {7, 6, 5, 4, 3, 2, 1, 0}; + + for (int i=0;isymbol_table_bpsk[bits[i]], sizeof(bpsk_packed_t)); + } + for (int i=0;isymbol_table[(bits[8*(nbits/8)+i]&mask_bpsk[i])>>shift_bpsk[i]]; + } +} + +void mod_qpsk_bytes(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) { + uint8_t mask_qpsk[4] = {0xc0, 0x30, 0x0c, 0x03}; + uint8_t shift_qpsk[4] = {6, 4, 2, 0}; + for (int i=0;isymbol_table_qpsk[bits[i]], sizeof(qpsk_packed_t)); + } + // Encode last 1, 2 or 3 bit pairs if not multiple of 8 + for (int i=0;i<(nbits%8)/2;i++) { + symbols[4*(nbits/8)+i] = q->symbol_table[(bits[nbits/8]&mask_qpsk[i])>>shift_qpsk[i]]; + } +} + +void mod_16qam_bytes(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) { + for (int i=0;isymbol_table_16qam[bits[i]], sizeof(qam16_packed_t)); + } + // Encode last 4 bits if not multiple of 8 + if (nbits%8) { + symbols[2*(nbits/8)] = q->symbol_table[(bits[nbits/8]&0xf0)>>4]; + } +} + +void mod_64qam_bytes(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) { + uint8_t in0, in1, in2, in3; + uint32_t in80, in81, in82; + + for (int i=0;i>2; + in1 = (in80&0x03)<<4 | ((in81&0xf0)>>4); + in2 = (in81&0x0f)<<2 | ((in82&0xc0)>>6); + in3 = in82&0x3f; + + symbols[i*4+0] = q->symbol_table[in0]; + symbols[i*4+1] = q->symbol_table[in1]; + symbols[i*4+2] = q->symbol_table[in2]; + symbols[i*4+3] = q->symbol_table[in3]; + } + if (nbits%24 >= 6) { + in80 = bits[3*(nbits/24)+0]; + in0 = (in80&0xfc)>>2; + + symbols[4*(nbits/24)+0] = q->symbol_table[in0]; + } + if (nbits%24 >= 12) { + in81 = bits[3*(nbits/24)+1]; + in1 = (in80&0x03)<<4 | ((in81&0xf0)>>4); + + symbols[4*(nbits/24)+1] = q->symbol_table[in1]; + } + if (nbits%24 >= 18) { + in82 = bits[3*(nbits/24)+2]; + in2 = (in81&0x0f)<<2 | ((in82&0xc0)>>6); + + symbols[4*(nbits/24)+2] = q->symbol_table[in2]; + } +} + +/* Assumes packet bits as input */ +int srslte_mod_modulate_bytes(srslte_modem_table_t* q, uint8_t *bits, cf_t* symbols, uint32_t nbits) +{ + + if (!q->byte_tables_init) { + fprintf(stderr, "Error need to initiated modem tables for packeted bits before calling srslte_mod_modulate_bytes()\n"); + return -1; + } + if (nbits % q->nbits_x_symbol) { + fprintf(stderr, "Error modulator expects number of bits (%d) to be multiple of %d\n", nbits, q->nbits_x_symbol); + return -1; + } + switch(q->nbits_x_symbol) { + case 1: + mod_bpsk_bytes(q, bits, symbols, nbits); + break; + case 2: + mod_qpsk_bytes(q, bits, symbols, nbits); + break; + case 4: + mod_16qam_bytes(q, bits, symbols, nbits); + break; + case 6: + mod_64qam_bytes(q, bits, symbols, nbits); + break; + default: + fprintf(stderr, "srslte_mod_modulate_bytes() accepts QPSK/16QAM/64QAM modulations only\n"); + return -1; + } + return nbits/q->nbits_x_symbol; +} diff --git a/lib/src/phy/modem/modem_table.c b/lib/src/phy/modem/modem_table.c new file mode 100644 index 0000000..3c4ad24 --- /dev/null +++ b/lib/src/phy/modem/modem_table.c @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/modem/modem_table.h" +#include "srslte/phy/utils/vector.h" +#include "lte_tables.h" + +/** Internal functions */ +static int table_create(srslte_modem_table_t* q) { + q->symbol_table = srslte_vec_malloc(q->nsymbols*sizeof(cf_t)); + return q->symbol_table==NULL; +} + +void srslte_modem_table_init(srslte_modem_table_t* q) { + bzero((void*)q,sizeof(srslte_modem_table_t)); +} +void srslte_modem_table_free(srslte_modem_table_t* q) { + if (q->symbol_table) { + free(q->symbol_table); + } + if (q->symbol_table_bpsk) { + free(q->symbol_table_bpsk); + } + if (q->symbol_table_qpsk) { + free(q->symbol_table_qpsk); + } + if (q->symbol_table_16qam) { + free(q->symbol_table_16qam); + } + bzero(q, sizeof(srslte_modem_table_t)); +} +void srslte_modem_table_reset(srslte_modem_table_t* q) { + srslte_modem_table_free(q); + srslte_modem_table_init(q); +} + +int srslte_modem_table_set(srslte_modem_table_t* q, cf_t* table, uint32_t nsymbols, uint32_t nbits_x_symbol) { + if (q->nsymbols) { + return SRSLTE_ERROR; + } + q->nsymbols = nsymbols; + if (table_create(q)) { + return SRSLTE_ERROR; + } + memcpy(q->symbol_table,table,q->nsymbols*sizeof(cf_t)); + q->nbits_x_symbol = nbits_x_symbol; + return SRSLTE_SUCCESS; +} + +int srslte_modem_table_lte(srslte_modem_table_t* q, srslte_mod_t modulation) { + srslte_modem_table_init(q); + switch(modulation) { + case SRSLTE_MOD_LAST: + case SRSLTE_MOD_BPSK: + q->nbits_x_symbol = 1; + q->nsymbols = 2; + if (table_create(q)) { + return SRSLTE_ERROR; + } + set_BPSKtable(q->symbol_table); + break; + case SRSLTE_MOD_QPSK: + q->nbits_x_symbol = 2; + q->nsymbols = 4; + if (table_create(q)) { + return SRSLTE_ERROR; + } + set_QPSKtable(q->symbol_table); + break; + case SRSLTE_MOD_16QAM: + q->nbits_x_symbol = 4; + q->nsymbols = 16; + if (table_create(q)) { + return SRSLTE_ERROR; + } + set_16QAMtable(q->symbol_table); + break; + case SRSLTE_MOD_64QAM: + q->nbits_x_symbol = 6; + q->nsymbols = 64; + if (table_create(q)) { + return SRSLTE_ERROR; + } + set_64QAMtable(q->symbol_table); + break; + } + return SRSLTE_SUCCESS; +} + +void srslte_modem_table_bytes(srslte_modem_table_t* q) { + uint8_t mask_qpsk[4] = {0xc0, 0x30, 0xc, 0x3}; + uint8_t mask_16qam[2] = {0xf0, 0xf}; + + switch(q->nbits_x_symbol) { + case 1: + q->symbol_table_bpsk = srslte_vec_malloc(sizeof(bpsk_packed_t)*256); + for (uint32_t i=0;i<256;i++) { + for (int j=0;j<8;j++) { + q->symbol_table_bpsk[i].symbol[j] = q->symbol_table[(i&(1<<(7-j)))>>(7-j)]; + } + } + q->byte_tables_init = true; + break; + case 2: + q->symbol_table_qpsk = srslte_vec_malloc(sizeof(qpsk_packed_t)*256); + for (uint32_t i=0;i<256;i++) { + for (int j=0;j<4;j++) { + q->symbol_table_qpsk[i].symbol[j] = q->symbol_table[(i&mask_qpsk[j])>>(6-j*2)]; + } + } + q->byte_tables_init = true; + break; + case 4: + q->symbol_table_16qam = srslte_vec_malloc(sizeof(qam16_packed_t)*256); + for (uint32_t i=0;i<256;i++) { + for (int j=0;j<2;j++) { + q->symbol_table_16qam[i].symbol[j] = q->symbol_table[(i&mask_16qam[j])>>(4-j*4)]; + } + } + q->byte_tables_init = true; + break; + case 6: + q->byte_tables_init = true; + break; + } +} + + + diff --git a/lib/src/phy/modem/test/CMakeLists.txt b/lib/src/phy/modem/test/CMakeLists.txt new file mode 100644 index 0000000..4d28d2f --- /dev/null +++ b/lib/src/phy/modem/test/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# MODEM TEST +######################################################################## + +add_executable(modem_test modem_test.c) +target_link_libraries(modem_test srslte_phy) + +add_test(modem_bpsk modem_test -n 1024 -m 1) +add_test(modem_qpsk modem_test -n 1024 -m 2) +add_test(modem_qam16 modem_test -n 1024 -m 4) +add_test(modem_qam64 modem_test -n 1008 -m 6) + +add_test(modem_bpsk_soft modem_test -n 1024 -m 1) +add_test(modem_qpsk_soft modem_test -n 1024 -m 2) +add_test(modem_qam16_soft modem_test -n 1024 -m 4) +add_test(modem_qam64_soft modem_test -n 1008 -m 6) + +add_executable(soft_demod_test soft_demod_test.c) +target_link_libraries(soft_demod_test srslte_phy) + + + + diff --git a/lib/src/phy/modem/test/modem_test.c b/lib/src/phy/modem/test/modem_test.c new file mode 100644 index 0000000..ed9bef5 --- /dev/null +++ b/lib/src/phy/modem/test/modem_test.c @@ -0,0 +1,212 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +time_t start, finish; +struct timeval x, y; + +int num_bits = 1000; +srslte_mod_t modulation = SRSLTE_MOD_BPSK; + +void usage(char *prog) { + printf("Usage: %s [nmse]\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-m modulation (1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64) [Default BPSK]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nm")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 'm': + switch(atoi(argv[optind])) { + case 1: + modulation = SRSLTE_MOD_BPSK; + break; + case 2: + modulation = SRSLTE_MOD_QPSK; + break; + case 4: + modulation = SRSLTE_MOD_16QAM; + break; + case 6: + modulation = SRSLTE_MOD_64QAM; + break; + default: + fprintf(stderr, "Invalid modulation %d. Possible values: " + "(1: BPSK, 2: QPSK, 3: QAM16, 4: QAM64)\n", atoi(argv[optind])); + break; + } + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + int i; + srslte_modem_table_t mod; + uint8_t *input, *input_bytes, *output; + cf_t *symbols, *symbols_bytes; + float *llr, *llr2; + + parse_args(argc, argv); + + /* initialize objects */ + if (srslte_modem_table_lte(&mod, modulation)) { + fprintf(stderr, "Error initializing modem table\n"); + exit(-1); + } + + srslte_modem_table_bytes(&mod); + + /* check that num_bits is multiple of num_bits x symbol */ + if (num_bits % mod.nbits_x_symbol) { + fprintf(stderr, "Error num_bits must be multiple of %d\n", mod.nbits_x_symbol); + exit(-1); + } + + /* allocate buffers */ + input = srslte_vec_malloc(sizeof(uint8_t) * num_bits); + if (!input) { + perror("malloc"); + exit(-1); + } + input_bytes = srslte_vec_malloc(sizeof(uint8_t) * num_bits/8); + if (!input_bytes) { + perror("malloc"); + exit(-1); + } + output = srslte_vec_malloc(sizeof(uint8_t) * num_bits); + if (!output) { + perror("malloc"); + exit(-1); + } + symbols = srslte_vec_malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol); + if (!symbols) { + perror("malloc"); + exit(-1); + } + symbols_bytes = srslte_vec_malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol); + if (!symbols_bytes) { + perror("malloc"); + exit(-1); + } + + llr = srslte_vec_malloc(sizeof(float) * num_bits); + if (!llr) { + perror("malloc"); + exit(-1); + } + + llr2 = srslte_vec_malloc(sizeof(float) * num_bits); + if (!llr2) { + perror("malloc"); + exit(-1); + } + + /* generate random data */ + for (i=0;i=0 ? 1 : 0; + } + + /* check errors */ + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +int nof_frames = 10; +int num_bits = 1000; +srslte_mod_t modulation = 10; + +void usage(char *prog) { + printf("Usage: %s [nfv] -m modulation (1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", prog); + printf("\t-n num_bits [Default %d]\n", num_bits); + printf("\t-f nof_frames [Default %d]\n", nof_frames); + printf("\t-v srslte_verbose [Default None]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nmvf")) != -1) { + switch (opt) { + case 'n': + num_bits = atoi(argv[optind]); + break; + case 'f': + nof_frames = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + case 'm': + switch(atoi(argv[optind])) { + case 1: + modulation = SRSLTE_MOD_BPSK; + break; + case 2: + modulation = SRSLTE_MOD_QPSK; + break; + case 4: + modulation = SRSLTE_MOD_16QAM; + break; + case 6: + modulation = SRSLTE_MOD_64QAM; + break; + default: + fprintf(stderr, "Invalid modulation %d. Possible values: " + "(1: BPSK, 2: QPSK, 4: QAM16, 6: QAM64)\n", atoi(argv[optind])); + break; + } + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (modulation == 10) { + usage(argv[0]); + exit(-1); + } +} + +float mse_threshold() { + switch(modulation) { + case SRSLTE_MOD_BPSK: + return 1.0e-6; + case SRSLTE_MOD_QPSK: + return 1.0e-6; + case SRSLTE_MOD_16QAM: + return 0.11; + case SRSLTE_MOD_64QAM: + return 0.19; + default: + return -1.0; + } +} + +int main(int argc, char **argv) { + int i; + srslte_modem_table_t mod; + uint8_t *input, *output; + cf_t *symbols; + float *llr; + short *llr_s; + + parse_args(argc, argv); + + /* initialize objects */ + if (srslte_modem_table_lte(&mod, modulation)) { + fprintf(stderr, "Error initializing modem table\n"); + exit(-1); + } + + /* check that num_bits is multiple of num_bits x symbol */ + num_bits = mod.nbits_x_symbol * (num_bits / mod.nbits_x_symbol); + + /* allocate buffers */ + input = srslte_vec_malloc(sizeof(uint8_t) * num_bits); + if (!input) { + perror("malloc"); + exit(-1); + } + output = srslte_vec_malloc(sizeof(uint8_t) * num_bits); + if (!output) { + perror("malloc"); + exit(-1); + } + symbols = srslte_vec_malloc(sizeof(cf_t) * num_bits / mod.nbits_x_symbol); + if (!symbols) { + perror("malloc"); + exit(-1); + } + + llr = srslte_vec_malloc(sizeof(float) * num_bits); + if (!llr) { + perror("malloc"); + exit(-1); + } + + llr_s = srslte_vec_malloc(sizeof(short) * num_bits); + if (!llr_s) { + perror("malloc"); + exit(-1); + } + + /* generate random data */ + srand(0); + + int ret = -1; + struct timeval t[3]; + float mean_texec = 0.0; + float mean_texec_s = 0.0; + for (int n=0;n 0) { + mean_texec = SRSLTE_VEC_CMA((float) t[0].tv_usec, mean_texec, n-1); + } + + gettimeofday(&t[1], NULL); + srslte_demod_soft_demodulate_s(modulation, symbols, llr_s, num_bits / mod.nbits_x_symbol); + gettimeofday(&t[2], NULL); + get_time_interval(t); + + if (n > 0) { + mean_texec_s = SRSLTE_VEC_CMA((float) t[0].tv_usec, mean_texec_s, n-1); + } + + if (SRSLTE_VERBOSE_ISDEBUG()) { + printf("bits="); + srslte_vec_fprint_b(stdout, input, num_bits); + + printf("symbols="); + srslte_vec_fprint_c(stdout, symbols, num_bits/mod.nbits_x_symbol); + + printf("llr="); + srslte_vec_fprint_f(stdout, llr, num_bits); + + printf("llr_s="); + srslte_vec_fprint_s(stdout, llr_s, num_bits); + + } + + // Check demodulation errors + for (int i=0;i0?1:0)) { + printf("Error in bit %d\n", i); + goto clean_exit; + } + } + } + ret = 0; + +clean_exit: + free(llr); + free(symbols); + free(output); + free(input); + + srslte_modem_table_free(&mod); + + printf("Mean Throughput: %.2f/%.2f. Mbps ExTime: %.2f/%.2f us\n", + num_bits/mean_texec, num_bits/mean_texec_s, mean_texec, mean_texec_s); + exit(ret); +} diff --git a/lib/src/phy/phch/CMakeLists.txt b/lib/src/phy/phch/CMakeLists.txt new file mode 100644 index 0000000..a0a7169 --- /dev/null +++ b/lib/src/phy/phch/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_phch OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/phch/cqi.c b/lib/src/phy/phch/cqi.c new file mode 100644 index 0000000..7c94585 --- /dev/null +++ b/lib/src/phy/phch/cqi.c @@ -0,0 +1,552 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/cqi.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +/******************************************************* + * PACKING FUNCTIONS * + *******************************************************/ +int srslte_cqi_hl_subband_pack(srslte_cqi_hl_subband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) +{ + uint8_t *body_ptr = buff; + uint32_t bit_count = 0; + + /* Unpack codeword 0, common for 3GPP 36.212 Tables 5.2.2.6.2-1 and 5.2.2.6.2-2 */ + srslte_bit_unpack(msg->wideband_cqi_cw0, &body_ptr, 4); + srslte_bit_unpack(msg->subband_diff_cqi_cw0, &body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + + /* Unpack codeword 1, 3GPP 36.212 Table 5.2.2.6.2-2 */ + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->wideband_cqi_cw1, &body_ptr, 4); + srslte_bit_unpack(msg->subband_diff_cqi_cw1, &body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + } + + /* If PMI is present, unpack it */ + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + srslte_bit_unpack(msg->pmi, &body_ptr, 4); + bit_count += 4; + } else { + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->pmi, &body_ptr, 1); + bit_count += 1; + } else { + srslte_bit_unpack(msg->pmi, &body_ptr, 2); + bit_count += 2; + } + } + } + + return bit_count; +} + +int srslte_cqi_ue_subband_pack(srslte_cqi_ue_subband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) +{ + uint8_t *body_ptr = buff; + srslte_bit_unpack(msg->wideband_cqi, &body_ptr, 4); + srslte_bit_unpack(msg->subband_diff_cqi, &body_ptr, 2); + srslte_bit_unpack(msg->subband_diff_cqi, &body_ptr, msg->L); + + return 4+2+msg->L; +} + +/* Pack CQI following 3GPP TS 36.212 Tables 5.2.3.3.1-1 and 5.2.3.3.1-2 */ +int srslte_cqi_format2_wideband_pack(srslte_cqi_format2_wideband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) +{ + uint8_t *body_ptr = buff; + + srslte_bit_unpack(msg->wideband_cqi, &body_ptr, 4); + + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->spatial_diff_cqi, &body_ptr, 3); + } + srslte_bit_unpack(msg->pmi, &body_ptr, 4); + } else { + if (msg->rank_is_not_one) { + srslte_bit_unpack(msg->spatial_diff_cqi, &body_ptr, 3); + srslte_bit_unpack(msg->pmi, &body_ptr, 1); + } else { + srslte_bit_unpack(msg->pmi, &body_ptr, 2); + } + } + } + + return (int)(body_ptr - buff); +} + +int srslte_cqi_format2_subband_pack(srslte_cqi_format2_subband_t *msg, uint8_t buff[SRSLTE_CQI_MAX_BITS]) +{ + uint8_t *body_ptr = buff; + srslte_bit_unpack(msg->subband_cqi, &body_ptr, 4); + srslte_bit_unpack(msg->subband_label, &body_ptr, msg->subband_label_2_bits?2:1); + return 4+(msg->subband_label_2_bits)?2:1; +} + +int srslte_cqi_value_pack(srslte_cqi_value_t *value, uint8_t buff[SRSLTE_CQI_MAX_BITS]) +{ + switch(value->type) { + case SRSLTE_CQI_TYPE_WIDEBAND: + return srslte_cqi_format2_wideband_pack(&value->wideband, buff); + case SRSLTE_CQI_TYPE_SUBBAND: + return srslte_cqi_format2_subband_pack(&value->subband, buff); + case SRSLTE_CQI_TYPE_SUBBAND_UE: + return srslte_cqi_ue_subband_pack(&value->subband_ue, buff); + case SRSLTE_CQI_TYPE_SUBBAND_HL: + return srslte_cqi_hl_subband_pack(&value->subband_hl, buff); + } + return -1; +} + + +/******************************************************* + * UNPACKING FUNCTIONS * + *******************************************************/ + +int srslte_cqi_hl_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_hl_subband_t *msg) +{ + uint8_t *body_ptr = buff; + uint32_t bit_count = 0; + + msg->wideband_cqi_cw0 = (uint8_t) srslte_bit_pack(&body_ptr, 4); + msg->subband_diff_cqi_cw0 = srslte_bit_pack(&body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + + /* Unpack codeword 1, 3GPP 36.212 Table 5.2.2.6.2-2 */ + if (msg->rank_is_not_one) { + msg->wideband_cqi_cw1 = (uint8_t) srslte_bit_pack(&body_ptr, 4); + msg->subband_diff_cqi_cw1 = srslte_bit_pack(&body_ptr, 2*msg->N); + bit_count += 4+2*msg->N; + } + + /* If PMI is present, unpack it */ + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 4); + bit_count += 4; + } else { + if (msg->rank_is_not_one) { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 1); + bit_count += 1; + } else { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 2); + bit_count += 2; + } + } + } + + return bit_count; +} + +int srslte_cqi_ue_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_ue_subband_t *msg) +{ + uint8_t *body_ptr = buff; + msg->wideband_cqi = srslte_bit_pack(&body_ptr, 4); + msg->subband_diff_cqi = srslte_bit_pack(&body_ptr, 2); + msg->subband_diff_cqi = srslte_bit_pack(&body_ptr, msg->L); + + return 4+2+msg->L; +} + +/* Unpack CQI following 3GPP TS 36.212 Tables 5.2.3.3.1-1 and 5.2.3.3.1-2 */ +int srslte_cqi_format2_wideband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_format2_wideband_t *msg) +{ + uint8_t *body_ptr = buff; + msg->wideband_cqi = (uint8_t) srslte_bit_pack(&body_ptr, 4); + + if (msg->pmi_present) { + if (msg->four_antenna_ports) { + if (msg->rank_is_not_one) { + msg->spatial_diff_cqi = (uint8_t) srslte_bit_pack(&body_ptr, 3); + } + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 4); + } else { + if (msg->rank_is_not_one) { + msg->spatial_diff_cqi = (uint8_t) srslte_bit_pack(&body_ptr, 3); + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 1); + } else { + msg->pmi = (uint8_t) srslte_bit_pack(&body_ptr, 2); + } + } + } + return 4; +} + +int srslte_cqi_format2_subband_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_format2_subband_t *msg) +{ + uint8_t *body_ptr = buff; + msg->subband_cqi = srslte_bit_pack(&body_ptr, 4); + msg->subband_label = srslte_bit_pack(&body_ptr, msg->subband_label_2_bits?2:1); + return 4+(msg->subband_label_2_bits)?2:1; +} + +int srslte_cqi_value_unpack(uint8_t buff[SRSLTE_CQI_MAX_BITS], srslte_cqi_value_t *value) +{ + switch(value->type) { + case SRSLTE_CQI_TYPE_WIDEBAND: + return srslte_cqi_format2_wideband_unpack(buff, &value->wideband); + case SRSLTE_CQI_TYPE_SUBBAND: + return srslte_cqi_format2_subband_unpack(buff, &value->subband); + case SRSLTE_CQI_TYPE_SUBBAND_UE: + return srslte_cqi_ue_subband_unpack(buff, &value->subband_ue); + case SRSLTE_CQI_TYPE_SUBBAND_HL: + return srslte_cqi_hl_subband_unpack(buff, &value->subband_hl); + } + return -1; +} + +/******************************************************* + * TO STRING FUNCTIONS * + *******************************************************/ + +static int srslte_cqi_format2_wideband_tostring(srslte_cqi_format2_wideband_t *msg, char *buff, uint32_t buff_len) { + int n = 0; + + n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->wideband_cqi); + + if (msg->pmi_present) { + if (msg->rank_is_not_one) { + n += snprintf(buff + n, buff_len - n, ", diff_cqi=%d", msg->spatial_diff_cqi); + } + n += snprintf(buff + n, buff_len - n, ", pmi=%d", msg->pmi); + } + + return n; +} + +static int srslte_cqi_format2_subband_tostring(srslte_cqi_format2_subband_t *msg, char *buff, uint32_t buff_len) { + int n = 0; + + n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->subband_cqi); + n += snprintf(buff + n, buff_len - n, ", label=%d", msg->subband_label); + + return n; +} + +static int srslte_cqi_ue_subband_tostring(srslte_cqi_ue_subband_t *msg, char *buff, uint32_t buff_len) { + int n = 0; + + n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->wideband_cqi); + n += snprintf(buff + n, buff_len - n, ", diff_cqi=%d", msg->subband_diff_cqi); + n += snprintf(buff + n, buff_len - n, ", L=%d", msg->L); + + return n; +} + +static int srslte_cqi_hl_subband_tostring(srslte_cqi_hl_subband_t *msg, char *buff, uint32_t buff_len) { + int n = 0; + + n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->wideband_cqi_cw0); + n += snprintf(buff + n, buff_len - n, ", diff=%d", msg->subband_diff_cqi_cw0); + + if (msg->rank_is_not_one) { + n += snprintf(buff + n, buff_len - n, ", cqi1=%d", msg->wideband_cqi_cw1); + n += snprintf(buff + n, buff_len - n, ", diff1=%d", msg->subband_diff_cqi_cw1); + } + + if (msg->pmi_present) { + n += snprintf(buff + n, buff_len - n, ", pmi=%d", msg->pmi); + } + + n += snprintf(buff + n, buff_len - n, ", N=%d", msg->N); + + return n; +} + +int srslte_cqi_value_tostring(srslte_cqi_value_t *value, char *buff, uint32_t buff_len) { + int ret = -1; + + switch (value->type) { + case SRSLTE_CQI_TYPE_WIDEBAND: + ret = srslte_cqi_format2_wideband_tostring(&value->wideband, buff, buff_len); + break; + case SRSLTE_CQI_TYPE_SUBBAND: + ret = srslte_cqi_format2_subband_tostring(&value->subband, buff, buff_len); + break; + case SRSLTE_CQI_TYPE_SUBBAND_UE: + ret = srslte_cqi_ue_subband_tostring(&value->subband_ue, buff, buff_len); + break; + case SRSLTE_CQI_TYPE_SUBBAND_HL: + ret = srslte_cqi_hl_subband_tostring(&value->subband_hl, buff, buff_len); + break; + default: + /* Do nothing */; + } + + return ret; +} + +int srslte_cqi_size(srslte_cqi_value_t *value) { + int size = 0; + + switch(value->type) { + case SRSLTE_CQI_TYPE_WIDEBAND: + /* Compute size according to 3GPP TS 36.212 Tables 5.2.3.3.1-1 and 5.2.3.3.1-2 */ + size = 4; + if (value->wideband.pmi_present) { + if (value->wideband.four_antenna_ports) { + if (value->wideband.rank_is_not_one) { + size += 3; // Differential + } else { + size += 0; // Differential + } + size += 4; // PMI + } else { + if (value->wideband.rank_is_not_one) { + size += 3; // Differential + size += 1; // PMI + } else { + size += 0; // Differential + size += 2; // PMI + } + } + } + break; + case SRSLTE_CQI_TYPE_SUBBAND: + size = 4 + (value->subband.subband_label_2_bits) ? 2 : 1; + break; + case SRSLTE_CQI_TYPE_SUBBAND_UE: + size = 4 + 2 + value->subband_ue.L; + break; + case SRSLTE_CQI_TYPE_SUBBAND_HL: + /* First codeword */ + size += 4 + 2 * value->subband_hl.N; + + /* Add Second codeword if required */ + if (value->subband_hl.rank_is_not_one && value->subband_hl.pmi_present) { + size += 4 + 2 * value->subband_hl.N; + } + + /* Add PMI if required*/ + if (value->subband_hl.pmi_present) { + if (value->subband_hl.four_antenna_ports) { + size += 4; + } else { + if (value->subband_hl.rank_is_not_one) { + size += 1; + } else { + size += 2; + } + } + } + break; + default: + size = SRSLTE_ERROR; + } + return size; +} + +static bool srslte_cqi_get_N(uint32_t I_cqi_pmi, uint32_t *N_p, uint32_t *N_offset) { + if (I_cqi_pmi <= 1) { + *N_p = 2; + *N_offset = I_cqi_pmi; + } else if (I_cqi_pmi <= 6) { + *N_p = 5; + *N_offset = I_cqi_pmi - 2; + } else if (I_cqi_pmi <= 16) { + *N_p = 10; + *N_offset = I_cqi_pmi - 7; + } else if (I_cqi_pmi <= 36) { + *N_p = 20; + *N_offset = I_cqi_pmi - 17; + } else if (I_cqi_pmi <= 76) { + *N_p = 40; + *N_offset = I_cqi_pmi - 37; + } else if (I_cqi_pmi <= 156) { + *N_p = 80; + *N_offset = I_cqi_pmi - 77; + } else if (I_cqi_pmi <= 316) { + *N_p = 160; + *N_offset = I_cqi_pmi - 157; + } else if (I_cqi_pmi == 317) { + return false; + } else if (I_cqi_pmi <= 349) { + *N_p = 32; + *N_offset = I_cqi_pmi - 318; + } else if (I_cqi_pmi <= 413) { + *N_p = 64; + *N_offset = I_cqi_pmi - 350; + } else if (I_cqi_pmi <= 541) { + *N_p = 128; + *N_offset = I_cqi_pmi - 414; + } else if (I_cqi_pmi <= 1023) { + return false; + } + return true; +} + +bool srslte_cqi_send(uint32_t I_cqi_pmi, uint32_t tti) { + + uint32_t N_p = 0; + uint32_t N_offset = 0; + + if (!srslte_cqi_get_N(I_cqi_pmi, &N_p, &N_offset)) { + return false; + } + + if (N_p) { + if ((tti-N_offset)%N_p == 0) { + return true; + } + } + return false; +} + +bool srslte_ri_send(uint32_t I_cqi_pmi, uint32_t I_ri, uint32_t tti) { + + uint32_t M_ri = 0; + uint32_t N_offset_ri = 0; + uint32_t N_p = 0; + uint32_t N_offset_p = 0; + + if (!srslte_cqi_get_N(I_cqi_pmi, &N_p, &N_offset_p)) { + return false; + } + + if (I_ri <= 160) { + M_ri = 1; + N_offset_ri = I_ri; + } else if (I_ri <= 161) { + M_ri = 2; + N_offset_ri = I_ri - 161; + } else if (I_ri <= 322) { + M_ri = 4; + N_offset_ri = I_ri - 322; + } else if (I_ri <= 483) { + M_ri = 8; + N_offset_ri = I_ri - 483; + } else if (I_ri <= 644) { + M_ri = 16; + N_offset_ri = I_ri - 644; + } else if (I_ri <= 805) { + M_ri = 32; + N_offset_ri = I_ri - 805; + } else if (I_ri <= 966) { + return false; + } + + if (M_ri && N_p) { + if ((tti - N_offset_p + N_offset_ri) % (N_p * M_ri) == 0) { + return true; + } + } + return false; +} + + +// CQI-to-Spectral Efficiency: 36.213 Table 7.2.3-1 */ +static float cqi_to_coderate[16] = {0, 0.1523, 0.2344, 0.3770, 0.6016, 0.8770, 1.1758, 1.4766, 1.9141, 2.4063, 2.7305, 3.3223, 3.9023, 4.5234, 5.1152, 5.5547}; + +float srslte_cqi_to_coderate(uint32_t cqi) { + if (cqi < 16) { + return cqi_to_coderate[cqi]; + } else { + return 0; + } +} + +/* SNR-to-CQI conversion, got from "Downlink SNR to CQI Mapping for Different Multiple Antenna Techniques in LTE" + * Table III. +*/ +// From paper +static float cqi_to_snr_table[15] = { 1.95, 4, 6, 8, 10, 11.95, 14.05, 16, 17.9, 20.9, 22.5, 24.75, 25.5, 27.30, 29}; + +// From experimental measurements @ 5 MHz +//static float cqi_to_snr_table[15] = { 1, 1.75, 3, 4, 5, 6, 7.5, 9, 11.5, 13.0, 15.0, 18, 20, 22.5, 26.5}; + +uint8_t srslte_cqi_from_snr(float snr) +{ + for (int cqi=14;cqi>=0;cqi--) { + if (snr >= cqi_to_snr_table[cqi]) { + return (uint8_t) cqi+1; + } + } + return 0; +} + +/* Returns the subband size for higher layer-configured subband feedback, + * i.e., the number of RBs per subband as a function of the cell bandwidth + * (Table 7.2.1-3 in TS 36.213) + */ +int srslte_cqi_hl_get_subband_size(int nof_prb) +{ + if (nof_prb < 7) { + return 0; + } else if (nof_prb <= 26) { + return 4; + } else if (nof_prb <= 63) { + return 6; + } else if (nof_prb <= 110) { + return 8; + } else { + return -1; + } +} + +/* Returns the number of subbands to be reported in CQI measurements as + * defined in clause 7.2 in TS 36.213, i.e., the N parameter + */ +int srslte_cqi_hl_get_no_subbands(int nof_prb) +{ + int hl_size = srslte_cqi_hl_get_subband_size(nof_prb); + if (hl_size > 0) { + return (int)ceil((float)nof_prb/hl_size); + } else { + return 0; + } +} + +void srslte_cqi_to_str(const uint8_t *cqi_value, int cqi_len, char *str, int str_len) { + int i = 0; + + for (i = 0; i < cqi_len && i < (str_len - 5); i++) { + str[i] = (cqi_value[i] == 0)?(char)'0':(char)'1'; + } + + if (i == (str_len - 5)) { + str[i++] = '.'; + str[i++] = '.'; + str[i++] = '.'; + str[i++] = (cqi_value[cqi_len - 1] == 0)?(char)'0':(char)'1'; + } + str[i] = '\0'; +} diff --git a/lib/src/phy/phch/dci.c b/lib/src/phy/phch/dci.c new file mode 100644 index 0000000..0a950b0 --- /dev/null +++ b/lib/src/phy/phch/dci.c @@ -0,0 +1,1499 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#include "dci_sz_table.h" + +#define HARQ_PID_LEN 3 + +/* Unpacks a DCI message and configures the DL grant object + */ +int srslte_dci_msg_to_dl_grant(srslte_dci_msg_t *msg, uint16_t msg_rnti, + uint32_t nof_prb, uint32_t nof_ports, + srslte_ra_dl_dci_t *dl_dci, + srslte_ra_dl_grant_t *grant) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && + grant != NULL) + { + ret = SRSLTE_ERROR; + + bzero(dl_dci, sizeof(srslte_ra_dl_dci_t)); + bzero(grant, sizeof(srslte_ra_dl_grant_t)); + + bool crc_is_crnti = false; + if (msg_rnti >= SRSLTE_CRNTI_START && msg_rnti <= SRSLTE_CRNTI_END) { + crc_is_crnti = true; + } + //srslte_dci_format_t tmp = msg->format; + ret = srslte_dci_msg_unpack_pdsch(msg, dl_dci, nof_prb, nof_ports, crc_is_crnti); + if (ret) { + //fprintf(stderr, "Can't unpack DCI message %s (%d)\n", srslte_dci_format_string(msg->format), msg->format); + return ret; + } + + if (!dl_dci->is_ra_order) { + if (srslte_ra_dl_dci_to_grant(dl_dci, nof_prb, msg_rnti, grant)) { + return ret; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_pdsch_fprint(stdout, dl_dci, nof_prb); + srslte_ra_dl_grant_fprint(stdout, grant); + } + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Creates the UL PUSCH resource allocation grant from the random access respone message + */ +int srslte_dci_rar_to_ul_grant(srslte_dci_rar_grant_t *rar, uint32_t nof_prb, + uint32_t n_rb_ho, + srslte_ra_ul_dci_t *ul_dci, + srslte_ra_ul_grant_t *grant) +{ + bzero(ul_dci, sizeof(srslte_ra_ul_dci_t)); + + if (!rar->hopping_flag) { + ul_dci->freq_hop_fl = SRSLTE_RA_PUSCH_HOP_DISABLED; + } else { + fprintf(stderr, "FIXME: Frequency hopping in RAR not implemented\n"); + ul_dci->freq_hop_fl = 1; + } + uint32_t riv = rar->rba; + // Truncate resource block assignment + uint32_t b = 0; + if (nof_prb <= 44) { + b = (uint32_t) (ceilf(log2((float) nof_prb*(nof_prb+1)/2))); + riv = riv & ((1<<(b+1))-1); + } + ul_dci->type2_alloc.riv = riv; + ul_dci->mcs_idx = rar->trunc_mcs; + + srslte_ra_type2_from_riv(riv, &ul_dci->type2_alloc.L_crb, &ul_dci->type2_alloc.RB_start, + nof_prb, nof_prb); + + if (srslte_ra_ul_dci_to_grant(ul_dci, nof_prb, n_rb_ho, grant)) { + return SRSLTE_ERROR; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_pusch_fprint(stdout, ul_dci, nof_prb); + srslte_ra_ul_grant_fprint(stdout, grant); + } + return SRSLTE_SUCCESS; +} + +/* Unpack RAR UL grant as defined in Section 6.2 of 36.213 */ +void srslte_dci_rar_grant_unpack(srslte_dci_rar_grant_t *rar, uint8_t grant[SRSLTE_RAR_GRANT_LEN]) +{ + uint8_t *grant_ptr = grant; + rar->hopping_flag = srslte_bit_pack(&grant_ptr, 1)?true:false; + rar->rba = srslte_bit_pack(&grant_ptr, 10); + rar->trunc_mcs = srslte_bit_pack(&grant_ptr, 4); + rar->tpc_pusch = srslte_bit_pack(&grant_ptr, 3); + rar->ul_delay = srslte_bit_pack(&grant_ptr, 1)?true:false; + rar->cqi_request = srslte_bit_pack(&grant_ptr, 1)?true:false; +} + +/* Pack RAR UL grant as defined in Section 6.2 of 36.213 */ +void srslte_dci_rar_grant_pack(srslte_dci_rar_grant_t *rar, uint8_t grant[SRSLTE_RAR_GRANT_LEN]) +{ + uint8_t *grant_ptr = grant; + srslte_bit_unpack(rar->hopping_flag?1:0, &grant_ptr, 1); + srslte_bit_unpack(rar->rba, &grant_ptr, 10); + srslte_bit_unpack(rar->trunc_mcs, &grant_ptr, 4); + srslte_bit_unpack(rar->tpc_pusch, &grant_ptr, 3); + srslte_bit_unpack(rar->ul_delay?1:0, &grant_ptr, 1); + srslte_bit_unpack(rar->cqi_request?1:0, &grant_ptr, 1); +} + +void srslte_dci_rar_grant_fprint(FILE *stream, srslte_dci_rar_grant_t *rar) { + fprintf(stream, "RBA: %d, MCS: %d, TPC: %d, Hopping=%s, UL-Delay=%s, CQI=%s\n", + rar->rba, rar->trunc_mcs, rar->tpc_pusch, + rar->hopping_flag?"yes":"no", + rar->ul_delay?"yes":"no", + rar->cqi_request?"yes":"no" + ); +} + +/* Creates the UL PUSCH resource allocation grant from a DCI format 0 message + */ +int srslte_dci_msg_to_ul_grant(srslte_dci_msg_t *msg, uint32_t nof_prb, + uint32_t n_rb_ho, + srslte_ra_ul_dci_t *ul_dci, + srslte_ra_ul_grant_t *grant, uint32_t harq_pid) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (msg != NULL && + ul_dci != NULL && + grant != NULL) + { + ret = SRSLTE_ERROR; + + bzero(ul_dci, sizeof(srslte_ra_ul_dci_t)); + bzero(grant, sizeof(srslte_ra_ul_grant_t)); + + if (srslte_dci_msg_unpack_pusch(msg, ul_dci, nof_prb)) { + return ret; + } + + if (srslte_ra_ul_dci_to_grant(ul_dci, nof_prb, n_rb_ho, grant)) { + return ret; + } + + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_pusch_fprint(stdout, ul_dci, nof_prb); + srslte_ra_ul_grant_fprint(stdout, grant); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_dci_location_set(srslte_dci_location_t *c, uint32_t L, uint32_t nCCE) { + if (L <= 3) { + c->L = L; + } else { + fprintf(stderr, "Invalid L %d\n", L); + return SRSLTE_ERROR; + } + if (nCCE <= 87) { + c->ncce = nCCE; + } else { + fprintf(stderr, "Invalid nCCE %d\n", nCCE); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +bool srslte_dci_location_isvalid(srslte_dci_location_t *c) { + if (c->L <= 3 && c->ncce <= 87) { + return true; + } else { + return false; + } +} + +uint32_t riv_nbits(uint32_t nof_prb) { + return (uint32_t) ceilf(log2f((float) nof_prb * ((float) nof_prb + 1) / 2)); +} + +const uint32_t ambiguous_sizes[10] = { 12, 14, 16, 20, 24, 26, 32, 40, 44, 56 }; + +bool is_ambiguous_size(uint32_t size) { + int i; + for (i = 0; i < 10; i++) { + if (size == ambiguous_sizes[i]) { + return true; + } + } + return false; +} + +/********************************** + * PAYLOAD sizeof functions + * ********************************/ +uint32_t dci_format0_sizeof_(uint32_t nof_prb) { + return 1 + 1 + riv_nbits(nof_prb) + 5 + 1 + 2 + 3 + 1; +} + +uint32_t dci_format1A_sizeof(uint32_t nof_prb) { + uint32_t n; + n = 1 + 1 + riv_nbits(nof_prb) + 5 + HARQ_PID_LEN + 1 + 2 + 2; + while (n < dci_format0_sizeof_(nof_prb)) { + n++; + } + if (is_ambiguous_size(n)) { + n++; + } + return n; +} + +uint32_t dci_format0_sizeof(uint32_t nof_prb) { + uint32_t n = dci_format0_sizeof_(nof_prb); + while (n < dci_format1A_sizeof(nof_prb)) { + n++; + } + return n; +} + +uint32_t dci_format1_sizeof(uint32_t nof_prb) { + + uint32_t n = (uint32_t) ceilf((float) nof_prb / srslte_ra_type0_P(nof_prb)) + 5 + HARQ_PID_LEN + 1 + 2 + + 2; + if (nof_prb > 10) { + n++; + } + while (n == dci_format0_sizeof(nof_prb) || n == dci_format1A_sizeof(nof_prb) + || is_ambiguous_size(n)) { + n++; + } + return n; +} + +uint32_t dci_format1C_sizeof(uint32_t nof_prb) { + uint32_t n_vrb_dl_gap1 = srslte_ra_type2_n_vrb_dl(nof_prb, true); + uint32_t n_step = srslte_ra_type2_n_rb_step(nof_prb); + uint32_t n = riv_nbits((uint32_t) n_vrb_dl_gap1 / n_step) + 5; + if (nof_prb >= 50) { + n++; + } + return n; +} + +// Number of TPMI bits +uint32_t tpmi_bits(uint32_t nof_ports) { + if (nof_ports <= 2) { + return 2; + } else { + return 4; + } +} + +uint32_t dci_format1B_sizeof(uint32_t nof_prb, uint32_t nof_ports) { + // same as format1A minus the differentiation bit plus TPMI + PMI + uint32_t n = dci_format1A_sizeof(nof_prb)-1+tpmi_bits(nof_ports)+1; + + while (is_ambiguous_size(n)) { + n++; + } + return n; +} + +uint32_t dci_format1D_sizeof(uint32_t nof_prb, uint32_t nof_ports) { + // same size as format1B + return dci_format1B_sizeof(nof_prb, nof_ports); +} + +// Number of bits for precoding information +uint32_t precoding_bits_f2(uint32_t nof_ports) { + if (nof_ports <= 2) { + return 3; + } else { + return 6; + } +} + +uint32_t dci_format2_sizeof(uint32_t nof_prb, uint32_t nof_ports) { + uint32_t n = (uint32_t) ceilf((float) nof_prb / srslte_ra_type0_P(nof_prb))+2+HARQ_PID_LEN+1+2*(5+1+2)+precoding_bits_f2(nof_ports); + if (nof_prb > 10) { + n++; + } + while (is_ambiguous_size(n)) { + n++; + } + return n; +} + +// Number of bits for precoding information +uint32_t precoding_bits_f2a(uint32_t nof_ports) { + if (nof_ports <= 2) { + return 0; + } else { + return 2; + } +} + +uint32_t dci_format2A_sizeof(uint32_t nof_prb, uint32_t nof_ports) { + uint32_t n = (uint32_t) ceilf((float) nof_prb / srslte_ra_type0_P(nof_prb))+2+HARQ_PID_LEN+1+2*(5+1+2)+precoding_bits_f2a(nof_ports); + if (nof_prb > 10) { + n++; + } + while (is_ambiguous_size(n)) { + n++; + } + return n; + +} + +uint32_t dci_format2B_sizeof(uint32_t nof_prb, uint32_t nof_ports) { + uint32_t n = (uint32_t) ceilf((float) nof_prb / srslte_ra_type0_P(nof_prb))+2+HARQ_PID_LEN+1+2*(5+1+2); + if (nof_prb > 10) { + n++; + } + while (is_ambiguous_size(n)) { + n++; + } + return n; + +} + +uint32_t srslte_dci_dl_info(char *info_str, uint32_t len, srslte_ra_dl_dci_t *dci_msg, srslte_dci_format_t format) +{ + int n = 0; + + switch(dci_msg->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + n += snprintf(&info_str[n], len-n, "type0={rbg=0x%x}, ", dci_msg->type0_alloc.rbg_bitmask); + break; + case SRSLTE_RA_ALLOC_TYPE1: + n += snprintf(&info_str[n], len-n, "type1={vrb=0x%x, rbg_s=%d, sh=%d}, ", + dci_msg->type1_alloc.vrb_bitmask, dci_msg->type1_alloc.rbg_subset, dci_msg->type1_alloc.shift); + break; + case SRSLTE_RA_ALLOC_TYPE2: + n += snprintf(&info_str[n], len-n, "type2={riv=%d, rb=(%d,%d), mode=%s", + dci_msg->type2_alloc.riv, + dci_msg->type2_alloc.RB_start, dci_msg->type2_alloc.RB_start+dci_msg->type2_alloc.L_crb-1, + dci_msg->type2_alloc.mode==SRSLTE_RA_TYPE2_LOC?"local":"dist"); + if (dci_msg->type2_alloc.mode==SRSLTE_RA_TYPE2_DIST) { + n += snprintf(&info_str[n], len-n, ", ngap=%s, nprb1a=%d", + dci_msg->type2_alloc.n_gap==SRSLTE_RA_TYPE2_NG1?"ng1":"ng2", + dci_msg->type2_alloc.n_prb1a==SRSLTE_RA_TYPE2_NPRB1A_2?2:3); + } + n += snprintf(&info_str[n], len-n, "}, "); + break; + } + n += snprintf(&info_str[n], len-n, "pid=%d, ", dci_msg->harq_process); + + n += snprintf(&info_str[n], len-n, "mcs={"); + if (dci_msg->tb_en[0]) { + n += snprintf(&info_str[n], len-n, "%d", dci_msg->mcs_idx); + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len-n, "/"); + } else { + n += snprintf(&info_str[n], len-n, "}, "); + } + } + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len - n, "%d}, ", dci_msg->mcs_idx_1); + } + n += snprintf(&info_str[n], len-n, "rv={"); + if (dci_msg->tb_en[0]) { + n += snprintf(&info_str[n], len-n, "%d", dci_msg->rv_idx); + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len-n, "/"); + } else { + n += snprintf(&info_str[n], len-n, "}, "); + } + } + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len - n, "%d}, ", dci_msg->rv_idx_1); + } + n += snprintf(&info_str[n], len-n, "ndi={"); + if (dci_msg->tb_en[0]) { + n += snprintf(&info_str[n], len-n, "%d", dci_msg->ndi); + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len-n, "/"); + } else { + n += snprintf(&info_str[n], len-n, "}, "); + } + } + if (dci_msg->tb_en[1]) { + n += snprintf(&info_str[n], len - n, "%d}, ", dci_msg->ndi_1); + } + + if (format == SRSLTE_DCI_FORMAT1 || format == SRSLTE_DCI_FORMAT1A || format == SRSLTE_DCI_FORMAT1B || + format == SRSLTE_DCI_FORMAT2 || format == SRSLTE_DCI_FORMAT2A || format == SRSLTE_DCI_FORMAT2B) { + n += snprintf(&info_str[n], len-n, "tpc_pucch=%d, ", dci_msg->tpc_pucch); + } + if (format == SRSLTE_DCI_FORMAT2 || format == SRSLTE_DCI_FORMAT2A || format == SRSLTE_DCI_FORMAT2B) { + n += snprintf(&info_str[n], len-n, "tb_sw=%d, pinfo=%d, ", dci_msg->tb_cw_swap, dci_msg->pinfo); + } + return n; +} + +uint32_t srslte_dci_ul_info(char *info_str, uint32_t len, srslte_ra_ul_dci_t *dci_msg) +{ + int n = 0; + + n += snprintf(&info_str[n], len-n, "riv=%d, rb=(%d,%d), ", dci_msg->type2_alloc.riv, + dci_msg->type2_alloc.RB_start, dci_msg->type2_alloc.RB_start+dci_msg->type2_alloc.L_crb-1); + + switch(dci_msg->freq_hop_fl) { + case SRSLTE_RA_PUSCH_HOP_DISABLED: + n += snprintf(&info_str[n], len-n, "f_h=n/a, "); + break; + default: + n += snprintf(&info_str[n], len-n, "f_h=%d, ", dci_msg->freq_hop_fl); + break; + } + n += snprintf(&info_str[n], len-n, "mcs=%d, rv=%d, ndi=%d, ", dci_msg->mcs_idx, dci_msg->rv_idx, dci_msg->ndi); + n += snprintf(&info_str[n], len-n, "tpc_pusch=%d, dmrs_cs=%d, cqi=%s, ", + dci_msg->tpc_pusch, dci_msg->n_dmrs, dci_msg->cqi_request?"yes":"no"); + + return n; +} + +uint32_t srslte_dci_format_sizeof(srslte_dci_format_t format, uint32_t nof_prb, uint32_t nof_ports) { + switch (format) { + case SRSLTE_DCI_FORMAT0: + return dci_format0_sizeof(nof_prb); + case SRSLTE_DCI_FORMAT1: + return dci_format1_sizeof(nof_prb); + case SRSLTE_DCI_FORMAT1A: + return dci_format1A_sizeof(nof_prb); + case SRSLTE_DCI_FORMAT1C: + return dci_format1C_sizeof(nof_prb); + case SRSLTE_DCI_FORMAT1B: + return dci_format1B_sizeof(nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT1D: + return dci_format1D_sizeof(nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT2: + return dci_format2_sizeof(nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT2A: + return dci_format2A_sizeof(nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT2B: + return dci_format2B_sizeof(nof_prb, nof_ports); + /* + case SRSLTE_DCI_FORMAT3: + return dci_format3_sizeof(nof_prb); + case SRSLTE_DCI_FORMAT3A: + return dci_format3A_sizeof(nof_prb); + */ + default: + printf("Error computing DCI bits: Unknown format %d\n", format); + return 0; + } +} + +uint32_t srslte_dci_format_sizeof_lut(srslte_dci_format_t format, uint32_t nof_prb) { + if (nof_prb < 101 && format < 4) { + return dci_sz_table[nof_prb][format]; + } else { + return 0; + } +} +/********************************** + * DCI Resource Allocation functions + * ********************************/ + +/* Packs DCI format 0 data to a sequence of bits and store them in msg according + * to 36.212 5.3.3.1.1 + * + * TODO: TPC and cyclic shift for DM RS not implemented + */ +int dci_format0_pack(srslte_ra_ul_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb) { + + /* pack bits */ + uint8_t *y = msg->data; + uint32_t n_ul_hop; + + *y++ = 0; // format differentiation + if (data->freq_hop_fl == SRSLTE_RA_PUSCH_HOP_DISABLED) { // frequency hopping + *y++ = 0; + n_ul_hop = 0; + } else { + *y++ = 1; + if (nof_prb < 50) { + n_ul_hop = 1; // Table 8.4-1 of 36.213 + *y++ = data->freq_hop_fl & 1; + } else { + n_ul_hop = 2; // Table 8.4-1 of 36.213 + *y++ = (data->freq_hop_fl & 2) >> 1; + *y++ = data->freq_hop_fl & 1; + } + } + + /* pack RIV according to 8.1 of 36.213 */ + uint32_t riv; + if (data->type2_alloc.L_crb) { + riv = srslte_ra_type2_to_riv(data->type2_alloc.L_crb, data->type2_alloc.RB_start, + nof_prb); + } else { + riv = data->type2_alloc.riv; + } + + srslte_bit_unpack(riv, &y, riv_nbits(nof_prb) - n_ul_hop); + + /* pack MCS according to 8.6.1 of 36.213 */ + srslte_bit_unpack(data->mcs_idx, &y, 5); + + *y++ = data->ndi; + + // TCP command for PUSCH + srslte_bit_unpack(data->tpc_pusch, &y, 2); + + // DM RS not implemented + srslte_bit_unpack(data->n_dmrs, &y, 3); + + // CQI request + *y++ = data->cqi_request; + + // Padding with zeros + uint32_t n = srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, nof_prb, 1); + while (y - msg->data < n) { + *y++ = 0; + } + msg->nof_bits = (y - msg->data); + return SRSLTE_SUCCESS; +} +/* Unpacks DCI format 0 data and store result in msg according + * to 36.212 5.3.3.1.1 + * + * TODO: TPC and cyclic shift for DM RS not implemented + */ +int dci_format0_unpack(srslte_dci_msg_t *msg, srslte_ra_ul_dci_t *data, uint32_t nof_prb) { + + /* pack bits */ + uint8_t *y = msg->data; + uint32_t n_ul_hop; + + /* Make sure it's a SRSLTE_DCI_FORMAT0 message */ + if (msg->nof_bits != srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, nof_prb, 1)) { + fprintf(stderr, "Invalid message length for format 0\n"); + return SRSLTE_ERROR; + } + if (*y++ != 0) { + INFO("DCI message is Format1A\n"); + return SRSLTE_ERROR; + } + if (*y++ == 0) { + data->freq_hop_fl = SRSLTE_RA_PUSCH_HOP_DISABLED; + n_ul_hop = 0; + } else { + if (nof_prb < 50) { + n_ul_hop = 1; // Table 8.4-1 of 36.213 + data->freq_hop_fl = *y++; + } else { + n_ul_hop = 2; // Table 8.4-1 of 36.213 + data->freq_hop_fl = y[0] << 1 | y[1]; + y += 2; + } + } + /* unpack RIV according to 8.1 of 36.213 */ + uint32_t riv = srslte_bit_pack(&y, riv_nbits(nof_prb) - n_ul_hop); + srslte_ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, + nof_prb, nof_prb); + data->type2_alloc.riv = riv; + + /* unpack MCS according to 8.6 of 36.213 */ + data->mcs_idx = srslte_bit_pack(&y, 5); + + data->ndi = *y++ ? true : false; + + // TPC command for scheduled PUSCH + data->tpc_pusch = srslte_bit_pack(&y, 2); + + // Cyclic shift for DMRS + data->n_dmrs = srslte_bit_pack(&y, 3); + + // CQI request + data->cqi_request = *y++ ? true : false; + + return SRSLTE_SUCCESS; +} + +/* Packs DCI format 1 data to a sequence of bits and store them in msg according + * to 36.212 5.3.3.1.2 + * + * TODO: TPC commands + */ + +int dci_format1_pack(srslte_ra_dl_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb) { + + /* pack bits */ + uint8_t *y = msg->data; + + if (nof_prb > 10) { + *y++ = data->alloc_type; + } + + /* Resource allocation: type0 or type 1 */ + uint32_t P = srslte_ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); + switch (data->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + srslte_bit_unpack((uint32_t) data->type0_alloc.rbg_bitmask, &y, alloc_size); + break; + case SRSLTE_RA_ALLOC_TYPE1: + srslte_bit_unpack((uint32_t) data->type1_alloc.rbg_subset, &y, (int) ceilf(log2f(P))); + *y++ = data->type1_alloc.shift ? 1 : 0; + srslte_bit_unpack((uint32_t) data->type1_alloc.vrb_bitmask, &y, + alloc_size - (int) ceilf(log2f(P)) - 1); + break; + default: + fprintf(stderr, + "Format 1 accepts type0 or type1 resource allocation only\n"); + return SRSLTE_ERROR; + + } + /* pack MCS */ + srslte_bit_unpack(data->mcs_idx, &y, 5); + + /* harq process number */ + srslte_bit_unpack(data->harq_process, &y, HARQ_PID_LEN); + + *y++ = data->ndi; + + // rv version + srslte_bit_unpack(data->rv_idx, &y, 2); + + // TCP command for PUCCH + srslte_bit_unpack(data->tpc_pucch, &y, 2); + + // Padding with zeros + uint32_t n = srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, nof_prb, 1); + while (y - msg->data < n) { + *y++ = 0; + } + msg->nof_bits = (y - msg->data); + + return SRSLTE_SUCCESS; +} + +int dci_format1_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb) { + + /* pack bits */ + uint8_t *y = msg->data; + + /* Make sure it's a SRSLTE_DCI_FORMAT1 message */ + if (msg->nof_bits != srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, nof_prb, 1)) { + fprintf(stderr, "Invalid message length for format 1\n"); + return SRSLTE_ERROR; + } + + if (nof_prb > 10) { + data->alloc_type = *y++; + } else { + data->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + } + + /* Resource allocation: type0 or type 1 */ + uint32_t P = srslte_ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); + switch (data->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + data->type0_alloc.rbg_bitmask = srslte_bit_pack(&y, alloc_size); + break; + case SRSLTE_RA_ALLOC_TYPE1: + data->type1_alloc.rbg_subset = srslte_bit_pack(&y, (int) ceilf(log2f(P))); + data->type1_alloc.shift = *y++ ? true : false; + data->type1_alloc.vrb_bitmask = srslte_bit_pack(&y, + alloc_size - (int) ceilf(log2f(P)) - 1); + break; + default: + fprintf(stderr, "Format 1 accepts type0 or type1 resource allocation only\n"); + return SRSLTE_ERROR; + + } + /* unpack MCS according to 7.1.7 of 36.213 */ + data->mcs_idx = srslte_bit_pack(&y, 5); + + /* harq process number */ + data->harq_process = srslte_bit_pack(&y, HARQ_PID_LEN); + + data->ndi = *y++ ? true : false; + // rv version + data->rv_idx = srslte_bit_pack(&y, 2); + + // TPC not implemented + + data->tb_en[0] = true; + data->tb_en[1] = false; + + return SRSLTE_SUCCESS; +} + +/* Packs DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3 + * + * TODO: RA procedure initiated by PDCCH, TPC commands + */ +int dci_format1As_pack(srslte_ra_dl_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb, + bool crc_is_crnti) { + + /* pack bits */ + uint8_t *y = msg->data; + + *y++ = 1; // format differentiation + + if (data->alloc_type != SRSLTE_RA_ALLOC_TYPE2) { + fprintf(stderr, "Format 1A accepts type2 resource allocation only\n"); + return SRSLTE_ERROR; + } + + data->dci_is_1a = true; + + *y++ = data->type2_alloc.mode; // localized or distributed VRB assignment + + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + if (data->type2_alloc.L_crb > nof_prb) { + fprintf(stderr, "L_CRB=%d can not exceed system BW for localized type2\n", + data->type2_alloc.L_crb); + return SRSLTE_ERROR; + } + } else { + uint32_t n_vrb_dl; + if (crc_is_crnti && nof_prb > 50) { + n_vrb_dl = 16; + } else { + n_vrb_dl = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + } + if (data->type2_alloc.L_crb > n_vrb_dl) { + fprintf(stderr, + "L_CRB=%d can not exceed N_vrb_dl=%d for distributed type2\n", + data->type2_alloc.L_crb, n_vrb_dl); + return SRSLTE_ERROR; + } + } + /* pack RIV according to 7.1.6.3 of 36.213 */ + uint32_t riv; + if (data->type2_alloc.L_crb) { + riv = srslte_ra_type2_to_riv(data->type2_alloc.L_crb, data->type2_alloc.RB_start, + nof_prb); + } else { + riv = data->type2_alloc.riv; + } + uint32_t nb_gap = 0; + if (crc_is_crnti && data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST && nof_prb >= 50) { + nb_gap = 1; + *y++ = data->type2_alloc.n_gap; + } + srslte_bit_unpack(riv, &y, riv_nbits(nof_prb) - nb_gap); + + // in format1A, MCS = TBS according to 7.1.7.2 of 36.213 + srslte_bit_unpack(data->mcs_idx, &y, 5); + + srslte_bit_unpack(data->harq_process, &y, HARQ_PID_LEN); + + if (crc_is_crnti) { + if (nof_prb >= 50 && data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST) { + *y++ = data->type2_alloc.n_gap; + } else { + y++; // bit reserved + } + } else { + *y++ = data->ndi; + } + + // rv version + srslte_bit_unpack(data->rv_idx, &y, 2); + + if (crc_is_crnti) { + // TPC not implemented + *y++ = 0; + *y++ = 0; + } else { + y++; // MSB of TPC is reserved + *y++ = data->type2_alloc.n_prb1a; // LSB indicates N_prb_1a for TBS + } + + // Padding with zeros + uint32_t n = srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1A, nof_prb, 1); + while (y - msg->data < n) { + *y++ = 0; + } + msg->nof_bits = (y - msg->data); + + return SRSLTE_SUCCESS; +} + +/* Unpacks DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3 + * + */ +int dci_format1As_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb, + bool crc_is_crnti) { + + /* pack bits */ + uint8_t *y = msg->data; + + /* Make sure it's a SRSLTE_DCI_FORMAT0 message */ + if (msg->nof_bits != srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1A, nof_prb, 1)) { + fprintf(stderr, "Invalid message length for format 1A\n"); + return SRSLTE_ERROR; + } + + if (*y++ != 1) { + INFO("DCI message is Format0\n"); + return SRSLTE_ERROR; + } + + data->dci_is_1a = true; + + // Check if RA procedure by PDCCH order + if (*y == 0) { + int nof_bits = riv_nbits(nof_prb); + int i=0; + // Check all bits in RBA are set to 1 + while(inof_bits-1 && y[i] == 0) { + i++; + } + if (i == msg->nof_bits-1) { + // This is a Random access order + y+=1+nof_bits; + + data->is_ra_order = true; + data->ra_preamble = srslte_bit_pack(&y, 6); + data->ra_mask_idx = srslte_bit_pack(&y, 4); + + return SRSLTE_SUCCESS; + } + } + } + + data->is_ra_order = false; + + data->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + data->type2_alloc.mode = *y++; + + // by default, set N_gap to 1 + data->type2_alloc.n_gap = SRSLTE_RA_TYPE2_NG1; + + /* unpack RIV according to 7.1.6.3 of 36.213 */ + uint32_t nb_gap = 0; + if (crc_is_crnti && data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST && nof_prb >= 50) { + nb_gap = 1; + data->type2_alloc.n_gap = *y++; + } + uint32_t nof_vrb; + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + nof_vrb = nof_prb; + } else { + nof_vrb = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + } + uint32_t riv = srslte_bit_pack(&y, riv_nbits(nof_prb) - nb_gap); + srslte_ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, + nof_prb, nof_vrb); + data->type2_alloc.riv = riv; + + // unpack MCS + data->mcs_idx = srslte_bit_pack(&y, 5); + + data->harq_process = srslte_bit_pack(&y, HARQ_PID_LEN); + + if (!crc_is_crnti) { + if (nof_prb >= 50 && data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST) { + data->type2_alloc.n_gap = *y++; + } else { + y++; // NDI reserved + } + } else { + data->ndi = *y++ ? true : false; + } + + // rv version + data->rv_idx = srslte_bit_pack(&y, 2); + + if (crc_is_crnti) { + // TPC not implemented + y++; + y++; + } else { + y++; // MSB of TPC is reserved + data->type2_alloc.n_prb1a = *y++; // LSB indicates N_prb_1a for TBS + } + + data->tb_en[0] = true; + data->tb_en[1] = false; + + return SRSLTE_SUCCESS; +} + +int dci_format1B_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb, uint32_t nof_ports) +{ + + /* pack bits */ + uint8_t *y = msg->data; + + data->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + data->type2_alloc.mode = *y++; + + // by default, set N_gap to 1 + data->type2_alloc.n_gap = SRSLTE_RA_TYPE2_NG1; + + /* unpack RIV according to 7.1.6.3 of 36.213 */ + uint32_t nb_gap = 0; + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST && nof_prb >= 50) { + nb_gap = 1; + data->type2_alloc.n_gap = *y++; + } + uint32_t nof_vrb; + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + nof_vrb = nof_prb; + } else { + nof_vrb = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + } + uint32_t riv = srslte_bit_pack(&y, riv_nbits(nof_prb) - nb_gap); + srslte_ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, + nof_prb, nof_vrb); + data->type2_alloc.riv = riv; + + // unpack MCS, Harq pid and ndi + data->mcs_idx = srslte_bit_pack(&y, 5); + data->harq_process = srslte_bit_pack(&y, HARQ_PID_LEN); + data->ndi = *y++ ? true : false; + data->rv_idx = srslte_bit_pack(&y, 2); + + // Ignore TPC command for PUCCH + y += 2; + + data->pinfo = srslte_bit_pack(&y, tpmi_bits(nof_ports)); + data->pconf = *y++ ? true : false; + + data->tb_en[0] = true; + data->tb_en[1] = false; + + return SRSLTE_SUCCESS; +} + +/* Format 1C for compact scheduling of PDSCH words + * + */ +int dci_format1Cs_pack(srslte_ra_dl_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb) { + + /* pack bits */ + uint8_t *y = msg->data; + + if (data->alloc_type != SRSLTE_RA_ALLOC_TYPE2 || data->type2_alloc.mode != SRSLTE_RA_TYPE2_DIST) { + fprintf(stderr, + "Format 1C accepts distributed type2 resource allocation only\n"); + return SRSLTE_ERROR; + } + + data->dci_is_1c = true; + + if (nof_prb >= 50) { + *y++ = data->type2_alloc.n_gap; + } + uint32_t n_step = srslte_ra_type2_n_rb_step(nof_prb); + uint32_t n_vrb_dl = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + + if (data->type2_alloc.L_crb > ((uint32_t) n_vrb_dl / n_step) * n_step) { + fprintf(stderr, "L_CRB=%d can not exceed N_vrb_dl=%d for distributed type2\n", + data->type2_alloc.L_crb, ((uint32_t) n_vrb_dl / n_step) * n_step); + return SRSLTE_ERROR; + } + if (data->type2_alloc.L_crb % n_step) { + fprintf(stderr, "L_crb must be multiple of n_step\n"); + return SRSLTE_ERROR; + } + if (data->type2_alloc.RB_start % n_step) { + fprintf(stderr, "RB_start must be multiple of n_step\n"); + return SRSLTE_ERROR; + } + uint32_t L_p = data->type2_alloc.L_crb / n_step; + uint32_t RB_p = data->type2_alloc.RB_start / n_step; + uint32_t n_vrb_p = (int) n_vrb_dl / n_step; + + uint32_t riv; + if (data->type2_alloc.L_crb) { + riv = srslte_ra_type2_to_riv(L_p, RB_p, n_vrb_p); + } else { + riv = data->type2_alloc.riv; + } + srslte_bit_unpack(riv, &y, riv_nbits((int) n_vrb_dl / n_step)); + + // in format1C, MCS = TBS according to 7.1.7.2 of 36.213 + srslte_bit_unpack(data->mcs_idx, &y, 5); + + msg->nof_bits = (y - msg->data); + + return SRSLTE_SUCCESS; +} + +int dci_format1Cs_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb) { + uint32_t L_p, RB_p; + + /* pack bits */ + uint8_t *y = msg->data; + + if (msg->nof_bits != srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1C, nof_prb, 1)) { + fprintf(stderr, "Invalid message length for format 1C\n"); + return SRSLTE_ERROR; + } + + data->dci_is_1c = true; + + data->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + data->type2_alloc.mode = SRSLTE_RA_TYPE2_DIST; + if (nof_prb >= 50) { + data->type2_alloc.n_gap = *y++; + } + uint32_t n_step = srslte_ra_type2_n_rb_step(nof_prb); + uint32_t n_vrb_dl = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + + uint32_t riv = srslte_bit_pack(&y, riv_nbits((int) n_vrb_dl / n_step)); + uint32_t n_vrb_p = (uint32_t) n_vrb_dl / n_step; + + srslte_ra_type2_from_riv(riv, &L_p, &RB_p, n_vrb_p, n_vrb_p); + data->type2_alloc.L_crb = L_p * n_step; + data->type2_alloc.RB_start = RB_p * n_step; + data->type2_alloc.riv = riv; + + data->mcs_idx = srslte_bit_pack(&y, 5); + + data->rv_idx = -1; // Get RV later + + msg->nof_bits = (y - msg->data); + + data->tb_en[0] = true; + data->tb_en[1] = false; + + return SRSLTE_SUCCESS; +} + +int dci_format1D_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb, uint32_t nof_ports) +{ + + /* pack bits */ + uint8_t *y = msg->data; + + data->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + data->type2_alloc.mode = *y++; + + // by default, set N_gap to 1 + data->type2_alloc.n_gap = SRSLTE_RA_TYPE2_NG1; + + /* unpack RIV according to 7.1.6.3 of 36.213 */ + uint32_t nb_gap = 0; + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_DIST && nof_prb >= 50) { + nb_gap = 1; + data->type2_alloc.n_gap = *y++; + } + uint32_t nof_vrb; + if (data->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + nof_vrb = nof_prb; + } else { + nof_vrb = srslte_ra_type2_n_vrb_dl(nof_prb, data->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1); + } + uint32_t riv = srslte_bit_pack(&y, riv_nbits(nof_prb) - nb_gap); + srslte_ra_type2_from_riv(riv, &data->type2_alloc.L_crb, &data->type2_alloc.RB_start, + nof_prb, nof_vrb); + data->type2_alloc.riv = riv; + + // unpack MCS, Harq pid and ndi + data->mcs_idx = srslte_bit_pack(&y, 5); + data->harq_process = srslte_bit_pack(&y, HARQ_PID_LEN); + data->ndi = *y++ ? true : false; + data->rv_idx = srslte_bit_pack(&y, 2); + + // Ignore TPC command for PUCCH + y += 2; + + data->pinfo = srslte_bit_pack(&y, tpmi_bits(nof_ports)); + data->power_offset = *y++ ? true : false; + + data->tb_en[0] = true; + data->tb_en[1] = false; + + return SRSLTE_SUCCESS; +} + + +int dci_format2AB_pack(srslte_ra_dl_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb, uint32_t nof_ports) { + + /* pack bits */ + uint8_t *y = msg->data; + + if (nof_prb > 10) { + *y++ = data->alloc_type; + } + + /* Resource allocation: type0 or type 1 */ + uint32_t P = srslte_ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); + switch (data->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + srslte_bit_unpack((uint32_t) data->type0_alloc.rbg_bitmask, &y, alloc_size); + break; + case SRSLTE_RA_ALLOC_TYPE1: + srslte_bit_unpack((uint32_t) data->type1_alloc.rbg_subset, &y, (int) ceilf(log2f(P))); + *y++ = data->type1_alloc.shift ? 1 : 0; + srslte_bit_unpack((uint32_t) data->type1_alloc.vrb_bitmask, &y, + alloc_size - (int) ceilf(log2f(P)) - 1); + break; + default: + fprintf(stderr, + "Format 1 accepts type0 or type1 resource allocation only\n"); + return SRSLTE_ERROR; + + } + + /* TCP command for PUCCH */ + srslte_bit_unpack(data->tpc_pucch, &y, 2); + + /* harq process number */ + srslte_bit_unpack(data->harq_process, &y, HARQ_PID_LEN); + + // Transpor block to codeword swap flag + if (msg->format == SRSLTE_DCI_FORMAT2B) { + *y++ = data->sram_id; + } else { + *y++ = data->tb_cw_swap; + } + + /* Force MCS_idx and RV_idx in function of block enable according to 7.1.7 of 36.213 */ + if (!data->tb_en[0]) { + data->mcs_idx = 0; + data->rv_idx= 1; + } + if (!data->tb_en[1]) { + data->mcs_idx_1 = 0; + data->rv_idx_1 = 1; + } + + /* pack TB1 */ + srslte_bit_unpack(data->mcs_idx, &y, 5); + *y++ = data->ndi; + srslte_bit_unpack(data->rv_idx, &y, 2); + + /* pack TB2 */ + srslte_bit_unpack(data->mcs_idx_1, &y, 5); + *y++ = data->ndi_1; + srslte_bit_unpack(data->rv_idx_1, &y, 2); + + // Precoding information + if (msg->format == SRSLTE_DCI_FORMAT2) { + srslte_bit_unpack(data->pinfo, &y, precoding_bits_f2(nof_ports)); + } else if (msg->format == SRSLTE_DCI_FORMAT2A) { + srslte_bit_unpack(data->pinfo, &y, precoding_bits_f2a(nof_ports)); + } + + // Padding with zeros + uint32_t n = srslte_dci_format_sizeof(msg->format, nof_prb, nof_ports); + while (y - msg->data < n) { + *y++ = 0; + } + msg->nof_bits = (y - msg->data); + + + + return SRSLTE_SUCCESS; +} + +int dci_format2AB_unpack(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, uint32_t nof_prb, uint32_t nof_ports) { + + /* pack bits */ + uint8_t *y = msg->data; + + if (nof_prb > 10) { + data->alloc_type = *y++; + } else { + data->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + } + + /* Resource allocation: type0 or type 1 */ + uint32_t P = srslte_ra_type0_P(nof_prb); + uint32_t alloc_size = (uint32_t) ceilf((float) nof_prb / P); + switch (data->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + data->type0_alloc.rbg_bitmask = srslte_bit_pack(&y, alloc_size); + break; + case SRSLTE_RA_ALLOC_TYPE1: + data->type1_alloc.rbg_subset = srslte_bit_pack(&y, (int) ceilf(log2f(P))); + data->type1_alloc.shift = *y++ ? true : false; + data->type1_alloc.vrb_bitmask = srslte_bit_pack(&y, + alloc_size - (int) ceilf(log2f(P)) - 1); + break; + default: + fprintf(stderr, "Format2 accepts type0 or type1 resource allocation only\n"); + return SRSLTE_ERROR; + + } + // unpack TPC command for PUCCH (not implemented) + y+=2; + + /* harq process number */ + data->harq_process = srslte_bit_pack(&y, HARQ_PID_LEN); + + // Transpor block to codeword swap flag + if (msg->format == SRSLTE_DCI_FORMAT2B) { + data->sram_id = *y++ ? true : false; + } else { + data->tb_cw_swap = *y++ ? true : false; + } + + /* unpack MCS according to 7.1.7 of 36.213 */ + data->mcs_idx = srslte_bit_pack(&y, 5); + data->ndi = *y++ ? true : false; + data->rv_idx = srslte_bit_pack(&y, 2); + if (data->mcs_idx == 0 && data->rv_idx == 1) { + data->tb_en[0] = false; + } else { + data->tb_en[0] = true; + } + + // same for tb1 + data->mcs_idx_1 = srslte_bit_pack(&y, 5); + data->ndi_1 = *y++ ? true : false; + data->rv_idx_1 = srslte_bit_pack(&y, 2); + if (data->mcs_idx_1 == 0 && data->rv_idx_1 == 1) { + data->tb_en[1] = false; + } else { + data->tb_en[1] = true; + } + + // Precoding information + if (msg->format == SRSLTE_DCI_FORMAT2) { + data->pinfo = srslte_bit_pack(&y, precoding_bits_f2(nof_ports)); + } else if (msg->format == SRSLTE_DCI_FORMAT2A) { + data->pinfo = srslte_bit_pack(&y, precoding_bits_f2a(nof_ports)); + } + + // Table 5.3.3.1.5-1 + if (SRSLTE_RA_DL_GRANT_NOF_TB(data) == 2) { + if (data->tb_cw_swap) { + uint32_t tmp = data->rv_idx; + data->rv_idx = data->rv_idx_1; + data->rv_idx_1 = tmp; + + tmp = data->mcs_idx; + data->mcs_idx = data->mcs_idx_1; + data->mcs_idx_1 = tmp; + + bool tmp_ndi = data->ndi; + data->ndi = data->ndi_1; + data->ndi_1 = tmp_ndi; + } + } + + // Table 5.3.3.1.5-2 + if (!data->tb_en[0]) { + data->rv_idx = data->rv_idx_1; + data->mcs_idx = data->mcs_idx_1; + data->ndi = data->ndi_1; + + data->tb_en[1] = false; + } + + return SRSLTE_SUCCESS; +} + + + +int srslte_dci_msg_pack_pdsch(srslte_ra_dl_dci_t *data, srslte_dci_format_t format, + srslte_dci_msg_t *msg, uint32_t nof_prb, uint32_t nof_ports, + bool crc_is_crnti) +{ + msg->format = format; + switch (format) { + case SRSLTE_DCI_FORMAT1: + msg->format = format; + return dci_format1_pack(data, msg, nof_prb); + case SRSLTE_DCI_FORMAT1A: + msg->format = format; + return dci_format1As_pack(data, msg, nof_prb, crc_is_crnti); + case SRSLTE_DCI_FORMAT1C: + msg->format = format; + return dci_format1Cs_pack(data, msg, nof_prb); + case SRSLTE_DCI_FORMAT2: + case SRSLTE_DCI_FORMAT2A: + case SRSLTE_DCI_FORMAT2B: + msg->format = format; + return dci_format2AB_pack(data, msg, nof_prb, nof_ports); + default: + fprintf(stderr, "DCI pack pdsch: Invalid DCI format %s\n", + srslte_dci_format_string(format)); + return SRSLTE_ERROR; + } +} + +int srslte_dci_msg_unpack_pdsch(srslte_dci_msg_t *msg, srslte_ra_dl_dci_t *data, + uint32_t nof_prb, uint32_t nof_ports, bool crc_is_crnti) +{ + switch (msg->format) { + case SRSLTE_DCI_FORMAT1: + return dci_format1_unpack(msg, data, nof_prb); + case SRSLTE_DCI_FORMAT1A: + return dci_format1As_unpack(msg, data, nof_prb, crc_is_crnti); + case SRSLTE_DCI_FORMAT1B: + return dci_format1B_unpack(msg, data, nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT1C: + return dci_format1Cs_unpack(msg, data, nof_prb); + case SRSLTE_DCI_FORMAT1D: + return dci_format1D_unpack(msg, data, nof_prb, nof_ports); + case SRSLTE_DCI_FORMAT2: + case SRSLTE_DCI_FORMAT2A: + case SRSLTE_DCI_FORMAT2B: + return dci_format2AB_unpack(msg, data, nof_prb, nof_ports); + default: + fprintf(stderr, "DCI unpack pdsch: Invalid DCI format %s\n", + srslte_dci_format_string(msg->format)); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +int srslte_dci_msg_pack_pusch(srslte_ra_ul_dci_t *data, srslte_dci_msg_t *msg, uint32_t nof_prb) { + return dci_format0_pack(data, msg, nof_prb); +} + +int srslte_dci_msg_unpack_pusch(srslte_dci_msg_t *msg, srslte_ra_ul_dci_t *data, uint32_t nof_prb) { + return dci_format0_unpack(msg, data, nof_prb); +} + +srslte_dci_format_t srslte_dci_format_from_string(char *str) { + if (!strcmp(str, "Format0")) { + return SRSLTE_DCI_FORMAT0; + } else if (!strcmp(str, "Format1")) { + return SRSLTE_DCI_FORMAT1; + } else if (!strcmp(str, "Format1A")) { + return SRSLTE_DCI_FORMAT1A; + } else if (!strcmp(str, "Format1B")) { + return SRSLTE_DCI_FORMAT1B; + } else if (!strcmp(str, "Format1C")) { + return SRSLTE_DCI_FORMAT1C; + } else if (!strcmp(str, "Format1D")) { + return SRSLTE_DCI_FORMAT1D; + } else if (!strcmp(str, "Format2")) { + return SRSLTE_DCI_FORMAT2; + } else if (!strcmp(str, "Format2A")) { + return SRSLTE_DCI_FORMAT2A; + } else if (!strcmp(str, "Format2B")) { + return SRSLTE_DCI_FORMAT2B; + } else { + return SRSLTE_DCI_NOF_FORMATS; + } +} + +char* srslte_dci_format_string(srslte_dci_format_t format) { + switch (format) { + case SRSLTE_DCI_FORMAT0: + return "Format0 "; + case SRSLTE_DCI_FORMAT1: + return "Format1 "; + case SRSLTE_DCI_FORMAT1A: + return "Format1A"; + case SRSLTE_DCI_FORMAT1B: + return "Format1B"; + case SRSLTE_DCI_FORMAT1C: + return "Format1C"; + case SRSLTE_DCI_FORMAT1D: + return "Format1D"; + case SRSLTE_DCI_FORMAT2: + return "Format2 "; + case SRSLTE_DCI_FORMAT2A: + return "Format2A"; + case SRSLTE_DCI_FORMAT2B: + return "Format2B"; + default: + return "N/A"; // fatal error + } +} + + +char* srslte_dci_format_string_short(srslte_dci_format_t format) { + switch (format) { + case SRSLTE_DCI_FORMAT0: + return "0"; + case SRSLTE_DCI_FORMAT1: + return "1"; + case SRSLTE_DCI_FORMAT1A: + return "1A"; + case SRSLTE_DCI_FORMAT1B: + return "1B"; + case SRSLTE_DCI_FORMAT1C: + return "1C"; + case SRSLTE_DCI_FORMAT1D: + return "1D"; + case SRSLTE_DCI_FORMAT2: + return "2"; + case SRSLTE_DCI_FORMAT2A: + return "2A"; + case SRSLTE_DCI_FORMAT2B: + return "2B"; + default: + return "N/A"; // fatal error + } +} + +void srslte_dci_msg_type_fprint(FILE *f, srslte_dci_msg_type_t type) { + switch (type.type) { + case SRSLTE_DCI_MSG_TYPE_PUSCH_SCHED: + fprintf(f, "%s PUSCH Scheduling\n", srslte_dci_format_string(type.format)); + break; + case SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED: + fprintf(f, "%s PDSCH Scheduling\n", srslte_dci_format_string(type.format)); + break; + case SRSLTE_DCI_MSG_TYPE_RA_PROC_PDCCH: + fprintf(f, "%s Random access initiated by PDCCH\n", + srslte_dci_format_string(type.format)); + break; + case SRSLTE_DCI_MSG_TYPE_MCCH_CHANGE: + fprintf(f, "%s MCCH change notification\n", srslte_dci_format_string(type.format)); + break; + case SRSLTE_DCI_MSG_TYPE_TPC_COMMAND: + fprintf(f, "%s TPC command\n", srslte_dci_format_string(type.format)); + break; + } +} + +/** Warning this function will be deprecated. Currently only used in test programs */ +int srslte_dci_msg_get_type(srslte_dci_msg_t *msg, srslte_dci_msg_type_t *type, uint32_t nof_prb, + uint16_t msg_rnti) +{ + DEBUG("Get message type: nof_bits=%d, msg_rnti=0x%x\n", msg->nof_bits, msg_rnti); + if (msg->nof_bits == srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, nof_prb, 1) + && !msg->data[0]) { + type->type = SRSLTE_DCI_MSG_TYPE_PUSCH_SCHED; + type->format = SRSLTE_DCI_FORMAT0; + return SRSLTE_SUCCESS; + } else if (msg->nof_bits == srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, nof_prb, 1)) { + type->type = SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED; // only these 2 types supported + type->format = SRSLTE_DCI_FORMAT1; + return SRSLTE_SUCCESS; + } else if (msg->nof_bits == srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1A, nof_prb, 1)) { + /* The RNTI is not the only condition. Also some fields in the packet. + * if (msg_rnti >= SRSLTE_CRNTI_START && msg_rnti <= SRSLTE_CRNTI_END) { + type->type = SRSLTE_DCI_MSG_TYPE_RA_PROC_PDCCH; + type->format = SRSLTE_DCI_FORMAT1A; + } else { + */ + type->type = SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED; // only these 2 types supported + type->format = SRSLTE_DCI_FORMAT1A; + //} + return SRSLTE_SUCCESS; + } else if (msg->nof_bits == srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1C, nof_prb, 1)) { + if (msg_rnti == SRSLTE_MRNTI) { + type->type = SRSLTE_DCI_MSG_TYPE_MCCH_CHANGE; + type->format = SRSLTE_DCI_FORMAT1C; + } else { + type->type = SRSLTE_DCI_MSG_TYPE_PDSCH_SCHED; // only these 2 types supported + type->format = SRSLTE_DCI_FORMAT1C; + } + return SRSLTE_SUCCESS; + } + return SRSLTE_ERROR; +} + diff --git a/lib/src/phy/phch/dci_sz_table.h b/lib/src/phy/phch/dci_sz_table.h new file mode 100644 index 0000000..f48f80d --- /dev/null +++ b/lib/src/phy/phch/dci_sz_table.h @@ -0,0 +1,132 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + + +static uint32_t dci_sz_table[101][4] = { + {15, 13, 15, 5}, + {15, 17, 15, 5}, + {17, 15, 17, 5}, + {18, 17, 18, 5}, + {19, 17, 19, 7}, + {19, 18, 19, 7}, + {21, 19, 21, 8}, + {21, 22, 21, 8}, + {21, 22, 21, 9}, + {21, 22, 21, 9}, + {21, 23, 21, 9}, + {22, 21, 22, 9}, + {22, 21, 22, 9}, + {22, 21, 22, 9}, + {22, 21, 22, 10}, + {22, 23, 22, 10}, + {23, 22, 23, 11}, + {23, 25, 23, 11}, + {23, 25, 23, 11}, + {23, 25, 23, 11}, + {23, 25, 23, 11}, + {23, 25, 23, 11}, + {23, 25, 23, 11}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 23, 25, 11}, + {25, 27, 25, 11}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 12}, + {25, 27, 25, 13}, + {25, 27, 25, 13}, + {25, 27, 25, 13}, + {25, 27, 25, 13}, + {25, 27, 25, 13}, + {25, 27, 25, 13}, + {25, 28, 25, 13}, + {25, 28, 25, 13}, + {25, 28, 25, 13}, + {25, 29, 25, 13}, + {25, 29, 25, 13}, + {27, 29, 27, 13}, + {27, 30, 27, 13}, + {27, 30, 27, 13}, + {27, 30, 27, 13}, + {27, 31, 27, 13}, + {27, 31, 27, 13}, + {27, 31, 27, 13}, + {27, 33, 27, 13}, + {27, 33, 27, 13}, + {27, 33, 27, 13}, + {27, 33, 27, 13}, + {27, 33, 27, 13}, + {27, 33, 27, 13}, + {27, 34, 27, 13}, + {27, 34, 27, 13}, + {27, 34, 27, 13}, + {27, 35, 27, 13}, + {27, 35, 27, 13}, + {27, 35, 27, 13}, + {27, 30, 27, 14}, + {27, 31, 27, 14}, + {27, 31, 27, 14}, + {27, 31, 27, 14}, + {27, 31, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 33, 27, 14}, + {27, 34, 27, 14}, + {27, 34, 27, 14}, + {27, 34, 27, 14}, + {27, 34, 27, 14}, + {27, 35, 27, 14}, + {27, 35, 27, 14}, + {27, 35, 27, 14}, + {27, 35, 27, 14}, + {27, 36, 27, 14}, + {27, 36, 27, 14}, + {27, 36, 27, 14}, + {27, 36, 27, 14}, + {27, 37, 27, 14}, + {27, 37, 27, 14}, + {28, 37, 28, 14}, + {28, 37, 28, 14}, + {28, 38, 28, 14}, + {28, 38, 28, 15}, + {28, 38, 28, 15}, + {28, 38, 28, 15}, + {28, 39, 28, 15}, + {28, 39, 28, 15}, + {28, 39, 28, 15}, + {28, 39, 28, 15} +}; + diff --git a/lib/src/phy/phch/pbch.c b/lib/src/phy/phch/pbch.c new file mode 100644 index 0000000..cdb5f48 --- /dev/null +++ b/lib/src/phy/phch/pbch.c @@ -0,0 +1,615 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prb_dl.h" +#include "srslte/phy/phch/pbch.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#define PBCH_RE_CP_NORM 240 +#define PBCH_RE_CP_EXT 216 + +const uint8_t srslte_crc_mask[4][16] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 } }; + +bool srslte_pbch_exists(int nframe, int nslot) { + return (!(nframe % 5) && nslot == 1); +} + +cf_t *offset_original; + +int srslte_pbch_cp(cf_t *input, cf_t *output, srslte_cell_t cell, bool put) { + int i; + cf_t *ptr; + + offset_original = input; + + if (put) { + ptr = input; + output += cell.nof_prb * SRSLTE_NRE / 2 - 36; + } else { + ptr = output; + input += cell.nof_prb * SRSLTE_NRE / 2 - 36; + } + + /* symbol 0 & 1 */ + for (i = 0; i < 2; i++) { + prb_cp_ref(&input, &output, cell.id % 3, 4, 4*6, put); + if (put) { + output += cell.nof_prb * SRSLTE_NRE - 2*36 + (cell.id%3==2?1:0); + } else { + input += cell.nof_prb * SRSLTE_NRE - 2*36 + (cell.id%3==2?1:0); + } + } + /* symbols 2 & 3 */ + if (SRSLTE_CP_ISNORM(cell.cp)) { + for (i = 0; i < 2; i++) { + prb_cp(&input, &output, 6); + if (put) { + output += cell.nof_prb * SRSLTE_NRE - 2*36; + } else { + input += cell.nof_prb * SRSLTE_NRE - 2*36; + } + } + } else { + prb_cp(&input, &output, 6); + if (put) { + output += cell.nof_prb * SRSLTE_NRE - 2*36; + } else { + input += cell.nof_prb * SRSLTE_NRE - 2*36; + } + prb_cp_ref(&input, &output, cell.id % 3, 4, 4*6, put); + } + if (put) { + return input - ptr; + } else { + return output - ptr; + } +} + +/** + * Puts PBCH in slot number 1 + * + * Returns the number of symbols written to slot1_data + * + * 36.211 10.3 section 6.6.4 + * + * @param[in] pbch PBCH complex symbols to place in slot1_data + * @param[out] slot1_data Complex symbol buffer for slot1 + * @param[in] cell Cell configuration + */ +int srslte_pbch_put(cf_t *pbch, cf_t *slot1_data, srslte_cell_t cell) { + return srslte_pbch_cp(pbch, slot1_data, cell, true); +} + +/** + * Extracts PBCH from slot number 1 + * + * Returns the number of symbols written to pbch + * + * 36.211 10.3 section 6.6.4 + * + * @param[in] slot1_data Complex symbols for slot1 + * @param[out] pbch Extracted complex PBCH symbols + * @param[in] cell Cell configuration + */ +int srslte_pbch_get(cf_t *slot1_data, cf_t *pbch, srslte_cell_t cell) { + return srslte_pbch_cp(slot1_data, pbch, cell, false); +} + +/** Initializes the PBCH transmitter and receiver. + * At the receiver, the field nof_ports in the cell structure indicates the + * maximum number of BS transmitter ports to look for. + */ +int srslte_pbch_init(srslte_pbch_t *q) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_pbch_t)); + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + goto clean; + } + int poly[3] = { 0x6D, 0x4F, 0x57 }; + if (srslte_viterbi_init(&q->decoder, SRSLTE_VITERBI_37, poly, 40, true)) { + goto clean; + } + if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC16, 16)) { + goto clean; + } + q->encoder.K = 7; + q->encoder.R = 3; + q->encoder.tail_biting = true; + memcpy(q->encoder.poly, poly, 3 * sizeof(int)); + + q->nof_symbols = PBCH_RE_CP_NORM; + + q->d = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->d) { + goto clean; + } + int i; + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->ce[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->ce[i]) { + goto clean; + } + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->x[i]) { + goto clean; + } + q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->nof_symbols); + if (!q->symbols[i]) { + goto clean; + } + } + q->llr = srslte_vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->llr) { + goto clean; + } + q->temp = srslte_vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->temp) { + goto clean; + } + q->rm_b = srslte_vec_malloc(sizeof(float) * q->nof_symbols * 4 * 2); + if (!q->rm_b) { + goto clean; + } + + ret = SRSLTE_SUCCESS; + } +clean: + if (ret == SRSLTE_ERROR) { + srslte_pbch_free(q); + } + return ret; +} + +void srslte_pbch_free(srslte_pbch_t *q) { + srslte_sequence_free(&q->seq); + srslte_modem_table_free(&q->mod); + srslte_viterbi_free(&q->decoder); + int i; + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->ce[i]) { + free(q->ce[i]); + } + if (q->x[i]) { + free(q->x[i]); + } + if (q->symbols[i]) { + free(q->symbols[i]); + } + } + if (q->llr) { + free(q->llr); + } + if (q->temp) { + free(q->temp); + } + if (q->rm_b) { + free(q->rm_b); + } + if (q->d) { + free(q->d); + } + bzero(q, sizeof(srslte_pbch_t)); +} + +int srslte_pbch_set_cell(srslte_pbch_t *q, srslte_cell_t cell) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + if (cell.nof_ports == 0) { + q->search_all_ports = true; + cell.nof_ports = SRSLTE_MAX_PORTS; + } else { + q->search_all_ports = false; + } + + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + if (srslte_sequence_pbch(&q->seq, q->cell.cp, q->cell.id)) { + return SRSLTE_ERROR; + } + } + q->nof_symbols = (SRSLTE_CP_ISNORM(q->cell.cp)) ? PBCH_RE_CP_NORM : PBCH_RE_CP_EXT; + + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +/** + * Unpacks MIB from PBCH message. + * + * @param[in] msg PBCH in an unpacked bit array of size 24 + * @param[out] sfn System frame number + * @param[out] cell MIB information about PHICH and system bandwidth will be saved here + */ +void srslte_pbch_mib_unpack(uint8_t *msg, srslte_cell_t *cell, uint32_t *sfn) { + int phich_res; + + uint32_t bw_idx = srslte_bit_pack(&msg, 3); + switch (bw_idx) { + case 0: + cell->nof_prb = 6; + break; + case 1: + cell->nof_prb = 15; + break; + default: + cell->nof_prb = (bw_idx - 1) * 25; + break; + } + if (*msg) { + cell->phich_length = SRSLTE_PHICH_EXT; + } else { + cell->phich_length = SRSLTE_PHICH_NORM; + } + msg++; + + phich_res = srslte_bit_pack(&msg, 2); + switch (phich_res) { + case 0: + cell->phich_resources = SRSLTE_PHICH_R_1_6; + break; + case 1: + cell->phich_resources = SRSLTE_PHICH_R_1_2; + break; + case 2: + cell->phich_resources = SRSLTE_PHICH_R_1; + break; + case 3: + cell->phich_resources = SRSLTE_PHICH_R_2; + break; + } + if (sfn) { + *sfn = srslte_bit_pack(&msg, 8) << 2; + } +} + +/** + * Packs MIB to PBCH message. + * + * @param[out] payload Output unpacked bit array of size 24 + * @param[in] sfn System frame number + * @param[in] cell Cell configuration to be encoded in MIB + */ +void srslte_pbch_mib_pack(srslte_cell_t *cell, uint32_t sfn, uint8_t *payload) { + int bw, phich_res = 0; + + uint8_t *msg = payload; + + bzero(msg, 24); + + if (cell->nof_prb <= 6) { + bw = 0; + } else if (cell->nof_prb <= 15) { + bw = 1; + } else { + bw = 1 + cell->nof_prb / 25; + } + srslte_bit_unpack(bw, &msg, 3); + + *msg = cell->phich_length == SRSLTE_PHICH_EXT; + msg++; + + switch (cell->phich_resources) { + case SRSLTE_PHICH_R_1_6: + phich_res = 0; + break; + case SRSLTE_PHICH_R_1_2: + phich_res = 1; + break; + case SRSLTE_PHICH_R_1: + phich_res = 2; + break; + case SRSLTE_PHICH_R_2: + phich_res = 3; + break; + } + srslte_bit_unpack(phich_res, &msg, 2); + srslte_bit_unpack(sfn >> 2, &msg, 8); + +} + +void srslte_pbch_decode_reset(srslte_pbch_t *q) { + q->frame_idx = 0; +} + +void srslte_crc_set_mask(uint8_t *data, int nof_ports) { + int i; + for (i = 0; i < 16; i++) { + data[SRSLTE_BCH_PAYLOAD_LEN + i] = (data[SRSLTE_BCH_PAYLOAD_LEN + i] + srslte_crc_mask[nof_ports - 1][i]) % 2; + } +} + +/* Checks CRC after applying the mask for the given number of ports. + * + * The bits buffer size must be at least 40 bytes. + * + * Returns 0 if the data is correct, -1 otherwise + */ +uint32_t srslte_pbch_crc_check(srslte_pbch_t *q, uint8_t *bits, uint32_t nof_ports) { + uint8_t data[SRSLTE_BCH_PAYLOADCRC_LEN]; + memcpy(data, bits, SRSLTE_BCH_PAYLOADCRC_LEN * sizeof(uint8_t)); + srslte_crc_set_mask(data, nof_ports); + int ret = srslte_crc_checksum(&q->crc, data, SRSLTE_BCH_PAYLOADCRC_LEN); + if (ret == 0) { + uint32_t chkzeros=0; + for (int i=0;itemp[dst * nof_bits], &q->llr[src * nof_bits], + n * nof_bits * sizeof(float)); + + /* descramble */ + srslte_scrambling_f_offset(&q->seq, &q->temp[dst * nof_bits], dst * nof_bits, + n * nof_bits); + + for (j = 0; j < dst * nof_bits; j++) { + q->temp[j] = SRSLTE_RX_NULL; + } + for (j = (dst + n) * nof_bits; j < 4 * nof_bits; j++) { + q->temp[j] = SRSLTE_RX_NULL; + } + + /* unrate matching */ + srslte_rm_conv_rx(q->temp, 4 * nof_bits, q->rm_f, SRSLTE_BCH_ENCODED_LEN); + + /* Normalize LLR */ + srslte_vec_sc_prod_fff(q->rm_f, 1.0/((float) 2*n), q->rm_f, SRSLTE_BCH_ENCODED_LEN); + + /* decode */ + srslte_viterbi_decode_f(&q->decoder, q->rm_f, q->data, SRSLTE_BCH_PAYLOADCRC_LEN); + + if (!srslte_pbch_crc_check(q, q->data, nof_ports)) { + return 1; + } else { + return SRSLTE_SUCCESS; + } + } else { + fprintf(stderr, "Error in PBCH decoder: Invalid frame pointers dst=%d, src=%d, n=%d\n", src, dst, n); + return -1; + } + +} + +/* Decodes the PBCH channel + * + * The PBCH spans in 40 ms. This function is called every 10 ms. It tries to decode the MIB + * given the symbols of a subframe (1 ms). Successive calls will use more subframes + * to help the decoding process. + * + * Returns 1 if successfully decoded MIB, 0 if not and -1 on error + */ +int srslte_pbch_decode(srslte_pbch_t *q, cf_t *slot1_symbols, cf_t *ce_slot1[SRSLTE_MAX_PORTS], float noise_estimate, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], uint32_t *nof_tx_ports, int *sfn_offset) +{ + uint32_t src, dst, nb; + uint32_t nant; + int i; + int nof_bits; + cf_t *x[SRSLTE_MAX_LAYERS]; + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + slot1_symbols != NULL) + { + for (i=0;icell.nof_ports;i++) { + if (ce_slot1[i] == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + + /* Set pointers for layermapping & precoding */ + nof_bits = 2 * q->nof_symbols; + + /* number of layers equals number of ports */ + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + x[i] = q->x[i]; + } + + /* extract symbols */ + if (q->nof_symbols != srslte_pbch_get(slot1_symbols, q->symbols[0], q->cell)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return SRSLTE_ERROR; + } + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (q->nof_symbols != srslte_pbch_get(ce_slot1[i], q->ce[i], q->cell)) { + fprintf(stderr, "There was an error getting the PBCH symbols\n"); + return SRSLTE_ERROR; + } + } + + q->frame_idx++; + ret = 0; + + uint32_t frame_idx = q->frame_idx; + + /* Try decoding for 1 to cell.nof_ports antennas */ + if (q->search_all_ports) { + nant = 1; + } else { + nant = q->cell.nof_ports; + } + + do { + if (nant != 3) { + DEBUG("Trying %d TX antennas with %d frames\n", nant, frame_idx); + + /* in control channels, only diversity is supported */ + if (nant == 1) { + /* no need for layer demapping */ + srslte_predecoding_single(q->symbols[0], q->ce[0], q->d, NULL, q->nof_symbols, 1.0f, noise_estimate); + } else { + srslte_predecoding_diversity(q->symbols[0], q->ce, x, nant, + q->nof_symbols, 1.0f); + srslte_layerdemap_diversity(x, q->d, nant, q->nof_symbols / nant); + } + + /* demodulate symbols */ + srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, &q->llr[nof_bits * (frame_idx - 1)], q->nof_symbols); + + /* We don't know where the 40 ms begin, so we try all combinations. E.g. if we received + * 4 frames, try 1,2,3,4 individually, 12, 23, 34 in pairs, 123, 234 and finally 1234. + * We know they are ordered. + */ + for (nb = 0; nb < frame_idx; nb++) { + for (dst = 0; (dst < 4 - nb); dst++) { + for (src = 0; src < frame_idx - nb; src++) { + ret = decode_frame(q, src, dst, nb + 1, nof_bits, nant); + if (ret == 1) { + if (sfn_offset) { + *sfn_offset = (int) dst - src + frame_idx - 1; + } + if (nof_tx_ports) { + *nof_tx_ports = nant; + } + if (bch_payload) { + memcpy(bch_payload, q->data, sizeof(uint8_t) * SRSLTE_BCH_PAYLOAD_LEN); + } + INFO("Decoded PBCH: src=%d, dst=%d, nb=%d, sfn_offset=%d\n", src, dst, nb+1, (int) dst - src + frame_idx - 1); + srslte_pbch_decode_reset(q); + return 1; + } + } + } + } + } + nant++; + } while(nant <= q->cell.nof_ports); + + /* If not found, make room for the next packet of radio frame symbols */ + if (q->frame_idx == 4) { + memmove(q->llr, &q->llr[nof_bits], nof_bits * 3 * sizeof(float)); + q->frame_idx = 3; + } + } + return ret; +} + +/** Converts the MIB message to symbols mapped to SLOT #1 ready for transmission + */ +int srslte_pbch_encode(srslte_pbch_t *q, uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], cf_t *slot1_symbols[SRSLTE_MAX_PORTS], uint32_t frame_idx) { + int i; + int nof_bits; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && + bch_payload != NULL) + { + for (i=0;icell.nof_ports;i++) { + if (slot1_symbols[i] == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + /* Set pointers for layermapping & precoding */ + nof_bits = 2 * q->nof_symbols; + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + frame_idx=frame_idx%4; + + memcpy(q->data, bch_payload, sizeof(uint8_t) * SRSLTE_BCH_PAYLOAD_LEN); + + /* encode & modulate */ + srslte_crc_attach(&q->crc, q->data, SRSLTE_BCH_PAYLOAD_LEN); + srslte_crc_set_mask(q->data, q->cell.nof_ports); + + srslte_convcoder_encode(&q->encoder, q->data, q->data_enc, SRSLTE_BCH_PAYLOADCRC_LEN); + + srslte_rm_conv_tx(q->data_enc, SRSLTE_BCH_ENCODED_LEN, q->rm_b, 4 * nof_bits); + + srslte_scrambling_b_offset(&q->seq, &q->rm_b[frame_idx * nof_bits], + frame_idx * nof_bits, nof_bits); + srslte_mod_modulate(&q->mod, &q->rm_b[frame_idx * nof_bits], q->d, + nof_bits); + + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d, x, q->cell.nof_ports, q->nof_symbols); + srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports, + q->nof_symbols / q->cell.nof_ports, 1.0f); + } else { + memcpy(q->symbols[0], q->d, q->nof_symbols * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + srslte_pbch_put(q->symbols[i], slot1_symbols[i], q->cell); + } + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + diff --git a/lib/src/phy/phch/pcfich.c b/lib/src/phy/phch/pcfich.c new file mode 100644 index 0000000..c6f1fe1 --- /dev/null +++ b/lib/src/phy/phch/pcfich.c @@ -0,0 +1,299 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/pcfich.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +// Table 5.3.4-1 +static uint8_t cfi_table[4][PCFICH_CFI_LEN] = { + { 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1 }, + { 1, 0, 1, + 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0 }, + { 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, + 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // reserved +}; + +bool srslte_pcfich_exists(int nframe, int nslot) { + return true; +} + +/** Initializes the pcfich channel receiver. + * On error, returns -1 and frees the structrure + */ +int srslte_pcfich_init(srslte_pcfich_t *q, uint32_t nof_rx_antennas) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_pcfich_t)); + q->nof_rx_antennas = nof_rx_antennas; + q->nof_symbols = PCFICH_RE; + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + goto clean; + } + + /* convert cfi bit tables to floats for demodulation */ + for (int i=0;i<3;i++) { + for (int j=0;jcfi_table_float[i][j] = (float) 2.0*cfi_table[i][j]-1.0; + } + } + + ret = SRSLTE_SUCCESS; + } + + clean: + if (ret == SRSLTE_ERROR) { + srslte_pcfich_free(q); + } + return ret; +} + +void srslte_pcfich_free(srslte_pcfich_t *q) { + for (int ns = 0; ns < SRSLTE_NSUBFRAMES_X_FRAME; ns++) { + srslte_sequence_free(&q->seq[ns]); + } + srslte_modem_table_free(&q->mod); + + bzero(q, sizeof(srslte_pcfich_t)); +} + +int srslte_pcfich_set_cell(srslte_pcfich_t *q, srslte_regs_t *regs, srslte_cell_t cell) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + regs != NULL && + srslte_cell_isvalid(&cell)) + { + q->regs = regs; + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + for (int nsf = 0; nsf < SRSLTE_NSUBFRAMES_X_FRAME; nsf++) { + if (srslte_sequence_pcfich(&q->seq[nsf], 2 * nsf, q->cell.id)) { + return SRSLTE_ERROR; + } + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +/** Finds the CFI with minimum distance with the vector of received 32 bits. + * Saves the CFI value in the cfi pointer and returns the distance. + */ +float srslte_pcfich_cfi_decode(srslte_pcfich_t *q, uint32_t *cfi) { + int i; + int index = 0; + float max_corr = 0; + float corr[3]; + + for (i = 0; i < 3; i++) { + corr[i] = srslte_vec_dot_prod_fff(q->cfi_table_float[i], q->data_f, PCFICH_CFI_LEN); + if (corr[i] > max_corr) { + max_corr = corr[i]; + index = i; + } + } + + if (cfi) { + *cfi = index + 1; + } + return max_corr; +} + +/** Encodes the CFI producing a vector of 32 bits. + * 36.211 10.3 section 5.3.4 + */ +int srslte_pcfich_cfi_encode(uint32_t cfi, uint8_t bits[PCFICH_CFI_LEN]) { + if (cfi < 1 || cfi > 3) { + return SRSLTE_ERROR_INVALID_INPUTS; + } else{ + memcpy(bits, cfi_table[cfi - 1], PCFICH_CFI_LEN * sizeof(uint8_t)); + return SRSLTE_SUCCESS; + } +} + +int srslte_pcfich_decode(srslte_pcfich_t *q, cf_t *sf_symbols, cf_t *ce[SRSLTE_MAX_PORTS], float noise_estimate, + uint32_t nsubframe, uint32_t *cfi, float *corr_result) +{ + cf_t *_sf_symbols[SRSLTE_MAX_PORTS]; + cf_t *_ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + + _sf_symbols[0] = sf_symbols; + for (int i=0;icell.nof_ports;i++) { + _ce[i][0] = ce[i]; + } + return srslte_pcfich_decode_multi(q, _sf_symbols, _ce, noise_estimate, nsubframe, cfi, corr_result); +} + +/* Decodes the PCFICH channel and saves the CFI in the cfi pointer. + * + * Returns 1 if successfully decoded the CFI, 0 if not and -1 on error + */ +int srslte_pcfich_decode_multi(srslte_pcfich_t *q, cf_t *sf_symbols[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, + uint32_t nsubframe, uint32_t *cfi, float *corr_result) +{ + + /* Set pointers for layermapping & precoding */ + int i; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && + sf_symbols != NULL && + nsubframe < SRSLTE_NSUBFRAMES_X_FRAME) + { + + /* number of layers equals number of ports */ + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + x[i] = q->x[i]; + } + + cf_t *q_symbols[SRSLTE_MAX_PORTS]; + cf_t *q_ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + + /* extract symbols */ + for (int j=0;jnof_rx_antennas;j++) { + if (q->nof_symbols + != srslte_regs_pcfich_get(q->regs, sf_symbols[j], q->symbols[j])) { + fprintf(stderr, "There was an error getting the PCFICH symbols\n"); + return SRSLTE_ERROR; + } + + q_symbols[j] = q->symbols[j]; + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (q->nof_symbols != srslte_regs_pcfich_get(q->regs, ce[i][j], q->ce[i][j])) { + fprintf(stderr, "There was an error getting the PCFICH symbols\n"); + return SRSLTE_ERROR; + } + q_ce[i][j] = q->ce[i][j]; + } + } + + /* in control channels, only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + srslte_predecoding_single_multi(q_symbols, q_ce[0], q->d, NULL, q->nof_rx_antennas, q->nof_symbols, 1.0f, noise_estimate); + } else { + srslte_predecoding_diversity_multi(q_symbols, q_ce, x, NULL, q->nof_rx_antennas, q->cell.nof_ports, q->nof_symbols, 1.0f); + srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports, q->nof_symbols / q->cell.nof_ports); + } + + /* demodulate symbols */ + srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, q->data_f, q->nof_symbols); + + /* Scramble with the sequence for slot nslot */ + srslte_scrambling_f(&q->seq[nsubframe], q->data_f); + + /* decode CFI */ + float corr = srslte_pcfich_cfi_decode(q, cfi); + if (corr_result) { + *corr_result = corr; + } + return 1; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } + +} + +/** Encodes CFI and maps symbols to the slot + */ +int srslte_pcfich_encode(srslte_pcfich_t *q, uint32_t cfi, cf_t *slot_symbols[SRSLTE_MAX_PORTS], + uint32_t subframe) { + int i; + + if (q != NULL && + cfi <= 3 && + slot_symbols != NULL && + subframe < SRSLTE_NSUBFRAMES_X_FRAME) + { + + /* Set pointers for layermapping & precoding */ + cf_t *x[SRSLTE_MAX_LAYERS]; + cf_t *q_symbols[SRSLTE_MAX_PORTS]; + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + q_symbols[i] = q->symbols[i]; + } + + /* pack CFI */ + srslte_pcfich_cfi_encode(cfi, q->data); + + /* scramble for slot sequence nslot */ + srslte_scrambling_b(&q->seq[subframe], q->data); + + srslte_mod_modulate(&q->mod, q->data, q->d, PCFICH_CFI_LEN); + + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d, x, q->cell.nof_ports, q->nof_symbols); + srslte_precoding_diversity(x, q_symbols, q->cell.nof_ports, q->nof_symbols / q->cell.nof_ports, 1.0f); + } else { + memcpy(q->symbols[0], q->d, q->nof_symbols * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (srslte_regs_pcfich_put(q->regs, q->symbols[i], slot_symbols[i]) < 0) { + fprintf(stderr, "Error putting PCHICH resource elements\n"); + return SRSLTE_ERROR; + } + } + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + diff --git a/lib/src/phy/phch/pdcch.c b/lib/src/phy/phch/pdcch.c new file mode 100644 index 0000000..1c40333 --- /dev/null +++ b/lib/src/phy/phch/pdcch.c @@ -0,0 +1,645 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/dci.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/pdcch.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +#define PDCCH_NOF_FORMATS 4 +#define PDCCH_FORMAT_NOF_CCE(i) (1<0&&cfi<4)?q->nof_cce[cfi-1]:0) +#define NOF_REGS(cfi) ((cfi>0&&cfi<4)?q->nof_regs[cfi-1]:0) + +float srslte_pdcch_coderate(uint32_t nof_bits, uint32_t l) { + return (float) (nof_bits+16)/(4*PDCCH_FORMAT_NOF_REGS(l)); +} + +/** Initializes the PDCCH transmitter and receiver */ +static int pdcch_init(srslte_pdcch_t *q, uint32_t max_prb, uint32_t nof_rx_antennas, bool is_ue) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_pdcch_t)); + q->nof_rx_antennas = nof_rx_antennas; + q->is_ue = is_ue; + /* Allocate memory for the maximum number of PDCCH bits (CFI=3) */ + q->max_bits = max_prb*3*12*2; + + INFO("Init PDCCH: Max bits: %d\n", q->max_bits); + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + goto clean; + } + if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC16, 16)) { + goto clean; + } + + int poly[3] = { 0x6D, 0x4F, 0x57 }; + if (srslte_viterbi_init(&q->decoder, SRSLTE_VITERBI_37, poly, SRSLTE_DCI_MAX_BITS + 16, true)) { + goto clean; + } + + q->e = srslte_vec_malloc(sizeof(uint8_t) * q->max_bits); + if (!q->e) { + goto clean; + } + + q->llr = srslte_vec_malloc(sizeof(float) * q->max_bits); + if (!q->llr) { + goto clean; + } + + bzero(q->llr, sizeof(float) * q->max_bits); + + q->d = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->d) { + goto clean; + } + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->x[i]) { + goto clean; + } + q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->symbols[i]) { + goto clean; + } + if (q->is_ue) { + for (int j = 0; j < q->nof_rx_antennas; j++) { + q->ce[i][j] = srslte_vec_malloc(sizeof(cf_t) * q->max_bits / 2); + if (!q->ce[i][j]) { + goto clean; + } + } + } + } + + ret = SRSLTE_SUCCESS; + } + clean: + if (ret == SRSLTE_ERROR) { + srslte_pdcch_free(q); + } + return ret; +} + +int srslte_pdcch_init_enb(srslte_pdcch_t *q, uint32_t max_prb) { + return pdcch_init(q, max_prb, 0, false); +} + +int srslte_pdcch_init_ue(srslte_pdcch_t *q, uint32_t max_prb, uint32_t nof_rx_antennas) { + return pdcch_init(q, max_prb, nof_rx_antennas, true); +} + +void srslte_pdcch_free(srslte_pdcch_t *q) { + + if (q->e) { + free(q->e); + } + if (q->llr) { + free(q->llr); + } + if (q->d) { + free(q->d); + } + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->x[i]) { + free(q->x[i]); + } + if (q->symbols[i]) { + free(q->symbols[i]); + } + if (q->is_ue) { + for (int j=0;jnof_rx_antennas;j++) { + if (q->ce[i][j]) { + free(q->ce[i][j]); + } + } + } + } + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + srslte_sequence_free(&q->seq[i]); + } + + srslte_modem_table_free(&q->mod); + srslte_viterbi_free(&q->decoder); + + bzero(q, sizeof(srslte_pdcch_t)); + +} + +int srslte_pdcch_set_cell(srslte_pdcch_t *q, srslte_regs_t *regs, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + regs != NULL && + srslte_cell_isvalid(&cell)) + { + q->regs = regs; + + for (int cfi=0;cfi<3;cfi++) { + q->nof_regs[cfi] = (srslte_regs_pdcch_nregs(q->regs, cfi+1) / 9) * 9; + q->nof_cce[cfi] = q->nof_regs[cfi]/ 9; + } + + /* Allocate memory for the maximum number of PDCCH bits (CFI=3) */ + q->max_bits = (NOF_REGS(3)/ 9) * 72; + + INFO("PDCCH: Cell config PCI=%d, %d ports.\n", + q->cell.id, q->cell.nof_ports); + + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + // we need to pregenerate the sequence for the maximum number of bits, which is 8 times + // the maximum number of REGs (for CFI=3) + if (srslte_sequence_pdcch(&q->seq[i], 2 * i, q->cell.id, 8*srslte_regs_pdcch_nregs(q->regs, 3))) { + return SRSLTE_ERROR; + } + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +uint32_t srslte_pdcch_ue_locations(srslte_pdcch_t *q, srslte_dci_location_t *c, uint32_t max_candidates, + uint32_t nsubframe, uint32_t cfi, uint16_t rnti) +{ + return srslte_pdcch_ue_locations_ncce(NOF_CCE(cfi), c, max_candidates, nsubframe, rnti); +} + + +uint32_t srslte_pdcch_ue_locations_ncce(uint32_t nof_cce, srslte_dci_location_t *c, uint32_t max_candidates, + uint32_t nsubframe, uint16_t rnti) +{ + return srslte_pdcch_ue_locations_ncce_L(nof_cce, c, max_candidates, nsubframe, rnti, -1); +} + +/** 36.213 v9.1.1 + * Computes up to max_candidates UE-specific candidates for DCI messages and saves them + * in the structure pointed by c. + * Returns the number of candidates saved in the array c. + */ +uint32_t srslte_pdcch_ue_locations_ncce_L(uint32_t nof_cce, srslte_dci_location_t *c, uint32_t max_candidates, + uint32_t nsubframe, uint16_t rnti, int Ls) { + + int l; // this must be int because of the for(;;--) loop + uint32_t i, k, L, m; + uint32_t Yk, ncce; + const int nof_candidates[4] = { 6, 6, 2, 2}; + + // Compute Yk for this subframe + Yk = rnti; + for (m = 0; m < nsubframe+1; m++) { + Yk = (39827 * Yk) % 65537; + } + + k = 0; + // All aggregation levels from 8 to 1 + for (l = 3; l >= 0; l--) { + L = (1 << l); + if (Ls<0 || Ls==L) { + // For all candidates as given in table 9.1.1-1 + for (i = 0; i < nof_candidates[l]; i++) { + if (nof_cce >= L) { + ncce = L * ((Yk + i) % (nof_cce / L)); + // Check if candidate fits in c vector and in CCE region + if (k < max_candidates && ncce + L <= nof_cce) + { + c[k].L = l; + c[k].ncce = ncce; + + DEBUG("UE-specific SS Candidate %d: nCCE: %d, L: %d\n", + k, c[k].ncce, c[k].L); + + k++; + } + } + } + } + } + + DEBUG("Initiated %d candidate(s) in the UE-specific search space for C-RNTI: 0x%x, nsubframe=%d, nof_cce=%d\n", + k, rnti, nsubframe, nof_cce); + + return k; +} + + + +/** + * 36.213 9.1.1 + * Computes up to max_candidates candidates in the common search space + * for DCI messages and saves them in the structure pointed by c. + * Returns the number of candidates saved in the array c. + */ +uint32_t srslte_pdcch_common_locations(srslte_pdcch_t *q, srslte_dci_location_t *c, uint32_t max_candidates, + uint32_t cfi) +{ + return srslte_pdcch_common_locations_ncce(NOF_CCE(cfi), c, max_candidates); +} + +uint32_t srslte_pdcch_common_locations_ncce(uint32_t nof_cce, srslte_dci_location_t *c, uint32_t max_candidates) +{ + uint32_t i, l, L, k; + + k = 0; + for (l = 3; l > 1; l--) { + L = (1 << l); + for (i = 0; i < SRSLTE_MIN(nof_cce, 16) / (L); i++) { + uint32_t ncce = (L) * (i % (nof_cce / (L))); + if (k < max_candidates && ncce + L <= nof_cce) { + c[k].L = l; + c[k].ncce = ncce; + DEBUG("Common SS Candidate %d: nCCE: %d, L: %d\n", + k, c[k].ncce, c[k].L); + k++; + } + } + } + + INFO("Initiated %d candidate(s) in the Common search space\n", k); + + return k; +} + + + + + + +/** 36.212 5.3.3.2 to 5.3.3.4 + * + * Returns XOR between parity and remainder bits + * + * TODO: UE transmit antenna selection CRC mask + */ +int srslte_pdcch_dci_decode(srslte_pdcch_t *q, float *e, uint8_t *data, uint32_t E, uint32_t nof_bits, uint16_t *crc) { + + uint16_t p_bits, crc_res; + uint8_t *x; + + if (q != NULL) { + if (data != NULL && + E <= q->max_bits && + nof_bits <= SRSLTE_DCI_MAX_BITS) + { + bzero(q->rm_f, sizeof(float)*3 * (SRSLTE_DCI_MAX_BITS + 16)); + + uint32_t coded_len = 3 * (nof_bits + 16); + + /* unrate matching */ + srslte_rm_conv_rx(e, E, q->rm_f, coded_len); + + /* viterbi decoder */ + srslte_viterbi_decode_f(&q->decoder, q->rm_f, data, nof_bits + 16); + + x = &data[nof_bits]; + p_bits = (uint16_t) srslte_bit_pack(&x, 16); + crc_res = ((uint16_t) srslte_crc_checksum(&q->crc, data, nof_bits) & 0xffff); + + if (crc) { + *crc = p_bits ^ crc_res; + } + + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters: E: %d, max_bits: %d, nof_bits: %d\n", E, q->max_bits, nof_bits); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/** Tries to decode a DCI message from the LLRs stored in the srslte_pdcch_t structure by the function + * srslte_pdcch_extract_llr(). This function can be called multiple times. + * The decoded message is stored in msg and the CRC remainder in crc_rem pointer + * + */ +int srslte_pdcch_decode_msg(srslte_pdcch_t *q, + srslte_dci_msg_t *msg, + srslte_dci_location_t *location, + srslte_dci_format_t format, + uint32_t cfi, + uint16_t *crc_rem) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + msg != NULL && + srslte_dci_location_isvalid(location)) + { + if (location->ncce * 72 + PDCCH_FORMAT_NOF_BITS(location->L) > + NOF_CCE(cfi)*72) { + fprintf(stderr, "Invalid location: nCCE: %d, L: %d, NofCCE: %d\n", + location->ncce, location->L, NOF_CCE(cfi)); + } else { + ret = SRSLTE_SUCCESS; + + uint32_t nof_bits = srslte_dci_format_sizeof(format, q->cell.nof_prb, q->cell.nof_ports); + uint32_t e_bits = PDCCH_FORMAT_NOF_BITS(location->L); + + double mean = 0; + for (int i=0;illr[location->ncce * 72 + i]); + } + mean /= e_bits; + if (mean > 0.5) { + ret = srslte_pdcch_dci_decode(q, &q->llr[location->ncce * 72], + msg->data, e_bits, nof_bits, crc_rem); + if (ret == SRSLTE_SUCCESS) { + msg->nof_bits = nof_bits; + // Check format differentiation + if (format == SRSLTE_DCI_FORMAT0 || format == SRSLTE_DCI_FORMAT1A) { + msg->format = (msg->data[0] == 0)?SRSLTE_DCI_FORMAT0:SRSLTE_DCI_FORMAT1A; + } else { + msg->format = format; + } + } else { + fprintf(stderr, "Error calling pdcch_dci_decode\n"); + } + if (crc_rem) { + DEBUG("Decoded DCI: nCCE=%d, L=%d, format=%s, msg_len=%d, mean=%f, crc_rem=0x%x\n", + location->ncce, location->L, srslte_dci_format_string(format), nof_bits, mean, *crc_rem); + } + } else { + DEBUG("Skipping DCI: nCCE=%d, L=%d, msg_len=%d, mean=%f\n", + location->ncce, location->L, nof_bits, mean); + } + } + } else { + fprintf(stderr, "Invalid parameters, location=%d,%d\n", location->ncce, location->L); + } + return ret; +} + +int cnt=0; + +int srslte_pdcch_extract_llr(srslte_pdcch_t *q, cf_t *sf_symbols, cf_t *ce[SRSLTE_MAX_PORTS], float noise_estimate, + uint32_t nsubframe, uint32_t cfi) +{ + cf_t *_sf_symbols[SRSLTE_MAX_PORTS]; + cf_t *_ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + + _sf_symbols[0] = sf_symbols; + for (int i=0;icell.nof_ports;i++) { + _ce[i][0] = ce[i]; + } + return srslte_pdcch_extract_llr_multi(q, _sf_symbols, _ce, noise_estimate, nsubframe, cfi); +} + +/** Extracts the LLRs from srslte_dci_location_t location of the subframe and stores them in the srslte_pdcch_t structure. + * DCI messages can be extracted from this location calling the function srslte_pdcch_decode_msg(). + * Every time this function is called (with a different location), the last demodulated symbols are overwritten and + * new messages from other locations can be decoded + */ +int srslte_pdcch_extract_llr_multi(srslte_pdcch_t *q, cf_t *sf_symbols[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, + uint32_t nsubframe, uint32_t cfi) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + /* Set pointers for layermapping & precoding */ + uint32_t i, nof_symbols; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && + nsubframe < 10 && + cfi > 0 && + cfi < 4) + { + + uint32_t e_bits = 72*NOF_CCE(cfi); + nof_symbols = e_bits/2; + ret = SRSLTE_ERROR; + bzero(q->llr, sizeof(float) * q->max_bits); + + DEBUG("Extracting LLRs: E: %d, SF: %d, CFI: %d\n", + e_bits, nsubframe, cfi); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + /* extract symbols */ + for (int j=0;jnof_rx_antennas;j++) { + int n = srslte_regs_pdcch_get(q->regs, cfi, sf_symbols[j], q->symbols[j]); + if (nof_symbols != n) { + fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", nof_symbols, n); + return ret; + } + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + n = srslte_regs_pdcch_get(q->regs, cfi, ce[i][j], q->ce[i][j]); + if (nof_symbols != n) { + fprintf(stderr, "Expected %d PDCCH symbols but got %d symbols\n", nof_symbols, n); + return ret; + } + } + } + + /* in control channels, only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + srslte_predecoding_single_multi(q->symbols, q->ce[0], q->d, NULL, q->nof_rx_antennas, nof_symbols, 1.0f, noise_estimate/2); + } else { + srslte_predecoding_diversity_multi(q->symbols, q->ce, x, NULL, q->nof_rx_antennas, q->cell.nof_ports, nof_symbols, 1.0f); + srslte_layerdemap_diversity(x, q->d, q->cell.nof_ports, nof_symbols / q->cell.nof_ports); + } + + /* demodulate symbols */ + srslte_demod_soft_demodulate(SRSLTE_MOD_QPSK, q->d, q->llr, nof_symbols); + + /* descramble */ + srslte_scrambling_f_offset(&q->seq[nsubframe], q->llr, 0, e_bits); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + + + +static void crc_set_mask_rnti(uint8_t *crc, uint16_t rnti) { + uint32_t i; + uint8_t mask[16]; + uint8_t *r = mask; + + DEBUG("Mask CRC with RNTI 0x%x\n", rnti); + + srslte_bit_unpack(rnti, &r, 16); + for (i = 0; i < 16; i++) { + crc[i] = (crc[i] + mask[i]) % 2; + } +} + +void srslte_pdcch_dci_encode_conv(srslte_pdcch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t *coded_data, uint16_t rnti) { + srslte_convcoder_t encoder; + int poly[3] = { 0x6D, 0x4F, 0x57 }; + encoder.K = 7; + encoder.R = 3; + encoder.tail_biting = true; + memcpy(encoder.poly, poly, 3 * sizeof(int)); + + srslte_crc_attach(&q->crc, data, nof_bits); + crc_set_mask_rnti(&data[nof_bits], rnti); + + srslte_convcoder_encode(&encoder, data, coded_data, nof_bits + 16); +} + +/** 36.212 5.3.3.2 to 5.3.3.4 + * TODO: UE transmit antenna selection CRC mask + */ +int srslte_pdcch_dci_encode(srslte_pdcch_t *q, uint8_t *data, uint8_t *e, uint32_t nof_bits, uint32_t E, + uint16_t rnti) +{ + uint8_t tmp[3 * (SRSLTE_DCI_MAX_BITS + 16)]; + + if (q != NULL && + data != NULL && + e != NULL && + nof_bits < SRSLTE_DCI_MAX_BITS && + E < q->max_bits) + { + + srslte_pdcch_dci_encode_conv(q, data, nof_bits, tmp, rnti); + + DEBUG("CConv output: "); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, tmp, 3 * (nof_bits + 16)); + } + + srslte_rm_conv_tx(tmp, 3 * (nof_bits + 16), e, E); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/** Encodes ONE DCI message and allocates the encoded bits to the srslte_dci_location_t indicated by + * the parameter location. The CRC is scrambled with the RNTI parameter. + * This function can be called multiple times and encoded DCI messages will be allocated to the + * sf_symbols buffer ready for transmission. + * If the same location is provided in multiple messages, the encoded bits will be overwritten. + * + * @TODO: Use a bitmask and CFI to ensure message locations are valid and old messages are not overwritten. + */ +int srslte_pdcch_encode(srslte_pdcch_t *q, srslte_dci_msg_t *msg, srslte_dci_location_t location, uint16_t rnti, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], uint32_t nsubframe, uint32_t cfi) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t i; + cf_t *x[SRSLTE_MAX_LAYERS]; + uint32_t nof_symbols; + + if (q != NULL && + sf_symbols != NULL && + nsubframe < 10 && + cfi > 0 && + cfi < 4 && + srslte_dci_location_isvalid(&location)) + { + + uint32_t e_bits = PDCCH_FORMAT_NOF_BITS(location.L); + nof_symbols = e_bits/2; + ret = SRSLTE_ERROR; + + if (location.ncce + PDCCH_FORMAT_NOF_CCE(location.L) <= NOF_CCE(cfi) && + msg->nof_bits < SRSLTE_DCI_MAX_BITS - 16) + { + DEBUG("Encoding DCI: Nbits: %d, E: %d, nCCE: %d, L: %d, RNTI: 0x%x\n", + msg->nof_bits, e_bits, location.ncce, location.L, rnti); + + srslte_pdcch_dci_encode(q, msg->data, q->e, msg->nof_bits, e_bits, rnti); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + srslte_scrambling_b_offset(&q->seq[nsubframe], q->e, 72 * location.ncce, e_bits); + + DEBUG("Scrambling output: "); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, q->e, e_bits); + } + + srslte_mod_modulate(&q->mod, q->e, q->d, e_bits); + + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d, x, q->cell.nof_ports, nof_symbols); + srslte_precoding_diversity(x, q->symbols, q->cell.nof_ports, nof_symbols / q->cell.nof_ports, 1.0f); + } else { + memcpy(q->symbols[0], q->d, nof_symbols * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + srslte_regs_pdcch_put_offset(q->regs, cfi, q->symbols[i], sf_symbols[i], + location.ncce * 9, PDCCH_FORMAT_NOF_REGS(location.L)); + } + + ret = SRSLTE_SUCCESS; + + } else { + fprintf(stderr, "Illegal DCI message nCCE: %d, L: %d, nof_cce: %d, nof_bits=%d\n", location.ncce, location.L, NOF_CCE(cfi), msg->nof_bits); + } + } else { + fprintf(stderr, "Invalid parameters: cfi=%d, L=%d, nCCE=%d\n", cfi, location.L, location.ncce); + } + return ret; +} + diff --git a/lib/src/phy/phch/pdsch.c b/lib/src/phy/phch/pdsch.c new file mode 100644 index 0000000..b38cfdb --- /dev/null +++ b/lib/src/phy/phch/pdsch.c @@ -0,0 +1,1117 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "prb_dl.h" +#include "srslte/phy/phch/pdsch.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + + +#define MAX_PDSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12) + + +const static srslte_mod_t modulations[4] = + { SRSLTE_MOD_BPSK, SRSLTE_MOD_QPSK, SRSLTE_MOD_16QAM, SRSLTE_MOD_64QAM }; + +//#define DEBUG_IDX + +#ifdef DEBUG_IDX +cf_t *offset_original=NULL; +extern int indices[100000]; +extern int indices_ptr; +#endif + + + +typedef struct { + /* Thread identifier: they must set before thread creation */ + pthread_t pthread; + uint32_t cw_idx; + uint32_t tb_idx; + void *pdsch_ptr; + bool *ack; + + /* Configuration Encoder/Decoder: they must be set before posting start semaphore */ + srslte_pdsch_cfg_t *cfg; + srslte_sch_t dl_sch; + uint16_t rnti; + + /* Encoder/Decoder data pointers: they must be set before posting start semaphore */ + uint8_t *data; + void *softbuffer; + + /* Execution status */ + int ret_status; + + /* Semaphores */ + sem_t start; + sem_t finish; + + /* Thread flags */ + bool started; + bool quit; +} srslte_pdsch_coworker_t; + +static void *srslte_pdsch_decode_thread (void *arg); + +int srslte_pdsch_cp(srslte_pdsch_t *q, cf_t *input, cf_t *output, srslte_ra_dl_grant_t *grant, uint32_t lstart_grant, uint32_t nsubframe, bool put) +{ + uint32_t s, n, l, lp, lstart, lend, nof_refs; + bool is_pbch, is_sss; + cf_t *in_ptr = input, *out_ptr = output; + uint32_t offset = 0; + +#ifdef DEBUG_IDX + indices_ptr = 0; + if (put) { + offset_original = output; + } else { + offset_original = input; + } +#endif + + if (q->cell.nof_ports == 1) { + nof_refs = 2; + } else { + nof_refs = 4; + } + + for (s = 0; s < 2; s++) { + for (l = 0; l < SRSLTE_CP_NSYMB(q->cell.cp); l++) { + for (n = 0; n < q->cell.nof_prb; n++) { + + // If this PRB is assigned + if (grant->prb_idx[s][n]) { + if (s == 0) { + lstart = lstart_grant; + } else { + lstart = 0; + } + lend = SRSLTE_CP_NSYMB(q->cell.cp); + is_pbch = is_sss = false; + + // Skip PSS/SSS signals + if (s == 0 && (nsubframe == 0 || nsubframe == 5)) { + if (n >= q->cell.nof_prb / 2 - 3 + && n < q->cell.nof_prb / 2 + 3 + (q->cell.nof_prb%2)) { + lend = SRSLTE_CP_NSYMB(q->cell.cp) - 2; + is_sss = true; + } + } + // Skip PBCH + if (s == 1 && nsubframe == 0) { + if (n >= q->cell.nof_prb / 2 - 3 + && n < q->cell.nof_prb / 2 + 3 + (q->cell.nof_prb%2)) { + lstart = 4; + is_pbch = true; + } + } + lp = l + s * SRSLTE_CP_NSYMB(q->cell.cp); + if (put) { + out_ptr = &output[(lp * q->cell.nof_prb + n) + * SRSLTE_NRE]; + } else { + in_ptr = &input[(lp * q->cell.nof_prb + n) + * SRSLTE_NRE]; + } + // This is a symbol in a normal PRB with or without references + if (l >= lstart && l < lend) { + if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { + if (nof_refs == 2) { + if (l == 0) { + offset = q->cell.id % 6; + } else { + offset = (q->cell.id + 3) % 6; + } + } else { + offset = q->cell.id % 3; + } + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs, put); + } else { + prb_cp(&in_ptr, &out_ptr, 1); + } + } + // This is a symbol in a PRB with PBCH or Synch signals (SS). + // If the number or total PRB is odd, half of the the PBCH or SS will fall into the symbol + if ((q->cell.nof_prb % 2) && ((is_pbch && l < lstart) || (is_sss && l >= lend))) { + if (n == q->cell.nof_prb / 2 - 3) { + if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put); + } else { + prb_cp_half(&in_ptr, &out_ptr, 1); + } + } else if (n == q->cell.nof_prb / 2 + 3) { + if (put) { + out_ptr += 6; + } else { + in_ptr += 6; + } + if (SRSLTE_SYMBOL_HAS_REF(l, q->cell.cp, q->cell.nof_ports)) { + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs/2, put); + } else { + prb_cp_half(&in_ptr, &out_ptr, 1); + } + } + } + } + } + } + } + + int r; + if (put) { + r = abs((int) (input - in_ptr)); + } else { + r = abs((int) (output - out_ptr)); + } + + return r; +} + +/** + * Puts PDSCH in slot number 1 + * + * Returns the number of symbols written to sf_symbols + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_pdsch_put(srslte_pdsch_t *q, cf_t *symbols, cf_t *sf_symbols, + srslte_ra_dl_grant_t *grant, uint32_t lstart, uint32_t subframe) +{ + return srslte_pdsch_cp(q, symbols, sf_symbols, grant, lstart, subframe, true); +} + +/** + * Extracts PDSCH from slot number 1 + * + * Returns the number of symbols written to PDSCH + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_pdsch_get(srslte_pdsch_t *q, cf_t *sf_symbols, cf_t *symbols, + srslte_ra_dl_grant_t *grant, uint32_t lstart, uint32_t subframe) +{ + return srslte_pdsch_cp(q, sf_symbols, symbols, grant, lstart, subframe, false); +} + +/** Initializes the PDSCH transmitter and receiver */ +static int pdsch_init(srslte_pdsch_t *q, uint32_t max_prb, bool is_ue, uint32_t nof_antennas) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + + bzero(q, sizeof(srslte_pdsch_t)); + ret = SRSLTE_ERROR; + + q->max_re = max_prb * MAX_PDSCH_RE(q->cell.cp); + q->is_ue = is_ue; + q->nof_rx_antennas = nof_antennas; + + INFO("Init PDSCH: %d PRBs, max_symbols: %d\n", max_prb, q->max_re); + + for (int i = 0; i < 4; i++) { + if (srslte_modem_table_lte(&q->mod[i], modulations[i])) { + goto clean; + } + srslte_modem_table_bytes(&q->mod[i]); + } + + if (srslte_sch_init(&q->dl_sch)) { + ERROR("Initiating DL SCH"); + goto clean; + } + + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + // Allocate int16_t for reception (LLRs) + q->e[i] = srslte_vec_malloc(sizeof(int16_t) * q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM)); + if (!q->e[i]) { + goto clean; + } + + q->d[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->d[i]) { + goto clean; + } + } + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->x[i]) { + goto clean; + } + q->symbols[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->symbols[i]) { + goto clean; + } + if (q->is_ue) { + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + q->ce[i][j] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->ce[i][j]) { + goto clean; + } + } + } + } + + q->users = calloc(sizeof(srslte_pdsch_user_t*), q->is_ue?1:(1+SRSLTE_SIRNTI)); + if (!q->users) { + perror("malloc"); + goto clean; + } + + if (srslte_sequence_init(&q->tmp_seq, q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) { + goto clean; + } + + ret = SRSLTE_SUCCESS; + } + + clean: + if (ret == SRSLTE_ERROR) { + srslte_pdsch_free(q); + } + return ret; +} + +int srslte_pdsch_init_ue(srslte_pdsch_t *q, uint32_t max_prb, uint32_t nof_antennas) +{ + return pdsch_init(q, max_prb, true, nof_antennas); +} + +int srslte_pdsch_init_enb(srslte_pdsch_t *q, uint32_t max_prb) +{ + return pdsch_init(q, max_prb, false, 0); +} + +static void srslte_pdsch_disable_coworker(srslte_pdsch_t *q) { + srslte_pdsch_coworker_t *h = (srslte_pdsch_coworker_t *) q->coworker_ptr; + if (h) { + /* Stop threads */ + h->quit = true; + sem_post(&h->start); + + pthread_join(h->pthread, NULL); + pthread_detach(h->pthread); + + srslte_sch_free(&h->dl_sch); + + free(h); + + q->coworker_ptr = NULL; + } +} + +void srslte_pdsch_free(srslte_pdsch_t *q) { + srslte_pdsch_disable_coworker(q); + + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + + if (q->e[i]) { + free(q->e[i]); + } + + if (q->d[i]) { + free(q->d[i]); + } + + if (q->csi[i]) { + free(q->csi[i]); + } + } + + /* Free sch objects */ + srslte_sch_free(&q->dl_sch); + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->x[i]) { + free(q->x[i]); + } + if (q->symbols[i]) { + free(q->symbols[i]); + } + if (q->is_ue) { + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + if (q->ce[i][j]) { + free(q->ce[i][j]); + } + } + } + } + if (q->users) { + if (q->is_ue) { + srslte_pdsch_free_rnti(q, 0); + } else { + for (int u=0;u<=SRSLTE_SIRNTI;u++) { + if (q->users[u]) { + srslte_pdsch_free_rnti(q, u); + } + } + } + free(q->users); + } + + srslte_sequence_free(&q->tmp_seq); + + for (int i = 0; i < 4; i++) { + srslte_modem_table_free(&q->mod[i]); + } + + bzero(q, sizeof(srslte_pdsch_t)); +} + +int srslte_pdsch_set_cell(srslte_pdsch_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + q->max_re = q->cell.nof_prb * MAX_PDSCH_RE(q->cell.cp); + + INFO("PDSCH: Cell config PCI=%d, %d ports, %d PRBs, max_symbols: %d\n", + q->cell.id, q->cell.nof_ports, q->cell.nof_prb, q->max_re); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Precalculate the PDSCH scramble sequences for a given RNTI. This function takes a while + * to execute, so shall be called once the final C-RNTI has been allocated for the session. + */ +int srslte_pdsch_set_rnti(srslte_pdsch_t *q, uint16_t rnti) { + uint32_t rnti_idx = q->is_ue?0:rnti; + + if (!q->users[rnti_idx] || q->is_ue) { + if (!q->users[rnti_idx]) { + q->users[rnti_idx] = calloc(1, sizeof(srslte_pdsch_user_t)); + if(!q->users[rnti_idx]) { + perror("calloc"); + return -1; + } + } + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) { + if (srslte_sequence_pdsch(&q->users[rnti_idx]->seq[j][i], rnti, j, 2 * i, q->cell.id, + q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) + { + fprintf(stderr, "Error initializing PDSCH scrambling sequence\n"); + srslte_pdsch_free_rnti(q, rnti); + return SRSLTE_ERROR; + } + } + } + q->ue_rnti = rnti; + q->users[rnti_idx]->cell_id = q->cell.id; + q->users[rnti_idx]->sequence_generated = true; + } else { + fprintf(stderr, "Error generating PDSCH sequence: rnti=0x%x already generated\n", rnti); + } + return SRSLTE_SUCCESS; +} + +void srslte_pdsch_set_power_allocation(srslte_pdsch_t *q, float rho_a) { + if (q) { + q->rho_a = rho_a; + } +} + +int srslte_pdsch_enable_csi(srslte_pdsch_t *q, bool enable) { + if (enable) { + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (!q->csi[i]) { + q->csi[i] = srslte_vec_malloc(sizeof(float) * q->max_re * 2); + if (!q->csi[i]) { + return SRSLTE_ERROR; + } + } + } + } + q->csi_enabled = enable; + + return SRSLTE_SUCCESS; +} + +void srslte_pdsch_free_rnti(srslte_pdsch_t* q, uint16_t rnti) +{ + uint32_t rnti_idx = q->is_ue?0:rnti; + if (q->users[rnti_idx]) { + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + for (int j = 0; j < SRSLTE_MAX_CODEWORDS; j++) { + srslte_sequence_free(&q->users[rnti_idx]->seq[j][i]); + } + } + free(q->users[rnti_idx]); + q->users[rnti_idx] = NULL; + q->ue_rnti = 0; + } +} + +static void pdsch_decode_debug(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]) +{ + if (SRSLTE_VERBOSE_ISDEBUG()) { + char filename[FILENAME_MAX]; + for (int j = 0; j < q->nof_rx_antennas; j++) { + if (snprintf(filename, FILENAME_MAX, "subframe_p%d.dat", j) < 0) { + ERROR("Generating file name"); + break; + } + DEBUG("SAVED FILE %s: received subframe symbols\n", filename); + srslte_vec_save_file(filename, sf_symbols[j], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + + for (int i = 0; i < q->cell.nof_ports; i++) { + if (snprintf(filename, FILENAME_MAX, "hest_%d%d.dat", i, j) < 0) { + ERROR("Generating file name"); + break; + } + DEBUG("SAVED FILE %s: channel estimates for Tx %d and Rx %d\n", filename, j, i); + srslte_vec_save_file(filename, ce[i][j], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + } + } + for (int i=0;inof_layers;i++) { + if (snprintf(filename, FILENAME_MAX, "pdsch_symbols_%d.dat", i) < 0) { + ERROR("Generating file name"); + break; + } + DEBUG("SAVED FILE %s: symbols after equalization\n", filename); + srslte_vec_save_file(filename, q->d[i], cfg->nbits[0].nof_re*sizeof(cf_t)); + + if (snprintf(filename, FILENAME_MAX, "llr_%d.dat", i) < 0) { + ERROR("Generating file name"); + break; + } + DEBUG("SAVED FILE %s: LLR estimates after demodulation and descrambling\n", filename); + srslte_vec_save_file(filename, q->e[i], cfg->nbits[0].nof_bits*sizeof(int16_t)); + } + } +} + + +/* Configures the structure srslte_pdsch_cfg_t from the DL DCI allocation dci_msg. + * If dci_msg is NULL, the grant is assumed to be already stored in cfg->grant + */ +int srslte_pdsch_cfg(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra_dl_grant_t *grant, uint32_t cfi, + uint32_t sf_idx, int rvidx) { + int _rvids[SRSLTE_MAX_CODEWORDS] = {1}; + _rvids[0] = rvidx; + + return srslte_pdsch_cfg_mimo(cfg, cell, grant, cfi, sf_idx, _rvids, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA, 0); +} + +/* Configures the structure srslte_pdsch_cfg_t from the DL DCI allocation dci_msg. + * If dci_msg is NULL, the grant is assumed to be already stored in cfg->grant + */ +int srslte_pdsch_cfg_mimo(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra_dl_grant_t *grant, uint32_t cfi, + uint32_t sf_idx, int rvidx[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type, + uint32_t pmi) { + if (cfg && grant) { + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(grant); + memcpy(&cfg->grant, grant, sizeof(srslte_ra_dl_grant_t)); + + + for (int cw = 0; cw < SRSLTE_MAX_CODEWORDS; cw++) { + if (grant->tb_en[cw]) { + if (srslte_cbsegm(&cfg->cb_segm[cw], (uint32_t) cfg->grant.mcs[cw].tbs)) { + fprintf(stderr, "Error computing Codeword (%d) segmentation for TBS=%d\n", cw, cfg->grant.mcs[cw].tbs); + return SRSLTE_ERROR; + } + } + } + srslte_ra_dl_grant_to_nbits(&cfg->grant, cfi, cell, sf_idx, cfg->nbits); + + cfg->sf_idx = sf_idx; + memcpy(cfg->rv, rvidx, sizeof(uint32_t) * SRSLTE_MAX_CODEWORDS); + cfg->mimo_type = mimo_type; + cfg->tb_cw_swap = grant->tb_cw_swap; + + /* Check and configure PDSCH transmission modes */ + switch(mimo_type) { + case SRSLTE_MIMO_TYPE_SINGLE_ANTENNA: + if (nof_tb != 1) { + ERROR("Wrong number of transport blocks (%d) for single antenna.", nof_tb); + return SRSLTE_ERROR; + } + cfg->nof_layers = 1; + break; + case SRSLTE_MIMO_TYPE_TX_DIVERSITY: + if (nof_tb != 1) { + ERROR("Wrong number of transport blocks (%d) for transmit diversity.", nof_tb); + return SRSLTE_ERROR; + } + cfg->nof_layers = cell.nof_ports; + break; + case SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX: + if (nof_tb == 1) { + cfg->codebook_idx = pmi; + cfg->nof_layers = 1; + } else if (nof_tb == 2) { + cfg->codebook_idx = pmi + 1; + cfg->nof_layers = 2; + } else { + ERROR("Wrong number of transport blocks (%d) for spatial multiplexing.", nof_tb); + return SRSLTE_ERROR; + } + INFO("PDSCH configured for Spatial Multiplex; nof_codewords=%d; nof_layers=%d; codebook_idx=%d;\n", + nof_tb, cfg->nof_layers, cfg->codebook_idx); + break; + case SRSLTE_MIMO_TYPE_CDD: + if (nof_tb != 2) { + ERROR("Wrong number of transport blocks (%d) for CDD.", nof_tb); + return SRSLTE_ERROR; + } + cfg->nof_layers = 2; + break; + } + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +static srslte_sequence_t *get_user_sequence(srslte_pdsch_t *q, uint16_t rnti, + uint32_t codeword_idx, uint32_t sf_idx, uint32_t len) +{ + uint32_t rnti_idx = q->is_ue?0:rnti; + + // The scrambling sequence is pregenerated for all RNTIs in the eNodeB but only for C-RNTI in the UE + if (q->users[rnti_idx] && q->users[rnti_idx]->sequence_generated && + q->users[rnti_idx]->cell_id == q->cell.id && + q->ue_rnti == rnti && + ((rnti >= SRSLTE_CRNTI_START && rnti < SRSLTE_CRNTI_END) || !q->is_ue)) + { + return &q->users[rnti_idx]->seq[codeword_idx][sf_idx]; + } else { + srslte_sequence_pdsch(&q->tmp_seq, rnti, codeword_idx, 2 * sf_idx, q->cell.id, len); + return &q->tmp_seq; + } +} + +static int srslte_pdsch_codeword_encode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, + srslte_softbuffer_tx_t *softbuffer, uint16_t rnti, uint8_t *data, + uint32_t codeword_idx, uint32_t tb_idx) { + srslte_ra_nbits_t *nbits = &cfg->nbits[tb_idx]; + srslte_ra_mcs_t *mcs = &cfg->grant.mcs[tb_idx]; + uint32_t rv = cfg->rv[tb_idx]; + bool valid_inputs = true; + + if (!softbuffer) { + ERROR("Error encoding (TB%d -> CW%d), softbuffer=NULL", tb_idx, codeword_idx); + valid_inputs = false; + } + + if (nbits->nof_bits && valid_inputs) { + INFO("Encoding PDSCH SF: %d (TB%d -> CW%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", + cfg->sf_idx, tb_idx, codeword_idx, srslte_mod_string(mcs->mod), mcs->tbs, + nbits->nof_re, nbits->nof_bits, rv); + + /* Channel coding */ + if (srslte_dlsch_encode2(&q->dl_sch, cfg, softbuffer, data, q->e[codeword_idx], tb_idx)) { + ERROR("Error encoding (TB%d -> CW%d)", tb_idx, codeword_idx); + return SRSLTE_ERROR; + } + + /* Select scrambling sequence */ + srslte_sequence_t *seq = get_user_sequence(q, rnti, codeword_idx, cfg->sf_idx, nbits->nof_bits); + + /* Bit scrambling */ + srslte_scrambling_bytes(seq, (uint8_t *) q->e[codeword_idx], nbits->nof_bits); + + /* Bit mapping */ + srslte_mod_modulate_bytes(&q->mod[mcs->mod], + (uint8_t *) q->e[codeword_idx], + q->d[codeword_idx], nbits->nof_bits); + + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + return SRSLTE_SUCCESS; +} + +static int srslte_pdsch_codeword_decode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, srslte_sch_t *dl_sch, + srslte_softbuffer_rx_t *softbuffer, uint16_t rnti, uint8_t *data, + uint32_t codeword_idx, uint32_t tb_idx, bool *ack) { + srslte_ra_nbits_t *nbits = &cfg->nbits[tb_idx]; + srslte_ra_mcs_t *mcs = &cfg->grant.mcs[tb_idx]; + uint32_t rv = cfg->rv[tb_idx]; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (softbuffer && data && ack && nbits->nof_bits && nbits->nof_re) { + INFO("Decoding PDSCH SF: %d (CW%d -> TB%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", + cfg->sf_idx, codeword_idx, tb_idx, srslte_mod_string(mcs->mod), mcs->tbs, + nbits->nof_re, nbits->nof_bits, rv); + + /* demodulate symbols + * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation, + * thus we don't need tot set it in the LLRs normalization + */ + srslte_demod_soft_demodulate_s(mcs->mod, q->d[codeword_idx], q->e[codeword_idx], nbits->nof_re); + + /* Select scrambling sequence */ + srslte_sequence_t *seq = get_user_sequence(q, rnti, codeword_idx, cfg->sf_idx, nbits->nof_bits); + + /* Bit scrambling */ + srslte_scrambling_s_offset(seq, q->e[codeword_idx], 0, nbits->nof_bits); + + uint32_t qm = 0; + switch(cfg->grant.mcs[tb_idx].mod) { + + case SRSLTE_MOD_BPSK: + qm = 1; + break; + case SRSLTE_MOD_QPSK: + qm = 2; + break; + case SRSLTE_MOD_16QAM: + qm = 4; + break; + case SRSLTE_MOD_64QAM: + qm = 6; + break; + default: + ERROR("No modulation"); + } + + int16_t *e = q->e[codeword_idx]; + + if (q->csi_enabled) { + const uint32_t csi_max_idx = srslte_vec_max_fi(q->csi[codeword_idx], nbits->nof_bits / qm); + float csi_max = 1.0f; + if (csi_max_idx < nbits->nof_bits / qm) { + csi_max = q->csi[codeword_idx][csi_max_idx]; + } + for (int i = 0; i < nbits->nof_bits / qm; i++) { + const float csi = q->csi[codeword_idx][i] / csi_max; + for (int k = 0; k < qm; k++) { + e[qm * i + k] = (int16_t) ((float) e[qm * i + k] * csi); + } + } + } + + /* Return */ + ret = srslte_dlsch_decode2(dl_sch, cfg, softbuffer, q->e[codeword_idx], data, tb_idx); + + q->last_nof_iterations[codeword_idx] = srslte_sch_last_noi(&q->dl_sch); + + if (ret == SRSLTE_SUCCESS) { + *ack = true; + } else if (ret == SRSLTE_ERROR) { + *ack = false; + ret = SRSLTE_SUCCESS; + } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { + *ack = false; + ret = SRSLTE_ERROR; + } + } else { + ERROR("Detected NULL pointer in TB%d &softbuffer=%p &data=%p &ack=%p, nbits=%d, nof_re=%d", + codeword_idx, softbuffer, (void*)data, ack, nbits->nof_bits, nbits->nof_re); + } + + return ret; +} + +static void *srslte_pdsch_decode_thread(void *arg) { + srslte_pdsch_coworker_t *q = (srslte_pdsch_coworker_t *) arg; + + INFO("[PDSCH Coworker] waiting for data\n"); + + sem_wait(&q->start); + while (!q->quit) { + q->ret_status = srslte_pdsch_codeword_decode(q->pdsch_ptr, + q->cfg, + &q->dl_sch, + q->softbuffer, + q->rnti, + q->data, + q->cw_idx, + q->tb_idx, + q->ack); + + /* Post finish semaphore */ + sem_post(&q->finish); + + /* Wait for next loop */ + sem_wait(&q->start); + } + sem_post(&q->finish); + + pthread_exit(NULL); + return q; +} + +/** Decodes the PDSCH from the received symbols + */ +int srslte_pdsch_decode(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffers[SRSLTE_MAX_CODEWORDS], + cf_t *sf_symbols[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], + float noise_estimate, uint16_t rnti, uint8_t *data[SRSLTE_MAX_CODEWORDS], + bool acks[SRSLTE_MAX_CODEWORDS]) +{ + + /* Set pointers for layermapping & precoding */ + uint32_t i; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && + sf_symbols != NULL && + data != NULL && + cfg != NULL) + { + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant); + + INFO("Decoding PDSCH SF: %d, RNTI: 0x%x, NofSymbols: %d, C_prb=%d, mimo_type=%s, nof_layers=%d, nof_tb=%d\n", + cfg->sf_idx, rnti, cfg->nbits[0].nof_re, cfg->grant.nof_prb, srslte_mod_string(cfg->grant.mcs->mod), cfg->nof_layers, nof_tb); + + // Extract Symbols and Channel Estimates + for (int j=0;jnof_rx_antennas;j++) { + int n = srslte_pdsch_get(q, sf_symbols[j], q->symbols[j], &cfg->grant, cfg->nbits[0].lstart, cfg->sf_idx); + if (n != cfg->nbits[0].nof_re) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits[0].nof_re, n); + return SRSLTE_ERROR; + } + + for (i = 0; i < q->cell.nof_ports; i++) { + n = srslte_pdsch_get(q, ce[i][j], q->ce[i][j], &cfg->grant, cfg->nbits[0].lstart, cfg->sf_idx); + if (n != cfg->nbits[0].nof_re) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits[0].nof_re, n); + return SRSLTE_ERROR; + } + } + } + + // Prepare layers + int nof_symbols [SRSLTE_MAX_CODEWORDS]; + nof_symbols[0] = cfg->nbits[0].nof_re * nof_tb / cfg->nof_layers; + nof_symbols[1] = cfg->nbits[1].nof_re * nof_tb / cfg->nof_layers; + + if (cfg->nof_layers == nof_tb) { + /* Skip layer demap */ + for (i = 0; i < cfg->nof_layers; i++) { + x[i] = q->d[i]; + } + } else { + /* number of layers equals number of ports */ + for (i = 0; i < cfg->nof_layers; i++) { + x[i] = q->x[i]; + } + memset(&x[cfg->nof_layers], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - cfg->nof_layers)); + } + + float pdsch_scaling = 1.0f; + if (q->rho_a != 0.0f) { + pdsch_scaling = q->rho_a; + } + + // Pre-decoder + if (srslte_predecoding_type(q->symbols, q->ce, x, q->csi, q->nof_rx_antennas, q->cell.nof_ports, cfg->nof_layers, + cfg->codebook_idx, cfg->nbits[0].nof_re, cfg->mimo_type, pdsch_scaling, noise_estimate)<0) { + DEBUG("Error predecoding\n"); + return SRSLTE_ERROR; + } + + // Layer demapping only if necessary + if (cfg->nof_layers != nof_tb) { + srslte_layerdemap_type(x, q->d, cfg->nof_layers, nof_tb, + nof_symbols[0], nof_symbols, cfg->mimo_type); + } + + /* Codeword decoding: Implementation of 3GPP 36.212 Table 5.3.3.1.5-1 and Table 5.3.3.1.5-2 */ + uint32_t cw_idx = (nof_tb == SRSLTE_MAX_TB && cfg->tb_cw_swap) ? 1 : 0; + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + /* Decode only if transport block is enabled and the default ACK is not true */ + if (cfg->grant.tb_en[tb_idx]) { + if (!acks[tb_idx]) { + int ret = SRSLTE_SUCCESS; + if (SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant) > 1 && tb_idx == 0 && q->coworker_ptr) { + srslte_pdsch_coworker_t *h = (srslte_pdsch_coworker_t *) q->coworker_ptr; + + h->pdsch_ptr = q; + h->cfg = cfg; + h->softbuffer = softbuffers[tb_idx]; + h->rnti = rnti; + h->data = data[tb_idx]; + h->cw_idx = cw_idx; + h->tb_idx = tb_idx; + h->ack = &acks[tb_idx]; + h->dl_sch.max_iterations = q->dl_sch.max_iterations; + h->started = true; + sem_post(&h->start); + + } else { + ret = srslte_pdsch_codeword_decode(q, + cfg, + &q->dl_sch, + softbuffers[tb_idx], + rnti, + data[tb_idx], + cw_idx, + tb_idx, + &acks[tb_idx]); + } + + /* Check if there has been any execution error */ + if (ret) { + /* Do Nothing */ + } + } + + cw_idx = (cw_idx + 1) % SRSLTE_MAX_CODEWORDS; + } + } + + if (q->coworker_ptr) { + srslte_pdsch_coworker_t *h = (srslte_pdsch_coworker_t *) q->coworker_ptr; + if (h->started) { + int err = sem_wait(&h->finish); + if (err) { + printf("SCH coworker: %s (nof_tb=%d)\n", strerror(errno), SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant)); + } + if (h->ret_status) { + ERROR("PDSCH Coworker Decoder: Error decoding"); + } + + h->started = false; + } + } + + pdsch_decode_debug(q, cfg, sf_symbols, ce); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_pdsch_pmi_select(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, uint32_t nof_ce, + uint32_t pmi[SRSLTE_MAX_LAYERS], float sinr[SRSLTE_MAX_LAYERS][SRSLTE_MAX_CODEBOOKS]) { + + if (q->cell.nof_ports == 2 && q->nof_rx_antennas <= 2) { + int nof_layers = 1; + for (; nof_layers <= q->nof_rx_antennas; nof_layers++ ) { + if (sinr[nof_layers - 1] && pmi) { + if (srslte_precoding_pmi_select(ce, nof_ce, noise_estimate, nof_layers, &pmi[nof_layers - 1], + sinr[nof_layers - 1]) < 0) { + ERROR("PMI Select for %d layers", nof_layers); + return SRSLTE_ERROR; + } + } + } + + /* FIXME: Set other layers to 0 */ + for (; nof_layers <= SRSLTE_MAX_LAYERS; nof_layers++ ) { + if (sinr[nof_layers - 1] && pmi) { + for (int cb = 0; cb < SRSLTE_MAX_CODEBOOKS; cb++) { + sinr[nof_layers - 1][cb] = -INFINITY; + } + pmi[nof_layers - 1] = 0; + } + } + } else { + DEBUG("Not implemented configuration"); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + return SRSLTE_SUCCESS; +} + +int srslte_pdsch_cn_compute(srslte_pdsch_t *q, + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], uint32_t nof_ce, float *cn) { + return srslte_precoding_cn(ce, q->cell.nof_ports, q->nof_rx_antennas, nof_ce, cn); +} + +int srslte_pdsch_encode(srslte_pdsch_t *q, + srslte_pdsch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffers[SRSLTE_MAX_CODEWORDS], + uint8_t *data[SRSLTE_MAX_CODEWORDS], uint16_t rnti, cf_t *sf_symbols[SRSLTE_MAX_PORTS]) +{ + + int i; + /* Set pointers for layermapping & precoding */ + cf_t *x[SRSLTE_MAX_LAYERS]; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + cfg != NULL) { + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant); + + + for (i = 0; i < q->cell.nof_ports; i++) { + if (sf_symbols[i] == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + + /* If both transport block size is zero return error */ + if (!nof_tb) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (cfg->nbits[0].nof_re > q->max_re || cfg->nbits[1].nof_re > q->max_re) { + fprintf(stderr, + "Error too many RE per subframe (%d). PDSCH configured for %d RE (%d PRB)\n", + cfg->nbits[0].nof_re, q->max_re, q->cell.nof_prb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + /* Implementation of 3GPP 36.212 Table 5.3.3.1.5-1 and Table 5.3.3.1.5-2 */ + uint32_t cw_idx = (nof_tb == SRSLTE_MAX_TB && cfg->tb_cw_swap) ? 1 : 0; + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + if (cfg->grant.tb_en[tb_idx]) { + ret |= srslte_pdsch_codeword_encode(q, cfg, softbuffers[tb_idx], rnti, data[tb_idx], cw_idx, tb_idx); + cw_idx = (cw_idx + 1) % SRSLTE_MAX_CODEWORDS; + } + } + + /* Set scaling configured by Power Allocation */ + float scaling = 1.0f; + if (q->rho_a != 0.0f) { + scaling = q->rho_a; + } + + // Layer mapping & precode if necessary + if (q->cell.nof_ports > 1) { + int nof_symbols; + /* If number of layers is equal to transport blocks (codewords) skip layer mapping */ + if (cfg->nof_layers == nof_tb) { + for (i = 0; i < cfg->nof_layers; i++) { + x[i] = q->d[i]; + } + nof_symbols = cfg->nbits[0].nof_re; + } else { + /* Initialise layer map pointers */ + for (i = 0; i < cfg->nof_layers; i++) { + x[i] = q->x[i]; + } + memset(&x[cfg->nof_layers], 0, sizeof(cf_t *) * (SRSLTE_MAX_LAYERS - cfg->nof_layers)); + + nof_symbols = srslte_layermap_type(q->d, x, nof_tb, cfg->nof_layers, + (int[SRSLTE_MAX_CODEWORDS]) {cfg->nbits[0].nof_re, cfg->nbits[1].nof_re}, + cfg->mimo_type); + } + + /* Precode */ + srslte_precoding_type(x, q->symbols, cfg->nof_layers, q->cell.nof_ports, cfg->codebook_idx, + nof_symbols, scaling, cfg->mimo_type); + } else { + if (scaling == 1.0f) { + memcpy(q->symbols[0], q->d[0], cfg->nbits[0].nof_re * sizeof(cf_t)); + } else { + srslte_vec_sc_prod_cfc(q->d[0], scaling, q->symbols[0], cfg->nbits[0].nof_re); + } + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + srslte_pdsch_put(q, q->symbols[i], sf_symbols[i], &cfg->grant, cfg->nbits[0].lstart, cfg->sf_idx); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +void srslte_pdsch_set_max_noi(srslte_pdsch_t *q, uint32_t max_iter) { + srslte_sch_set_max_noi(&q->dl_sch, max_iter); +} + +float srslte_pdsch_last_noi(srslte_pdsch_t *q) { + float niters = 0; + int active_cw = 0; + for (int i=0;ilast_nof_iterations[i]) { + niters += q->last_nof_iterations[i]; + active_cw++; + } + } + if (active_cw) { + return niters/active_cw; + } else { + return 0; + } +} + +int srslte_pdsch_enable_coworker(srslte_pdsch_t *q) { + int ret = SRSLTE_SUCCESS; + + if (!q->coworker_ptr) { + srslte_pdsch_coworker_t *h = calloc(sizeof(srslte_pdsch_coworker_t), 1); + + if (!h) { + ERROR("Allocating coworker"); + ret = SRSLTE_ERROR; + goto clean; + } + q->coworker_ptr = h; + + if (srslte_sch_init(&h->dl_sch)) { + ERROR("Initiating DL SCH"); + ret = SRSLTE_ERROR; + goto clean; + } + + if (sem_init(&h->start, 0, 0)) { + ERROR("Creating semaphore"); + ret = SRSLTE_ERROR; + goto clean; + } + if (sem_init(&h->finish, 0, 0)) { + ERROR("Creating semaphore"); + ret = SRSLTE_ERROR; + goto clean; + } + pthread_create(&h->pthread, NULL, srslte_pdsch_decode_thread, (void *) h); + } + + clean: + if (ret) { + srslte_pdsch_disable_coworker(q); + } + return ret; +} + +uint32_t srslte_pdsch_last_noi_cw(srslte_pdsch_t *q, uint32_t cw_idx) { + return q->last_nof_iterations[cw_idx]; +} + + + diff --git a/lib/src/phy/phch/phich.c b/lib/src/phy/phch/phich.c new file mode 100644 index 0000000..21bfb04 --- /dev/null +++ b/lib/src/phy/phch/phich.c @@ -0,0 +1,425 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/phch/phich.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +/** Table 6.9.1-2 */ +const cf_t w_normal[SRSLTE_PHICH_NORM_NSEQUENCES][4] = { { 1, 1, 1, 1 }, + { 1, -1, 1, -1 }, { 1, 1, -1, -1 }, { 1, -1, -1, 1 }, { I, I, I, I }, { + I, -I, I, -I }, { I, I, -I, -I }, { I, -I, -I, I } }; +const cf_t w_ext[SRSLTE_PHICH_EXT_NSEQUENCES][2] = { { 1, 1 }, { 1, -1 }, { I, I }, { +I, -I } }; + + +uint32_t srslte_phich_ngroups(srslte_phich_t *q) { + return srslte_regs_phich_ngroups(q->regs); +} + +uint32_t srslte_phich_nsf(srslte_phich_t *q) { + if (SRSLTE_CP_ISNORM(q->cell.cp)) { + return SRSLTE_PHICH_NORM_NSF; + } else { + return SRSLTE_PHICH_EXT_NSF; + } +} + +void srslte_phich_reset(srslte_phich_t *q, cf_t *slot_symbols[SRSLTE_MAX_PORTS]) { + int i; + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + srslte_regs_phich_reset(q->regs, slot_symbols[i]); + } +} + +/** Initializes the phich channel receiver */ +int srslte_phich_init(srslte_phich_t *q, uint32_t nof_rx_antennas) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + + bzero(q, sizeof(srslte_phich_t)); + ret = SRSLTE_ERROR; + + q->nof_rx_antennas = nof_rx_antennas; + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_BPSK)) { + goto clean; + } + ret = SRSLTE_SUCCESS; + } + clean: + if (ret == SRSLTE_ERROR) { + srslte_phich_free(q); + } + return ret; +} + +void srslte_phich_free(srslte_phich_t *q) { + for (int ns = 0; ns < SRSLTE_NSUBFRAMES_X_FRAME; ns++) { + srslte_sequence_free(&q->seq[ns]); + } + srslte_modem_table_free(&q->mod); + + bzero(q, sizeof(srslte_phich_t)); +} + +int srslte_phich_set_cell(srslte_phich_t *q, srslte_regs_t *regs, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + regs != NULL && + srslte_cell_isvalid(&cell)) + { + + q->regs = regs; + + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + for (int nsf = 0; nsf < SRSLTE_NSUBFRAMES_X_FRAME; nsf++) { + if (srslte_sequence_phich(&q->seq[nsf], 2 * nsf, q->cell.id)) { + return SRSLTE_ERROR; + } + } + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + + + +/* Computes n_group and n_seq according to Section 9.1.2 in 36.213 */ +void srslte_phich_calc(srslte_phich_t *q, uint32_t n_prb_lowest, uint32_t n_dmrs, + uint32_t *ngroup, uint32_t *nseq) +{ + uint32_t Ngroups = srslte_phich_ngroups(q); + *ngroup = (n_prb_lowest+n_dmrs)%Ngroups; + *nseq = ((n_prb_lowest/Ngroups)+n_dmrs)%(2*srslte_phich_nsf(q)); +} + + +/* Decodes ACK + * + */ +uint8_t srslte_phich_ack_decode(float bits[SRSLTE_PHICH_NBITS], float *distance) { + int i; + float ack_table[2][3] = {{-1.0, -1.0, -1.0}, {1.0, 1.0, 1.0}}; + float max_corr = -9999; + uint8_t index=0; + + if (SRSLTE_VERBOSE_ISINFO()) { + INFO("Received bits: "); + srslte_vec_fprint_f(stdout, bits, SRSLTE_PHICH_NBITS); + } + + for (i = 0; i < 2; i++) { + float corr = srslte_vec_dot_prod_fff(ack_table[i], bits, SRSLTE_PHICH_NBITS); + INFO("Corr%d=%f\n", i, corr); + if (corr > max_corr) { + max_corr = corr; + if (distance) { + *distance = max_corr; + } + index = i; + } + } + return index; +} + +/** Encodes the ACK + * 36.212 + */ +void srslte_phich_ack_encode(uint8_t ack, uint8_t bits[SRSLTE_PHICH_NBITS]) { + memset(bits, ack, 3 * sizeof(uint8_t)); +} + +int srslte_phich_decode(srslte_phich_t *q, cf_t *sf_symbols[SRSLTE_MAX_PORTS], + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, + uint32_t ngroup, uint32_t nseq, uint32_t subframe, uint8_t *ack, float *distance) { + + /* Set pointers for layermapping & precoding */ + int i, j; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q == NULL || sf_symbols == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (subframe >= SRSLTE_NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", subframe); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + if (nseq >= SRSLTE_PHICH_EXT_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } else { + if (nseq >= SRSLTE_PHICH_NORM_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + if (ngroup >= srslte_regs_phich_ngroups(q->regs)) { + fprintf(stderr, "Invalid ngroup %d\n", ngroup); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + DEBUG("Decoding PHICH Ngroup: %d, Nseq: %d\n", ngroup, nseq); + + /* number of layers equals number of ports */ + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + x[i] = q->x[i]; + } + + cf_t *q_ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + cf_t *q_sf_symbols[SRSLTE_MAX_PORTS]; + + /* extract symbols */ + for (int j=0;jnof_rx_antennas;j++) { + if (SRSLTE_PHICH_MAX_NSYMB + != srslte_regs_phich_get(q->regs, sf_symbols[j], q->sf_symbols[j], ngroup)) { + fprintf(stderr, "There was an error getting the phich symbols\n"); + return SRSLTE_ERROR; + } + q_sf_symbols[j] = q->sf_symbols[j]; + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (SRSLTE_PHICH_MAX_NSYMB != srslte_regs_phich_get(q->regs, ce[i][j], q->ce[i][j], ngroup)) { + fprintf(stderr, "There was an error getting the phich symbols\n"); + return SRSLTE_ERROR; + } + q_ce[i][j] = q->ce[i][j]; + } + + } + + /* in control channels, only diversity is supported */ + if (q->cell.nof_ports == 1) { + /* no need for layer demapping */ + srslte_predecoding_single_multi(q_sf_symbols, q_ce[0], q->d0, NULL, q->nof_rx_antennas, SRSLTE_PHICH_MAX_NSYMB, 1.0f, noise_estimate); + } else { + srslte_predecoding_diversity_multi(q_sf_symbols, q_ce, x, NULL, q->nof_rx_antennas, q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB, 1.0f); + srslte_layerdemap_diversity(x, q->d0, q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB / q->cell.nof_ports); + } + DEBUG("Recv!!: \n"); + DEBUG("d0: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->d0, SRSLTE_PHICH_MAX_NSYMB); + + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + if (ngroup % 2) { + for (i = 0; i < SRSLTE_PHICH_EXT_MSYMB / 2; i++) { + q->d[2 * i + 0] = q->d0[4 * i + 2]; + q->d[2 * i + 1] = q->d0[4 * i + 3]; + } + } else { + for (i = 0; i < SRSLTE_PHICH_EXT_MSYMB / 2; i++) { + q->d[2 * i + 0] = q->d0[4 * i]; + q->d[2 * i + 1] = q->d0[4 * i + 1]; + } + } + } else { + memcpy(q->d, q->d0, SRSLTE_PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + DEBUG("d: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->d, SRSLTE_PHICH_EXT_MSYMB); + + srslte_scrambling_c(&q->seq[subframe], q->d); + + /* De-spreading */ + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + for (i = 0; i < SRSLTE_PHICH_NBITS; i++) { + q->z[i] = 0; + for (j = 0; j < SRSLTE_PHICH_EXT_NSF; j++) { + q->z[i] += conjf(w_ext[nseq][j]) + * q->d[i * SRSLTE_PHICH_EXT_NSF + j] / SRSLTE_PHICH_EXT_NSF; + } + } + } else { + for (i = 0; i < SRSLTE_PHICH_NBITS; i++) { + q->z[i] = 0; + for (j = 0; j < SRSLTE_PHICH_NORM_NSF; j++) { + q->z[i] += conjf(w_normal[nseq][j]) + * q->d[i * SRSLTE_PHICH_NORM_NSF + j] / SRSLTE_PHICH_NORM_NSF; + } + } + } + + DEBUG("z: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->z, SRSLTE_PHICH_NBITS); + + srslte_demod_soft_demodulate(SRSLTE_MOD_BPSK, q->z, q->data_rx, SRSLTE_PHICH_NBITS); + + if (ack) { + *ack = srslte_phich_ack_decode(q->data_rx, distance); + } + + return SRSLTE_SUCCESS; +} + +/** Encodes ACK/NACK bits, modulates and inserts into resource. + * The parameter ack is an array of srslte_phich_ngroups() pointers to buffers of nof_sequences uint8_ts + */ +int srslte_phich_encode(srslte_phich_t *q, uint8_t ack, uint32_t ngroup, uint32_t nseq, uint32_t subframe, + cf_t *slot_symbols[SRSLTE_MAX_PORTS]) { + int i; + + if (q == NULL || slot_symbols == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (subframe >= SRSLTE_NSUBFRAMES_X_FRAME) { + fprintf(stderr, "Invalid nslot %d\n", subframe); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + if (nseq >= SRSLTE_PHICH_EXT_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } else { + if (nseq >= SRSLTE_PHICH_NORM_NSEQUENCES) { + fprintf(stderr, "Invalid nseq %d\n", nseq); + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + if (ngroup >= srslte_regs_phich_ngroups(q->regs)) { + fprintf(stderr, "Invalid ngroup %d\n", ngroup); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + + /* Set pointers for layermapping & precoding */ + cf_t *x[SRSLTE_MAX_LAYERS]; + cf_t *symbols_precoding[SRSLTE_MAX_PORTS]; + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + symbols_precoding[i] = q->sf_symbols[i]; + } + + /* encode ACK/NACK bit */ + srslte_phich_ack_encode(ack, q->data); + + srslte_mod_modulate(&q->mod, q->data, q->z, SRSLTE_PHICH_NBITS); + + DEBUG("data: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->z, SRSLTE_PHICH_NBITS); + + /* Spread with w */ + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + for (i = 0; i < SRSLTE_PHICH_EXT_MSYMB; i++) { + q->d[i] = w_ext[nseq][i % SRSLTE_PHICH_EXT_NSF] + * q->z[i / SRSLTE_PHICH_EXT_NSF]; + } + } else { + for (i = 0; i < SRSLTE_PHICH_NORM_MSYMB; i++) { + q->d[i] = w_normal[nseq][i % SRSLTE_PHICH_NORM_NSF] + * q->z[i / SRSLTE_PHICH_NORM_NSF]; + } + } + + DEBUG("d: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->d, SRSLTE_PHICH_EXT_MSYMB); + + srslte_scrambling_c(&q->seq[subframe], q->d); + + /* align to REG */ + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + if (ngroup % 2) { + for (i = 0; i < SRSLTE_PHICH_EXT_MSYMB / 2; i++) { + q->d0[4 * i + 0] = 0; + q->d0[4 * i + 1] = 0; + q->d0[4 * i + 2] = q->d[2 * i]; + q->d0[4 * i + 3] = q->d[2 * i + 1]; + } + } else { + for (i = 0; i < SRSLTE_PHICH_EXT_MSYMB / 2; i++) { + q->d0[4 * i + 0] = q->d[2 * i]; + q->d0[4 * i + 1] = q->d[2 * i + 1]; + q->d0[4 * i + 2] = 0; + q->d0[4 * i + 3] = 0; + } + } + } else { + memcpy(q->d0, q->d, SRSLTE_PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + DEBUG("d0: "); + if (SRSLTE_VERBOSE_ISDEBUG()) + srslte_vec_fprint_c(stdout, q->d0, SRSLTE_PHICH_MAX_NSYMB); + + /* layer mapping & precoding */ + if (q->cell.nof_ports > 1) { + srslte_layermap_diversity(q->d0, x, q->cell.nof_ports, SRSLTE_PHICH_MAX_NSYMB); + srslte_precoding_diversity(x, symbols_precoding, q->cell.nof_ports, + SRSLTE_PHICH_MAX_NSYMB / q->cell.nof_ports, 1.0f); + /**FIXME: According to 6.9.2, Precoding for 4 tx ports is different! */ + } else { + memcpy(q->sf_symbols[0], q->d0, SRSLTE_PHICH_MAX_NSYMB * sizeof(cf_t)); + } + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + if (srslte_regs_phich_add(q->regs, q->sf_symbols[i], ngroup, slot_symbols[i]) + < 0) { + fprintf(stderr, "Error putting PCHICH resource elements\n"); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; +} + diff --git a/lib/src/phy/phch/pmch.c b/lib/src/phy/phch/pmch.c new file mode 100644 index 0000000..c5d4ad2 --- /dev/null +++ b/lib/src/phy/phch/pmch.c @@ -0,0 +1,495 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "prb_dl.h" +#include "srslte/phy/phch/sch.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + + +#define MAX_PMCH_RE (2 * SRSLTE_CP_EXT_NSYMB * 12) + + +const static srslte_mod_t modulations[4] = + { SRSLTE_MOD_BPSK, SRSLTE_MOD_QPSK, SRSLTE_MOD_16QAM, SRSLTE_MOD_64QAM }; + +//#define DEBUG_IDX + +#ifdef DEBUG_IDX +cf_t *offset_original=NULL; +extern int indices[100000]; +extern int indices_ptr; +#endif + +float srslte_pmch_coderate(uint32_t tbs, uint32_t nof_re) +{ + return (float) (tbs + 24)/(nof_re); +} + +int srslte_pmch_cp(srslte_pmch_t *q, cf_t *input, cf_t *output, uint32_t lstart_grant, bool put) +{ + uint32_t s, n, l, lp, lstart, lend, nof_refs; + cf_t *in_ptr = input, *out_ptr = output; + uint32_t offset = 0; + + #ifdef DEBUG_IDX + indices_ptr = 0; + if (put) { + offset_original = output; + } else { + offset_original = input; + } + #endif + nof_refs = 6; + for (s = 0; s < 2; s++) { + for (l = 0; l < SRSLTE_CP_EXT_NSYMB; l++) { + for (n = 0; n < q->cell.nof_prb; n++) { + // If this PRB is assigned + if (true) { + if (s == 0) { + lstart = lstart_grant; + } else { + lstart = 0; + } + lend = SRSLTE_CP_EXT_NSYMB; + lp = l + s * SRSLTE_CP_EXT_NSYMB; + if (put) { + out_ptr = &output[(lp * q->cell.nof_prb + n) * SRSLTE_NRE]; + } else { + in_ptr = &input[(lp * q->cell.nof_prb + n) * SRSLTE_NRE]; + } + // This is a symbol in a normal PRB with or without references + if (l >= lstart && l < lend) { + if (SRSLTE_SYMBOL_HAS_REF_MBSFN(l,s)) { + if (l == 0 && s == 1) { + offset = 1; + } else { + offset = 0; + } + prb_cp_ref(&in_ptr, &out_ptr, offset, nof_refs, nof_refs, put); + } else { + prb_cp(&in_ptr, &out_ptr, 1); + } + } + } + } + } + } + + int r; + if (put) { + r = abs((int) (input - in_ptr)); + } else { + r = abs((int) (output - out_ptr)); + } + + return r; +} + +/** + * Puts PMCH in slot number 1 + * + * Returns the number of symbols written to sf_symbols + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_pmch_put(srslte_pmch_t *q, cf_t *symbols, cf_t *sf_symbols, uint32_t lstart) +{ + return srslte_pmch_cp(q, symbols, sf_symbols, lstart, true); +} + +/** + * Extracts PMCH from slot number 1 + * + * Returns the number of symbols written to PMCH + * + * 36.211 10.3 section 6.3.5 + */ +int srslte_pmch_get(srslte_pmch_t *q, cf_t *sf_symbols, cf_t *symbols, uint32_t lstart) +{ + return srslte_pmch_cp(q, sf_symbols, symbols, lstart, false); +} + +int srslte_pmch_init(srslte_pmch_t *q, uint32_t max_prb) +{ + return srslte_pmch_init_multi(q, max_prb, 1); +} + +int srslte_pmch_init_multi(srslte_pmch_t *q, uint32_t max_prb, uint32_t nof_rx_antennas) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + nof_rx_antennas <= SRSLTE_MAX_PORTS) + { + + bzero(q, sizeof(srslte_pmch_t)); + ret = SRSLTE_ERROR; + + q->cell.nof_prb = max_prb; + q->cell.nof_ports = 1; + q->max_re = max_prb * MAX_PMCH_RE; + q->nof_rx_antennas = nof_rx_antennas; + + INFO("Init PMCH: %d PRBs, max_symbols: %d\n", + max_prb, q->max_re); + + for (int i = 0; i < 4; i++) { + if (srslte_modem_table_lte(&q->mod[i], modulations[i])) { + goto clean; + } + srslte_modem_table_bytes(&q->mod[i]); + } + + srslte_sch_init(&q->dl_sch); + + // Allocate int16_t for reception (LLRs) + q->e = srslte_vec_malloc(sizeof(int16_t) * q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM)); + if (!q->e) { + goto clean; + } + + q->d = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->d) { + goto clean; + } + + for (int i = 0; i < SRSLTE_MAX_PORTS; i++) { + q->x[i] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->x[i]) { + goto clean; + } + for (int j=0;jnof_rx_antennas;j++) { + q->ce[i][j] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->ce[i][j]) { + goto clean; + } + } + } + for (int j=0;jnof_rx_antennas;j++) { + q->symbols[j] = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->symbols[j]) { + goto clean; + } + } + + q->seqs = calloc(SRSLTE_MAX_MBSFN_AREA_IDS, sizeof(srslte_pmch_seq_t*)); + if (!q->seqs) { + perror("calloc"); + goto clean; + } + + ret = SRSLTE_SUCCESS; + } + clean: + if (ret == SRSLTE_ERROR) { + srslte_pmch_free(q); + } + return ret; +} + +void srslte_pmch_free(srslte_pmch_t *q) { + uint16_t i; + + if (q->e) { + free(q->e); + } + if (q->d) { + free(q->d); + } + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + if (q->x[i]) { + free(q->x[i]); + } + for (int j=0;jnof_rx_antennas;j++) { + if (q->ce[i][j]) { + free(q->ce[i][j]); + } + } + } + for (i=0;inof_rx_antennas;i++) { + if (q->symbols[i]) { + free(q->symbols[i]); + } + } + if (q->seqs) { + for (i=0; iseqs[i]) { + srslte_pmch_free_area_id(q, i); + } + } + free(q->seqs); + } + for (i = 0; i < 4; i++) { + srslte_modem_table_free(&q->mod[i]); + } + + srslte_sch_free(&q->dl_sch); + + bzero(q, sizeof(srslte_pmch_t)); + +} + +int srslte_pmch_set_cell(srslte_pmch_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + q->max_re = q->cell.nof_prb * MAX_PMCH_RE; + + INFO("PMCH: Cell config PCI=%d, %d ports, %d PRBs, max_symbols: %d\n", q->cell.nof_ports, + q->cell.id, q->cell.nof_prb, q->max_re); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Precalculate the scramble sequences for a given MBSFN area ID. This function takes a while + * to execute. + */ +int srslte_pmch_set_area_id(srslte_pmch_t *q, uint16_t area_id) { + uint32_t i; + if (!q->seqs[area_id]) { + q->seqs[area_id] = calloc(1, sizeof(srslte_pmch_seq_t)); + if (q->seqs[area_id]) { + for (i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + if (srslte_sequence_pmch(&q->seqs[area_id]->seq[i], 2 * i , area_id, q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) { + return SRSLTE_ERROR; + } + } + } + } + return SRSLTE_SUCCESS; +} + +void srslte_pmch_free_area_id(srslte_pmch_t* q, uint16_t area_id) +{ + if (q->seqs[area_id]) { + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + srslte_sequence_free(&q->seqs[area_id]->seq[i]); + } + free(q->seqs[area_id]); + q->seqs[area_id] = NULL; + } +} + +int srslte_pmch_cfg(srslte_pdsch_cfg_t *cfg, srslte_cell_t cell, srslte_ra_dl_grant_t *grant, uint32_t cfi, uint32_t sf_idx) +{ + if (cfg) { + if (grant) { + memcpy(&cfg->grant, grant, sizeof(srslte_ra_dl_grant_t)); + } + if (srslte_cbsegm(&cfg->cb_segm[0], cfg->grant.mcs[0].tbs)) { + fprintf(stderr, "Error computing Codeblock segmentation for TBS=%d\n", cfg->grant.mcs[0].tbs); + return SRSLTE_ERROR; + } + srslte_ra_dl_grant_to_nbits(&cfg->grant, cfi, cell, sf_idx, cfg->nbits); + cfg->sf_idx = sf_idx; + cfg->rv[0] = SRSLTE_PMCH_RV; + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + +int srslte_pmch_decode(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols, cf_t *ce[SRSLTE_MAX_PORTS], float noise_estimate, + uint16_t area_id, uint8_t *data) +{ + cf_t *_sf_symbols[SRSLTE_MAX_PORTS]; + cf_t *_ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + + _sf_symbols[0] = sf_symbols; + for (int i=0;icell.nof_ports;i++) { + _ce[i][0] = ce[i]; + } + return srslte_pmch_decode_multi(q, cfg, softbuffer, _sf_symbols, _ce, noise_estimate, area_id, data); +} + +/** Decodes the pmch from the received symbols + */ +int srslte_pmch_decode_multi(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols[SRSLTE_MAX_PORTS], cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS], float noise_estimate, + uint16_t area_id, uint8_t *data) +{ + + /* Set pointers for layermapping & precoding */ + uint32_t i, n; + cf_t *x[SRSLTE_MAX_LAYERS]; + + if (q != NULL && + sf_symbols != NULL && + data != NULL && + cfg != NULL) + { + INFO("Decoding PMCH SF: %d, MBSFN area ID: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, C_prb=%d, cfi=%d\n", + cfg->sf_idx, area_id, srslte_mod_string(cfg->grant.mcs[0].mod), cfg->grant.mcs[0].tbs, cfg->nbits[0].nof_re, + cfg->nbits[0].nof_bits, 0, cfg->grant.nof_prb, cfg->nbits[0].lstart-1); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + for (int j=0;jnof_rx_antennas;j++) { + /* extract symbols */ + n = srslte_pmch_get(q, sf_symbols[j], q->symbols[j], cfg->nbits[0].lstart); + if (n != cfg->nbits[0].nof_re) { + + fprintf(stderr, "PMCH 1 extract symbols error expecting %d symbols but got %d, lstart %d\n", cfg->nbits[0].nof_re, n, cfg->nbits[0].lstart); + return SRSLTE_ERROR; + } + + /* extract channel estimates */ + for (i = 0; i < q->cell.nof_ports; i++) { + n = srslte_pmch_get(q, ce[i][j], q->ce[i][j], cfg->nbits[0].lstart); + if (n != cfg->nbits[0].nof_re) { + fprintf(stderr, "PMCH 2 extract chest error expecting %d symbols but got %d\n", cfg->nbits[0].nof_re, n); + return SRSLTE_ERROR; + } + } + } + + // No tx diversity in MBSFN + srslte_predecoding_single_multi(q->symbols, q->ce[0], q->d, NULL, q->nof_rx_antennas, cfg->nbits[0].nof_re, 1.0f, noise_estimate); + + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE subframe.dat: received subframe symbols\n"); + srslte_vec_save_file("subframe.dat", sf_symbols, SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + DEBUG("SAVED FILE hest0.dat: channel estimates for port 4\n"); + srslte_vec_save_file("hest0.dat", ce[0], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + DEBUG("SAVED FILE pmch_symbols.dat: symbols after equalization\n"); + srslte_vec_save_file("pmch_symbols.bin", q->d, cfg->nbits[0].nof_re*sizeof(cf_t)); + } + + /* demodulate symbols + * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation, + * thus we don't need tot set it in thde LLRs normalization + */ + srslte_demod_soft_demodulate_s(cfg->grant.mcs[0].mod, q->d, q->e, cfg->nbits[0].nof_re); + + /* descramble */ + srslte_scrambling_s_offset(&q->seqs[area_id]->seq[cfg->sf_idx], q->e, 0, cfg->nbits[0].nof_bits); + + if (SRSLTE_VERBOSE_ISDEBUG()) { + DEBUG("SAVED FILE llr.dat: LLR estimates after demodulation and descrambling\n"); + srslte_vec_save_file("llr.dat", q->e, cfg->nbits[0].nof_bits*sizeof(int16_t)); + } + return srslte_dlsch_decode(&q->dl_sch, cfg, softbuffer, q->e, data); + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_pmch_encode(srslte_pmch_t *q, + srslte_pdsch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, uint16_t area_id, cf_t *sf_symbols[SRSLTE_MAX_PORTS]) +{ + + int i; + /* Set pointers for layermapping & precoding */ + cf_t *x[SRSLTE_MAX_LAYERS]; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && cfg != NULL) + { + for (i=0;icell.nof_ports;i++) { + if (sf_symbols[i] == NULL) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + } + + if (cfg->grant.mcs[0].tbs == 0) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (cfg->nbits[0].nof_re > q->max_re) { + fprintf(stderr, + "Error too many RE per subframe (%d). PMCH configured for %d RE (%d PRB)\n", + cfg->nbits[0].nof_re, q->max_re, q->cell.nof_prb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + INFO("Encoding PMCH SF: %d, Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", + cfg->sf_idx, srslte_mod_string(cfg->grant.mcs[0].mod), cfg->grant.mcs[0].tbs, + cfg->nbits[0].nof_re, cfg->nbits[0].nof_bits, 0); + + /* number of layers equals number of ports */ + for (i = 0; i < q->cell.nof_ports; i++) { + x[i] = q->x[i]; + } + memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSLTE_MAX_LAYERS - q->cell.nof_ports)); + + // TODO: use tb_encode directly + if (srslte_dlsch_encode(&q->dl_sch, cfg, softbuffer, data, q->e)) { + fprintf(stderr, "Error encoding TB\n"); + return SRSLTE_ERROR; + } + + /* scramble */ + srslte_scrambling_bytes(&q->seqs[area_id]->seq[cfg->sf_idx], (uint8_t*) q->e, cfg->nbits[0].nof_bits); + + srslte_mod_modulate_bytes(&q->mod[cfg->grant.mcs[0].mod], (uint8_t*) q->e, q->d, cfg->nbits[0].nof_bits); + + /* No tx diversity in MBSFN */ + memcpy(q->symbols[0], q->d, cfg->nbits[0].nof_re * sizeof(cf_t)); + + /* mapping to resource elements */ + for (i = 0; i < q->cell.nof_ports; i++) { + srslte_pmch_put(q, q->symbols[i], sf_symbols[i], cfg->nbits[0].lstart); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +uint32_t srslte_pmch_last_noi(srslte_pmch_t *q) { + return q->dl_sch.nof_iterations; +} + + + + diff --git a/lib/src/phy/phch/prach.c b/lib/src/phy/phch/prach.c new file mode 100644 index 0000000..0aca3bf --- /dev/null +++ b/lib/src/phy/phch/prach.c @@ -0,0 +1,716 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/prach.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +float save_corr[4096]; + +//PRACH detection threshold is PRACH_DETECT_FACTOR*average +#define PRACH_DETECT_FACTOR 18 + +#define N_SEQS 64 // Number of prach sequences available +#define N_RB_SC 12 // Number of subcarriers per resource block +#define DELTA_F 15000 // Normal subcarrier spacing +#define DELTA_F_RA 1250 // PRACH subcarrier spacing +#define DELTA_F_RA_4 7500 // PRACH subcarrier spacing for format 4 +#define PHI 7 // PRACH phi parameter +#define PHI_4 2 // PRACH phi parameter for format 4 +#define MAX_ROOTS 838 // Max number of root sequences + +#define PRACH_AMP 1.0 + +/****************************************************** + * Reference tables from 3GPP TS 36.211 v10.7.0 + *****************************************************/ + +// Table 5.7.1-1 T_cp for preamble formats +uint32_t prach_Tcp[5] = {3168, 21024, 6240, 21024, 448}; + +// Table 5.7.1-1 T_seq for preamble formats +uint32_t prach_Tseq[5] = {24576, 24576, 2 * 24576, 2 * 24576, 4096}; + +// Table 5.7.2-2 - N_cs values for unrestricted sets +uint32_t prach_Ncs_unrestricted[16] = {0, 13, 15, 18, 22, 26, 32, 38, 46, 59, 76, 93, 119, 167, 279, 419}; + +#define MAX_N_zc 839 + +// Table 5.7.2-2 - N_cs values for restricted sets +uint32_t prach_Ncs_restricted[15] = {15, 18, 22, 26, 32, 38, 46, 55, 68, 82, 100, 128, 158, 202, 237}; + +// Table 5.7.2-3 - N_cs values for preamble format 4 +uint32_t prach_Ncs_format4[7] = {2, 4, 6, 8, 10, 12, 15}; + +// Table 5.7.2-4 - Root ZC sequence order +uint32_t prach_zc_roots[838] = { + 129, 710, 140, 699, 120, 719, 210, 629, 168, 671, 84, 755, + 105, 734, 93, 746, 70, 769, 60, 779, 2, 837, 1, 838, + 56, 783, 112, 727, 148, 691, 80, 759, 42, 797, 40, 799, + 35, 804, 73, 766, 146, 693, 31, 808, 28, 811, 30, 809, + 27, 812, 29, 810, 24, 815, 48, 791, 68, 771, 74, 765, + 178, 661, 136, 703, 86, 753, 78, 761, 43, 796, 39, 800, + 20, 819, 21, 818, 95, 744, 202, 637, 190, 649, 181, 658, + 137, 702, 125, 714, 151, 688, 217, 622, 128, 711, 142, 697, + 122, 717, 203, 636, 118, 721, 110, 729, 89, 750, 103, 736, + 61, 778, 55, 784, 15, 824, 14, 825, 12, 827, 23, 816, + 34, 805, 37, 802, 46, 793, 207, 632, 179, 660, 145, 694, + 130, 709, 223, 616, 228, 611, 227, 612, 132, 707, 133, 706, + 143, 696, 135, 704, 161, 678, 201, 638, 173, 666, 106, 733, + 83, 756, 91, 748, 66, 773, 53, 786, 10, 829, 9, 830, + 7, 832, 8, 831, 16, 823, 47, 792, 64, 775, 57, 782, + 104, 735, 101, 738, 108, 731, 208, 631, 184, 655, 197, 642, + 191, 648, 121, 718, 141, 698, 149, 690, 216, 623, 218, 621, + 152, 687, 144, 695, 134, 705, 138, 701, 199, 640, 162, 677, + 176, 663, 119, 720, 158, 681, 164, 675, 174, 665, 171, 668, + 170, 669, 87, 752, 169, 670, 88, 751, 107, 732, 81, 758, + 82, 757, 100, 739, 98, 741, 71, 768, 59, 780, 65, 774, + 50, 789, 49, 790, 26, 813, 17, 822, 13, 826, 6, 833, + 5, 834, 33, 806, 51, 788, 75, 764, 99, 740, 96, 743, + 97, 742, 166, 673, 172, 667, 175, 664, 187, 652, 163, 676, + 185, 654, 200, 639, 114, 725, 189, 650, 115, 724, 194, 645, + 195, 644, 192, 647, 182, 657, 157, 682, 156, 683, 211, 628, + 154, 685, 123, 716, 139, 700, 212, 627, 153, 686, 213, 626, + 215, 624, 150, 689, 225, 614, 224, 615, 221, 618, 220, 619, + 127, 712, 147, 692, 124, 715, 193, 646, 205, 634, 206, 633, + 116, 723, 160, 679, 186, 653, 167, 672, 79, 760, 85, 754, + 77, 762, 92, 747, 58, 781, 62, 777, 69, 770, 54, 785, + 36, 803, 32, 807, 25, 814, 18, 821, 11, 828, 4, 835, + 3, 836, 19, 820, 22, 817, 41, 798, 38, 801, 44, 795, + 52, 787, 45, 794, 63, 776, 67, 772, 72, 767, 76, 763, + 94, 745, 102, 737, 90, 749, 109, 730, 165, 674, 111, 728, + 209, 630, 204, 635, 117, 722, 188, 651, 159, 680, 198, 641, + 113, 726, 183, 656, 180, 659, 177, 662, 196, 643, 155, 684, + 214, 625, 126, 713, 131, 708, 219, 620, 222, 617, 226, 613, + 230, 609, 232, 607, 262, 577, 252, 587, 418, 421, 416, 423, + 413, 426, 411, 428, 376, 463, 395, 444, 283, 556, 285, 554, + 379, 460, 390, 449, 363, 476, 384, 455, 388, 451, 386, 453, + 361, 478, 387, 452, 360, 479, 310, 529, 354, 485, 328, 511, + 315, 524, 337, 502, 349, 490, 335, 504, 324, 515, 323, 516, + 320, 519, 334, 505, 359, 480, 295, 544, 385, 454, 292, 547, + 291, 548, 381, 458, 399, 440, 380, 459, 397, 442, 369, 470, + 377, 462, 410, 429, 407, 432, 281, 558, 414, 425, 247, 592, + 277, 562, 271, 568, 272, 567, 264, 575, 259, 580, 237, 602, + 239, 600, 244, 595, 243, 596, 275, 564, 278, 561, 250, 589, + 246, 593, 417, 422, 248, 591, 394, 445, 393, 446, 370, 469, + 365, 474, 300, 539, 299, 540, 364, 475, 362, 477, 298, 541, + 312, 527, 313, 526, 314, 525, 353, 486, 352, 487, 343, 496, + 327, 512, 350, 489, 326, 513, 319, 520, 332, 507, 333, 506, + 348, 491, 347, 492, 322, 517, 330, 509, 338, 501, 341, 498, + 340, 499, 342, 497, 301, 538, 366, 473, 401, 438, 371, 468, + 408, 431, 375, 464, 249, 590, 269, 570, 238, 601, 234, 605, + 257, 582, 273, 566, 255, 584, 254, 585, 245, 594, 251, 588, + 412, 427, 372, 467, 282, 557, 403, 436, 396, 443, 392, 447, + 391, 448, 382, 457, 389, 450, 294, 545, 297, 542, 311, 528, + 344, 495, 345, 494, 318, 521, 331, 508, 325, 514, 321, 518, + 346, 493, 339, 500, 351, 488, 306, 533, 289, 550, 400, 439, + 378, 461, 374, 465, 415, 424, 270, 569, 241, 598, 231, 608, + 260, 579, 268, 571, 276, 563, 409, 430, 398, 441, 290, 549, + 304, 535, 308, 531, 358, 481, 316, 523, 293, 546, 288, 551, + 284, 555, 368, 471, 253, 586, 256, 583, 263, 576, 242, 597, + 274, 565, 402, 437, 383, 456, 357, 482, 329, 510, 317, 522, + 307, 532, 286, 553, 287, 552, 266, 573, 261, 578, 236, 603, + 303, 536, 356, 483, 355, 484, 405, 434, 404, 435, 406, 433, + 235, 604, 267, 572, 302, 537, 309, 530, 265, 574, 233, 606, + 367, 472, 296, 543, 336, 503, 305, 534, 373, 466, 280, 559, + 279, 560, 419, 420, 240, 599, 258, 581, 229, 610}; + +// Table 5.7.2-5 - Root ZC sequence order for preamble format 4 +uint32_t prach_zc_roots_format4[138] = { + 1, 138, 2, 137, 3, 136, 4, 135, 5, 134, 6, 133, + 7, 132, 8, 131, 9, 130, 10, 129, 11, 128, 12, 127, + 13, 126, 14, 125, 15, 124, 16, 123, 17, 122, 18, 121, + 19, 120, 20, 119, 21, 118, 22, 117, 23, 116, 24, 115, + 25, 114, 26, 113, 27, 112, 28, 111, 29, 110, 30, 109, + 31, 108, 32, 107, 33, 106, 34, 105, 35, 104, 36, 103, + 37, 102, 38, 101, 39, 100, 40, 99, 41, 98, 42, 97, + 43, 96, 44, 95, 45, 94, 46, 93, 47, 92, 48, 91, + 49, 90, 50, 89, 51, 88, 52, 87, 53, 86, 54, 85, + 55, 84, 56, 83, 57, 82, 58, 81, 59, 80, 60, 79, + 61, 78, 62, 77, 63, 76, 64, 75, 65, 74, 66, 73, + 67, 72, 68, 71, 69, 70}; + +srslte_prach_sf_config_t prach_sf_config[16] = { + {1, {1, 0, 0, 0, 0}}, + {1, {4, 0, 0, 0, 0}}, + {1, {7, 0, 0, 0, 0}}, + {1, {1, 0, 0, 0, 0}}, + {1, {4, 0, 0, 0, 0}}, + {1, {7, 0, 0, 0, 0}}, + {2, {1, 6, 0, 0, 0}}, + {2, {2, 7, 0, 0, 0}}, + {2, {3, 8, 0, 0, 0}}, + {3, {1, 4, 7, 0, 0}}, + {3, {2, 5, 8, 0, 0}}, + {3, {3, 6, 9, 0, 0}}, + {5, {0, 2, 4, 6, 8}}, + {5, {1, 3, 5, 7, 9}}, + {-1, {0, 0, 0, 0, 0}}, // this means all subframes + {1, {9, 0, 0, 0, 0}}}; + +uint32_t srslte_prach_get_preamble_format(uint32_t config_idx) { + return config_idx / 16; +} + +srslte_prach_sfn_t srslte_prach_get_sfn(uint32_t config_idx) { + if ((config_idx % 16) < 3 || (config_idx % 16) == 15) { + return SRSLTE_PRACH_SFN_EVEN; + } else { + return SRSLTE_PRACH_SFN_ANY; + } +} + +/* Returns true if current_tti is a valid opportunity for PRACH transmission and the is an allowed subframe, + * or allowed_subframe == -1 + */ +bool srslte_prach_tti_opportunity(srslte_prach_t *p, uint32_t current_tti, int allowed_subframe) { + uint32_t config_idx = p->config_idx; + return srslte_prach_tti_opportunity_config(config_idx,current_tti,allowed_subframe); +} + +bool srslte_prach_tti_opportunity_config(uint32_t config_idx, uint32_t current_tti, int allowed_subframe) +{ + // Get SFN and sf_idx from the PRACH configuration index + srslte_prach_sfn_t prach_sfn = srslte_prach_get_sfn(config_idx); + + // This is the only option which provides always an opportunity for PRACH transmission. + if (config_idx == 14) { + return true; + } + + if ((prach_sfn == SRSLTE_PRACH_SFN_EVEN && ((current_tti / 10) % 2) == 0) || + prach_sfn == SRSLTE_PRACH_SFN_ANY) { + srslte_prach_sf_config_t sf_config; + srslte_prach_sf_config(config_idx, &sf_config); + for (int i = 0; i < sf_config.nof_sf; i++) { + if (((current_tti % 10) == sf_config.sf[i] && allowed_subframe == -1) || + ((current_tti % 10) == sf_config.sf[i] && (current_tti % 10) == allowed_subframe)) { + return true; + } + } + } + return false; + +} + +void srslte_prach_sf_config(uint32_t config_idx, srslte_prach_sf_config_t *sf_config) { + memcpy(sf_config, &prach_sf_config[config_idx % 16], sizeof(srslte_prach_sf_config_t)); +} + +// For debug use only +void print(void *d, uint32_t size, uint32_t len, char *file_str) { + FILE *f; + f = fopen(file_str, "wb"); + fwrite(d, size, len, f); + fclose(f); +} + +int srslte_prach_gen_seqs(srslte_prach_t *p) { + uint32_t u = 0; + uint32_t v = 1; + int v_max = 0; + uint32_t p_ = 0; + uint32_t d_u = 0; + uint32_t d_start = 0; + uint32_t N_shift = 0; + int N_neg_shift = 0; + uint32_t N_group = 0; + uint32_t C_v = 0; + cf_t root[839]; + + // Generate our 64 preamble sequences + for (int i = 0; i < N_SEQS; i++) { + + if (v > v_max) { + // Get a new root sequence + if (4 == p->f) { + u = prach_zc_roots_format4[(p->rsi + p->N_roots) % 138]; + } else { + u = prach_zc_roots[(p->rsi + p->N_roots) % 838]; + } + + for (int j = 0; j < p->N_zc; j++) { + double phase = -M_PI * u * j * (j + 1) / p->N_zc; + root[j] = cexp(phase * I); + } + p->root_seqs_idx[p->N_roots++] = i; + + // Determine v_max + if (p->hs) { + // High-speed cell + for (p_ = 1; p_ <= p->N_zc; p_++) { + if (((p_ * u) % p->N_zc) == 1) + break; + } + if (p_ < p->N_zc / 2) { + d_u = p_; + } else { + d_u = p->N_zc - p_; + } + if (d_u >= p->N_cs && d_u < p->N_zc / 3) { + N_shift = d_u / p->N_cs; + d_start = 2 * d_u + N_shift * p->N_cs; + N_group = p->N_zc / d_start; + if (p->N_zc > 2 * d_u + N_group * d_start) { + N_neg_shift = (p->N_zc - 2 * d_u - N_group * d_start) / p->N_cs; + } else { + N_neg_shift = 0; + } + } else if (p->N_zc / 3 <= d_u && d_u <= (p->N_zc - p->N_cs) / 2) { + N_shift = (p->N_zc - 2 * d_u) / p->N_cs; + d_start = p->N_zc - 2 * d_u + N_shift * p->N_cs; + N_group = d_u / d_start; + if (d_u > N_group * d_start) { + N_neg_shift = (d_u - N_group * d_start) / p->N_cs; + } else { + N_neg_shift = 0; + } + if (N_neg_shift > N_shift) + N_neg_shift = N_shift; + } else { + N_shift = 0; + } + v_max = N_shift * N_group + N_neg_shift - 1; + if (v_max < 0) { + v_max = 0; + } + } else { + // Normal cell + if (0 == p->N_cs) { + v_max = 0; + } else { + v_max = (p->N_zc / p->N_cs) - 1; + } + } + + v = 0; + } + + // Shift root and add to set + if (p->hs) { + if (N_shift == 0) { + C_v = 0; + } else { + C_v = d_start * floor(v / N_shift) + (v % N_shift) * p->N_cs; + } + } else { + C_v = v * p->N_cs; + } + for (int j = 0; j < p->N_zc; j++) { + p->seqs[i][j] = root[(j + C_v) % p->N_zc]; + } + + v++; + } + return 0; +} + +int srslte_prach_init_cfg(srslte_prach_t *p, srslte_prach_cfg_t *cfg, uint32_t nof_prb) { + if (srslte_prach_init(p, srslte_symbol_sz(nof_prb))) { + return -1; + } + return srslte_prach_set_cell(p, + srslte_symbol_sz(nof_prb), + cfg->config_idx, + cfg->root_seq_idx, + cfg->hs_flag, + cfg->zero_corr_zone); +} + +int srslte_prach_init(srslte_prach_t *p, uint32_t max_N_ifft_ul) { + int ret = SRSLTE_ERROR; + if (p != NULL && + max_N_ifft_ul < 2049) { + bzero(p, sizeof(srslte_prach_t)); + + p->max_N_ifft_ul = max_N_ifft_ul; + + // Set up containers + p->prach_bins = srslte_vec_malloc(sizeof(cf_t) * MAX_N_zc); + p->corr_spec = srslte_vec_malloc(sizeof(cf_t) * MAX_N_zc); + p->corr = srslte_vec_malloc(sizeof(float) * MAX_N_zc); + + // Set up ZC FFTS + if (srslte_dft_plan(&p->zc_fft, MAX_N_zc, SRSLTE_DFT_FORWARD, SRSLTE_DFT_COMPLEX)) { + return SRSLTE_ERROR; + } + srslte_dft_plan_set_mirror(&p->zc_fft, false); + srslte_dft_plan_set_norm(&p->zc_fft, true); + + if (srslte_dft_plan(&p->zc_ifft, MAX_N_zc, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + return SRSLTE_ERROR; + } + srslte_dft_plan_set_mirror(&p->zc_ifft, false); + srslte_dft_plan_set_norm(&p->zc_ifft, false); + + uint32_t fft_size_alloc = max_N_ifft_ul * DELTA_F / DELTA_F_RA; + + p->ifft_in = (cf_t *) srslte_vec_malloc(fft_size_alloc * sizeof(cf_t)); + p->ifft_out = (cf_t *) srslte_vec_malloc(fft_size_alloc * sizeof(cf_t)); + if (srslte_dft_plan(&p->ifft, fft_size_alloc, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error creating DFT plan\n"); + return -1; + } + srslte_dft_plan_set_mirror(&p->ifft, true); + srslte_dft_plan_set_norm(&p->ifft, true); + + if (srslte_dft_plan(&p->fft, fft_size_alloc, SRSLTE_DFT_FORWARD, SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error creating DFT plan\n"); + return -1; + } + + p->signal_fft = srslte_vec_malloc(sizeof(cf_t) * fft_size_alloc); + if (!p->signal_fft) { + fprintf(stderr, "Error allocating memory\n"); + return -1; + } + + srslte_dft_plan_set_mirror(&p->fft, true); + srslte_dft_plan_set_norm(&p->fft, false); + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters\n"); + } + + return ret; +} + +int srslte_prach_set_cell(srslte_prach_t *p, + uint32_t N_ifft_ul, + uint32_t config_idx, + uint32_t root_seq_index, + bool high_speed_flag, + uint32_t zero_corr_zone_config) { + int ret = SRSLTE_ERROR; + if (p != NULL && + N_ifft_ul < 2049 && + config_idx < 64 && + root_seq_index < MAX_ROOTS) { + if (N_ifft_ul > p->max_N_ifft_ul) { + fprintf(stderr, "PRACH: Error in set_cell(): N_ifft_ul must be lower or equal max_N_ifft_ul in init()\n"); + return -1; + } + + uint32_t preamble_format = srslte_prach_get_preamble_format(config_idx); + p->config_idx = config_idx; + p->f = preamble_format; + p->rsi = root_seq_index; + p->hs = high_speed_flag; + p->zczc = zero_corr_zone_config; + p->detect_factor = PRACH_DETECT_FACTOR; + + + // Determine N_zc and N_cs + if (4 == preamble_format) { + if (p->zczc < 7) { + p->N_zc = 139; + p->N_cs = prach_Ncs_format4[p->zczc]; + } else { + fprintf(stderr, "Invalid zeroCorrelationZoneConfig=%d for format4\n", p->zczc); + return SRSLTE_ERROR; + } + } else { + p->N_zc = MAX_N_zc; + if (p->hs) { + if (p->zczc < 15) { + p->N_cs = prach_Ncs_restricted[p->zczc]; + } else { + fprintf(stderr, "Invalid zeroCorrelationZoneConfig=%d for restricted set\n", p->zczc); + return SRSLTE_ERROR; + } + } else { + if (p->zczc < 16) { + p->N_cs = prach_Ncs_unrestricted[p->zczc]; + } else { + fprintf(stderr, "Invalid zeroCorrelationZoneConfig=%d\n", p->zczc); + return SRSLTE_ERROR; + } + } + } + + // Set up ZC FFTS + if (p->N_zc != MAX_N_zc) { + if (srslte_dft_replan(&p->zc_fft, p->N_zc)) { + return SRSLTE_ERROR; + } + if (srslte_dft_replan(&p->zc_ifft, p->N_zc)) { + return SRSLTE_ERROR; + } + } + + // Generate our 64 sequences + p->N_roots = 0; + srslte_prach_gen_seqs(p); + + // Generate sequence FFTs + for (int i = 0; i < N_SEQS; i++) { + srslte_dft_run(&p->zc_fft, p->seqs[i], p->dft_seqs[i]); + } + + // Create our FFT objects and buffers + p->N_ifft_ul = N_ifft_ul; + if (4 == preamble_format) { + p->N_ifft_prach = p->N_ifft_ul * DELTA_F / DELTA_F_RA_4; + } else { + p->N_ifft_prach = p->N_ifft_ul * DELTA_F / DELTA_F_RA; + } + + /* The deadzone specifies the number of samples at the end of the correlation window + * that will be considered as belonging to the next preamble + */ + p->deadzone = 0; + /* + if(p->N_cs != 0) { + float samp_rate=15000*p->N_ifft_ul; + p->deadzone = (uint32_t) ceil((float) samp_rate/((float) p->N_zc*subcarrier_spacing)); + }*/ + + if (srslte_dft_replan(&p->ifft, p->N_ifft_prach)) { + fprintf(stderr, "Error creating DFT plan\n"); + return -1; + } + if (srslte_dft_replan(&p->fft, p->N_ifft_prach)) { + fprintf(stderr, "Error creating DFT plan\n"); + return -1; + } + + p->N_seq = prach_Tseq[p->f] * p->N_ifft_ul / 2048; + p->N_cp = prach_Tcp[p->f] * p->N_ifft_ul / 2048; + p->T_seq = prach_Tseq[p->f] * SRSLTE_LTE_TS; + p->T_tot = (prach_Tseq[p->f] + prach_Tcp[p->f]) * SRSLTE_LTE_TS; + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters\n"); + } + + return ret; +} + +int srslte_prach_gen(srslte_prach_t *p, + uint32_t seq_index, + uint32_t freq_offset, + cf_t *signal) { + int ret = SRSLTE_ERROR; + if (p != NULL && + seq_index < N_SEQS && + signal != NULL) { + // Calculate parameters + uint32_t N_rb_ul = srslte_nof_prb(p->N_ifft_ul); + uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; + uint32_t K = DELTA_F / DELTA_F_RA; + uint32_t begin = PHI + (K * k_0) + (K / 2); + + if (6 + freq_offset > N_rb_ul) { + fprintf(stderr, "Error no space for PRACH: frequency offset=%d, N_rb_ul=%d\n", freq_offset, N_rb_ul); + return ret; + } + + DEBUG("N_zc: %d, N_cp: %d, N_seq: %d, N_ifft_prach=%d begin: %d\n", + p->N_zc, p->N_cp, p->N_seq, p->N_ifft_prach, begin); + + // Map dft-precoded sequence to ifft bins + memset(p->ifft_in, 0, begin * sizeof(cf_t)); + memcpy(&p->ifft_in[begin], p->dft_seqs[seq_index], p->N_zc * sizeof(cf_t)); + memset(&p->ifft_in[begin + p->N_zc], 0, (p->N_ifft_prach - begin - p->N_zc) * sizeof(cf_t)); + + srslte_dft_run(&p->ifft, p->ifft_in, p->ifft_out); + + // Copy CP into buffer + memcpy(signal, &p->ifft_out[p->N_ifft_prach - p->N_cp], p->N_cp * sizeof(cf_t)); + + // Copy preamble sequence into buffer + for (int i = 0; i < p->N_seq; i++) { + signal[p->N_cp + i] = p->ifft_out[i % p->N_ifft_prach]; + } + + ret = SRSLTE_SUCCESS; + } + + return ret; +} + +void srslte_prach_set_detect_factor(srslte_prach_t *p, float ratio) { + p->detect_factor = ratio; +} + +int srslte_prach_detect(srslte_prach_t *p, + uint32_t freq_offset, + cf_t *signal, + uint32_t sig_len, + uint32_t *indices, + uint32_t *n_indices) { + return srslte_prach_detect_offset(p, freq_offset, signal, sig_len, indices, NULL, NULL, n_indices); +} + +int srslte_prach_detect_offset(srslte_prach_t *p, + uint32_t freq_offset, + cf_t *signal, + uint32_t sig_len, + uint32_t *indices, + float *t_offsets, + float *peak_to_avg, + uint32_t *n_indices) { + int ret = SRSLTE_ERROR; + if (p != NULL && + signal != NULL && + sig_len > 0 && + indices != NULL) { + + if (sig_len < p->N_ifft_prach) { + fprintf(stderr, "srslte_prach_detect: Signal length is %d and should be %d\n", sig_len, p->N_ifft_prach); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // FFT incoming signal + srslte_dft_run(&p->fft, signal, p->signal_fft); + + *n_indices = 0; + + // Extract bins of interest + uint32_t N_rb_ul = srslte_nof_prb(p->N_ifft_ul); + uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; + uint32_t K = DELTA_F / DELTA_F_RA; + uint32_t begin = PHI + (K * k_0) + (K / 2); + + memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t)); + + for (int i = 0; i < p->N_roots; i++) { + cf_t *root_spec = p->dft_seqs[p->root_seqs_idx[i]]; + + srslte_vec_prod_conj_ccc(p->prach_bins, root_spec, p->corr_spec, p->N_zc); + + srslte_dft_run(&p->zc_ifft, p->corr_spec, p->corr_spec); + + srslte_vec_abs_square_cf(p->corr_spec, p->corr, p->N_zc); + + float corr_ave = srslte_vec_acc_ff(p->corr, p->N_zc) / p->N_zc; + + uint32_t winsize = 0; + if (p->N_cs != 0) { + winsize = p->N_cs; + } else { + winsize = p->N_zc; + } + uint32_t n_wins = p->N_zc / winsize; + + float max_peak = 0; + for (int j = 0; j < n_wins; j++) { + uint32_t start = (p->N_zc - (j * p->N_cs)) % p->N_zc; + uint32_t end = start + winsize; + if (end > p->deadzone) { + end -= p->deadzone; + } + start += p->deadzone; + p->peak_values[j] = 0; + for (int k = start; k < end; k++) { + if (p->corr[k] > p->peak_values[j]) { + p->peak_values[j] = p->corr[k]; + p->peak_offsets[j] = k - start; + if (p->peak_values[j] > max_peak) { + max_peak = p->peak_values[j]; + } + } + } + } + if (max_peak > p->detect_factor * corr_ave) { + for (int j = 0; j < n_wins; j++) { + if (p->peak_values[j] > p->detect_factor * corr_ave) { + //printf("saving prach correlation\n"); + //memcpy(save_corr, p->corr, p->N_zc*sizeof(float)); + if (indices) { + indices[*n_indices] = (i * n_wins) + j; + } + if (peak_to_avg) { + peak_to_avg[*n_indices] = p->peak_values[j] / corr_ave; + } + if (t_offsets) { + t_offsets[*n_indices] = (float) p->peak_offsets[j] * p->T_seq / p->N_zc; + } + (*n_indices)++; + } + } + } + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_prach_free(srslte_prach_t *p) { + free(p->prach_bins); + free(p->corr_spec); + free(p->corr); + srslte_dft_plan_free(&p->ifft); + free(p->ifft_in); + free(p->ifft_out); + srslte_dft_plan_free(&p->fft); + srslte_dft_plan_free(&p->zc_fft); + srslte_dft_plan_free(&p->zc_ifft); + + if (p->signal_fft) { + free(p->signal_fft); + } + + bzero(p, sizeof(srslte_prach_t)); + + return 0; +} + +int srslte_prach_print_seqs(srslte_prach_t *p) { + for (int i = 0; i < N_SEQS; i++) { + FILE *f; + char str[32]; + sprintf(str, "prach_seq_%d.bin", i); + f = fopen(str, "wb"); + fwrite(p->seqs[i], sizeof(cf_t), p->N_zc, f); + fclose(f); + } + for (int i = 0; i < N_SEQS; i++) { + FILE *f; + char str[32]; + sprintf(str, "prach_dft_seq_%d.bin", i); + f = fopen(str, "wb"); + fwrite(p->dft_seqs[i], sizeof(cf_t), p->N_zc, f); + fclose(f); + } + for (int i = 0; i < p->N_roots; i++) { + FILE *f; + char str[32]; + sprintf(str, "prach_root_seq_%d.bin", i); + f = fopen(str, "wb"); + fwrite(p->seqs[p->root_seqs_idx[i]], sizeof(cf_t), p->N_zc, f); + fclose(f); + } + return 0; +} diff --git a/lib/src/phy/phch/prb_dl.c b/lib/src/phy/phch/prb_dl.c new file mode 100644 index 0000000..ac8d6f3 --- /dev/null +++ b/lib/src/phy/phch/prb_dl.c @@ -0,0 +1,108 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "prb_dl.h" +#include "srslte/phy/common/phy_common.h" + +//#define DEBUG_IDX + +#ifdef DEBUG_IDX +extern cf_t *offset_original; +SRSLTE_API int indices[100000]; +SRSLTE_API int indices_ptr=0; +#endif + +void print_indexes(cf_t *offset, int len) { +#ifdef DEBUG_IDX + for (int i=0;i 0) { + if (advance_output) { + (*output)++; + } else { + (*input)++; + } + memcpy(*output, *input, (ref_interval - offset) * sizeof(cf_t)); + print_indexes(*input, ref_interval-offset); + *output += (ref_interval - offset); + *input += (ref_interval - offset); + } +} + +void prb_cp(cf_t **input, cf_t **output, int nof_prb) { + memcpy(*output, *input, sizeof(cf_t) * SRSLTE_NRE * nof_prb); + print_indexes(*input, SRSLTE_NRE); + *input += nof_prb * SRSLTE_NRE; + *output += nof_prb * SRSLTE_NRE; +} + + +void prb_cp_half(cf_t **input, cf_t **output, int nof_prb) { + memcpy(*output, *input, sizeof(cf_t) * SRSLTE_NRE * nof_prb / 2); + print_indexes(*input, SRSLTE_NRE/2); + *input += nof_prb * SRSLTE_NRE / 2; + *output += nof_prb * SRSLTE_NRE / 2; +} + +void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, + int nof_intervals) { + prb_cp_ref(input, output, offset, nof_refs, nof_intervals, false); +} + +void prb_get_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, + int nof_intervals) { + prb_cp_ref(input, output, offset, nof_refs, nof_intervals, true); +} + diff --git a/lib/src/phy/phch/prb_dl.h b/lib/src/phy/phch/prb_dl.h new file mode 100644 index 0000000..48613be --- /dev/null +++ b/lib/src/phy/phch/prb_dl.h @@ -0,0 +1,34 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/config.h" + +void prb_cp_ref(cf_t **input, cf_t **output, int offset, int nof_refs, + int nof_intervals, bool advance_input); +void prb_cp(cf_t **input, cf_t **output, int nof_prb); +void prb_cp_half(cf_t **input, cf_t **output, int nof_prb); +void prb_put_ref_(cf_t **input, cf_t **output, int offset, int nof_refs, + int nof_intervals); diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c new file mode 100644 index 0000000..834963d --- /dev/null +++ b/lib/src/phy/phch/pucch.c @@ -0,0 +1,874 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/refsignal_ul.h" +#include "srslte/phy/phch/pucch.h" +#include "srslte/phy/common/sequence.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/mimo/precoding.h" +#include "srslte/phy/scrambling/scrambling.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/modem/demod_soft.h" + +#define MAX_PUSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12) + +uint32_t pucch_symbol_format1_cpnorm[4] = {0, 1, 5, 6}; +uint32_t pucch_symbol_format1_cpext[4] = {0, 1, 4, 5}; +uint32_t pucch_symbol_format2_cpnorm[5] = {0, 2, 3, 4, 6}; +uint32_t pucch_symbol_format2_cpext[5] = {0, 1, 2, 4, 5}; + +float w_n_oc[2][3][4] = { + // Table 5.4.1-2 Orthogonal sequences w for N_sf=4 (complex argument) + {{0, 0, 0, 0}, + {0,M_PI, 0, M_PI}, + {0,M_PI, M_PI, 0}}, + // Table 5.4.1-3 Orthogonal sequences w for N_sf=3 + {{0, 0, 0, 0}, + {0,2*M_PI/3, 4*M_PI/3,0}, + {0,4*M_PI/3,2*M_PI/3,0}}, + +}; + + +/* Verify PUCCH configuration as given in Section 5.4 36.211 */ +bool srslte_pucch_cfg_isvalid(srslte_pucch_cfg_t *cfg, uint32_t nof_prb) { + if (cfg->delta_pucch_shift > 0 && cfg->delta_pucch_shift < 4 && + cfg->N_cs < 8 && (cfg->N_cs%cfg->delta_pucch_shift) == 0 && + cfg->n_rb_2 <= nof_prb) { + return true; + } else { + return false; + } +} + +// Verifies n_2_pucch as defined in 5.4 +bool srslte_pucch_n2_isvalid(srslte_pucch_cfg_t *cfg, uint32_t n_pucch_2) { + if (n_pucch_2 < cfg->n_rb_2*SRSLTE_NRE+(uint32_t) ceilf((float) cfg->N_cs/8)*(SRSLTE_NRE-cfg->N_cs-2)) { + return true; + } else { + return false; + } +} + +void srslte_pucch_cfg_default(srslte_pucch_cfg_t *cfg) { + cfg->delta_pucch_shift = 1; +} + +uint32_t get_N_sf(srslte_pucch_format_t format, uint32_t slot_idx, bool shortened) { + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + if (!slot_idx) { + return 4; + } else { + return shortened?3:4; + } + case SRSLTE_PUCCH_FORMAT_2: + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + return 5; + default: + return 0; + } + return 0; +} + +uint32_t srslte_pucch_nof_symbols(srslte_pucch_cfg_t *cfg, srslte_pucch_format_t format, bool shortened) { + uint32_t len=0; + for (uint32_t ns=0;ns<2;ns++) { + len += SRSLTE_NRE*get_N_sf(format, ns, shortened); + } + return len; +} + +// Number of bits per subframe (M_bit) Table 5.4-1 36.211 +uint32_t srslte_pucch_nbits_format(srslte_pucch_format_t format) { + switch(format) { + case SRSLTE_PUCCH_FORMAT_1: + return 0; + case SRSLTE_PUCCH_FORMAT_1A: + return 1; + case SRSLTE_PUCCH_FORMAT_1B: + return 2; + case SRSLTE_PUCCH_FORMAT_2: + return 20; + case SRSLTE_PUCCH_FORMAT_2A: + return 21; + case SRSLTE_PUCCH_FORMAT_2B: + return 22; + default: + return 0; + } + return 0; +} + +uint32_t get_pucch_symbol(uint32_t m, srslte_pucch_format_t format, srslte_cp_t cp) { + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + if (m < 4) { + if (SRSLTE_CP_ISNORM(cp)) { + return pucch_symbol_format1_cpnorm[m]; + } else { + return pucch_symbol_format1_cpext[m]; + } + } + break; + case SRSLTE_PUCCH_FORMAT_2: + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + if (m < 5) { + if (SRSLTE_CP_ISNORM(cp)) { + return pucch_symbol_format2_cpnorm[m]; + } else { + return pucch_symbol_format2_cpext[m]; + } + } + break; + default: + return 0; + } + return 0; +} + +/* Choose PUCCH format based on pending transmission as described in 10.1 of 36.213 */ +srslte_pucch_format_t srslte_pucch_get_format(srslte_uci_data_t *uci_data, srslte_cp_t cp) +{ + srslte_pucch_format_t format = SRSLTE_PUCCH_FORMAT_ERROR; + // No CQI data + if (uci_data->uci_cqi_len == 0 && uci_data->uci_ri_len == 0) { + // 1-bit ACK + optional SR + if (uci_data->uci_ack_len == 1) { + format = SRSLTE_PUCCH_FORMAT_1A; + } + // 2-bit ACK + optional SR + else if (uci_data->uci_ack_len == 2) { + format = SRSLTE_PUCCH_FORMAT_1B; + } + // SR only + else if (uci_data->scheduling_request) { + format = SRSLTE_PUCCH_FORMAT_1; + } + } + // CQI data + else { + // CQI and no ack + if (uci_data->uci_ack_len == 0) { + format = SRSLTE_PUCCH_FORMAT_2; + } + // CQI + 1-bit ACK + else if (uci_data->uci_ack_len == 1 && SRSLTE_CP_ISNORM(cp)) { + format = SRSLTE_PUCCH_FORMAT_2A; + } + // CQI + 2-bit ACK + else if (uci_data->uci_ack_len == 2) { + format = SRSLTE_PUCCH_FORMAT_2B; + } + // CQI + 2-bit ACK + cyclic prefix + else if (uci_data->uci_ack_len == 1 && SRSLTE_CP_ISEXT(cp)) { + format = SRSLTE_PUCCH_FORMAT_2B; + } + } + return format; +} + +/** Choose PUCCH resource as desribed in 10.1 of 36.213 */ +uint32_t srslte_pucch_get_npucch(uint32_t n_cce, srslte_pucch_format_t format, bool has_scheduling_request, srslte_pucch_sched_t *pucch_sched) +{ + uint32_t n_pucch = 0; + if (has_scheduling_request) { + n_pucch = pucch_sched->n_pucch_sr; + } else if (format < SRSLTE_PUCCH_FORMAT_2) { + if (pucch_sched->sps_enabled) { + n_pucch = pucch_sched->n_pucch_1[pucch_sched->tpc_for_pucch%4]; + } else { + n_pucch = n_cce + pucch_sched->N_pucch_1; + } + } else { + n_pucch = pucch_sched->n_pucch_2; + } + return n_pucch; +} + +uint32_t srslte_pucch_n_prb(srslte_pucch_cfg_t *cfg, srslte_pucch_format_t format, uint32_t n_pucch, + uint32_t nof_prb, srslte_cp_t cp, uint32_t ns) +{ + uint32_t m = srslte_pucch_m(cfg, format, n_pucch, cp); + // Determine n_prb + uint32_t n_prb = m/2; + if ((m+ns)%2) { + n_prb = nof_prb-1-m/2; + } + return n_prb; +} + +// Compute m according to Section 5.4.3 of 36.211 +uint32_t srslte_pucch_m(srslte_pucch_cfg_t *cfg, srslte_pucch_format_t format, uint32_t n_pucch, srslte_cp_t cp) { + uint32_t m=0; + switch (format) { + case SRSLTE_PUCCH_FORMAT_1: + case SRSLTE_PUCCH_FORMAT_1A: + case SRSLTE_PUCCH_FORMAT_1B: + m = cfg->n_rb_2; + + uint32_t c=SRSLTE_CP_ISNORM(cp)?3:2; + if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { + m = (n_pucch-c*cfg->N_cs/cfg->delta_pucch_shift)/(c*SRSLTE_NRE/cfg->delta_pucch_shift) + +cfg->n_rb_2+(uint32_t)ceilf((float) cfg->N_cs/8); + } + break; + case SRSLTE_PUCCH_FORMAT_2: + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + m = n_pucch/SRSLTE_NRE; + break; + default: + m = 0; + break; + } + return m; +} + +/* Generates n_cs_cell according to Sec 5.4 of 36.211 */ +int srslte_pucch_n_cs_cell(srslte_cell_t cell, uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB]) +{ + srslte_sequence_t seq; + bzero(&seq, sizeof(srslte_sequence_t)); + + srslte_sequence_LTE_pr(&seq, 8*SRSLTE_CP_NSYMB(cell.cp)*SRSLTE_NSLOTS_X_FRAME, cell.id); + + for (uint32_t ns=0;nsN_cs/cfg->delta_pucch_shift)?cfg->N_cs:SRSLTE_NRE; + + uint32_t n_prime = n_pucch; + if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { + n_prime = (n_pucch-c*cfg->N_cs/cfg->delta_pucch_shift)%(c*SRSLTE_NRE/cfg->delta_pucch_shift); + } + if (ns%2) { + if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { + n_prime = (c*(n_prime+1))%(c*SRSLTE_NRE/cfg->delta_pucch_shift+1)-1; + } else { + uint32_t d=SRSLTE_CP_ISNORM(cp)?2:0; + uint32_t h=(n_prime+d)%(c*N_prime/cfg->delta_pucch_shift); + n_prime = (h/c)+(h%c)*N_prime/cfg->delta_pucch_shift; + } + } + + if (n_prime_ns) { + *n_prime_ns = n_prime; + } + + uint32_t n_oc_div = (!is_dmrs && SRSLTE_CP_ISEXT(cp))?2:1; + + uint32_t n_oc = n_prime*cfg->delta_pucch_shift/N_prime; + if (!is_dmrs && SRSLTE_CP_ISEXT(cp)) { + n_oc *= 2; + } + if (n_oc_ptr) { + *n_oc_ptr = n_oc; + } + uint32_t n_cs = 0; + if (SRSLTE_CP_ISNORM(cp)) { + n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+(n_oc%cfg->delta_pucch_shift))%N_prime)%SRSLTE_NRE; + } else { + n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+n_oc/n_oc_div)%N_prime)%SRSLTE_NRE; + } + + DEBUG("n_cs=%d, N_prime=%d, delta_pucch=%d, n_prime=%d, ns=%d, l=%d, ns_cs_cell=%d\n", + n_cs, N_prime, cfg->delta_pucch_shift, n_prime, ns, l, n_cs_cell[ns][l]); + + return 2 * M_PI * (n_cs) / SRSLTE_NRE; +} + +/* Calculates alpha for format 2/a/b according to 5.4.2 of 36.211 */ +float srslte_pucch_alpha_format2(uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB], + srslte_pucch_cfg_t *cfg, + uint32_t n_pucch, + uint32_t ns, uint32_t l) +{ + uint32_t n_prime = n_pucch%SRSLTE_NRE; + if (n_pucch >= SRSLTE_NRE*cfg->n_rb_2) { + n_prime = (n_pucch + cfg->N_cs + 1)%SRSLTE_NRE; + } + if (ns%2) { + n_prime = (SRSLTE_NRE*(n_prime+1))%(SRSLTE_NRE+1)-1; + if (n_pucch >= SRSLTE_NRE*cfg->n_rb_2) { + int x = (SRSLTE_NRE-2-(int) n_pucch)%SRSLTE_NRE; + if (x >= 0) { + n_prime = (uint32_t) x; + } else { + n_prime = SRSLTE_NRE+x; + } + } + } + uint32_t n_cs = (n_cs_cell[ns][l]+n_prime)%SRSLTE_NRE; + float alpha = 2 * M_PI * (n_cs) / SRSLTE_NRE; + DEBUG("n_pucch: %d, ns: %d, l: %d, n_prime: %d, n_cs: %d, alpha=%f\n", n_pucch, ns, l, n_prime, n_cs, alpha); + return alpha; +} + +/* Map PUCCH symbols to physical resources according to 5.4.3 in 36.211 */ +static int pucch_cp(srslte_pucch_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *source, cf_t *dest, bool source_is_grid) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q && source && dest) { + ret = SRSLTE_ERROR; + uint32_t nsymbols = SRSLTE_CP_ISNORM(q->cell.cp)?SRSLTE_CP_NORM_NSYMB:SRSLTE_CP_EXT_NSYMB; + + uint32_t n_re = 0; + uint32_t N_sf_0 = get_N_sf(format, 0, q->shortened); + for (uint32_t ns=0;ns<2;ns++) { + uint32_t N_sf = get_N_sf(format, ns%2, q->shortened); + + // Determine n_prb + uint32_t n_prb = srslte_pucch_n_prb(&q->pucch_cfg, format, n_pucch, q->cell.nof_prb, q->cell.cp, ns); + q->last_n_prb = n_prb; + if (n_prb < q->cell.nof_prb) { + for (uint32_t i=0;icell.cp); + if (!source_is_grid) { + memcpy(&dest[SRSLTE_RE_IDX(q->cell.nof_prb, l+ns*nsymbols, n_prb*SRSLTE_NRE)], + &source[i*SRSLTE_NRE+ns*N_sf_0*SRSLTE_NRE], + SRSLTE_NRE*sizeof(cf_t)); + } else { + memcpy(&dest[i*SRSLTE_NRE+ns*N_sf_0*SRSLTE_NRE], + &source[SRSLTE_RE_IDX(q->cell.nof_prb, l+ns*nsymbols, n_prb*SRSLTE_NRE)], + SRSLTE_NRE*sizeof(cf_t)); + } + n_re += SRSLTE_NRE; + } + } else { + return SRSLTE_ERROR; + } + } + ret = n_re; + } + return ret; +} + +static int pucch_put(srslte_pucch_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *z, cf_t *output) { + return pucch_cp(q, format, n_pucch, z, output, false); +} + +static int pucch_get(srslte_pucch_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *input, cf_t *z) { + return pucch_cp(q, format, n_pucch, input, z, true); +} + +void srslte_pucch_set_threshold(srslte_pucch_t *q, float format1_threshold) { + q->threshold_format1 = format1_threshold; +} + +/** Initializes the PDCCH transmitter and receiver */ +int srslte_pucch_init(srslte_pucch_t *q) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_pucch_t)); + + if (srslte_modem_table_lte(&q->mod, SRSLTE_MOD_QPSK)) { + return SRSLTE_ERROR; + } + + q->users = calloc(sizeof(srslte_pucch_user_t*), 1+SRSLTE_SIRNTI); + if (!q->users) { + perror("malloc"); + goto clean_exit; + } + + srslte_uci_cqi_pucch_init(&q->cqi); + + q->z = srslte_vec_malloc(sizeof(cf_t)*SRSLTE_PUCCH_MAX_SYMBOLS); + q->z_tmp = srslte_vec_malloc(sizeof(cf_t)*SRSLTE_PUCCH_MAX_SYMBOLS); + q->ce = srslte_vec_malloc(sizeof(cf_t)*SRSLTE_PUCCH_MAX_SYMBOLS); + + q->threshold_format1 = 0.8; + + ret = SRSLTE_SUCCESS; + } +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_pucch_free(q); + } + return ret; +} + +void srslte_pucch_free(srslte_pucch_t *q) { + if (q->users) { + for (int rnti=0;rnti<=SRSLTE_SIRNTI;rnti++) { + srslte_pucch_clear_rnti(q, rnti); + } + free(q->users); + } + srslte_uci_cqi_pucch_free(&q->cqi); + if (q->z) { + free(q->z); + } + if (q->z_tmp) { + free(q->z_tmp); + } + if (q->ce) { + free(q->ce); + } + + srslte_modem_table_free(&q->mod); + bzero(q, sizeof(srslte_pucch_t)); +} + +int srslte_pucch_set_cell(srslte_pucch_t *q, srslte_cell_t cell) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && srslte_cell_isvalid(&cell)) { + + srslte_pucch_cfg_default(&q->pucch_cfg); + + if (cell.id != q->cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + // Precompute group hopping values u. + if (srslte_group_hopping_f_gh(q->f_gh, q->cell.id)) { + return SRSLTE_ERROR; + } + + if (srslte_pucch_n_cs_cell(q->cell, q->n_cs_cell)) { + return SRSLTE_ERROR; + } + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +void srslte_pucch_clear_rnti(srslte_pucch_t *q, uint16_t rnti) { + if (q->users[rnti]) { + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + srslte_sequence_free(&q->users[rnti]->seq_f2[i]); + } + free(q->users[rnti]); + q->users[rnti] = NULL; + } +} + +int srslte_pucch_set_crnti(srslte_pucch_t *q, uint16_t rnti) { + if (!q->users[rnti]) { + q->users[rnti] = calloc(1, sizeof(srslte_pucch_user_t)); + if (q->users[rnti]) { + for (uint32_t sf_idx=0;sf_idxusers[rnti]->seq_f2[sf_idx], rnti, 2*sf_idx, q->cell.id)) { + fprintf(stderr, "Error computing PUCCH Format 2 scrambling sequence\n"); + srslte_pucch_clear_rnti(q, rnti); + return SRSLTE_ERROR; + } + } + q->users[rnti]->sequence_generated = true; + } + } + return SRSLTE_SUCCESS; +} + +bool srslte_pucch_set_cfg(srslte_pucch_t *q, srslte_pucch_cfg_t *cfg, bool group_hopping_en) +{ + q->group_hopping_en = group_hopping_en; + if (cfg) { + if (srslte_pucch_cfg_isvalid(cfg, q->cell.nof_prb)) { + memcpy(&q->pucch_cfg, cfg, sizeof(srslte_pucch_cfg_t)); + return true; + } else { + return false; + } + } else { + return false; + } +} + +static cf_t uci_encode_format1() { + return 1.0; +} + +static cf_t uci_encode_format1a(uint8_t bit) { + return bit?-1.0:1.0; +} + +static cf_t uci_encode_format1b(uint8_t bits[2]) { + if (bits[0] == 0) { + if (bits[1] == 0) { + return 1; + } else { + return -I; + } + } else { + if (bits[1] == 0) { + return I; + } else { + return -1.0; + } + } +} + +/* Modulates bit 20 and 21 for Formats 2a and 2b as in Table 5.4.2-1 in 36.211 */ +int srslte_pucch_format2ab_mod_bits(srslte_pucch_format_t format, uint8_t bits[2], cf_t *d_10) { + if (d_10) { + if (format == SRSLTE_PUCCH_FORMAT_2A) { + *d_10 = bits[0]?-1.0:1.0; + return SRSLTE_SUCCESS; + } else if (format == SRSLTE_PUCCH_FORMAT_2B) { + if (bits[0] == 0) { + if (bits[1] == 0) { + *d_10 = 1.0; + } else { + *d_10 = -I; + } + } else { + if (bits[1] == 0) { + *d_10 = I; + } else { + *d_10 = -1.0; + } + } + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } +} + +/* Encode PUCCH bits according to Table 5.4.1-1 in Section 5.4.1 of 36.211 */ +static int uci_mod_bits(srslte_pucch_t *q, srslte_pucch_format_t format, uint8_t bits[SRSLTE_PUCCH_MAX_BITS], uint32_t sf_idx, uint16_t rnti) +{ + uint8_t tmp[2]; + + switch(format) { + case SRSLTE_PUCCH_FORMAT_1: + q->d[0] = uci_encode_format1(); + break; + case SRSLTE_PUCCH_FORMAT_1A: + q->d[0] = uci_encode_format1a(bits[0]); + break; + case SRSLTE_PUCCH_FORMAT_1B: + tmp[0] = bits[0]; + tmp[1] = bits[1]; + q->d[0] = uci_encode_format1b(tmp); + break; + case SRSLTE_PUCCH_FORMAT_2: + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + if (q->users[rnti] && q->users[rnti]->sequence_generated) { + memcpy(q->bits_scram, bits, SRSLTE_PUCCH2_NOF_BITS*sizeof(uint8_t)); + srslte_scrambling_b(&q->users[rnti]->seq_f2[sf_idx], q->bits_scram); + srslte_mod_modulate(&q->mod, q->bits_scram, q->d, SRSLTE_PUCCH2_NOF_BITS); + } else { + fprintf(stderr, "Error modulating PUCCH2 bits: rnti not set\n"); + return -1; + } + break; + default: + fprintf(stderr, "PUCCH format 2 not supported\n"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +// Declare this here, since we can not include refsignal_ul.h +void srslte_refsignal_r_uv_arg_1prb(float *arg, uint32_t u); + + +static int pucch_encode_(srslte_pucch_t* q, srslte_pucch_format_t format, + uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti, + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS], bool signal_only) +{ + if (!signal_only) { + if (uci_mod_bits(q, format, bits, sf_idx, rnti)) { + fprintf(stderr, "Error encoding PUCCH bits\n"); + return SRSLTE_ERROR; + } + } else { + for (int i=0;id[i] = 1.0; + } + } + uint32_t N_sf_0 = get_N_sf(format, 0, q->shortened); + for (uint32_t ns=2*sf_idx;ns<2*(sf_idx+1);ns++) { + uint32_t N_sf = get_N_sf(format, ns%2, q->shortened); + DEBUG("ns=%d, N_sf=%d\n", ns, N_sf); + // Get group hopping number u + uint32_t f_gh=0; + if (q->group_hopping_en) { + f_gh = q->f_gh[ns]; + } + uint32_t u = (f_gh + (q->cell.id%30))%30; + + srslte_refsignal_r_uv_arg_1prb(q->tmp_arg, u); + uint32_t N_sf_widx = N_sf==3?1:0; + for (uint32_t m=0;mcell.cp); + float alpha=0; + if (format >= SRSLTE_PUCCH_FORMAT_2) { + alpha = srslte_pucch_alpha_format2(q->n_cs_cell, &q->pucch_cfg, n_pucch, ns, l); + for (uint32_t n=0;nd[(ns%2)*N_sf+m]*cexpf(I*(q->tmp_arg[n]+alpha*n)); + } + } else { + uint32_t n_prime_ns=0; + uint32_t n_oc=0; + alpha = srslte_pucch_alpha_format1(q->n_cs_cell, &q->pucch_cfg, n_pucch, q->cell.cp, true, ns, l, &n_oc, &n_prime_ns); + float S_ns = 0; + if (n_prime_ns%2) { + S_ns = M_PI/2; + } + DEBUG("PUCCH d_0: %.1f+%.1fi, alpha: %.1f, n_oc: %d, n_prime_ns: %d, n_rb_2=%d\n", + __real__ q->d[0], __imag__ q->d[0], alpha, n_oc, n_prime_ns, q->pucch_cfg.n_rb_2); + + for (uint32_t n=0;nd[0]*cexpf(I*(w_n_oc[N_sf_widx][n_oc%3][m]+q->tmp_arg[n]+alpha*n+S_ns)); + } + } + } + } + return SRSLTE_SUCCESS; +} + +static int pucch_encode(srslte_pucch_t* q, srslte_pucch_format_t format, + uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti, + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], cf_t z[SRSLTE_PUCCH_MAX_SYMBOLS]) +{ + return pucch_encode_(q, format, n_pucch, sf_idx, rnti, bits, z, false); +} + + +/* Encode, modulate and resource mapping of PUCCH bits according to Section 5.4.1 of 36.211 */ +int srslte_pucch_encode(srslte_pucch_t* q, srslte_pucch_format_t format, + uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti, uint8_t bits[SRSLTE_PUCCH_MAX_BITS], + cf_t *sf_symbols) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + sf_symbols != NULL) + { + ret = SRSLTE_ERROR; + + // Shortened PUCCH happen in every cell-specific SRS subframes for Format 1/1a/1b + if (q->pucch_cfg.srs_configured && format < SRSLTE_PUCCH_FORMAT_2) { + q->shortened = false; + // If CQI is not transmitted, PUCCH will be normal unless ACK/NACK and SRS simultaneous transmission is enabled + if (q->pucch_cfg.srs_simul_ack) { + // If simultaneous ACK and SRS is enabled, PUCCH is shortened in cell-specific SRS subframes + if (srslte_refsignal_srs_send_cs(q->pucch_cfg.srs_cs_subf_cfg, sf_idx) == 1) { + q->shortened = true; + } + } + } + + q->last_n_pucch = n_pucch; + + if (pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z)) { + return SRSLTE_ERROR; + } + if (pucch_put(q, format, n_pucch, q->z, sf_symbols) < 0) { + fprintf(stderr, "Error putting PUCCH symbols\n"); + return SRSLTE_ERROR; + } + ret = SRSLTE_SUCCESS; + } + + return ret; +} + +float srslte_pucch_get_last_corr(srslte_pucch_t* q) +{ + return q->last_corr; +} + +/* Equalize, demodulate and decode PUCCH bits according to Section 5.4.1 of 36.211 */ +int srslte_pucch_decode(srslte_pucch_t* q, srslte_pucch_format_t format, + uint32_t n_pucch, uint32_t sf_idx, uint16_t rnti, cf_t *sf_symbols, cf_t *ce, float noise_estimate, + uint8_t bits[SRSLTE_PUCCH_MAX_BITS], uint32_t nof_bits) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL && + ce != NULL && + sf_symbols != NULL) + { + ret = SRSLTE_ERROR; + cf_t ref[SRSLTE_PUCCH_MAX_SYMBOLS]; + int16_t llr_pucch2[32]; + + // Shortened PUCCH happen in every cell-specific SRS subframes for Format 1/1a/1b + if (q->pucch_cfg.srs_configured && format < SRSLTE_PUCCH_FORMAT_2) { + q->shortened = false; + // If CQI is not transmitted, PUCCH will be normal unless ACK/NACK and SRS simultaneous transmission is enabled + if (q->pucch_cfg.srs_simul_ack) { + // If simultaneous ACK and SRS is enabled, PUCCH is shortened in cell-specific SRS subframes + if (srslte_refsignal_srs_send_cs(q->pucch_cfg.srs_cs_subf_cfg, sf_idx) == 1) { + q->shortened = true; + } + } + } + + q->last_n_pucch = n_pucch; + + int nof_re = pucch_get(q, format, n_pucch, sf_symbols, q->z_tmp); + if (nof_re < 0) { + fprintf(stderr, "Error getting PUCCH symbols\n"); + return SRSLTE_ERROR; + } + + if (pucch_get(q, format, n_pucch, ce, q->ce) < 0) { + fprintf(stderr, "Error getting PUCCH symbols\n"); + return SRSLTE_ERROR; + } + + // Equalization + srslte_predecoding_single(q->z_tmp, q->ce, q->z, NULL, nof_re, 1.0f, noise_estimate); + + // Perform ML-decoding + float corr=0, corr_max=-1e9; + uint8_t b_max = 0, b2_max = 0; // default bit value, eg. HI is NACK + switch(format) { + case SRSLTE_PUCCH_FORMAT_1: + bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); + pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp); + corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re); + if (corr >= q->threshold_format1) { + ret = 1; + } else { + ret = 0; + } + q->last_corr = corr; + DEBUG("format1 corr=%f, nof_re=%d, th=%f\n", corr, nof_re, q->threshold_format1); + break; + case SRSLTE_PUCCH_FORMAT_1A: + bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); + ret = 0; + for (uint8_t b=0;b<2;b++) { + bits[0] = b; + pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp); + corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re); + if (corr > corr_max) { + corr_max = corr; + b_max = b; + } + if (corr_max > q->threshold_format1) { // check with format1 in case ack+sr because ack only is binary + ret = 1; + } + DEBUG("format1a b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re); + } + q->last_corr = corr_max; + bits[0] = b_max; + break; + case SRSLTE_PUCCH_FORMAT_1B: + bzero(bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); + ret = 0; + for (uint8_t b=0;b<2;b++) { + for (uint8_t b2 = 0; b2 < 2; b2++) { + bits[0] = b; + bits[1] = b2; + pucch_encode(q, format, n_pucch, sf_idx, rnti, bits, q->z_tmp); + corr = srslte_vec_corr_ccc(q->z, q->z_tmp, nof_re); + if (corr > corr_max) { + corr_max = corr; + b_max = b; + b2_max = b2; + } + if (corr_max > q->threshold_format1) { // check with format1 in case ack+sr because ack only is binary + ret = 1; + } + DEBUG("format1b b=%d, corr=%f, nof_re=%d\n", b, corr, nof_re); + } + } + q->last_corr = corr_max; + bits[0] = b_max; + bits[1] = b2_max; + break; + case SRSLTE_PUCCH_FORMAT_2: + case SRSLTE_PUCCH_FORMAT_2A: + case SRSLTE_PUCCH_FORMAT_2B: + if (q->users[rnti] && q->users[rnti]->sequence_generated) { + pucch_encode_(q, format, n_pucch, sf_idx, rnti, NULL, ref, true); + srslte_vec_prod_conj_ccc(q->z, ref, q->z_tmp, SRSLTE_PUCCH_MAX_SYMBOLS); + for (int i=0;iz[i] = 0; + for (int j=0;jz[i] += q->z_tmp[i*SRSLTE_NRE+j]/SRSLTE_NRE; + } + } + srslte_demod_soft_demodulate_s(SRSLTE_MOD_QPSK, q->z, llr_pucch2, SRSLTE_PUCCH2_NOF_BITS/2); + srslte_scrambling_s(&q->users[rnti]->seq_f2[sf_idx], llr_pucch2); + q->last_corr = (float) srslte_uci_decode_cqi_pucch(&q->cqi, llr_pucch2, bits, nof_bits)/2000; + ret = 1; + } else { + fprintf(stderr, "Decoding PUCCH2: rnti not set\n"); + return -1; + } + break; + default: + fprintf(stderr, "PUCCH format %d not implemented\n", format); + return SRSLTE_ERROR; + } + } + + return ret; +} + + + diff --git a/lib/src/phy/phch/pusch.c b/lib/src/phy/phch/pusch.c new file mode 100644 index 0000000..aa049f7 --- /dev/null +++ b/lib/src/phy/phch/pusch.c @@ -0,0 +1,653 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/ch_estimation/refsignal_ul.h" +#include "srslte/phy/phch/pusch.h" +#include "srslte/phy/phch/pusch_cfg.h" +#include "srslte/phy/phch/uci.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/dft/dft_precoding.h" + +#define MAX_PUSCH_RE(cp) (2 * SRSLTE_CP_NSYMB(cp) * 12) + + + +const static srslte_mod_t modulations[4] = + { SRSLTE_MOD_BPSK, SRSLTE_MOD_QPSK, SRSLTE_MOD_16QAM, SRSLTE_MOD_64QAM }; + +static int f_hop_sum(srslte_pusch_t *q, uint32_t i) { + uint32_t sum = 0; + for (uint32_t k=i*10+1;kseq_type2_fo.c[k]<<(k-(i*10+1))); + } + return sum; +} + +static int f_hop(srslte_pusch_t *q, srslte_pusch_hopping_cfg_t *hopping, int i) { + if (i == -1) { + return 0; + } else { + if (hopping->n_sb == 1) { + return 0; + } else if (hopping->n_sb == 2) { + return (f_hop(q, hopping, i-1) + f_hop_sum(q, i))%2; + } else { + return (f_hop(q, hopping, i-1) + f_hop_sum(q, i)%(hopping->n_sb-1)+1)%hopping->n_sb; + } + } +} + +static int f_m(srslte_pusch_t *q, srslte_pusch_hopping_cfg_t *hopping, uint32_t i, uint32_t current_tx_nb) { + if (hopping->n_sb == 1) { + if (hopping->hop_mode == SRSLTE_PUSCH_HOP_MODE_INTER_SF) { + return current_tx_nb%2; + } else { + return i%2; + } + } else { + return q->seq_type2_fo.c[i*10]; + } +} + +/* Computes PUSCH frequency hopping as defined in Section 8.4 of 36.213 */ +void compute_freq_hopping(srslte_pusch_t *q, srslte_ra_ul_grant_t *grant, + srslte_pusch_hopping_cfg_t *hopping, + uint32_t sf_idx, uint32_t current_tx_nb) +{ + + for (uint32_t slot=0;slot<2;slot++) { + + INFO("PUSCH Freq hopping: %d\n", grant->freq_hopping); + uint32_t n_prb_tilde = grant->n_prb[slot]; + + if (grant->freq_hopping == 1) { + if (hopping->hop_mode == SRSLTE_PUSCH_HOP_MODE_INTER_SF) { + n_prb_tilde = grant->n_prb[current_tx_nb%2]; + } else { + n_prb_tilde = grant->n_prb[slot]; + } + } + if (grant->freq_hopping == 2) { + /* Freq hopping type 2 as defined in 5.3.4 of 36.211 */ + uint32_t n_vrb_tilde = grant->n_prb[0]; + if (hopping->n_sb > 1) { + n_vrb_tilde -= (hopping->hopping_offset-1)/2+1; + } + int i=0; + if (hopping->hop_mode == SRSLTE_PUSCH_HOP_MODE_INTER_SF) { + i = sf_idx; + } else { + i = 2*sf_idx+slot; + } + uint32_t n_rb_sb = q->cell.nof_prb; + if (hopping->n_sb > 1) { + n_rb_sb = (n_rb_sb-hopping->hopping_offset-hopping->hopping_offset%2)/hopping->n_sb; + } + n_prb_tilde = (n_vrb_tilde+f_hop(q, hopping, i)*n_rb_sb+ + (n_rb_sb-1)-2*(n_vrb_tilde%n_rb_sb)*f_m(q, hopping, i, current_tx_nb))%(n_rb_sb*hopping->n_sb); + + INFO("n_prb_tilde: %d, n_vrb_tilde: %d, n_rb_sb: %d, n_sb: %d\n", + n_prb_tilde, n_vrb_tilde, n_rb_sb, hopping->n_sb); + if (hopping->n_sb > 1) { + n_prb_tilde += (hopping->hopping_offset-1)/2+1; + } + + } + grant->n_prb_tilde[slot] = n_prb_tilde; + } +} + + +/* Allocate/deallocate PUSCH RBs to the resource grid + */ +int pusch_cp(srslte_pusch_t *q, srslte_ra_ul_grant_t *grant, cf_t *input, cf_t *output, bool advance_input) +{ + cf_t *in_ptr = input; + cf_t *out_ptr = output; + + uint32_t L_ref = 3; + if (SRSLTE_CP_ISEXT(q->cell.cp)) { + L_ref = 2; + } + for (uint32_t slot=0;slot<2;slot++) { + uint32_t N_srs = 0; + if (q->shortened && slot == 1) { + N_srs = 1; + } + INFO("%s PUSCH %d PRB to index %d at slot %d\n",advance_input?"Allocating":"Getting",grant->L_prb, grant->n_prb_tilde[slot], slot); + for (uint32_t l=0;lcell.cp)-N_srs;l++) { + if (l != L_ref) { + uint32_t idx = SRSLTE_RE_IDX(q->cell.nof_prb, l+slot*SRSLTE_CP_NSYMB(q->cell.cp), + grant->n_prb_tilde[slot]*SRSLTE_NRE); + if (advance_input) { + out_ptr = &output[idx]; + } else { + in_ptr = &input[idx]; + } + memcpy(out_ptr, in_ptr, grant->L_prb * SRSLTE_NRE * sizeof(cf_t)); + if (advance_input) { + in_ptr += grant->L_prb*SRSLTE_NRE; + } else { + out_ptr += grant->L_prb*SRSLTE_NRE; + } + } + } + } + if (advance_input) { + return in_ptr - input; + } else { + return out_ptr - output; + } +} + +int pusch_put(srslte_pusch_t *q, srslte_ra_ul_grant_t *grant, cf_t *input, cf_t *output) { + return pusch_cp(q, grant, input, output, true); +} + +int pusch_get(srslte_pusch_t *q, srslte_ra_ul_grant_t *grant, cf_t *input, cf_t *output) { + return pusch_cp(q, grant, input, output, false); +} + + +/** Initializes the PDCCH transmitter and receiver */ +int pusch_init(srslte_pusch_t *q, uint32_t max_prb, bool is_ue) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + int i; + + if (q != NULL) + { + + bzero(q, sizeof(srslte_pusch_t)); + ret = SRSLTE_ERROR; + q->max_re = max_prb * MAX_PUSCH_RE(SRSLTE_CP_NORM); + + INFO("Init PUSCH: %d PRBs\n", max_prb); + + for (i = 0; i < 4; i++) { + if (srslte_modem_table_lte(&q->mod[i], modulations[i])) { + goto clean; + } + srslte_modem_table_bytes(&q->mod[i]); + } + + q->is_ue = is_ue; + + q->users = calloc(sizeof(srslte_pusch_user_t*), q->is_ue?1:(1+SRSLTE_SIRNTI)); + if (!q->users) { + perror("malloc"); + goto clean; + } + + if (srslte_sequence_init(&q->tmp_seq, q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) { + goto clean; + } + + srslte_sch_init(&q->ul_sch); + + if (srslte_dft_precoding_init(&q->dft_precoding, max_prb, is_ue)) { + fprintf(stderr, "Error initiating DFT transform precoding\n"); + goto clean; + } + + // Allocate int16 for reception (LLRs). Buffer casted to uint8_t for transmission + q->q = srslte_vec_malloc(sizeof(int16_t) * q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM)); + if (!q->q) { + goto clean; + } + + // Allocate int16 for reception (LLRs). Buffer casted to uint8_t for transmission + q->g = srslte_vec_malloc(sizeof(int16_t) * q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM)); + if (!q->g) { + goto clean; + } + q->d = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->d) { + goto clean; + } + + if (!q->is_ue) { + q->ce = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->ce) { + goto clean; + } + } + q->z = srslte_vec_malloc(sizeof(cf_t) * q->max_re); + if (!q->z) { + goto clean; + } + + ret = SRSLTE_SUCCESS; + } + clean: + if (ret == SRSLTE_ERROR) { + srslte_pusch_free(q); + } + return ret; +} + +int srslte_pusch_init_ue(srslte_pusch_t *q, uint32_t max_prb) { + return pusch_init(q, max_prb, true); +} + +int srslte_pusch_init_enb(srslte_pusch_t *q, uint32_t max_prb) { + return pusch_init(q, max_prb, false); +} + +void srslte_pusch_free(srslte_pusch_t *q) { + int i; + + if (q->q) { + free(q->q); + } + if (q->d) { + free(q->d); + } + if (q->g) { + free(q->g); + } + if (q->ce) { + free(q->ce); + } + if (q->z) { + free(q->z); + } + + srslte_dft_precoding_free(&q->dft_precoding); + + if (q->users) { + if (q->is_ue) { + srslte_pusch_free_rnti(q, 0); + } else { + for (int rnti=0;rnti<=SRSLTE_SIRNTI;rnti++) { + srslte_pusch_free_rnti(q, rnti); + } + } + free(q->users); + } + + srslte_sequence_free(&q->seq_type2_fo); + + srslte_sequence_free(&q->tmp_seq); + + for (i = 0; i < 4; i++) { + srslte_modem_table_free(&q->mod[i]); + } + srslte_sch_free(&q->ul_sch); + + bzero(q, sizeof(srslte_pusch_t)); + +} + +int srslte_pusch_set_cell(srslte_pusch_t *q, srslte_cell_t cell) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_cell_isvalid(&cell)) + { + + q->max_re = cell.nof_prb * MAX_PUSCH_RE(q->cell.cp); + + INFO("PUSCH: Cell config PCI=%d, %d ports %d PRBs, max_symbols: %d\n", + q->cell.id, q->cell.nof_ports, q->cell.nof_prb, q->max_re); + + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + /* Precompute sequence for type2 frequency hopping */ + if (srslte_sequence_LTE_pr(&q->seq_type2_fo, 210, q->cell.id)) { + fprintf(stderr, "Error initiating type2 frequency hopping sequence\n"); + return SRSLTE_ERROR; + } + + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + + + +/* Configures the structure srslte_pusch_cfg_t from the UL DCI allocation dci_msg. + * If dci_msg is NULL, the grant is assumed to be already stored in cfg->grant + */ +int srslte_pusch_cfg(srslte_pusch_t *q, + srslte_pusch_cfg_t *cfg, + srslte_ra_ul_grant_t *grant, + srslte_uci_cfg_t *uci_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg, + uint32_t tti, + uint32_t rv_idx, + uint32_t current_tx_nb) +{ + if (q && cfg && grant) { + memcpy(&cfg->grant, grant, sizeof(srslte_ra_ul_grant_t)); + + if (srslte_cbsegm(&cfg->cb_segm, cfg->grant.mcs.tbs)) { + fprintf(stderr, "Error computing Codeblock segmentation for TBS=%d\n", cfg->grant.mcs.tbs); + return SRSLTE_ERROR; + } + + /* Compute PUSCH frequency hopping */ + if (hopping_cfg) { + compute_freq_hopping(q, &cfg->grant, hopping_cfg, tti%10, current_tx_nb); + } else { + cfg->grant.n_prb_tilde[0] = cfg->grant.n_prb[0]; + cfg->grant.n_prb_tilde[1] = cfg->grant.n_prb[1]; + } + if (srs_cfg) { + q->shortened = false; + if (srs_cfg->configured) { + // If UE-specific SRS is configured, PUSCH is shortened every time UE transmits SRS even if overlaping in the same RB or not + if (srslte_refsignal_srs_send_cs(srs_cfg->subframe_config, tti%10) == 1 && + srslte_refsignal_srs_send_ue(srs_cfg->I_srs, tti) == 1) + { + q->shortened = true; + /* If RBs are contiguous, PUSCH is not shortened */ + uint32_t k0_srs = srslte_refsignal_srs_rb_start_cs(srs_cfg->bw_cfg, q->cell.nof_prb); + uint32_t nrb_srs = srslte_refsignal_srs_rb_L_cs(srs_cfg->bw_cfg, q->cell.nof_prb); + for (uint32_t ns=0;ns<2 && q->shortened;ns++) { + if (cfg->grant.n_prb_tilde[ns] == k0_srs + nrb_srs || // If PUSCH is contiguous on the right-hand side of SRS + cfg->grant.n_prb_tilde[ns] + cfg->grant.L_prb == k0_srs) // If SRS is contiguous on the left-hand side of PUSCH + { + q->shortened = false; + } + } + } + // If not coincides with UE transmission. PUSCH shall be shortened if cell-specific SRS transmission RB + //coincides with PUSCH allocated RB + if (!q->shortened) { + if (srslte_refsignal_srs_send_cs(srs_cfg->subframe_config, tti%10) == 1) { + uint32_t k0_srs = srslte_refsignal_srs_rb_start_cs(srs_cfg->bw_cfg, q->cell.nof_prb); + uint32_t nrb_srs = srslte_refsignal_srs_rb_L_cs(srs_cfg->bw_cfg, q->cell.nof_prb); + for (uint32_t ns=0;ns<2 && !q->shortened;ns++) { + if ((cfg->grant.n_prb_tilde[ns] >= k0_srs && cfg->grant.n_prb_tilde[ns] < k0_srs + nrb_srs) || + (cfg->grant.n_prb_tilde[ns] + cfg->grant.L_prb >= k0_srs && + cfg->grant.n_prb_tilde[ns] + cfg->grant.L_prb < k0_srs + nrb_srs) || + (cfg->grant.n_prb_tilde[ns] <= k0_srs && cfg->grant.n_prb_tilde[ns] + cfg->grant.L_prb >= k0_srs + nrb_srs)) + { + q->shortened = true; + } + } + } + } + } + } + + /* Compute final number of bits and RE */ + srslte_ra_ul_grant_to_nbits(&cfg->grant, q->cell.cp, q->shortened?1:0, &cfg->nbits); + + cfg->sf_idx = tti%10; + cfg->tti = tti; + cfg->rv = rv_idx; + cfg->cp = q->cell.cp; + + // Save UCI configuration + if (uci_cfg) { + memcpy(&cfg->uci_cfg, uci_cfg, sizeof(srslte_uci_cfg_t)); + } + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/* Precalculate the PUSCH scramble sequences for a given RNTI. This function takes a while + * to execute, so shall be called once the final C-RNTI has been allocated for the session. + * For the connection procedure, use srslte_pusch_encode() functions */ +int srslte_pusch_set_rnti(srslte_pusch_t *q, uint16_t rnti) { + uint32_t i; + + uint32_t rnti_idx = q->is_ue?0:rnti; + + if (!q->users[rnti_idx] || q->is_ue) { + if (!q->users[rnti_idx]) { + q->users[rnti_idx] = calloc(1, sizeof(srslte_pusch_user_t)); + if (!q->users[rnti_idx]) { + perror("calloc"); + return -1; + } + } + for (i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + if (srslte_sequence_pusch(&q->users[rnti_idx]->seq[i], rnti, 2 * i, q->cell.id, + q->max_re * srslte_mod_bits_x_symbol(SRSLTE_MOD_64QAM))) + { + fprintf(stderr, "Error initializing PUSCH scrambling sequence\n"); + srslte_pusch_free_rnti(q, rnti); + return SRSLTE_ERROR; + } + } + q->ue_rnti = rnti; + q->users[rnti_idx]->cell_id = q->cell.id; + q->users[rnti_idx]->sequence_generated = true; + } else { + fprintf(stderr, "Error generating PUSCH sequence: rnti=0x%x already generated\n", rnti); + } + return SRSLTE_SUCCESS; +} + +void srslte_pusch_free_rnti(srslte_pusch_t *q, uint16_t rnti) { + + uint32_t rnti_idx = q->is_ue?0:rnti; + + if (q->users[rnti_idx]) { + for (int i = 0; i < SRSLTE_NSUBFRAMES_X_FRAME; i++) { + srslte_sequence_free(&q->users[rnti_idx]->seq[i]); + } + free(q->users[rnti_idx]); + q->users[rnti_idx] = NULL; + q->ue_rnti = 0; + } +} + +static srslte_sequence_t *get_user_sequence(srslte_pusch_t *q, uint16_t rnti, uint32_t sf_idx, uint32_t len) +{ + uint32_t rnti_idx = q->is_ue?0:rnti; + + // The scrambling sequence is pregenerated for all RNTIs in the eNodeB but only for C-RNTI in the UE + if (q->users[rnti_idx] && q->users[rnti_idx]->sequence_generated && + q->users[rnti_idx]->cell_id == q->cell.id && + q->ue_rnti == rnti && + ((rnti >= SRSLTE_CRNTI_START && rnti < SRSLTE_CRNTI_END) || !q->is_ue)) + { + return &q->users[rnti_idx]->seq[sf_idx]; + } else { + srslte_sequence_pusch(&q->tmp_seq, rnti, 2 * sf_idx, q->cell.id, len); + return &q->tmp_seq; + } +} + +/** Converts the PUSCH data bits to symbols mapped to the slot ready for transmission + */ +int srslte_pusch_encode(srslte_pusch_t *q, srslte_pusch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, srslte_uci_data_t uci_data, uint16_t rnti, + cf_t *sf_symbols) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + cfg != NULL) + { + + if (cfg->nbits.nof_re > q->max_re) { + fprintf(stderr, "Error too many RE per subframe (%d). PUSCH configured for %d RE (%d PRB)\n", + cfg->nbits.nof_re, q->max_re, q->cell.nof_prb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + INFO("Encoding PUSCH SF: %d, Mod %s, RNTI: %d, TBS: %d, NofRE: %d, NofSymbols=%d, NofBitsE: %d, rv_idx: %d\n", + cfg->sf_idx, srslte_mod_string(cfg->grant.mcs.mod), rnti, + cfg->grant.mcs.tbs, cfg->nbits.nof_re, cfg->nbits.nof_symb, cfg->nbits.nof_bits, cfg->rv); + + bzero(q->q, cfg->nbits.nof_bits); + if (srslte_ulsch_uci_encode(&q->ul_sch, cfg, softbuffer, data, uci_data, q->g, q->q)) { + fprintf(stderr, "Error encoding TB\n"); + return SRSLTE_ERROR; + } + + // Generate scrambling sequence if not pre-generated + srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits); + + // Run scrambling + srslte_scrambling_bytes(seq, (uint8_t*) q->q, cfg->nbits.nof_bits); + + // Correct UCI placeholder/repetition bits + uint8_t *d = q->q; + for (int i = 0; i < q->ul_sch.nof_ri_ack_bits; i++) { + if (q->ul_sch.ack_ri_bits[i].type == UCI_BIT_PLACEHOLDER) { + d[q->ul_sch.ack_ri_bits[i].position/8] |= (1<<(7-q->ul_sch.ack_ri_bits[i].position%8)); + } else if (q->ul_sch.ack_ri_bits[i].type == UCI_BIT_REPETITION) { + if (q->ul_sch.ack_ri_bits[i].position > 1) { + uint32_t p=q->ul_sch.ack_ri_bits[i].position; + uint8_t bit = d[(p-1)/8] & (1<<(7-(p-1)%8)); + if (bit) { + d[p/8] |= 1<<(7-p%8); + } else { + d[p/8] &= ~(1<<(7-p%8)); + } + } + } + } + + // Bit mapping + srslte_mod_modulate_bytes(&q->mod[cfg->grant.mcs.mod], (uint8_t*) q->q, q->d, cfg->nbits.nof_bits); + + // DFT precoding + srslte_dft_precoding(&q->dft_precoding, q->d, q->z, cfg->grant.L_prb, cfg->nbits.nof_symb); + + // Mapping to resource elements + pusch_put(q, &cfg->grant, q->z, sf_symbols); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +/** Decodes the PUSCH from the received symbols + */ +int srslte_pusch_decode(srslte_pusch_t *q, + srslte_pusch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + cf_t *sf_symbols, + cf_t *ce, float noise_estimate, uint16_t rnti, + uint8_t *data, srslte_cqi_value_t *cqi_value, srslte_uci_data_t *uci_data) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t n; + + if (q != NULL && + sf_symbols != NULL && + data != NULL && + cfg != NULL) + { + + INFO("Decoding PUSCH SF: %d, Mod %s, NofBits: %d, NofRE: %d, NofSymbols=%d, NofBitsE: %d, rv_idx: %d\n", + cfg->sf_idx, srslte_mod_string(cfg->grant.mcs.mod), cfg->grant.mcs.tbs, + cfg->nbits.nof_re, cfg->nbits.nof_symb, cfg->nbits.nof_bits, cfg->rv); + + /* extract symbols */ + n = pusch_get(q, &cfg->grant, sf_symbols, q->d); + if (n != cfg->nbits.nof_re) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits.nof_re, n); + return SRSLTE_ERROR; + } + + /* extract channel estimates */ + n = pusch_get(q, &cfg->grant, ce, q->ce); + if (n != cfg->nbits.nof_re) { + fprintf(stderr, "Error expecting %d symbols but got %d\n", cfg->nbits.nof_re, n); + return SRSLTE_ERROR; + } + + // Equalization + srslte_predecoding_single(q->d, q->ce, q->z, NULL, cfg->nbits.nof_re, 1.0f, noise_estimate); + + // DFT predecoding + srslte_dft_precoding(&q->dft_precoding, q->z, q->d, cfg->grant.L_prb, cfg->nbits.nof_symb); + + // Soft demodulation + srslte_demod_soft_demodulate_s(cfg->grant.mcs.mod, q->d, q->q, cfg->nbits.nof_re); + + // Generate scrambling sequence if not pre-generated + srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits); + + // Set CQI len assuming RI = 1 (3GPP 36.212 Clause 5.2.4.1. Uplink control information on PUSCH without UL-SCH data) + if (cqi_value) { + if (cqi_value->type == SRSLTE_CQI_TYPE_SUBBAND_HL && cqi_value->subband_hl.ri_present) { + cqi_value->subband_hl.rank_is_not_one = false; + uci_data->uci_ri_len = (q->cell.nof_ports == 4) ? 2 : 1; + } + uci_data->uci_cqi_len = (uint32_t) srslte_cqi_size(cqi_value); + } + + // Decode RI/HARQ bits before descrambling + if (srslte_ulsch_uci_decode_ri_ack(&q->ul_sch, cfg, softbuffer, q->q, seq->c, uci_data)) { + fprintf(stderr, "Error decoding RI/HARQ bits\n"); + return SRSLTE_ERROR; + } + + // Set CQI len with corresponding RI + if (cqi_value) { + if (cqi_value->type == SRSLTE_CQI_TYPE_SUBBAND_HL) { + cqi_value->subband_hl.rank_is_not_one = (uci_data->uci_ri != 0); + } + uci_data->uci_cqi_len = (uint32_t) srslte_cqi_size(cqi_value); + } + + // Descrambling + srslte_scrambling_s_offset(seq, q->q, 0, cfg->nbits.nof_bits); + + // Decode + ret = srslte_ulsch_uci_decode(&q->ul_sch, cfg, softbuffer, q->q, q->g, data, uci_data); + + // Unpack CQI value if available + if (cqi_value) { + srslte_cqi_value_unpack(uci_data->uci_cqi, cqi_value); + } + } + + return ret; +} + +uint32_t srslte_pusch_last_noi(srslte_pusch_t *q) { + return q->ul_sch.nof_iterations; +} + + + diff --git a/lib/src/phy/phch/ra.c b/lib/src/phy/phch/ra.c new file mode 100644 index 0000000..4a90435 --- /dev/null +++ b/lib/src/phy/phch/ra.c @@ -0,0 +1,879 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/phch/ra.h" +#include "srslte/phy/utils/bit.h" + +#include "tbs_tables.h" + +#define min(a,b) (a= nof_prb / 2 - 3 && prb_idx < nof_prb / 2 + 3 + (nof_prb%2))) { + if (subframe == 0) { + if (slot == 0) { + re = (SRSLTE_CP_NSYMB(cp_) - nof_ctrl_symbols - 2) * SRSLTE_NRE; + } else { + if (SRSLTE_CP_ISEXT(cp_)) { + re = (SRSLTE_CP_NSYMB(cp_) - 4) * SRSLTE_NRE; + skip_refs = false; + } else { + re = (SRSLTE_CP_NSYMB(cp_) - 4) * SRSLTE_NRE + 2 * nof_ports; + } + } + } else if (subframe == 5) { + if (slot == 0) { + re = (SRSLTE_CP_NSYMB(cp_) - nof_ctrl_symbols - 2) * SRSLTE_NRE; + } + } + if ((nof_prb % 2) + && (prb_idx == nof_prb / 2 - 3 || prb_idx == nof_prb / 2 + 3)) { + if (slot == 0) { + re += 2 * SRSLTE_NRE / 2; + } else if (subframe == 0) { + re += 4 * SRSLTE_NRE / 2 - nof_ports; + if (SRSLTE_CP_ISEXT(cp_)) { + re -= nof_ports > 2 ? 2 : nof_ports; + } + } + } + } + + // remove references + if (skip_refs) { + if(sf_type == SRSLTE_SF_NORM){ + switch (nof_ports) { + case 1: + case 2: + re -= 2 * (slot + 1) * nof_ports; + break; + case 4: + if (slot == 1) { + re -= 12; + } else { + re -= 4; + if (nof_ctrl_symbols == 1) { + re -= 4; + } + } + break; + } + } + if(sf_type == SRSLTE_SF_MBSFN){ + re -= 6*(slot + 1); + } + } + return re; +} + +int srslte_ra_ul_dci_to_grant_prb_allocation(srslte_ra_ul_dci_t *dci, srslte_ra_ul_grant_t *grant, uint32_t n_rb_ho, uint32_t nof_prb) +{ + bzero(grant, sizeof(srslte_ra_ul_grant_t)); + + grant->ncs_dmrs = dci->n_dmrs; + grant->L_prb = dci->type2_alloc.L_crb; + uint32_t n_prb_1 = dci->type2_alloc.RB_start; + uint32_t n_rb_pusch = 0; + + if (n_rb_ho%2) { + n_rb_ho++; + } + + if (dci->freq_hop_fl == SRSLTE_RA_PUSCH_HOP_DISABLED || dci->freq_hop_fl == SRSLTE_RA_PUSCH_HOP_TYPE2) { + /* For no freq hopping or type2 freq hopping, n_prb is the same + * n_prb_tilde is calculated during resource mapping + */ + for (uint32_t i=0;i<2;i++) { + grant->n_prb[i] = n_prb_1; + } + if (dci->freq_hop_fl == SRSLTE_RA_PUSCH_HOP_DISABLED) { + grant->freq_hopping = 0; + } else { + grant->freq_hopping = 2; + } + INFO("prb1: %d, prb2: %d, L: %d\n", grant->n_prb[0], grant->n_prb[1], grant->L_prb); + } else { + /* Type1 frequency hopping as defined in 8.4.1 of 36.213 + * frequency offset between 1st and 2nd slot is fixed. + */ + n_rb_pusch = nof_prb - n_rb_ho - (nof_prb%2); + + // starting prb idx for slot 0 is as given by resource grant + grant->n_prb[0] = n_prb_1; + if (n_prb_1 < n_rb_ho/2) { + INFO("Invalid Frequency Hopping parameters. Offset: %d, n_prb_1: %d\n", n_rb_ho, n_prb_1); + return SRSLTE_ERROR; + } + uint32_t n_prb_1_tilde = n_prb_1; + + // prb idx for slot 1 + switch(dci->freq_hop_fl) { + case SRSLTE_RA_PUSCH_HOP_QUART: + grant->n_prb[1] = (n_rb_pusch/4+ n_prb_1_tilde)%n_rb_pusch; + break; + case SRSLTE_RA_PUSCH_HOP_QUART_NEG: + if (n_prb_1 < n_rb_pusch/4) { + grant->n_prb[1] = (n_rb_pusch+ n_prb_1_tilde -n_rb_pusch/4); + } else { + grant->n_prb[1] = (n_prb_1_tilde -n_rb_pusch/4); + } + break; + case SRSLTE_RA_PUSCH_HOP_HALF: + grant->n_prb[1] = (n_rb_pusch/2+ n_prb_1_tilde)%n_rb_pusch; + break; + default: + break; + } + INFO("n_rb_pusch: %d, prb1: %d, prb2: %d, L: %d\n", n_rb_pusch, grant->n_prb[0], grant->n_prb[1], grant->L_prb); + grant->freq_hopping = 1; + } + + if (grant->n_prb[0] + grant->L_prb <= nof_prb && + grant->n_prb[1] + grant->L_prb <= nof_prb) + { + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +static void ul_dci_to_grant_mcs(srslte_ra_ul_dci_t *dci, srslte_ra_ul_grant_t *grant) { + // 8.6.2 First paragraph + if (dci->mcs_idx <= 28) { + /* Table 8.6.1-1 on 36.213 */ + if (dci->mcs_idx < 11) { + grant->mcs.mod = SRSLTE_MOD_QPSK; + grant->mcs.tbs = srslte_ra_tbs_from_idx(dci->mcs_idx, grant->L_prb); + } else if (dci->mcs_idx < 21) { + grant->mcs.mod = SRSLTE_MOD_16QAM; + grant->mcs.tbs = srslte_ra_tbs_from_idx(dci->mcs_idx-1, grant->L_prb); + } else if (dci->mcs_idx < 29) { + grant->mcs.mod = SRSLTE_MOD_64QAM; + grant->mcs.tbs = srslte_ra_tbs_from_idx(dci->mcs_idx-2, grant->L_prb); + } else { + fprintf(stderr, "Invalid MCS index %d\n", dci->mcs_idx); + } + } else if (dci->mcs_idx == 29 && dci->cqi_request && grant->L_prb <= 4) { + // 8.6.1 and 8.6.2 36.213 second paragraph + grant->mcs.mod = SRSLTE_MOD_QPSK; + grant->mcs.tbs = 0; + dci->rv_idx = 1; + } else if (dci->mcs_idx >= 29) { + // Else use last TBS/Modulation and use mcs to obtain rv_idx + grant->mcs.tbs = -1; + grant->mcs.mod = SRSLTE_MOD_LAST; + dci->rv_idx = dci->mcs_idx - 28; + DEBUG("mcs_idx=%d, tbs=%d, mod=%d, rv=%d\n", + dci->mcs_idx, grant->mcs.tbs/8, grant->mcs.mod, dci->rv_idx); + } +} + +void srslte_ra_ul_grant_to_nbits(srslte_ra_ul_grant_t *grant, srslte_cp_t cp, uint32_t N_srs, srslte_ra_nbits_t *nbits) +{ + nbits->nof_symb = 2*(SRSLTE_CP_NSYMB(cp)-1) - N_srs; + nbits->nof_re = nbits->nof_symb*grant->M_sc; + nbits->nof_bits = nbits->nof_re * grant->Qm; +} + +/** Compute PRB allocation for Uplink as defined in 8.1 and 8.4 of 36.213 */ +int srslte_ra_ul_dci_to_grant(srslte_ra_ul_dci_t *dci, uint32_t nof_prb, uint32_t n_rb_ho, srslte_ra_ul_grant_t *grant) +{ + + // Compute PRB allocation + if (!srslte_ra_ul_dci_to_grant_prb_allocation(dci, grant, n_rb_ho, nof_prb)) { + + // Compute MCS + ul_dci_to_grant_mcs(dci, grant); + + // Fill rest of grant structure + grant->mcs.idx = dci->mcs_idx; + grant->M_sc = grant->L_prb*SRSLTE_NRE; + grant->M_sc_init = grant->M_sc; // FIXME: What should M_sc_init be? + grant->Qm = srslte_mod_bits_x_symbol(grant->mcs.mod); + + } else { + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +uint32_t srslte_ra_dl_approx_nof_re(srslte_cell_t cell, uint32_t nof_prb, uint32_t nof_ctrl_symbols) +{ + uint32_t nof_refs = 0; + uint32_t nof_symb = 2*SRSLTE_CP_NSYMB(cell.cp)-nof_ctrl_symbols; + switch(cell.nof_ports) { + case 1: + nof_refs = 2*3; + break; + case 2: + nof_refs = 4*3; + break; + case 4: + nof_refs = 4*4; + break; + } + return nof_prb * (nof_symb*SRSLTE_NRE-nof_refs); +} + +/* Computes the number of RE for each PRB in the prb_dist structure */ +uint32_t srslte_ra_dl_grant_nof_re(srslte_ra_dl_grant_t *grant, srslte_cell_t cell, + uint32_t sf_idx, uint32_t nof_ctrl_symbols) +{ + uint32_t j, s; + // Compute number of RE per PRB + uint32_t nof_re = 0; + for (s = 0; s < 2; s++) { + for (j = 0; j < cell.nof_prb; j++) { + if (grant->prb_idx[s][j]) { + nof_re += ra_re_x_prb(sf_idx, s, j, cell.nof_prb, cell.nof_ports, + nof_ctrl_symbols, cell.cp, grant->sf_type); + } + } + } + return nof_re; +} + + +/** Compute PRB allocation for Downlink as defined in 7.1.6 of 36.213 + * Decode dci->type?_alloc to grant + * This function only reads dci->type?_alloc and dci->alloc_type fields. + * This function only writes grant->prb_idx and grant->nof_prb. + */ +/** Compute PRB allocation for Downlink as defined in 7.1.6 of 36.213 */ +int srslte_ra_dl_dci_to_grant_prb_allocation(srslte_ra_dl_dci_t *dci, srslte_ra_dl_grant_t *grant, uint32_t nof_prb) { + int i, j; + uint32_t bitmask; + uint32_t P = srslte_ra_type0_P(nof_prb); + uint32_t n_rb_rbg_subset, n_rb_type1; + + bzero(grant, sizeof(srslte_ra_dl_grant_t)); + switch (dci->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + bitmask = dci->type0_alloc.rbg_bitmask; + int nb = (int) ceilf((float) nof_prb / P); + for (i = 0; i < nb; i++) { + if (bitmask & (1 << (nb - i - 1))) { + for (j = 0; j < P; j++) { + if (i*P+j < nof_prb) { + grant->prb_idx[0][i * P + j] = true; + grant->nof_prb++; + } + } + } + } + memcpy(&grant->prb_idx[1], &grant->prb_idx[0], SRSLTE_MAX_PRB*sizeof(bool)); + break; + case SRSLTE_RA_ALLOC_TYPE1: + // Make sure the rbg_subset is valid + if (dci->type1_alloc.rbg_subset >= P) { + return SRSLTE_ERROR; + } + n_rb_type1 = srslte_ra_type1_N_rb(nof_prb); + uint32_t temp = ((nof_prb - 1) / P) % P; + if (dci->type1_alloc.rbg_subset < temp) { + n_rb_rbg_subset = ((nof_prb - 1) / (P * P)) * P + P; + } else if (dci->type1_alloc.rbg_subset == temp) { + n_rb_rbg_subset = ((nof_prb - 1) / (P * P)) * P + ((nof_prb - 1) % P) + 1; + } else { + n_rb_rbg_subset = ((nof_prb - 1) / (P * P)) * P; + } + int shift = dci->type1_alloc.shift ? (n_rb_rbg_subset - n_rb_type1) : 0; + bitmask = dci->type1_alloc.vrb_bitmask; + for (i = 0; i < n_rb_type1; i++) { + if (bitmask & (1 << (n_rb_type1 - i - 1))) { + uint32_t idx = (((i + shift) / P) * P * P + dci->type1_alloc.rbg_subset * P + (i + shift) % P); + if (idx < nof_prb) { + grant->prb_idx[0][idx] = true; + grant->nof_prb++; + } else { + return SRSLTE_ERROR; + } + } + } + memcpy(&grant->prb_idx[1], &grant->prb_idx[0], SRSLTE_MAX_PRB*sizeof(bool)); + break; + case SRSLTE_RA_ALLOC_TYPE2: + if (dci->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + for (i = 0; i < dci->type2_alloc.L_crb; i++) { + grant->prb_idx[0][i + dci->type2_alloc.RB_start] = true; + grant->nof_prb++; + } + memcpy(&grant->prb_idx[1], &grant->prb_idx[0], SRSLTE_MAX_PRB*sizeof(bool)); + } else { + /* Mapping of Virtual to Physical RB for distributed type is defined in + * 6.2.3.2 of 36.211 + */ + int N_gap, N_tilde_vrb, n_tilde_vrb, n_tilde_prb, n_tilde2_prb, N_null, + N_row, n_vrb; + int n_tilde_prb_odd, n_tilde_prb_even; + if (dci->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1) { + N_tilde_vrb = srslte_ra_type2_n_vrb_dl(nof_prb, true); + N_gap = srslte_ra_type2_ngap(nof_prb, true); + } else { + N_tilde_vrb = 2 * srslte_ra_type2_n_vrb_dl(nof_prb, true); + N_gap = srslte_ra_type2_ngap(nof_prb, false); + } + N_row = (int) ceilf((float) N_tilde_vrb / (4 * P)) * P; + N_null = 4 * N_row - N_tilde_vrb; + for (i = 0; i < dci->type2_alloc.L_crb; i++) { + n_vrb = i + dci->type2_alloc.RB_start; + n_tilde_vrb = n_vrb % N_tilde_vrb; + n_tilde_prb = 2 * N_row * (n_tilde_vrb % 2) + n_tilde_vrb / 2 + + N_tilde_vrb * (n_vrb / N_tilde_vrb); + n_tilde2_prb = N_row * (n_tilde_vrb % 4) + n_tilde_vrb / 4 + + N_tilde_vrb * (n_vrb / N_tilde_vrb); + + if (N_null != 0 && n_tilde_vrb >= (N_tilde_vrb - N_null) + && (n_tilde_vrb % 2) == 1) { + n_tilde_prb_odd = n_tilde_prb - N_row; + } else if (N_null != 0 && n_tilde_vrb >= (N_tilde_vrb - N_null) + && (n_tilde_vrb % 2) == 0) { + n_tilde_prb_odd = n_tilde_prb - N_row + N_null / 2; + } else if (N_null != 0 && n_tilde_vrb < (N_tilde_vrb - N_null) + && (n_tilde_vrb % 4) >= 2) { + n_tilde_prb_odd = n_tilde2_prb - N_null / 2; + } else { + n_tilde_prb_odd = n_tilde2_prb; + } + n_tilde_prb_even = (n_tilde_prb_odd + N_tilde_vrb / 2) % N_tilde_vrb + + N_tilde_vrb * (n_vrb / N_tilde_vrb); + + if (n_tilde_prb_odd < N_tilde_vrb / 2) { + if (n_tilde_prb_odd < nof_prb) { + grant->prb_idx[0][n_tilde_prb_odd] = true; + } else { + return SRSLTE_ERROR; + } + } else { + if (n_tilde_prb_odd + N_gap - N_tilde_vrb / 2 < nof_prb) { + grant->prb_idx[0][n_tilde_prb_odd + N_gap - N_tilde_vrb / 2] = true; + } else { + return SRSLTE_ERROR; + } + } + grant->nof_prb++; + if (n_tilde_prb_even < N_tilde_vrb / 2) { + if(n_tilde_prb_even < nof_prb) { + grant->prb_idx[1][n_tilde_prb_even] = true; + } else { + return SRSLTE_ERROR; + } + } else { + if (n_tilde_prb_even + N_gap - N_tilde_vrb / 2 < nof_prb) { + grant->prb_idx[1][n_tilde_prb_even + N_gap - N_tilde_vrb / 2] = true; + } else { + return SRSLTE_ERROR; + } + } + } + } + break; + default: + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +} + +int srslte_dl_fill_ra_mcs(srslte_ra_mcs_t *mcs, uint32_t nprb) { + int i_tbs = 0; + if (mcs->idx < 10) { + mcs->mod = SRSLTE_MOD_QPSK; + i_tbs = mcs->idx; + } else if (mcs->idx < 17) { + mcs->mod = SRSLTE_MOD_16QAM; + i_tbs = mcs->idx-1; + } else if (mcs->idx < 29) { + mcs->mod = SRSLTE_MOD_64QAM; + i_tbs = mcs->idx-2; + } else if (mcs->idx == 29) { + mcs->mod = SRSLTE_MOD_QPSK; + i_tbs = -1; + } else if (mcs->idx == 30) { + mcs->mod = SRSLTE_MOD_16QAM; + i_tbs = -1; + } else if (mcs->idx == 31) { + mcs->mod = SRSLTE_MOD_64QAM; + i_tbs = -1; + } + + int tbs = -1; + if (i_tbs >= 0) { + tbs = srslte_ra_tbs_from_idx(i_tbs, nprb); + mcs->tbs = tbs; + } + return tbs; +} + +int srslte_dl_fill_ra_mcs_pmch(srslte_ra_mcs_t *mcs, uint32_t nprb) { + uint32_t i_tbs = 0; + int tbs = -1; + if (mcs->idx < 5) { + mcs->mod = SRSLTE_MOD_QPSK; + i_tbs = mcs->idx*2; + }else if (mcs->idx < 6) { + mcs->mod = SRSLTE_MOD_16QAM; + i_tbs = mcs->idx*2; + }else if (mcs->idx < 11) { + mcs->mod = SRSLTE_MOD_16QAM; + i_tbs = mcs->idx + 5; + }else if (mcs->idx < 20) { + mcs->mod = SRSLTE_MOD_64QAM; + i_tbs = mcs->idx + 5; + }else if (mcs->idx < 28) { + //mcs->mod = SRSLTE_MOD_256QAM; + i_tbs = mcs->idx + 5; + }else if (mcs->idx == 28) { + mcs->mod = SRSLTE_MOD_QPSK; + tbs = 0; + i_tbs = 0; + }else if (mcs->idx == 29) { + mcs->mod = SRSLTE_MOD_16QAM; + tbs = 0; + i_tbs = 0; + }else if (mcs->idx == 30) { + mcs->mod = SRSLTE_MOD_64QAM; + tbs = 0; + i_tbs = 0; + }else if (mcs->idx == 31) { + mcs->mod = SRSLTE_MOD_64QAM; + tbs = 0; + i_tbs = 0; + } + + + if (tbs == -1) { + tbs = srslte_ra_tbs_from_idx(i_tbs, nprb); + if (tbs >= 0) { + mcs->tbs = tbs; + } + } + return tbs; +} + +/* Modulation order and transport block size determination 7.1.7 in 36.213 + * This looks at DCI type, type of RNTI and reads fields dci->type?_alloc, dci->mcs_idx, + * dci->dci_is_1a and dci->dci_is_1c + * Reads global variable last_dl_tbs if mcs>=29 + * Writes global variable last_dl_tbs if mcs<29 + * */ +static int dl_dci_to_grant_mcs(srslte_ra_dl_dci_t *dci, srslte_ra_dl_grant_t *grant, bool crc_is_crnti) { + uint32_t n_prb=0; + int tbs = -1; + uint32_t i_tbs = 0; + + if (!crc_is_crnti) { + if (dci->dci_is_1a) { + n_prb = dci->type2_alloc.n_prb1a == SRSLTE_RA_TYPE2_NPRB1A_2 ? 2 : 3; + i_tbs = dci->mcs_idx; + tbs = srslte_ra_tbs_from_idx(i_tbs, n_prb); + } else if (dci->dci_is_1c) { + if (dci->mcs_idx < 32) { + tbs = tbs_format1c_table[dci->mcs_idx]; + } else { + fprintf(stderr, "Error decoding DCI: Invalid mcs_idx=%d in Format1C\n", dci->mcs_idx); + } + } else { + fprintf(stderr, "Error decoding DCI: P/SI/RA-RNTI supports Format1A/1C only\n"); + return SRSLTE_ERROR; + } + grant->mcs[0].mod = SRSLTE_MOD_QPSK; + grant->mcs[0].tbs = (uint32_t) tbs; + grant->mcs[0].idx = dci->mcs_idx; + } else { + n_prb = grant->nof_prb; + if (dci->tb_en[0]) { + grant->mcs[0].idx = dci->mcs_idx; + grant->mcs[0].tbs = srslte_dl_fill_ra_mcs(&grant->mcs[0], n_prb); + } else { + grant->mcs[0].tbs = 0; + } + if (dci->tb_en[1]) { + grant->mcs[1].idx = dci->mcs_idx_1; + grant->mcs[1].tbs = srslte_dl_fill_ra_mcs(&grant->mcs[1], n_prb); + } else { + grant->mcs[1].tbs = 0; + } + } + for (int tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + grant->tb_en[tb] = dci->tb_en[tb]; + if (dci->tb_en[tb]) { + grant->Qm[tb] = srslte_mod_bits_x_symbol(grant->mcs[tb].mod); + } + } + grant->pinfo = dci->pinfo; + grant->tb_cw_swap = dci->tb_cw_swap; + + if (grant->mcs[0].tbs < 0 || grant->mcs[1].tbs < 0) { + return SRSLTE_ERROR; + } else { + return SRSLTE_SUCCESS; + } +} + +void srslte_ra_dl_grant_to_nbits(srslte_ra_dl_grant_t *grant, uint32_t cfi, srslte_cell_t cell, uint32_t sf_idx, + srslte_ra_nbits_t nbits[SRSLTE_MAX_CODEWORDS]) +{ + // Compute number of RE + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + /* Compute number of RE for first transport block */ + nbits[i].nof_re = srslte_ra_dl_grant_nof_re(grant, cell, sf_idx, cell.nof_prb < 10 ? (cfi + 1) : cfi); + nbits[i].lstart = cell.nof_prb < 10 ? (cfi + 1) : cfi; + if (SRSLTE_SF_NORM == grant->sf_type) { + nbits[i].nof_symb = 2 * SRSLTE_CP_NSYMB(cell.cp) - nbits[0].lstart; + } else if (SRSLTE_SF_MBSFN == grant->sf_type) { + nbits[i].nof_symb = 2 * SRSLTE_CP_EXT_NSYMB - nbits[0].lstart; + } + if (grant->tb_en[i]) { + nbits[i].nof_bits = nbits[i].nof_re * grant->Qm[i]; + } + } +} + +/** Obtains a DL grant from a DCI grant for PDSCH */ +int srslte_ra_dl_dci_to_grant(srslte_ra_dl_dci_t *dci, + uint32_t nof_prb, uint16_t msg_rnti, srslte_ra_dl_grant_t *grant) +{ + grant->sf_type = SRSLTE_SF_NORM; + bool crc_is_crnti = false; + if (msg_rnti >= SRSLTE_CRNTI_START && msg_rnti <= SRSLTE_CRNTI_END) { + crc_is_crnti = true; + } + // Compute PRB allocation + int ret =srslte_ra_dl_dci_to_grant_prb_allocation(dci, grant, nof_prb); + if (!ret) { + // Compute MCS + ret = dl_dci_to_grant_mcs(dci, grant, crc_is_crnti); + if (ret == SRSLTE_SUCCESS) { + // Apply Section 7.1.7.3. If RA-RNTI and Format1C rv_idx=0 + if (dci->dci_is_1c) { + if ((msg_rnti >= SRSLTE_RARNTI_START && msg_rnti <= SRSLTE_RARNTI_END) || msg_rnti == SRSLTE_PRNTI) + { + dci->rv_idx = 0; + } + } + } else { + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +/* RBG size for type0 scheduling as in table 7.1.6.1-1 of 36.213 */ +uint32_t srslte_ra_type0_P(uint32_t nof_prb) { + if (nof_prb <= 10) { + return 1; + } else if (nof_prb <= 26) { + return 2; + } else if (nof_prb <= 63) { + return 3; + } else { + return 4; + } +} + +/* Returns N_rb_type1 according to section 7.1.6.2 */ +uint32_t srslte_ra_type1_N_rb(uint32_t nof_prb) { + uint32_t P = srslte_ra_type0_P(nof_prb); + return (uint32_t) ceilf((float) nof_prb / P) - (uint32_t) ceilf(log2f((float) P)) - 1; +} + +/* Convert Type2 scheduling L_crb and RB_start to RIV value */ +uint32_t srslte_ra_type2_to_riv(uint32_t L_crb, uint32_t RB_start, uint32_t nof_prb) { + uint32_t riv; + if ((L_crb - 1) <= nof_prb / 2) { + riv = nof_prb * (L_crb - 1) + RB_start; + } else { + riv = nof_prb * (nof_prb - L_crb + 1) + nof_prb - 1 - RB_start; + } + return riv; +} + +/* Convert Type2 scheduling RIV value to L_crb and RB_start values */ +void srslte_ra_type2_from_riv(uint32_t riv, uint32_t *L_crb, uint32_t *RB_start, + uint32_t nof_prb, uint32_t nof_vrb) { + *L_crb = (uint32_t) (riv / nof_prb) + 1; + *RB_start = (uint32_t) (riv % nof_prb); + if (*L_crb > nof_vrb - *RB_start) { + *L_crb = nof_prb - (int) (riv / nof_prb) + 1; + *RB_start = nof_prb - riv % nof_prb - 1; + } +} + +/* Table 6.2.3.2-1 in 36.211 */ +uint32_t srslte_ra_type2_ngap(uint32_t nof_prb, bool ngap_is_1) { + if (nof_prb <= 10) { + return nof_prb / 2; + } else if (nof_prb == 11) { + return 4; + } else if (nof_prb <= 19) { + return 8; + } else if (nof_prb <= 26) { + return 12; + } else if (nof_prb <= 44) { + return 18; + } else if (nof_prb <= 49) { + return 27; + } else if (nof_prb <= 63) { + return ngap_is_1 ? 27 : 9; + } else if (nof_prb <= 79) { + return ngap_is_1 ? 32 : 16; + } else { + return ngap_is_1 ? 48 : 16; + } +} + +/* Table 7.1.6.3-1 in 36.213 */ +uint32_t srslte_ra_type2_n_rb_step(uint32_t nof_prb) { + if (nof_prb < 50) { + return 2; + } else { + return 4; + } +} + +/* as defined in 6.2.3.2 of 36.211 */ +uint32_t srslte_ra_type2_n_vrb_dl(uint32_t nof_prb, bool ngap_is_1) { + uint32_t ngap = srslte_ra_type2_ngap(nof_prb, ngap_is_1); + if (ngap_is_1) { + return 2 * (ngap < (nof_prb - ngap) ? ngap : nof_prb - ngap); + } else { + return ((uint32_t) nof_prb / ngap) * 2 * ngap; + } +} + +/* Modulation and TBS index table for PDSCH from 3GPP TS 36.213 v10.3.0 table 7.1.7.1-1 */ +int srslte_ra_tbs_idx_from_mcs(uint32_t mcs) { + if(mcs < 29) { + return mcs_tbs_idx_table[mcs]; + } else { + return SRSLTE_ERROR; + } +} + +srslte_mod_t srslte_ra_mod_from_mcs(uint32_t mcs) { + if (mcs <= 10 || mcs == 29) { + return SRSLTE_MOD_QPSK; + } else if (mcs <= 17 || mcs == 30) { + return SRSLTE_MOD_16QAM; + } else { + return SRSLTE_MOD_64QAM; + } +} + +int srslte_ra_mcs_from_tbs_idx(uint32_t tbs_idx) { + for (int i=0;i<29;i++) { + if (tbs_idx == mcs_tbs_idx_table[i]) { + return i; + } + } + return SRSLTE_ERROR; +} + +/* Table 7.1.7.2.1-1: Transport block size table on 36.213 */ +int srslte_ra_tbs_from_idx(uint32_t tbs_idx, uint32_t n_prb) { + if (tbs_idx < 27 && n_prb > 0 && n_prb <= SRSLTE_MAX_PRB) { + return tbs_table[tbs_idx][n_prb - 1]; + } else { + return SRSLTE_ERROR; + } +} + +/* Returns lowest nearest index of TBS value in table 7.1.7.2 on 36.213 + * or -1 if the TBS value is not within the valid TBS values + */ +int srslte_ra_tbs_to_table_idx(uint32_t tbs, uint32_t n_prb) { + uint32_t idx; + if (n_prb > 0 && n_prb <= SRSLTE_MAX_PRB) { + + if (tbs <= tbs_table[0][n_prb-1]) { + return 0; + } + if (tbs >= tbs_table[26][n_prb-1]) { + return 27; + } + for (idx = 0; idx < 26; idx++) { + if (tbs_table[idx][n_prb-1] <= tbs && tbs_table[idx+1][n_prb-1] >= tbs) { + return idx+1; + } + } + } + return SRSLTE_ERROR; +} + +void srslte_ra_pusch_fprint(FILE *f, srslte_ra_ul_dci_t *dci, uint32_t nof_prb) { + fprintf(f, " - Resource Allocation Type 2 mode :\t%s\n", + dci->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC ? "Localized" : "Distributed"); + + fprintf(f, " + Frequency Hopping:\t\t\t"); + if (dci->freq_hop_fl == SRSLTE_RA_PUSCH_HOP_DISABLED) { + fprintf(f, "No\n"); + } else { + fprintf(f, "Yes\n"); + } + fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv); + if (dci->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n", + dci->type2_alloc.L_crb, dci->type2_alloc.RB_start); + } else { + fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n", + dci->type2_alloc.L_crb, dci->type2_alloc.RB_start); + fprintf(f, " + VRB gap selection:\t\t\tGap %d\n", + dci->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1 ? 1 : 2); + fprintf(f, " + VRB gap:\t\t\t\t%d\n", + srslte_ra_type2_ngap(nof_prb, dci->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1)); + + } + + fprintf(f, " - Modulation and coding scheme index:\t%d\n", dci->mcs_idx); + fprintf(f, " - New data indicator:\t\t\t%s\n", dci->ndi ? "Yes" : "No"); + fprintf(f, " - Redundancy version:\t\t\t%d\n", dci->rv_idx); + fprintf(f, " - TPC command for PUCCH:\t\t--\n"); +} + +void srslte_ra_ul_grant_fprint(FILE *f, srslte_ra_ul_grant_t *grant) { + fprintf(f, " - Number of PRBs:\t\t\t%d\n", grant->L_prb); + fprintf(f, " - Modulation type:\t\t\t%s\n", srslte_mod_string(grant->mcs.mod)); + fprintf(f, " - Transport block size:\t\t%d\n", grant->mcs.tbs); +} + +char *ra_type_string(srslte_ra_type_t alloc_type) { + switch (alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + return "Type 0"; + case SRSLTE_RA_ALLOC_TYPE1: + return "Type 1"; + case SRSLTE_RA_ALLOC_TYPE2: + return "Type 2"; + default: + return "N/A"; + } +} + +void srslte_ra_pdsch_fprint(FILE *f, srslte_ra_dl_dci_t *dci, uint32_t nof_prb) { + fprintf(f, " - Resource Allocation Type:\t\t%s\n", + ra_type_string(dci->alloc_type)); + switch (dci->alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + fprintf(f, " + Resource Block Group Size:\t\t%d\n", srslte_ra_type0_P(nof_prb)); + fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type0_alloc.rbg_bitmask); + break; + case SRSLTE_RA_ALLOC_TYPE1: + fprintf(f, " + Resource Block Group Size:\t\t%d\n", srslte_ra_type0_P(nof_prb)); + fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type1_alloc.vrb_bitmask); + fprintf(f, " + RBG Subset:\t\t\t%d\n", dci->type1_alloc.rbg_subset); + fprintf(f, " + RBG Shift:\t\t\t\t%s\n", + dci->type1_alloc.shift ? "Yes" : "No"); + break; + case SRSLTE_RA_ALLOC_TYPE2: + fprintf(f, " + Type:\t\t\t\t%s\n", + dci->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC ? "Localized" : "Distributed"); + fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv); + if (dci->type2_alloc.mode == SRSLTE_RA_TYPE2_LOC) { + fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n", + dci->type2_alloc.L_crb, dci->type2_alloc.RB_start); + } else { + fprintf(f, " + VRB Assignment:\t\t\t%d VRB starting with VRB %d\n", + dci->type2_alloc.L_crb, dci->type2_alloc.RB_start); + fprintf(f, " + VRB gap selection:\t\t\tGap %d\n", + dci->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1 ? 1 : 2); + fprintf(f, " + VRB gap:\t\t\t\t%d\n", + srslte_ra_type2_ngap(nof_prb, dci->type2_alloc.n_gap == SRSLTE_RA_TYPE2_NG1)); + } + break; + } + fprintf(f, " - HARQ process:\t\t\t%d\n", dci->harq_process); + fprintf(f, " - TPC command for PUCCH:\t\t--\n"); + fprintf(f, " - Transport blocks swapped:\t\t%s\n", (dci->tb_cw_swap)?"true":"false"); + fprintf(f, " - Transport block 1 enabled:\t\t%s\n", (dci->tb_en[0])?"true":"false"); + if (dci->tb_en[0]) { + fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->mcs_idx); + fprintf(f, " + New data indicator:\t\t\t%s\n", dci->ndi ? "Yes" : "No"); + fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->rv_idx); + } + fprintf(f, " - Transport block 2 enabled:\t\t%s\n", (dci->tb_en[1])?"true":"false"); + if (dci->tb_en[1]) { + fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->mcs_idx_1); + fprintf(f, " + New data indicator:\t\t\t%s\n", dci->ndi_1 ? "Yes" : "No"); + fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->rv_idx_1); + } +} + +void srslte_ra_dl_grant_fprint(FILE *f, srslte_ra_dl_grant_t *grant) { + srslte_ra_prb_fprint(f, grant); + fprintf(f, " - Number of PRBs:\t\t\t%d\n", grant->nof_prb); + fprintf(f, " - Number of TBs:\t\t\t%d\n", SRSLTE_RA_DL_GRANT_NOF_TB(grant)); + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (grant->tb_en[i]) { + fprintf(f, " - Transport block:\t\t\t%d\n", i); + fprintf(f, " -> Modulation type:\t\t\t%s\n", srslte_mod_string(grant->mcs[i].mod)); + fprintf(f, " -> Transport block size:\t\t%d\n", grant->mcs[i].tbs); + } + } +} + +void srslte_ra_prb_fprint(FILE *f, srslte_ra_dl_grant_t *grant) { + if (grant->nof_prb > 0) { + for (int j=0;j<2;j++) { + fprintf(f, " - PRB Bitmap Assignment %dst slot:\n", j); + for (int i=0;iprb_idx[j][i]) { + fprintf(f, "%d, ", i); + } + } + fprintf(f, "\n"); + } + } + +} diff --git a/lib/src/phy/phch/regs.c b/lib/src/phy/phch/regs.c new file mode 100644 index 0000000..a89e0a5 --- /dev/null +++ b/lib/src/phy/phch/regs.c @@ -0,0 +1,815 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/phch/regs.h" +#include "srslte/phy/utils/debug.h" + +#define REG_IDX(r, i, n) r->k[i]+r->l*n*SRSLTE_NRE + + +srslte_regs_reg_t *regs_find_reg(srslte_regs_t *h, uint32_t k, uint32_t l); +int regs_put_reg(srslte_regs_reg_t *reg, + cf_t *reg_data, + cf_t *slot_symbols, + uint32_t nof_prb); + +int regs_add_reg(srslte_regs_reg_t *reg, + cf_t *reg_data, + cf_t *slot_symbols, + uint32_t nof_prb); + +int regs_get_reg(srslte_regs_reg_t *reg, + cf_t *slot_symbols, + cf_t *reg_data, + uint32_t nof_prb); + +int regs_reset_reg(srslte_regs_reg_t *reg, cf_t *slot_symbols, uint32_t nof_prb); + + +/*************************************************************** + * + * PDCCH REG ALLOCATION + * + ***************************************************************/ + +void regs_pdcch_free(srslte_regs_t *h) { + int i; + for (i=0;i<3;i++) { + if (h->pdcch[i].regs) { + free(h->pdcch[i].regs); + h->pdcch[i].regs = NULL; + } + } +} + +#define PDCCH_NCOLS 32 +const uint8_t PDCCH_PERM[PDCCH_NCOLS] = + { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31, 0, 16, 8, + 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30 }; + +/** Initialize REGs for PDCCH + * 36.211 10.3 section 6.8.5 + */ +int regs_pdcch_init(srslte_regs_t *h) { + int i, m, cfi, nof_ctrl_symbols; + int ret = SRSLTE_ERROR; + int nrows, ndummy, j; + uint32_t k, kp; + srslte_regs_reg_t **tmp = NULL; + + bzero(&h->pdcch, sizeof(srslte_regs_ch_t)); + + for (cfi=0;cfi<3;cfi++) { + if (h->cell.nof_prb <= 10) { + nof_ctrl_symbols = cfi+2; + } else { + nof_ctrl_symbols = cfi+1; + } + + tmp = malloc(sizeof(srslte_regs_reg_t*) * h->nof_regs); + if (!tmp) { + perror("malloc"); + goto clean_and_exit; + } + + /* Number and count REGs for this CFI */ + m=0; + for (i=0;inof_regs;i++) { + if (h->regs[i].l < nof_ctrl_symbols && !h->regs[i].assigned) { + tmp[m] = &h->regs[i]; + m++; + } + } + + h->pdcch[cfi].nof_regs = m; + + h->pdcch[cfi].regs = malloc(sizeof(srslte_regs_reg_t*) * h->pdcch[cfi].nof_regs); + if (!h->pdcch[cfi].regs) { + perror("malloc"); + goto clean_and_exit; + } + + /* Interleave REGs */ + nrows = (h->pdcch[cfi].nof_regs-1)/PDCCH_NCOLS+1; + ndummy = PDCCH_NCOLS*nrows - h->pdcch[cfi].nof_regs; + if (ndummy < 0) { + ndummy = 0; + } + + k=0; + for (j = 0; j < PDCCH_NCOLS; j++) { + for (i = 0; i < nrows; i++) { + if (i*PDCCH_NCOLS + PDCCH_PERM[j] >= ndummy) { + m = i*PDCCH_NCOLS + PDCCH_PERM[j]-ndummy; + if (k < h->cell.id) { + kp = (h->pdcch[cfi].nof_regs + k-(h->cell.id%h->pdcch[cfi].nof_regs))%h->pdcch[cfi].nof_regs; + } else { + kp = (k-h->cell.id)%h->pdcch[cfi].nof_regs; + } + h->pdcch[cfi].regs[m] = tmp[kp]; + k++; + } + } + } + h->pdcch[cfi].nof_regs = (h->pdcch[cfi].nof_regs/9)*9; + INFO("Init PDCCH REG space CFI %d. %d useful REGs (%d CCEs)\n",cfi+1, + h->pdcch[cfi].nof_regs, h->pdcch[cfi].nof_regs/9); + free(tmp); + tmp = NULL; + } + + ret = SRSLTE_SUCCESS; +clean_and_exit: + if (tmp) { + free(tmp); + } + if (ret == SRSLTE_ERROR) { + regs_pdcch_free(h); + } + return ret; +} + +int srslte_regs_pdcch_nregs(srslte_regs_t *h, uint32_t cfi) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return SRSLTE_ERROR; + } else { + return (int) h->pdcch[cfi-1].nof_regs; + } +} + +int srslte_regs_pdcch_ncce(srslte_regs_t *h, uint32_t cfi) { + int nregs = srslte_regs_pdcch_nregs(h, cfi); + if (nregs > 0) { + return (uint32_t) (nregs / 9); + } else { + return SRSLTE_ERROR; + } +} + +/** Copy quadruplets to REGs and cyclic shift them, according to the + * second part of 6.8.5 in 36.211 + */ + +int srslte_regs_pdcch_put_offset(srslte_regs_t *h, uint32_t cfi, cf_t *d, cf_t *slot_symbols, uint32_t start_reg, uint32_t nof_regs) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return SRSLTE_ERROR; + } + if (start_reg + nof_regs <= h->pdcch[cfi-1].nof_regs) { + uint32_t i, k; + k = 0; + for (i=start_reg;ipdcch[cfi-1].regs[i], &d[k], slot_symbols, h->cell.nof_prb); + k += 4; + } + return k; + } else { + fprintf(stderr, "Out of range: start_reg + nof_reg must be lower than %d\n", h->pdcch[cfi-1].nof_regs); + return SRSLTE_ERROR; + } +} + +int srslte_regs_pdcch_put(srslte_regs_t *h, uint32_t cfi, cf_t *d, cf_t *slot_symbols) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return SRSLTE_ERROR; + } + return srslte_regs_pdcch_put_offset(h, cfi, d, slot_symbols, 0, h->pdcch[cfi-1].nof_regs); +} + +int srslte_regs_pdcch_get_offset(srslte_regs_t *h, uint32_t cfi, cf_t *slot_symbols, cf_t *d, uint32_t start_reg, uint32_t nof_regs) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return SRSLTE_ERROR; + } + if (start_reg + nof_regs <= h->pdcch[cfi-1].nof_regs) { + uint32_t i, k; + k = 0; + for (i=start_reg;ipdcch[cfi-1].regs[i], slot_symbols, &d[k], h->cell.nof_prb); + k += 4; + } + return k; + } else { + fprintf(stderr, "Out of range: start_reg + nof_reg must be lower than %d\n", h->pdcch[cfi-1].nof_regs); + return SRSLTE_ERROR; + } +} + + +int srslte_regs_pdcch_get(srslte_regs_t *h, uint32_t cfi, cf_t *slot_symbols, cf_t *d) { + if (cfi < 1 || cfi > 3) { + fprintf(stderr, "Invalid CFI=%d\n", cfi); + return SRSLTE_ERROR; + } + return srslte_regs_pdcch_get_offset(h, cfi, slot_symbols, d, 0, h->pdcch[cfi-1].nof_regs); +} + + + + +/*************************************************************** + * + * PHICH REG ALLOCATION + * + ***************************************************************/ + +/** Initialize REGs for PHICH + * 36.211 10.3 section 6.9.3 + */ +int regs_phich_init(srslte_regs_t *h) { + float ng; + uint32_t i, ni, li, n[3], nreg, mi; + srslte_regs_reg_t **regs_phich[3]; + int ret = SRSLTE_ERROR; + + for (int i=0;i<3;i++) { + regs_phich[i] = NULL; + } + + switch(h->phich_res) { + case SRSLTE_PHICH_R_1_6: + ng = (float) 1/6; + break; + case SRSLTE_PHICH_R_1_2: + ng = (float) 1/2; + break; + case SRSLTE_PHICH_R_1: + ng = 1; + break; + case SRSLTE_PHICH_R_2: + ng = 2; + break; + default: + ng = 0; + break; + } + h->ngroups_phich = (int) ceilf(ng * ((float) h->cell.nof_prb/8)); + h->phich = malloc(sizeof(srslte_regs_ch_t) * h->ngroups_phich); + if (!h->phich) { + perror("malloc"); + return -1; + } + INFO("Creating %d PHICH mapping units. %s length, Ng=%.2f\n",h->ngroups_phich, + h->phich_len==SRSLTE_PHICH_EXT?"Extended":"Normal",ng); + for (i=0;ingroups_phich;i++) { + h->phich[i].nof_regs = REGS_PHICH_REGS_X_GROUP; + h->phich[i].regs = malloc(sizeof(srslte_regs_reg_t*) * REGS_PHICH_REGS_X_GROUP); + if (!h->phich[i].regs) { + perror("malloc"); + goto clean_and_exit; + } + } + + /** Here begins the mapping algorithm */ + + /* Step 2. Count REGs not assigned to PCFICH */ + bzero(n, 3*sizeof(int)); + for (i=0;inof_regs;i++) { + if (h->regs[i].l < 3 && !h->regs[i].assigned) { + n[h->regs[i].l]++; + } + } + + bzero(regs_phich, sizeof(srslte_regs_reg_t*) * 3); + for (i=0;i<3;i++) { + regs_phich[i] = malloc(n[i] * sizeof(srslte_regs_reg_t*)); + if (!regs_phich[i]) { + perror("malloc"); + goto clean_and_exit; + } + } + + bzero(n, 3 * sizeof(int)); + /* Step 3. Number REGs not assigned to PCFICH */ + for (i=0;inof_regs;i++) { + // they are already sorted starting from the REG with the lowest frequency-domain index + if (h->regs[i].l < 3 && !h->regs[i].assigned) { + regs_phich[h->regs[i].l][n[h->regs[i].l]++] = &h->regs[i]; + } + } + + nreg=0; + for (mi=0;mingroups_phich;mi++) { // here ngroups is the number of mapping units + for (i=0;i<3;i++) { + li=h->phich_len==SRSLTE_PHICH_EXT?i:0; // Step 7 + ni=((h->cell.id*n[li]/n[0])+mi+i*n[li]/3) % n[li]; // Step 8 + h->phich[mi].regs[i] = regs_phich[li][ni]; + h->phich[mi].regs[i]->assigned = true; + DEBUG("Assigned PHICH REG#%d (%d,%d)\n",nreg,h->phich[mi].regs[i]->k0,li); + nreg++; + } + } + + // now the number of mapping units = number of groups for normal cp. For extended cp + // ngroups = 2 * number mapping units + if (SRSLTE_CP_ISEXT(h->cell.cp)) { + h->ngroups_phich *= 2; + } + ret = SRSLTE_SUCCESS; + +clean_and_exit: + if (ret == SRSLTE_ERROR) { + if (h->phich) { + for (i=0;ingroups_phich;i++) { + if (h->phich[i].regs) { + free(h->phich[i].regs); + } + } + free(h->phich); + } + } + for (i=0;i<3;i++) { + if (regs_phich[i]) { + free(regs_phich[i]); + } + } + return ret; +} + +void regs_phich_free(srslte_regs_t *h) { + uint32_t i; + if (h->phich) { + if (SRSLTE_CP_ISEXT(h->cell.cp)) { + h->ngroups_phich /= 2; + } + for (i=0;ingroups_phich;i++) { + if (h->phich[i].regs) { + free(h->phich[i].regs); + h->phich[i].regs = NULL; + } + } + free(h->phich); + h->phich = NULL; + } +} + +uint32_t srslte_regs_phich_nregs(srslte_regs_t *h) { + uint32_t i; + uint32_t n; + n=0; + for (i=0;ingroups_phich;i++) { + n += h->phich[i].nof_regs; + } + return n; +} + + +uint32_t srslte_regs_phich_ngroups(srslte_regs_t *h) { + return h->ngroups_phich; +} + +/** + * Adds the PHICH symbols to the resource grid pointed by slot_symbols. + * + * Each subframe, the user shall call the srslte_v function before adding PHICH symbols. + * + * Returns the number of written symbols, or -1 on error + */ +int srslte_regs_phich_add(srslte_regs_t *h, cf_t symbols[REGS_PHICH_NSYM], uint32_t ngroup, cf_t *slot_symbols) { + uint32_t i; + if (ngroup >= h->ngroups_phich) { + fprintf(stderr, "Error invalid ngroup %d\n", ngroup); + return SRSLTE_ERROR_INVALID_INPUTS; + } + if (SRSLTE_CP_ISEXT(h->cell.cp)) { + ngroup /= 2; + } + srslte_regs_ch_t *rch = &h->phich[ngroup]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_add_reg(rch->regs[i], &symbols[i*REGS_RE_X_REG], slot_symbols, h->cell.nof_prb); + } + return i*REGS_RE_X_REG; +} + +/** + * Resets the PHICH symbols + * + * Returns the number of written symbols, or -1 on error + */ +int srslte_regs_phich_reset(srslte_regs_t *h, cf_t *slot_symbols) { + uint32_t i; + uint32_t ngroup, ng; + for (ngroup = 0;ngroup < h->ngroups_phich;SRSLTE_CP_ISEXT(h->cell.cp)?ngroup+=2:ngroup++) { + if (SRSLTE_CP_ISEXT(h->cell.cp)) { + ng = ngroup/2; + } else { + ng = ngroup; + } + srslte_regs_ch_t *rch = &h->phich[ng]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_reset_reg(rch->regs[i], slot_symbols, h->cell.nof_prb); + } + } + return SRSLTE_SUCCESS; +} + +/** + * Gets the PHICH symbols from the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int srslte_regs_phich_get(srslte_regs_t *h, cf_t *slot_symbols, cf_t symbols[REGS_PHICH_NSYM], uint32_t ngroup) { + uint32_t i; + if (ngroup >= h->ngroups_phich) { + fprintf(stderr, "Error invalid ngroup %d\n", ngroup); + return SRSLTE_ERROR_INVALID_INPUTS; + } + if (SRSLTE_CP_ISEXT(h->cell.cp)) { + ngroup /= 2; + } + srslte_regs_ch_t *rch = &h->phich[ngroup]; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PHICH_NSYM; i++) { + regs_get_reg(rch->regs[i], slot_symbols, &symbols[i*REGS_RE_X_REG], h->cell.nof_prb); + } + return i*REGS_RE_X_REG; +} + + + + + + + + + + +/*************************************************************** + * + * PCFICH REG ALLOCATION + * + ***************************************************************/ + + +/** Initialize REGs for PCFICH + * 36.211 10.3 section 6.7.4 + */ +int regs_pcfich_init(srslte_regs_t *h) { + uint32_t i; + uint32_t k_hat, k; + srslte_regs_ch_t *ch = &h->pcfich; + + ch->regs = malloc(sizeof(srslte_regs_reg_t*) * REGS_PCFICH_NREGS); + if (!ch->regs) { + perror("malloc"); + return SRSLTE_ERROR; + } + ch->nof_regs = REGS_PCFICH_NREGS; + + INFO("PCFICH allocating %d regs. CellID: %d, PRB: %d\n", ch->nof_regs, h->cell.id, h->cell.nof_prb); + + k_hat = (SRSLTE_NRE / 2) * (h->cell.id % (2 * h->cell.nof_prb)); + for (i = 0; i < REGS_PCFICH_NREGS; i++) { + + k = (k_hat + (i * h->cell.nof_prb / 2) * (SRSLTE_NRE / 2)) + % (h->cell.nof_prb * SRSLTE_NRE); + ch->regs[i] = regs_find_reg(h, k, 0); + if (!ch->regs[i]) { + fprintf(stderr, "Error allocating PCFICH: REG (%d,0) not found\n", + k); + return SRSLTE_ERROR; + } else if (ch->regs[i]->assigned) { + fprintf(stderr, + "Error allocating PCFICH: REG (%d,0) already allocated\n", + k); + return SRSLTE_ERROR; + } else { + ch->regs[i]->assigned = true; + DEBUG("Assigned PCFICH REG#%d (%d,0)\n", i, k); + } + } + return SRSLTE_SUCCESS; +} + +void regs_pcfich_free(srslte_regs_t *h) { + if (h->pcfich.regs) { + free(h->pcfich.regs); + h->pcfich.regs = NULL; + } +} + +uint32_t srslte_regs_pcfich_nregs(srslte_regs_t *h) { + return h->pcfich.nof_regs; +} + +/** + * Maps the PCFICH symbols to the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int srslte_regs_pcfich_put(srslte_regs_t *h, cf_t symbols[REGS_PCFICH_NSYM], cf_t *slot_symbols) { + srslte_regs_ch_t *rch = &h->pcfich; + + uint32_t i; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { + regs_put_reg(rch->regs[i], &symbols[i*REGS_RE_X_REG], slot_symbols, h->cell.nof_prb); + } + return i*REGS_RE_X_REG; +} + +/** + * Gets the PCFICH symbols from the resource grid pointed by slot_symbols + * + * Returns the number of written symbols, or -1 on error + */ +int srslte_regs_pcfich_get(srslte_regs_t *h, cf_t *slot_symbols, cf_t ch_data[REGS_PCFICH_NSYM]) { + srslte_regs_ch_t *rch = &h->pcfich; + uint32_t i; + for (i = 0; i < rch->nof_regs && i*REGS_RE_X_REG < REGS_PCFICH_NSYM; i++) { + regs_get_reg(rch->regs[i], slot_symbols, &ch_data[i*REGS_RE_X_REG], h->cell.nof_prb); + } + return i*REGS_RE_X_REG; +} + + + + + + + + + + + + + + +/*************************************************************** + * + * COMMON FUNCTIONS + * + ***************************************************************/ + +srslte_regs_reg_t *regs_find_reg(srslte_regs_t *h, uint32_t k, uint32_t l) { + uint32_t i; + for (i=0;inof_regs;i++) { + if (h->regs[i].l == l && h->regs[i].k0 == k) { + return &h->regs[i]; + } + } + return NULL; +} + +/** + * Returns the number of REGs in a PRB + * 36.211 Section 6.2.4 + */ +int regs_num_x_symbol(uint32_t symbol, uint32_t nof_port, srslte_cp_t cp) { + + switch (symbol) { + case 0: + return 2; + case 1: + switch (nof_port) { + case 1: + case 2: + return 3; + case 4: + return 2; + default: + fprintf(stderr, "Invalid number of ports %d\n", nof_port); + return SRSLTE_ERROR; + } + break; + case 2: + return 3; + case 3: + if (SRSLTE_CP_ISNORM(cp)) { + return 3; + } else { + return 2; + } + default: + fprintf(stderr, "Invalid symbol %d\n", symbol); + return SRSLTE_ERROR; + } +} + +/** + * Initializes the indices of a REG + * 36.211 Section 6.2.4 + */ +int regs_reg_init(srslte_regs_reg_t *reg, uint32_t symbol, uint32_t nreg, uint32_t k0, uint32_t maxreg, uint32_t vo) { + uint32_t i, j, z; + + reg->l = symbol; + reg->assigned = false; + + switch (maxreg) { + case 2: + reg->k0 = k0 + nreg * 6; + /* there are two references in the middle */ + j = z = 0; + for (i = 0; i < vo; i++) { + reg->k[j] = k0 + nreg * 6 + i; + j++; + } + for (i = 0; i < 2; i++) { + reg->k[j] = k0 + nreg * 6 + i + vo + 1; + j++; + } + z = j; + for (i = 0; i < 4 - z; i++) { + reg->k[j] = k0 + nreg * 6 + vo + 3 + i + 1; + j++; + } + if (j != 4) { + fprintf(stderr, "Something went wrong: expected 2 references\n"); + return SRSLTE_ERROR; + } + break; + + case 3: + reg->k0 = k0 + nreg * 4; + /* there is no reference */ + for (i = 0; i < 4; i++) { + reg->k[i] = k0 + nreg * 4 + i; + } + break; + default: + fprintf(stderr, "Invalid number of REGs per PRB: %d\n", maxreg); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +void srslte_regs_free(srslte_regs_t *h) { + if (h->regs) { + free(h->regs); + } + regs_pcfich_free(h); + regs_phich_free(h); + regs_pdcch_free(h); + + bzero(h, sizeof(srslte_regs_t)); +} + +/** + * Initializes REGs structure. + * Sets all REG indices and initializes PCFICH, PHICH and PDCCH REGs + * Returns 0 if OK, -1 on error + */ +int srslte_regs_init(srslte_regs_t *h, srslte_cell_t cell) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t i, k; + uint32_t j[4], jmax, prb; + uint32_t n[4], vo; + uint32_t max_ctrl_symbols; + + if (h != NULL && + srslte_cell_isvalid(&cell)) + { + bzero(h, sizeof(srslte_regs_t)); + ret = SRSLTE_ERROR; + + max_ctrl_symbols = cell.nof_prb<=10?4:3; + vo = cell.id % 3; + h->cell = cell; + h->max_ctrl_symbols = max_ctrl_symbols; + h->phich_res = cell.phich_resources; + h->phich_len = cell.phich_length; + + h->nof_regs = 0; + for (i = 0; i < max_ctrl_symbols; i++) { + n[i] = regs_num_x_symbol(i, h->cell.nof_ports, h->cell.cp); + if (n[i] == -1) { + goto clean_and_exit; + } + h->nof_regs += h->cell.nof_prb * n[i]; + } + INFO("Indexing %d REGs. CellId: %d, %d PRB, CP: %s\n", h->nof_regs, h->cell.id, h->cell.nof_prb, + SRSLTE_CP_ISNORM(h->cell.cp)?"Normal":"Extended"); + h->regs = malloc(sizeof(srslte_regs_reg_t) * h->nof_regs); + if (!h->regs) { + perror("malloc"); + goto clean_and_exit; + } + + /* Sort REGs according to PDCCH mapping, beggining from the lowest l index then k */ + bzero(j, sizeof(int) * 4); + k = i = prb = jmax = 0; + while (k < h->nof_regs) { + if (n[i] == 3 || (n[i] == 2 && jmax != 1)) { + if (regs_reg_init(&h->regs[k], i, j[i], prb * SRSLTE_NRE, n[i], vo)) { + fprintf(stderr, "Error initializing REGs\n"); + goto clean_and_exit; + } + /*DEBUG("Available REG #%3d: l=%d, prb=%d, nreg=%d (k0=%d)\n", k, i, prb, j[i], + h->regs[k].k0); + */ + j[i]++; + k++; + } + i++; + if (i == max_ctrl_symbols) { + i = 0; + jmax++; + } + if (jmax == 3) { + prb++; + bzero(j, sizeof(int) * 4); + jmax = 0; + } + } + if (regs_pcfich_init(h)) { + fprintf(stderr, "Error initializing PCFICH REGs\n"); + goto clean_and_exit; + } + + if (regs_phich_init(h)) { + fprintf(stderr, "Error initializing PHICH REGs\n"); + goto clean_and_exit; + } + if (regs_pdcch_init(h)) { + fprintf(stderr, "Error initializing PDCCH REGs\n"); + goto clean_and_exit; + } + + ret = SRSLTE_SUCCESS; + } +clean_and_exit: + if (h) { + if (ret != SRSLTE_SUCCESS) { + srslte_regs_free(h); + } + } + return ret; +} + +/** + * Puts one REG data (4 symbols) in the slot symbols array + */ +int regs_put_reg(srslte_regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; + for (i = 0; i < REGS_RE_X_REG; i++) { + slot_symbols[REG_IDX(reg, i, nof_prb)] = reg_data[i]; + } + return REGS_RE_X_REG; +} + +/** + * Adds one REG data (4 symbols) in the slot symbols array + * Used by PHICH + */ +int regs_add_reg(srslte_regs_reg_t *reg, cf_t *reg_data, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; + for (i = 0; i < REGS_RE_X_REG; i++) { + slot_symbols[REG_IDX(reg, i, nof_prb)] += reg_data[i]; + } + return REGS_RE_X_REG; +} + + +/** + * Reset REG data (4 symbols) in the slot symbols array + */ +int regs_reset_reg(srslte_regs_reg_t *reg, cf_t *slot_symbols, uint32_t nof_prb) { + uint32_t i; + for (i = 0; i < REGS_RE_X_REG; i++) { + slot_symbols[REG_IDX(reg, i, nof_prb)] = 0; + } + return REGS_RE_X_REG; +} + +/** + * Gets one REG data (4 symbols) from the slot symbols array + */ +int regs_get_reg(srslte_regs_reg_t *reg, cf_t *slot_symbols, cf_t *reg_data, uint32_t nof_prb) { + uint32_t i; + for (i = 0; i < REGS_RE_X_REG; i++) { + reg_data[i] = slot_symbols[REG_IDX(reg, i, nof_prb)]; + } + return REGS_RE_X_REG; +} + + diff --git a/lib/src/phy/phch/sch.c b/lib/src/phy/phch/sch.c new file mode 100644 index 0000000..aa1f297 --- /dev/null +++ b/lib/src/phy/phch/sch.c @@ -0,0 +1,860 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "srslte/phy/phch/pdsch.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +#define SRSLTE_PDSCH_MAX_TDEC_ITERS 4 + +/* 36.213 Table 8.6.3-1: Mapping of HARQ-ACK offset values and the index signalled by higher layers */ +float beta_harq_offset[16] = {2.0, 2.5, 3.125, 4.0, 5.0, 6.250, 8.0, 10.0, + 12.625, 15.875, 20.0, 31.0, 50.0, 80.0, 126.0, -1.0}; + +/* 36.213 Table 8.6.3-2: Mapping of RI offset values and the index signalled by higher layers */ +float beta_ri_offset[16] = {1.25, 1.625, 2.0, 2.5, 3.125, 4.0, 5.0, 6.25, 8.0, 10.0, + 12.625, 15.875, 20.0, -1.0, -1.0, -1.0}; + +/* 36.213 Table 8.6.3-3: Mapping of CQI offset values and the index signalled by higher layers */ +float beta_cqi_offset[16] = {-1.0, -1.0, 1.125, 1.25, 1.375, 1.625, 1.750, 2.0, 2.25, 2.5, 2.875, + 3.125, 3.5, 4.0, 5.0, 6.25}; + + +float srslte_sch_beta_cqi(uint32_t I_cqi) { + if (I_cqi < 16) { + return beta_cqi_offset[I_cqi]; + } else { + return 0; + } +} + +uint32_t srslte_sch_find_Ioffset_ack(float beta) { + for (int i=0;i<16;i++) { + if (beta_harq_offset[i] >= beta) { + return i; + } + } + return 0; +} + +uint32_t srslte_sch_find_Ioffset_ri(float beta) { + for (int i=0;i<16;i++) { + if (beta_ri_offset[i] >= beta) { + return i; + } + } + return 0; +} + +uint32_t srslte_sch_find_Ioffset_cqi(float beta) { + for (int i=0;i<16;i++) { + if (beta_cqi_offset[i] >= beta) { + return i; + } + } + return 0; +} + +int srslte_sch_init(srslte_sch_t *q) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q) { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_sch_t)); + + if (srslte_crc_init(&q->crc_tb, SRSLTE_LTE_CRC24A, 24)) { + fprintf(stderr, "Error initiating CRC\n"); + goto clean; + } + if (srslte_crc_init(&q->crc_cb, SRSLTE_LTE_CRC24B, 24)) { + fprintf(stderr, "Error initiating CRC\n"); + goto clean; + } + + if (srslte_tcod_init(&q->encoder, SRSLTE_TCOD_MAX_LEN_CB)) { + fprintf(stderr, "Error initiating Turbo Coder\n"); + goto clean; + } + if (srslte_tdec_init(&q->decoder, SRSLTE_TCOD_MAX_LEN_CB)) { + fprintf(stderr, "Error initiating Turbo Decoder\n"); + goto clean; + } + + q->max_iterations = SRSLTE_PDSCH_MAX_TDEC_ITERS; + + srslte_rm_turbo_gentables(); + + // Allocate int16 for reception (LLRs) + q->cb_in = srslte_vec_malloc(sizeof(uint8_t) * (SRSLTE_TCOD_MAX_LEN_CB+8)/8); + if (!q->cb_in) { + goto clean; + } + + q->parity_bits = srslte_vec_malloc(sizeof(uint8_t) * (3 * SRSLTE_TCOD_MAX_LEN_CB + 16) / 8); + if (!q->parity_bits) { + goto clean; + } + q->temp_g_bits = srslte_vec_malloc(sizeof(uint8_t)*SRSLTE_MAX_PRB*12*12*12); + if (!q->temp_g_bits) { + goto clean; + } + bzero(q->temp_g_bits, SRSLTE_MAX_PRB*12*12*12); + q->ul_interleaver = srslte_vec_malloc(sizeof(uint32_t)*SRSLTE_MAX_PRB*12*12*12); + if (!q->ul_interleaver) { + goto clean; + } + if (srslte_uci_cqi_init(&q->uci_cqi)) { + goto clean; + } + + ret = SRSLTE_SUCCESS; + } +clean: + if (ret == SRSLTE_ERROR) { + srslte_sch_free(q); + } + return ret; +} + +void srslte_sch_free(srslte_sch_t *q) { + srslte_rm_turbo_free_tables(); + + if (q->cb_in) { + free(q->cb_in); + } + if (q->parity_bits) { + free(q->parity_bits); + } + if (q->temp_g_bits) { + free(q->temp_g_bits); + } + if (q->ul_interleaver) { + free(q->ul_interleaver); + } + srslte_tdec_free(&q->decoder); + srslte_tcod_free(&q->encoder); + srslte_uci_cqi_free(&q->uci_cqi); + bzero(q, sizeof(srslte_sch_t)); +} + +void srslte_sch_set_max_noi(srslte_sch_t *q, uint32_t max_iterations) { + q->max_iterations = max_iterations; +} + +uint32_t srslte_sch_last_noi(srslte_sch_t *q) { + return q->nof_iterations; +} + +/* Encode a transport block according to 36.212 5.3.2 + * + */ +static int encode_tb_off(srslte_sch_t *q, + srslte_softbuffer_tx_t *softbuffer, srslte_cbsegm_t *cb_segm, + uint32_t Qm, uint32_t rv, uint32_t nof_e_bits, + uint8_t *data, uint8_t *e_bits, uint32_t w_offset) +{ + uint8_t parity[3] = {0, 0, 0}; + uint32_t par; + uint32_t i; + uint32_t cb_len=0, rp=0, wp=0, rlen=0, n_e=0; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + e_bits != NULL && + cb_segm != NULL && + softbuffer != NULL) + { + + if (cb_segm->F) { + fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n"); + return SRSLTE_ERROR; + } + + if (cb_segm->C > softbuffer->max_cb) { + fprintf(stderr, "Error number of CB (%d) exceeds soft buffer size (%d CBs)\n", cb_segm->C, softbuffer->max_cb); + return -1; + } + + uint32_t Gp = nof_e_bits / Qm; + + uint32_t gamma = Gp; + if (cb_segm->C > 0) { + gamma = Gp%cb_segm->C; + } + + if (data) { + + /* Compute transport block CRC */ + par = srslte_crc_checksum_byte(&q->crc_tb, data, cb_segm->tbs); + + /* parity bits will be appended later */ + parity[0] = (par&(0xff<<16))>>16; + parity[1] = (par&(0xff<<8))>>8; + parity[2] = par&0xff; + } + + wp = 0; + rp = 0; + for (i = 0; i < cb_segm->C; i++) { + + uint32_t cblen_idx; + /* Get read lengths */ + if (i < cb_segm->C2) { + cb_len = cb_segm->K2; + cblen_idx = cb_segm->K2_idx; + } else { + cb_len = cb_segm->K1; + cblen_idx = cb_segm->K1_idx; + } + if (cb_segm->C > 1) { + rlen = cb_len - 24; + } else { + rlen = cb_len; + } + if (i <= cb_segm->C - gamma - 1) { + n_e = Qm * (Gp/cb_segm->C); + } else { + n_e = Qm * ((uint32_t) ceilf((float) Gp/cb_segm->C)); + } + + INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, E: %d\n", i, + cb_len, rlen, wp, rp, n_e); + + if (data) { + + /* Copy data to another buffer, making space for the Codeblock CRC */ + if (i < cb_segm->C - 1) { + // Copy data + memcpy(q->cb_in, &data[rp/8], rlen * sizeof(uint8_t)/8); + } else { + INFO("Last CB, appending parity: %d from %d and 24 to %d\n", + rlen - 24, rp, rlen - 24); + + /* Append Transport Block parity bits to the last CB */ + memcpy(q->cb_in, &data[rp/8], (rlen - 24) * sizeof(uint8_t)/8); + memcpy(&q->cb_in[(rlen - 24)/8], parity, 3 * sizeof(uint8_t)); + } + + /* Attach Codeblock CRC */ + if (cb_segm->C > 1) { + srslte_crc_attach_byte(&q->crc_cb, q->cb_in, rlen); + } + + /* Turbo Encoding */ + srslte_tcod_encode_lut(&q->encoder, q->cb_in, q->parity_bits, cblen_idx); + } + DEBUG("RM cblen_idx=%d, n_e=%d, wp=%d, nof_e_bits=%d\n",cblen_idx, n_e, wp, nof_e_bits); + + /* Rate matching */ + if (srslte_rm_turbo_tx_lut(softbuffer->buffer_b[i], q->cb_in, q->parity_bits, + &e_bits[(wp+w_offset)/8], cblen_idx, n_e, (wp+w_offset)%8, rv)) + { + fprintf(stderr, "Error in rate matching\n"); + return SRSLTE_ERROR; + } + + /* Set read/write pointers */ + rp += rlen; + wp += n_e; + } + + INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +static int encode_tb(srslte_sch_t *q, + srslte_softbuffer_tx_t *soft_buffer, srslte_cbsegm_t *cb_segm, + uint32_t Qm, uint32_t rv, uint32_t nof_e_bits, + uint8_t *data, uint8_t *e_bits) +{ + return encode_tb_off(q, soft_buffer, cb_segm, Qm, rv, nof_e_bits, data, e_bits, 0); +} + +bool decode_tb_cb(srslte_sch_t *q, + srslte_softbuffer_rx_t *softbuffer, srslte_cbsegm_t *cb_segm, + uint32_t Qm, uint32_t rv, uint32_t nof_e_bits, + int16_t *e_bits, uint8_t *data, + uint32_t cb_size_group) +{ + + bool cb_map[SRSLTE_MAX_CODEBLOCKS]; + + uint32_t cb_idx[SRSLTE_TDEC_MAX_NPAR]; + int16_t *decoder_input[SRSLTE_TDEC_MAX_NPAR]; + + uint32_t nof_cb = cb_size_group?cb_segm->C2:cb_segm->C1; + uint32_t first_cb = cb_size_group?cb_segm->C1:0; + uint32_t cb_len = cb_size_group?cb_segm->K2:cb_segm->K1; + uint32_t cb_len_idx = cb_size_group?cb_segm->K2_idx:cb_segm->K1_idx; + + uint32_t rlen = cb_segm->C==1?cb_len:(cb_len-24); + uint32_t Gp = nof_e_bits / Qm; + uint32_t gamma = cb_segm->C>0?Gp%cb_segm->C:Gp; + uint32_t n_e = Qm * (Gp/cb_segm->C); + + if (nof_cb > SRSLTE_MAX_CODEBLOCKS) { + fprintf(stderr, "Error SRSLTE_MAX_CODEBLOCKS=%d\n", SRSLTE_MAX_CODEBLOCKS); + return false; + } + + for (int i=0;idecoder);i++) { + cb_idx[i] = i+first_cb; + decoder_input[i] = NULL; + } + + uint32_t remaining_cb = 0; + for (int i=0;icb_crc[i]; + if (softbuffer->cb_crc[i] == false) { + remaining_cb ++; + } + } + + srslte_tdec_reset(&q->decoder, cb_len); + + q->nof_iterations = 0; + + while(remaining_cb>0) { + + // Unratematch the codeblocks left to decode + for (int i=0;idecoder);i++) { + + if (!decoder_input[i] && remaining_cb > 0) { + // Find an unprocessed CB + cb_idx[i]=first_cb; + while(cb_idx[i] cb_segm->C - gamma) { + n_e2 = n_e+Qm; + rp = (cb_segm->C - gamma)*n_e + (cb_idx[i]-(cb_segm->C - gamma))*n_e2; + } + + INFO("CB %d: rp=%d, n_e=%d, i=%d\n", cb_idx[i], rp, n_e2, i); + if (srslte_rm_turbo_rx_lut(&e_bits[rp], softbuffer->buffer_f[cb_idx[i]], n_e2, cb_len_idx, rv)) { + fprintf(stderr, "Error in rate matching\n"); + return SRSLTE_ERROR; + } + + decoder_input[i] = softbuffer->buffer_f[cb_idx[i]]; + } + } + } + + // Run 1 iteration for the codeblocks in queue + srslte_tdec_iteration_par(&q->decoder, decoder_input, cb_len); + + // Decide output bits and compute CRC + for (int i=0;idecoder);i++) { + if (decoder_input[i]) { + srslte_tdec_decision_byte_par_cb(&q->decoder, q->cb_in, i, cb_len); + + uint32_t len_crc; + srslte_crc_t *crc_ptr; + + if (cb_segm->C > 1) { + len_crc = cb_len; + crc_ptr = &q->crc_cb; + } else { + len_crc = cb_segm->tbs+24; + crc_ptr = &q->crc_tb; + } + + // CRC is OK + if (!srslte_crc_checksum_byte(crc_ptr, q->cb_in, len_crc)) { + + memcpy(softbuffer->data[cb_idx[i]], q->cb_in, rlen/8 * sizeof(uint8_t)); + softbuffer->cb_crc[cb_idx[i]] = true; + + q->nof_iterations += srslte_tdec_get_nof_iterations_cb(&q->decoder, i); + + // Reset number of iterations for that CB in the decoder + srslte_tdec_reset_cb(&q->decoder, i); + remaining_cb--; + decoder_input[i] = NULL; + cb_idx[i] = 0; + + // CRC is error and exceeded maximum iterations for this CB. + // Early stop the whole transport block. + } else if (srslte_tdec_get_nof_iterations_cb(&q->decoder, i) >= q->max_iterations) { + INFO("CB %d: Error. CB is erroneous. remaining_cb=%d, i=%d, first_cb=%d, nof_cb=%d\n", + cb_idx[i], remaining_cb, i, first_cb, nof_cb); + + q->nof_iterations += q->max_iterations; + srslte_tdec_reset_cb(&q->decoder, i); + remaining_cb--; + decoder_input[i] = NULL; + cb_idx[i] = 0; + } + } + } + } + + softbuffer->tb_crc = true; + for (int i = 0; i < nof_cb && softbuffer->tb_crc; i++) { + /* If one CB failed return false */ + softbuffer->tb_crc = softbuffer->cb_crc[i]; + } + if (softbuffer->tb_crc) { + for (int i = 0; i < nof_cb; i++) { + memcpy(&data[i * rlen / 8], softbuffer->data[i], rlen/8 * sizeof(uint8_t)); + } + } + + q->nof_iterations /= nof_cb; + return softbuffer->tb_crc; +} + +/** + * Decode a transport block according to 36.212 5.3.2 + * + * @param[in] q + * @param[inout] softbuffer Initialized softbuffer + * @param[in] cb_segm Code block segmentation parameters + * @param[in] e_bits Input transport block + * @param[in] Qm Modulation type + * @param[in] rv Redundancy Version. Indicates which part of FEC bits is in input buffer + * @param[out] softbuffer Initialized output softbuffer + * @param[out] data Decoded transport block + * @return negative if error in parameters or CRC error in decoding + */ +static int decode_tb(srslte_sch_t *q, + srslte_softbuffer_rx_t *softbuffer, srslte_cbsegm_t *cb_segm, + uint32_t Qm, uint32_t rv, uint32_t nof_e_bits, + int16_t *e_bits, uint8_t *data) +{ + + if (q != NULL && + data != NULL && + softbuffer != NULL && + e_bits != NULL && + cb_segm != NULL) + { + + if (cb_segm->tbs == 0 || cb_segm->C == 0) { + return SRSLTE_SUCCESS; + } + + if (cb_segm->F) { + fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n"); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (cb_segm->C > softbuffer->max_cb) { + fprintf(stderr, "Error number of CB (%d) exceeds soft buffer size (%d CBs)\n", cb_segm->C, softbuffer->max_cb); + return SRSLTE_ERROR_INVALID_INPUTS; + } + + bool crc_ok = true; + + uint32_t nof_cb_groups = cb_segm->C2>0?2:1; + + data[cb_segm->tbs/8+0] = 0; + data[cb_segm->tbs/8+1] = 0; + data[cb_segm->tbs/8+2] = 0; + + // Process Codeblocks in groups of equal CB size to parallelize according to SRSLTE_TDEC_MAX_NPAR + for (uint32_t i=0;icrc_tb, data, cb_segm->tbs); + + // check parity bits + par_tx = ((uint32_t) data[cb_segm->tbs/8+0])<<16 | + ((uint32_t) data[cb_segm->tbs/8+1])<<8 | + ((uint32_t) data[cb_segm->tbs/8+2]); + + if (par_rx == par_tx && par_rx) { + INFO("TB decoded OK\n"); + return SRSLTE_SUCCESS; + } else { + INFO("Error in TB parity: par_tx=0x%x, par_rx=0x%x\n", par_tx, par_rx); + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_dlsch_decode(srslte_sch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + int16_t *e_bits, uint8_t *data) { + return srslte_dlsch_decode2(q, cfg, softbuffer, e_bits, data, 0); +} + + +int srslte_dlsch_decode2(srslte_sch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + int16_t *e_bits, uint8_t *data, int tb_idx) { + uint32_t Nl = 1; + + if (cfg->nof_layers != SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant)) { + Nl = 2; + } + + return decode_tb(q, softbuffer, &cfg->cb_segm[tb_idx], + cfg->grant.Qm[tb_idx] * Nl, cfg->rv[tb_idx], cfg->nbits[tb_idx].nof_bits, + e_bits, data); +} + +/** + * Encode transport block. Segments into code blocks, adds channel coding, and does rate matching. + * + * @param[in] q Initialized + * @param[in] cfg Encoding parameters + * @param[inout] softbuffer Initialized softbuffer + * @param[in] data Byte array of data. Size is implicit in cfg->cb_segm + * @param e_bits + * @return Error code + */ +int srslte_dlsch_encode(srslte_sch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, uint8_t *e_bits) +{ + return srslte_dlsch_encode2(q, cfg, softbuffer, data, e_bits, 0); +} + +int srslte_dlsch_encode2(srslte_sch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, uint8_t *e_bits, int tb_idx) { + uint32_t Nl = 1; + + if (cfg->nof_layers != SRSLTE_RA_DL_GRANT_NOF_TB(&cfg->grant)) { + Nl = 2; + } + + return encode_tb(q, softbuffer, &cfg->cb_segm[tb_idx], cfg->grant.Qm[tb_idx]*Nl, cfg->rv[tb_idx], + cfg->nbits[tb_idx].nof_bits, data, e_bits); +} + +/* Compute the interleaving function on-the-fly, because it depends on number of RI bits + * Profiling show that the computation of this matrix is neglegible. + */ +static void ulsch_interleave_gen(uint32_t H_prime_total, uint32_t N_pusch_symbs, uint32_t Qm, + uint8_t *ri_present, uint32_t *interleaver_lut) +{ + uint32_t rows = H_prime_total/N_pusch_symbs; + uint32_t cols = N_pusch_symbs; + uint32_t idx = 0; + for(uint32_t j=0; j 0) { + for (uint32_t i=0;i 0) { + for (uint32_t i=0;i 0) { + for (uint32_t i=0;i 0) { + for (uint32_t i=0;inbits.nof_bits; + uint32_t Qm = cfg->grant.Qm; + + cfg->last_O_cqi = uci_data->uci_cqi_len; + + // Deinterleave and decode HARQ bits + if (uci_data->uci_ack_len > 0) { + uint8_t acks[2] = {0, 0}; + float beta = beta_harq_offset[cfg->uci_cfg.I_offset_ack]; + if (cfg->cb_segm.tbs == 0) { + beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; + } + ret = srslte_uci_decode_ack_ri(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, acks, uci_data->uci_ack_len, false); + if (ret < 0) { + return ret; + } + uci_data->uci_ack = acks[0]; + uci_data->uci_ack_2 = acks[1]; + Q_prime_ack = (uint32_t) ret; + + // Set zeros to HARQ bits + for (uint32_t i = 0; i < Q_prime_ack * Qm; i++) { + q_bits[q->ack_ri_bits[i].position] = 0; + } + } + + // Deinterleave and decode RI bits + if (uci_data->uci_ri_len > 0) { + float beta = beta_ri_offset[cfg->uci_cfg.I_offset_ri]; + if (cfg->cb_segm.tbs == 0) { + beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; + } + ret = srslte_uci_decode_ack_ri(cfg, q_bits, c_seq, beta, nb_q/Qm, uci_data->uci_cqi_len, q->ack_ri_bits, &uci_data->uci_ri, uci_data->uci_ri_len, true); + if (ret < 0) { + return ret; + } + Q_prime_ri = (uint32_t) ret; + } + + q->nof_ri_ack_bits = Q_prime_ri; + + return SRSLTE_SUCCESS; +} + +int srslte_ulsch_uci_decode(srslte_sch_t *q, srslte_pusch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, + int16_t *q_bits, int16_t *g_bits, uint8_t *data, srslte_uci_data_t *uci_data) +{ + int ret = 0; + + uint32_t Q_prime_ri = q->nof_ri_ack_bits; + uint32_t Q_prime_cqi = 0; + uint32_t e_offset = 0; + + uint32_t nb_q = cfg->nbits.nof_bits; + uint32_t Qm = cfg->grant.Qm; + + // Deinterleave data and CQI in ULSCH + ulsch_deinterleave(q_bits, Qm, nb_q/Qm, cfg->nbits.nof_symb, g_bits, q->ack_ri_bits, Q_prime_ri*Qm, + q->temp_g_bits, q->ul_interleaver); + + // Decode CQI (multiplexed at the front of ULSCH) + if (uci_data->uci_cqi_len > 0) { + ret = srslte_uci_decode_cqi_pusch(&q->uci_cqi, cfg, g_bits, + beta_cqi_offset[cfg->uci_cfg.I_offset_cqi], + Q_prime_ri, uci_data->uci_cqi_len, + uci_data->uci_cqi, &uci_data->cqi_ack); + if (ret < 0) { + return ret; + } + Q_prime_cqi = (uint32_t) ret; + } + + e_offset += Q_prime_cqi*Qm; + + // Decode ULSCH + if (cfg->cb_segm.tbs > 0) { + uint32_t G = nb_q/Qm - Q_prime_ri - Q_prime_cqi; + ret = decode_tb(q, softbuffer, &cfg->cb_segm, + Qm, cfg->rv, G*Qm, + &g_bits[e_offset], data); + if (ret) { + return ret; + } + } + return SRSLTE_SUCCESS; +} + +int srslte_ulsch_encode(srslte_sch_t *q, srslte_pusch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, uint8_t *g_bits, uint8_t *q_bits) +{ + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + return srslte_ulsch_uci_encode(q, cfg, softbuffer, data, uci_data, g_bits, q_bits); +} + +int srslte_ulsch_uci_encode(srslte_sch_t *q, + srslte_pusch_cfg_t *cfg, srslte_softbuffer_tx_t *softbuffer, + uint8_t *data, srslte_uci_data_t uci_data, + uint8_t *g_bits, uint8_t *q_bits) +{ + int ret; + + uint32_t e_offset = 0; + uint32_t Q_prime_cqi = 0; + uint32_t Q_prime_ack = 0; + uint32_t Q_prime_ri = 0; + + uint32_t nb_q = cfg->nbits.nof_bits; + uint32_t Qm = cfg->grant.Qm; + + if (Qm == 0) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (uci_data.uci_ri_len > 0) { + float beta = beta_ri_offset[cfg->uci_cfg.I_offset_ri]; + if (cfg->cb_segm.tbs == 0) { + beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; + } + uint8_t ri[2] = {uci_data.uci_ri, 0}; + ret = srslte_uci_encode_ack_ri(cfg, ri, uci_data.uci_ri_len, uci_data.uci_cqi_len, beta, nb_q/Qm, q->ack_ri_bits, true); + if (ret < 0) { + return ret; + } + Q_prime_ri = (uint32_t) ret; + } + + // Encode CQI + cfg->last_O_cqi = uci_data.uci_cqi_len; + if (uci_data.uci_cqi_len > 0) { + ret = srslte_uci_encode_cqi_pusch(&q->uci_cqi, cfg, + uci_data.uci_cqi, uci_data.uci_cqi_len, + beta_cqi_offset[cfg->uci_cfg.I_offset_cqi], + Q_prime_ri, q->temp_g_bits); + if (ret < 0) { + return ret; + } + Q_prime_cqi = (uint32_t) ret; + srslte_bit_pack_vector(q->temp_g_bits, g_bits, Q_prime_cqi*Qm); + // Reset the buffer because will be reused in ulsch_interleave + bzero(q->temp_g_bits, Q_prime_cqi*Qm); + } + + e_offset += Q_prime_cqi*Qm; + + // Encode UL-SCH + if (cfg->cb_segm.tbs > 0) { + uint32_t G = nb_q/Qm - Q_prime_ri - Q_prime_cqi; + ret = encode_tb_off(q, softbuffer, &cfg->cb_segm, + Qm, cfg->rv, G*Qm, + data, &g_bits[e_offset/8], e_offset%8); + if (ret) { + return ret; + } + } + + // Interleave UL-SCH (and RI and CQI) + ulsch_interleave(g_bits, Qm, nb_q/Qm, cfg->nbits.nof_symb, q_bits, q->ack_ri_bits, Q_prime_ri*Qm, + q->temp_g_bits, q->ul_interleaver); + + // Encode (and interleave) ACK + if (uci_data.uci_ack_len > 0) { + uint8_t acks [2] = {uci_data.uci_ack, uci_data.uci_ack_2}; + float beta = beta_harq_offset[cfg->uci_cfg.I_offset_ack]; + if (cfg->cb_segm.tbs == 0) { + beta /= beta_cqi_offset[cfg->uci_cfg.I_offset_cqi]; + } + ret = srslte_uci_encode_ack_ri(cfg, acks, uci_data.uci_ack_len, uci_data.uci_cqi_len, + beta, nb_q / Qm, &q->ack_ri_bits[Q_prime_ri * Qm], false); + if (ret < 0) { + return ret; + } + Q_prime_ack = (uint32_t) ret; + } + + q->nof_ri_ack_bits = (Q_prime_ack+Q_prime_ri)*Qm; + + for (uint32_t i=0;inof_ri_ack_bits;i++) { + uint32_t p = q->ack_ri_bits[i].position; + if (p < nb_q) { + if (q->ack_ri_bits[i].type == UCI_BIT_1) { + q_bits[p/8] |= (1<<(7-p%8)); + } else { + q_bits[p/8] &= ~(1<<(7-p%8)); + } + } else { + fprintf(stderr, "Invalid RI/ACK bit position %d. Max bits=%d\n", p, nb_q); + } + } + + + INFO("Q_prime_ack=%d, Q_prime_cqi=%d, Q_prime_ri=%d\n",Q_prime_ack, Q_prime_cqi, Q_prime_ri); + + return SRSLTE_SUCCESS; +} + diff --git a/lib/src/phy/phch/sequences.c b/lib/src/phy/phch/sequences.c new file mode 100644 index 0000000..dc9e45a --- /dev/null +++ b/lib/src/phy/phch/sequences.c @@ -0,0 +1,86 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/common/sequence.h" + +/** + * 36.211 6.6.1 + */ +int srslte_sequence_pbch(srslte_sequence_t *seq, srslte_cp_t cp, uint32_t cell_id) { + return srslte_sequence_LTE_pr(seq, SRSLTE_CP_ISNORM(cp)?1920:1728, cell_id); +} + +/** + * 36.211 6.7.1 + */ +int srslte_sequence_pcfich(srslte_sequence_t *seq, uint32_t nslot, uint32_t cell_id) { + return srslte_sequence_LTE_pr(seq, 32, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); +} + + +/** + * 36.211 6.9.1 + */ +int srslte_sequence_phich(srslte_sequence_t *seq, uint32_t nslot, uint32_t cell_id) { + return srslte_sequence_LTE_pr(seq, 12, (nslot/2+1) * (2*cell_id + 1) * 512 + cell_id); +} + +/** + * 36.211 6.8.2 + */ +int srslte_sequence_pdcch(srslte_sequence_t *seq, uint32_t nslot, uint32_t cell_id, uint32_t len) { + return srslte_sequence_LTE_pr(seq, len, (nslot/2) * 512 + cell_id); +} + +/** + * 36.211 6.3.1 + */ +int srslte_sequence_pdsch(srslte_sequence_t *seq, uint16_t rnti, int q, uint32_t nslot, uint32_t cell_id, uint32_t len) { + return srslte_sequence_LTE_pr(seq, len, (rnti<<14) + (q<<13) + ((nslot/2)<<9) + cell_id); +} + +/** + * 36.211 5.3.1 + */ +int srslte_sequence_pusch(srslte_sequence_t *seq, uint16_t rnti, uint32_t nslot, uint32_t cell_id, uint32_t len) { + return srslte_sequence_LTE_pr(seq, len, (rnti<<14) + ((nslot/2)<<9) + cell_id); +} + +/** + * 36.211 5.4.2 + */ +int srslte_sequence_pucch(srslte_sequence_t *seq, uint16_t rnti, uint32_t nslot, uint32_t cell_id) { + return srslte_sequence_LTE_pr(seq, 20, ((((nslot/2)+1)*(2*cell_id+1))<<16)+rnti); +} + +int srslte_sequence_pmch(srslte_sequence_t *seq, uint32_t nslot, uint32_t mbsfn_id , uint32_t len){ + bzero(seq,sizeof(srslte_sequence_t)); + return srslte_sequence_LTE_pr(seq, len, (((nslot/2)<<9) + mbsfn_id)); + +} \ No newline at end of file diff --git a/lib/src/phy/phch/tbs_tables.h b/lib/src/phy/phch/tbs_tables.h new file mode 100644 index 0000000..cb219d6 --- /dev/null +++ b/lib/src/phy/phch/tbs_tables.h @@ -0,0 +1,308 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +const int tbs_format1c_table[32] = { + 40, 56, 72, 120, 136, 144, 176, 208, 224, 256, 280, 296, 328, 336, 392, 488, + 552, 600, 632, 696, 776, 840, 904, 1000, 1064, 1128, 1224, 1288, 1384, 1480, 1608, 1736 +}; + +/* Modulation and TBS index table for PDSCH from 3GPP TS 36.213 v10.3.0 table 7.1.7.1-1 */ +const int mcs_tbs_idx_table[29] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 10, 11, 12, 13, 14, 15, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26}; + + +/* Transport Block Size from 3GPP TS 36.213 v10.3.0 table 7.1.7.2.1-1 */ +const int tbs_table[27][110] = {{ 16, 32, 56, 88, 120, 152, 176, 208, 224, 256, 288, + 328, 344, 376, 392, 424, 456, 488, 504, 536, 568, 600, + 616, 648, 680, 712, 744, 776, 776, 808, 840, 872, 904, + 936, 968, 1000, 1032, 1032, 1064, 1096, 1128, 1160, 1192, 1224, + 1256, 1256, 1288, 1320, 1352, 1384, 1416, 1416, 1480, 1480, 1544, + 1544, 1608, 1608, 1608, 1672, 1672, 1736, 1736, 1800, 1800, 1800, + 1864, 1864, 1928, 1928, 1992, 1992, 2024, 2088, 2088, 2088, 2152, + 2152, 2216, 2216, 2280, 2280, 2280, 2344, 2344, 2408, 2408, 2472, + 2472, 2536, 2536, 2536, 2600, 2600, 2664, 2664, 2728, 2728, 2728, + 2792, 2792, 2856, 2856, 2856, 2984, 2984, 2984, 2984, 2984, 3112}, + { 24, 56, 88, 144, 176, 208, 224, 256, 328, 344, 376, + 424, 456, 488, 520, 568, 600, 632, 680, 712, 744, 776, + 808, 872, 904, 936, 968, 1000, 1032, 1064, 1128, 1160, 1192, + 1224, 1256, 1288, 1352, 1384, 1416, 1416, 1480, 1544, 1544, 1608, + 1608, 1672, 1736, 1736, 1800, 1800, 1864, 1864, 1928, 1992, 1992, + 2024, 2088, 2088, 2152, 2152, 2216, 2280, 2280, 2344, 2344, 2408, + 2472, 2472, 2536, 2536, 2600, 2600, 2664, 2728, 2728, 2792, 2792, + 2856, 2856, 2856, 2984, 2984, 2984, 3112, 3112, 3112, 3240, 3240, + 3240, 3240, 3368, 3368, 3368, 3496, 3496, 3496, 3496, 3624, 3624, + 3624, 3752, 3752, 3752, 3752, 3880, 3880, 3880, 4008, 4008, 4008}, + { 32, 72, 144, 176, 208, 256, 296, 328, 376, 424, 472, + 520, 568, 616, 648, 696, 744, 776, 840, 872, 936, 968, + 1000, 1064, 1096, 1160, 1192, 1256, 1288, 1320, 1384, 1416, 1480, + 1544, 1544, 1608, 1672, 1672, 1736, 1800, 1800, 1864, 1928, 1992, + 2024, 2088, 2088, 2152, 2216, 2216, 2280, 2344, 2344, 2408, 2472, + 2536, 2536, 2600, 2664, 2664, 2728, 2792, 2856, 2856, 2856, 2984, + 2984, 3112, 3112, 3112, 3240, 3240, 3240, 3368, 3368, 3368, 3496, + 3496, 3496, 3624, 3624, 3624, 3752, 3752, 3880, 3880, 3880, 4008, + 4008, 4008, 4136, 4136, 4136, 4264, 4264, 4264, 4392, 4392, 4392, + 4584, 4584, 4584, 4584, 4584, 4776, 4776, 4776, 4776, 4968, 4968}, + { 40, 104, 176, 208, 256, 328, 392, 440, 504, 568, 616, + 680, 744, 808, 872, 904, 968, 1032, 1096, 1160, 1224, 1256, + 1320, 1384, 1416, 1480, 1544, 1608, 1672, 1736, 1800, 1864, 1928, + 1992, 2024, 2088, 2152, 2216, 2280, 2344, 2408, 2472, 2536, 2536, + 2600, 2664, 2728, 2792, 2856, 2856, 2984, 2984, 3112, 3112, 3240, + 3240, 3368, 3368, 3496, 3496, 3624, 3624, 3624, 3752, 3752, 3880, + 3880, 4008, 4008, 4136, 4136, 4264, 4264, 4392, 4392, 4392, 4584, + 4584, 4584, 4776, 4776, 4776, 4776, 4968, 4968, 4968, 5160, 5160, + 5160, 5352, 5352, 5352, 5352, 5544, 5544, 5544, 5736, 5736, 5736, + 5736, 5992, 5992, 5992, 5992, 6200, 6200, 6200, 6200, 6456, 6456}, + { 56, 120, 208, 256, 328, 408, 488, 552, 632, 696, 776, + 840, 904, 1000, 1064, 1128, 1192, 1288, 1352, 1416, 1480, 1544, + 1608, 1736, 1800, 1864, 1928, 1992, 2088, 2152, 2216, 2280, 2344, + 2408, 2472, 2600, 2664, 2728, 2792, 2856, 2984, 2984, 3112, 3112, + 3240, 3240, 3368, 3496, 3496, 3624, 3624, 3752, 3752, 3880, 4008, + 4008, 4136, 4136, 4264, 4264, 4392, 4392, 4584, 4584, 4584, 4776, + 4776, 4968, 4968, 4968, 5160, 5160, 5160, 5352, 5352, 5544, 5544, + 5544, 5736, 5736, 5736, 5992, 5992, 5992, 5992, 6200, 6200, 6200, + 6456, 6456, 6456, 6456, 6712, 6712, 6712, 6968, 6968, 6968, 6968, + 7224, 7224, 7224, 7480, 7480, 7480, 7480, 7736, 7736, 7736, 7992}, + { 72, 144, 224, 328, 424, 504, 600, 680, 776, 872, 968, + 1032, 1128, 1224, 1320, 1384, 1480, 1544, 1672, 1736, 1864, 1928, + 2024, 2088, 2216, 2280, 2344, 2472, 2536, 2664, 2728, 2792, 2856, + 2984, 3112, 3112, 3240, 3368, 3496, 3496, 3624, 3752, 3752, 3880, + 4008, 4008, 4136, 4264, 4392, 4392, 4584, 4584, 4776, 4776, 4776, + 4968, 4968, 5160, 5160, 5352, 5352, 5544, 5544, 5736, 5736, 5736, + 5992, 5992, 5992, 6200, 6200, 6200, 6456, 6456, 6712, 6712, 6712, + 6968, 6968, 6968, 7224, 7224, 7224, 7480, 7480, 7480, 7736, 7736, + 7736, 7992, 7992, 7992, 8248, 8248, 8248, 8504, 8504, 8760, 8760, + 8760, 8760, 9144, 9144, 9144, 9144, 9528, 9528, 9528, 9528, 9528}, + { 328, 176, 256, 392, 504, 600, 712, 808, 936, 1032, 1128, + 1224, 1352, 1480, 1544, 1672, 1736, 1864, 1992, 2088, 2216, 2280, + 2408, 2472, 2600, 2728, 2792, 2984, 2984, 3112, 3240, 3368, 3496, + 3496, 3624, 3752, 3880, 4008, 4136, 4136, 4264, 4392, 4584, 4584, + 4776, 4776, 4968, 4968, 5160, 5160, 5352, 5352, 5544, 5736, 5736, + 5992, 5992, 5992, 6200, 6200, 6456, 6456, 6456, 6712, 6712, 6968, + 6968, 6968, 7224, 7224, 7480, 7480, 7736, 7736, 7736, 7992, 7992, + 8248, 8248, 8248, 8504, 8504, 8760, 8760, 8760, 9144, 9144, 9144, + 9144, 9528, 9528, 9528, 9528, 9912, 9912, 9912,10296,10296,10296, + 10296,10680,10680,10680,10680,11064,11064,11064,11448,11448,11448}, + { 104, 224, 328, 472, 584, 712, 840, 968, 1096, 1224, 1320, + 1480, 1608, 1672, 1800, 1928, 2088, 2216, 2344, 2472, 2536, 2664, + 2792, 2984, 3112, 3240, 3368, 3368, 3496, 3624, 3752, 3880, 4008, + 4136, 4264, 4392, 4584, 4584, 4776, 4968, 4968, 5160, 5352, 5352, + 5544, 5736, 5736, 5992, 5992, 6200, 6200, 6456, 6456, 6712, 6712, + 6712, 6968, 6968, 7224, 7224, 7480, 7480, 7736, 7736, 7992, 7992, + 8248, 8248, 8504, 8504, 8760, 8760, 8760, 9144, 9144, 9144, 9528, + 9528, 9528, 9912, 9912, 9912,10296,10296,10296,10680,10680,10680, + 11064,11064,11064,11448,11448,11448,11448,11832,11832,11832,12216, + 12216,12216,12576,12576,12576,12960,12960,12960,12960,13536,13536}, + { 120, 256, 392, 536, 680, 808, 968, 1096, 1256, 1384, 1544, + 1672, 1800, 1928, 2088, 2216, 2344, 2536, 2664, 2792, 2984, 3112, + 3240, 3368, 3496, 3624, 3752, 3880, 4008, 4264, 4392, 4584, 4584, + 4776, 4968, 4968, 5160, 5352, 5544, 5544, 5736, 5992, 5992, 6200, + 6200, 6456, 6456, 6712, 6968, 6968, 7224, 7224, 7480, 7480, 7736, + 7736, 7992, 7992, 8248, 8504, 8504, 8760, 8760, 9144, 9144, 9144, + 9528, 9528, 9528, 9912, 9912, 9912,10296,10296,10680,10680,10680, + 11064,11064,11064,11448,11448,11448,11832,11832,12216,12216,12216, + 12576,12576,12576,12960,12960,12960,13536,13536,13536,13536,14112, + 14112,14112,14112,14688,14688,14688,14688,15264,15264,15264,15264}, + { 136, 296, 456, 616, 776, 936, 1096, 1256, 1416, 1544, 1736, + 1864, 2024, 2216, 2344, 2536, 2664, 2856, 2984, 3112, 3368, 3496, + 3624, 3752, 4008, 4136, 4264, 4392, 4584, 4776, 4968, 5160, 5160, + 5352, 5544, 5736, 5736, 5992, 6200, 6200, 6456, 6712, 6712, 6968, + 6968, 7224, 7480, 7480, 7736, 7992, 7992, 8248, 8248, 8504, 8760, + 8760, 9144, 9144, 9144, 9528, 9528, 9912, 9912,10296,10296,10296, + 10680,10680,11064,11064,11064,11448,11448,11832,11832,11832,12216, + 12216,12576,12576,12960,12960,12960,13536,13536,13536,13536,14112, + 14112,14112,14112,14688,14688,14688,15264,15264,15264,15264,15840, + 15840,15840,16416,16416,16416,16416,16992,16992,16992,16992,17568}, + { 144, 328, 504, 680, 872, 1032, 1224, 1384, 1544, 1736, 1928, + 2088, 2280, 2472, 2664, 2792, 2984, 3112, 3368, 3496, 3752, 3880, + 4008, 4264, 4392, 4584, 4776, 4968, 5160, 5352, 5544, 5736, 5736, + 5992, 6200, 6200, 6456, 6712, 6712, 6968, 7224, 7480, 7480, 7736, + 7992, 7992, 8248, 8504, 8504, 8760, 9144, 9144, 9144, 9528, 9528, + 9912, 9912,10296,10296,10680,10680,11064,11064,11448,11448,11448, + 11832,11832,12216,12216,12576,12576,12960,12960,12960,13536,13536, + 13536,14112,14112,14112,14688,14688,14688,14688,15264,15264,15264, + 15840,15840,15840,16416,16416,16416,16992,16992,16992,16992,17568, + 17568,17568,18336,18336,18336,18336,18336,19080,19080,19080,19080}, + { 176, 376, 584, 776, 1000, 1192, 1384, 1608, 1800, 2024, 2216, + 2408, 2600, 2792, 2984, 3240, 3496, 3624, 3880, 4008, 4264, 4392, + 4584, 4776, 4968, 5352, 5544, 5736, 5992, 5992, 6200, 6456, 6712, + 6968, 6968, 7224, 7480, 7736, 7736, 7992, 8248, 8504, 8760, 8760, + 9144, 9144, 9528, 9528, 9912, 9912,10296,10680,10680,11064,11064, + 11448,11448,11832,11832,12216,12216,12576,12576,12960,12960,13536, + 13536,13536,14112,14112,14112,14688,14688,14688,15264,15264,15840, + 15840,15840,16416,16416,16416,16992,16992,16992,17568,17568,17568, + 18336,18336,18336,18336,19080,19080,19080,19080,19848,19848,19848, + 19848,20616,20616,20616,21384,21384,21384,21384,22152,22152,22152}, + { 208, 440, 680, 904, 1128, 1352, 1608, 1800, 2024, 2280, 2472, + 2728, 2984, 3240, 3368, 3624, 3880, 4136, 4392, 4584, 4776, 4968, + 5352, 5544, 5736, 5992, 6200, 6456, 6712, 6712, 6968, 7224, 7480, + 7736, 7992, 8248, 8504, 8760, 8760, 9144, 9528, 9528, 9912, 9912, + 10296,10680,10680,11064,11064,11448,11832,11832,12216,12216,12576, + 12576,12960,12960,13536,13536,14112,14112,14112,14688,14688,15264, + 15264,15264,15840,15840,16416,16416,16416,16992,16992,17568,17568, + 17568,18336,18336,18336,19080,19080,19080,19080,19848,19848,19848, + 20616,20616,20616,21384,21384,21384,21384,22152,22152,22152,22920, + 22920,22920,23688,23688,23688,23688,24496,24496,24496,24496,25456}, + { 224, 488, 744, 1000, 1256, 1544, 1800, 2024, 2280, 2536, 2856, + 3112, 3368, 3624, 3880, 4136, 4392, 4584, 4968, 5160, 5352, 5736, + 5992, 6200, 6456, 6712, 6968, 7224, 7480, 7736, 7992, 8248, 8504, + 8760, 9144, 9144, 9528, 9912, 9912,10296,10680,10680,11064,11448, + 11448,11832,12216,12216,12576,12960,12960,13536,13536,14112,14112, + 14688,14688,14688,15264,15264,15840,15840,16416,16416,16992,16992, + 16992,17568,17568,18336,18336,18336,19080,19080,19080,19848,19848, + 19848,20616,20616,20616,21384,21384,21384,22152,22152,22152,22920, + 22920,22920,23688,23688,23688,24496,24496,24496,25456,25456,25456, + 25456,26416,26416,26416,26416,27376,27376,27376,27376,28336,28336}, + { 256, 552, 840, 1128, 1416, 1736, 1992, 2280, 2600, 2856, 3112, + 3496, 3752, 4008, 4264, 4584, 4968, 5160, 5544, 5736, 5992, 6200, + 6456, 6968, 7224, 7480, 7736, 7992, 8248, 8504, 8760, 9144, 9528, + 9912, 9912,10296,10680,11064,11064,11448,11832,12216,12216,12576, + 12960,12960,13536,13536,14112,14112,14688,14688,15264,15264,15840, + 15840,16416,16416,16992,16992,17568,17568,18336,18336,18336,19080, + 19080,19848,19848,19848,20616,20616,20616,21384,21384,22152,22152, + 22152,22920,22920,22920,23688,23688,24496,24496,24496,25456,25456, + 25456,25456,26416,26416,26416,27376,27376,27376,28336,28336,28336, + 28336,29296,29296,29296,29296,30576,30576,30576,30576,31704,31704}, + { 280, 600, 904, 1224, 1544, 1800, 2152, 2472, 2728, 3112, 3368, + 3624, 4008, 4264, 4584, 4968, 5160, 5544, 5736, 6200, 6456, 6712, + 6968, 7224, 7736, 7992, 8248, 8504, 8760, 9144, 9528, 9912,10296, + 10296,10680,11064,11448,11832,11832,12216,12576,12960,12960,13536, + 13536,14112,14688,14688,15264,15264,15840,15840,16416,16416,16992, + 16992,17568,17568,18336,18336,18336,19080,19080,19848,19848,20616, + 20616,20616,21384,21384,22152,22152,22152,22920,22920,23688,23688, + 23688,24496,24496,24496,25456,25456,25456,26416,26416,26416,27376, + 27376,27376,28336,28336,28336,29296,29296,29296,29296,30576,30576, + 30576,30576,31704,31704,31704,31704,32856,32856,32856,34008,34008}, + { 328, 632, 968, 1288, 1608, 1928, 2280, 2600, 2984, 3240, 3624, + 3880, 4264, 4584, 4968, 5160, 5544, 5992, 6200, 6456, 6712, 7224, + 7480, 7736, 7992, 8504, 8760, 9144, 9528, 9912, 9912,10296,10680, + 11064,11448,11832,12216,12216,12576,12960,13536,13536,14112,14112, + 14688,14688,15264,15840,15840,16416,16416,16992,16992,17568,17568, + 18336,18336,19080,19080,19848,19848,19848,20616,20616,21384,21384, + 22152,22152,22152,22920,22920,23688,23688,24496,24496,24496,25456, + 25456,25456,26416,26416,26416,27376,27376,27376,28336,28336,28336, + 29296,29296,29296,30576,30576,30576,30576,31704,31704,31704,31704, + 32856,32856,32856,34008,34008,34008,34008,35160,35160,35160,35160}, + { 336, 696, 1064, 1416, 1800, 2152, 2536, 2856, 3240, 3624, 4008, + 4392, 4776, 5160, 5352, 5736, 6200, 6456, 6712, 7224, 7480, 7992, + 8248, 8760, 9144, 9528, 9912,10296,10296,10680,11064,11448,11832, + 12216,12576,12960,13536,13536,14112,14688,14688,15264,15264,15840, + 16416,16416,16992,17568,17568,18336,18336,19080,19080,19848,19848, + 20616,20616,20616,21384,21384,22152,22152,22920,22920,23688,23688, + 24496,24496,24496,25456,25456,26416,26416,26416,27376,27376,27376, + 28336,28336,29296,29296,29296,30576,30576,30576,30576,31704,31704, + 31704,32856,32856,32856,34008,34008,34008,35160,35160,35160,35160, + 36696,36696,36696,36696,37888,37888,37888,39232,39232,39232,39232}, + { 376, 776, 1160, 1544, 1992, 2344, 2792, 3112, 3624, 4008, 4392, + 4776, 5160, 5544, 5992, 6200, 6712, 7224, 7480, 7992, 8248, 8760, + 9144, 9528, 9912,10296,10680,11064,11448,11832,12216,12576,12960, + 13536,14112,14112,14688,15264,15264,15840,16416,16416,16992,17568, + 17568,18336,18336,19080,19080,19848,19848,20616,21384,21384,22152, + 22152,22920,22920,23688,23688,24496,24496,24496,25456,25456,26416, + 26416,27376,27376,27376,28336,28336,29296,29296,29296,30576,30576, + 30576,31704,31704,31704,32856,32856,32856,34008,34008,34008,35160, + 35160,35160,36696,36696,36696,37888,37888,37888,37888,39232,39232, + 39232,40576,40576,40576,40576,42368,42368,42368,42368,43816,43816}, + { 408, 840, 1288, 1736, 2152, 2600, 2984, 3496, 3880, 4264, 4776, + 5160, 5544, 5992, 6456, 6968, 7224, 7736, 8248, 8504, 9144, 9528, + 9912,10296,10680,11064,11448,12216,12576,12960,13536,13536,14112, + 14688,15264,15264,15840,16416,16992,16992,17568,18336,18336,19080, + 19080,19848,20616,20616,21384,21384,22152,22152,22920,22920,23688, + 24496,24496,25456,25456,25456,26416,26416,27376,27376,28336,28336, + 29296,29296,29296,30576,30576,30576,31704,31704,32856,32856,32856, + 34008,34008,34008,35160,35160,35160,36696,36696,36696,37888,37888, + 37888,39232,39232,39232,40576,40576,40576,40576,42368,42368,42368, + 43816,43816,43816,43816,45352,45352,45352,46888,46888,46888,46888}, + { 440, 904, 1384, 1864, 2344, 2792, 3240, 3752, 4136, 4584, 5160, + 5544, 5992, 6456, 6968, 7480, 7992, 8248, 8760, 9144, 9912,10296, + 10680,11064,11448,12216,12576,12960,13536,14112,14688,14688,15264, + 15840,16416,16992,16992,17568,18336,18336,19080,19848,19848,20616, + 20616,21384,22152,22152,22920,22920,23688,24496,24496,25456,25456, + 26416,26416,27376,27376,28336,28336,29296,29296,29296,30576,30576, + 31704,31704,31704,32856,32856,34008,34008,34008,35160,35160,35160, + 36696,36696,36696,37888,37888,39232,39232,39232,40576,40576,40576, + 42368,42368,42368,42368,43816,43816,43816,45352,45352,45352,46888, + 46888,46888,46888,48936,48936,48936,48936,48936,51024,51024,51024}, + { 488, 1000, 1480, 1992, 2472, 2984, 3496, 4008, 4584, 4968, 5544, + 5992, 6456, 6968, 7480, 7992, 8504, 9144, 9528, 9912,10680,11064, + 11448,12216,12576,12960,13536,14112,14688,15264,15840,15840,16416, + 16992,17568,18336,18336,19080,19848,19848,20616,21384,21384,22152, + 22920,22920,23688,24496,24496,25456,25456,26416,26416,27376,27376, + 28336,28336,29296,29296,30576,30576,31704,31704,31704,32856,32856, + 34008,34008,35160,35160,35160,36696,36696,36696,37888,37888,39232, + 39232,39232,40576,40576,40576,42368,42368,42368,43816,43816,43816, + 45352,45352,45352,46888,46888,46888,46888,48936,48936,48936,48936, + 51024,51024,51024,51024,52752,52752,52752,52752,55056,55056,55056}, + { 520, 1064, 1608, 2152, 2664, 3240, 3752, 4264, 4776, 5352, 5992, + 6456, 6968, 7480, 7992, 8504, 9144, 9528,10296,10680,11448,11832, + 12576,12960,13536,14112,14688,15264,15840,16416,16992,16992,17568, + 18336,19080,19080,19848,20616,21384,21384,22152,22920,22920,23688, + 24496,24496,25456,25456,26416,27376,27376,28336,28336,29296,29296, + 30576,30576,31704,31704,32856,32856,34008,34008,34008,35160,35160, + 36696,36696,36696,37888,37888,39232,39232,40576,40576,40576,42368, + 42368,42368,43816,43816,43816,45352,45352,45352,46888,46888,46888, + 48936,48936,48936,48936,51024,51024,51024,51024,52752,52752,52752, + 55056,55056,55056,55056,57336,57336,57336,57336,59256,59256,59256}, + { 552, 1128, 1736, 2280, 2856, 3496, 4008, 4584, 5160, 5736, 6200, + 6968, 7480, 7992, 8504, 9144, 9912,10296,11064,11448,12216,12576, + 12960,13536,14112,14688,15264,15840,16416,16992,17568,18336,19080, + 19848,19848,20616,21384,22152,22152,22920,23688,24496,24496,25456, + 25456,26416,27376,27376,28336,28336,29296,29296,30576,30576,31704, + 31704,32856,32856,34008,34008,35160,35160,36696,36696,37888,37888, + 37888,39232,39232,40576,40576,40576,42368,42368,43816,43816,43816, + 45352,45352,45352,46888,46888,46888,48936,48936,48936,51024,51024, + 51024,51024,52752,52752,52752,55056,55056,55056,55056,57336,57336, + 57336,57336,59256,59256,59256,59256,61664,61664,61664,61664,63776}, + { 584, 1192, 1800, 2408, 2984, 3624, 4264, 4968, 5544, 5992, 6712, + 7224, 7992, 8504, 9144, 9912,10296,11064,11448,12216,12960,13536, + 14112,14688,15264,15840,16416,16992,17568,18336,19080,19848,19848, + 20616,21384,22152,22920,22920,23688,24496,25456,25456,26416,26416, + 27376,28336,28336,29296,29296,30576,31704,31704,32856,32856,34008, + 34008,35160,35160,36696,36696,36696,37888,37888,39232,39232,40576, + 40576,42368,42368,42368,43816,43816,45352,45352,45352,46888,46888, + 46888,48936,48936,48936,51024,51024,51024,52752,52752,52752,52752, + 55056,55056,55056,57336,57336,57336,57336,59256,59256,59256,61664, + 61664,61664,61664,63776,63776,63776,63776,66592,66592,66592,66592}, + { 616, 1256, 1864, 2536, 3112, 3752, 4392, 5160, 5736, 6200, 6968, + 7480, 8248, 8760, 9528,10296,10680,11448,12216,12576,13536,14112, + 14688,15264,15840,16416,16992,17568,18336,19080,19848,20616,20616, + 21384,22152,22920,23688,24496,24496,25456,26416,26416,27376,28336, + 28336,29296,29296,30576,31704,31704,32856,32856,34008,34008,35160, + 35160,36696,36696,37888,37888,39232,39232,40576,40576,40576,42368, + 42368,43816,43816,43816,45352,45352,46888,46888,46888,48936,48936, + 48936,51024,51024,51024,52752,52752,52752,55056,55056,55056,55056, + 57336,57336,57336,59256,59256,59256,61664,61664,61664,61664,63776, + 63776,63776,63776,66592,66592,66592,66592,68808,68808,68808,71112}, + { 712, 1480, 2216, 2984, 3752, 4392, 5160, 5992, 6712, 7480, 8248, + 8760, 9528,10296,11064,11832,12576,13536,14112,14688,15264,16416, + 16992,17568,18336,19080,19848,20616,21384,22152,22920,23688,24496, + 25456,25456,26416,27376,28336,29296,29296,30576,30576,31704,32856, + 32856,34008,35160,35160,36696,36696,37888,37888,39232,40576,40576, + 40576,42368,42368,43816,43816,45352,45352,46888,46888,48936,48936, + 48936,51024,51024,52752,52752,52752,55056,55056,55056,55056,57336, + 57336,57336,59256,59256,59256,61664,61664,61664,63776,63776,63776, + 66592,66592,66592,68808,68808,68808,71112,71112,71112,73712,73712, + 75376,75376,75376,75376,75376,75376,75376,75376,75376,75376,75376}}; diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt new file mode 100644 index 0000000..c93770a --- /dev/null +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -0,0 +1,350 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# PBCH TEST +######################################################################## + +add_executable(pbch_test pbch_test.c) +target_link_libraries(pbch_test srslte_phy) + +add_test(pbch_test_6 pbch_test -p 1 -n 6 -c 100) +add_test(pbch_test_62 pbch_test -p 2 -n 6 -c 100) +add_test(pbch_test_64 pbch_test -p 4 -n 6 -c 100) +add_test(pbch_test_50 pbch_test -p 1 -n 50 -c 50) +add_test(pbch_test_502 pbch_test -p 2 -n 50 -c 50) +add_test(pbch_test_504 pbch_test -p 4 -n 50 -c 50) + + +######################################################################## +# PCFICH TEST +######################################################################## + +add_executable(pcfich_test pcfich_test.c) +target_link_libraries(pcfich_test srslte_phy) + +add_test(pcfich_test_6 pcfich_test -p 1 -n 6) +add_test(pcfich_test_62 pcfich_test -p 2 -n 6) +add_test(pcfich_test_64 pcfich_test -p 4 -n 6) +add_test(pcfich_test_10 pcfich_test -p 1 -n 10) +add_test(pcfich_test_102 pcfich_test -p 2 -n 10) +add_test(pcfich_test_104 pcfich_test -p 4 -n 10) + +######################################################################## +# PHICH TEST +######################################################################## + +add_executable(phich_test phich_test.c) +target_link_libraries(phich_test srslte_phy) + +add_test(phich_test_6 phich_test -p 1 -n 6) +add_test(phich_test_62 phich_test -p 2 -n 6) +add_test(phich_test_64 phich_test -p 4 -n 6 -g 1/6) + +add_test(phich_test_6e phich_test -p 1 -n 6 -e) +add_test(phich_test_62e phich_test -p 2 -n 6 -e -l) +add_test(phich_test_64e phich_test -p 4 -n 6 -e -l -g 2) + +add_test(phich_test_10 phich_test -p 1 -n 10 -e) +add_test(phich_test_102 phich_test -p 2 -n 10 -g 2) +add_test(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2) + +######################################################################## +# PDCCH TEST +######################################################################## + +add_executable(pdcch_test pdcch_test.c) +target_link_libraries(pdcch_test srslte_phy) + +add_test(pdcch_test pdcch_test) + +######################################################################## +# PDSCH TEST +######################################################################## + +add_executable(pdsch_test pdsch_test.c) +target_link_libraries(pdsch_test srslte_phy) + +add_test(pdsch_test_qpsk pdsch_test -m 10 -n 50 -r 1) +add_test(pdsch_test_qam16 pdsch_test -m 20 -n 100) +add_test(pdsch_test_qam16 pdsch_test -m 20 -n 100 -r 2) +add_test(pdsch_test_qam64 pdsch_test -n 100) + +# PDSCH test for single transmision mode and 2 Rx antennas +add_test(pdsch_test_sin_6 pdsch_test -x single -a 2 -n 6) +add_test(pdsch_test_sin_12 pdsch_test -x single -a 2 -n 12) +add_test(pdsch_test_sin_25 pdsch_test -x single -a 2 -n 25) +add_test(pdsch_test_sin_50 pdsch_test -x single -a 2 -n 50) +add_test(pdsch_test_sin_75 pdsch_test -x single -a 2 -n 75) +add_test(pdsch_test_sin_100 pdsch_test -x single -a 2 -n 100) + +# PDSCH test for transmit diversity transmision mode (1 codeword) +add_test(pdsch_test_div_6 pdsch_test -x diversity -a 2 -n 6) +add_test(pdsch_test_div_12 pdsch_test -x diversity -a 2 -n 12) +add_test(pdsch_test_div_25 pdsch_test -x diversity -a 2 -n 25) +add_test(pdsch_test_div_50 pdsch_test -x diversity -a 2 -n 50) +add_test(pdsch_test_div_75 pdsch_test -x diversity -a 2 -n 75) +add_test(pdsch_test_div_100 pdsch_test -x diversity -a 2 -n 100) + +# PDSCH test for CDD transmision mode (2 codeword) +add_test(pdsch_test_cdd_6 pdsch_test -x cdd -a 2 -t 0 -n 6) +add_test(pdsch_test_cdd_12 pdsch_test -x cdd -a 2 -t 0 -n 12) +add_test(pdsch_test_cdd_25 pdsch_test -x cdd -a 2 -t 0 -n 25) +add_test(pdsch_test_cdd_50 pdsch_test -x cdd -a 2 -t 0 -n 50) +add_test(pdsch_test_cdd_75 pdsch_test -x cdd -a 2 -t 0 -n 75) +add_test(pdsch_test_cdd_100 pdsch_test -x cdd -a 2 -t 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (1 codeword) +add_test(pdsch_test_multiplex1cw_p0_6 pdsch_test -x multiplex -a 2 -p 0 -n 6) +add_test(pdsch_test_multiplex1cw_p0_12 pdsch_test -x multiplex -a 2 -p 0 -n 12) +add_test(pdsch_test_multiplex1cw_p0_25 pdsch_test -x multiplex -a 2 -p 0 -n 25) +add_test(pdsch_test_multiplex1cw_p0_50 pdsch_test -x multiplex -a 2 -p 0 -n 50) +add_test(pdsch_test_multiplex1cw_p0_75 pdsch_test -x multiplex -a 2 -p 0 -n 75) +add_test(pdsch_test_multiplex1cw_p0_100 pdsch_test -x multiplex -a 2 -p 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (1 codeword, swapped) +add_test(pdsch_test_multiplex1cw_p0_6_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 6 -F 1) +add_test(pdsch_test_multiplex1cw_p0_15_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 15) +add_test(pdsch_test_multiplex1cw_p0_25_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 25) +add_test(pdsch_test_multiplex1cw_p0_50_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 50) +add_test(pdsch_test_multiplex1cw_p0_75_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 75) +add_test(pdsch_test_multiplex1cw_p0_100_swap pdsch_test -x multiplex -a 2 -p 0 -m 0 -r 1 -M 28 -t 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 1 (1 codeword) +add_test(pdsch_test_multiplex1cw_p1_6 pdsch_test -x multiplex -a 2 -p 1 -n 6) +add_test(pdsch_test_multiplex1cw_p1_12 pdsch_test -x multiplex -a 2 -p 1 -n 12) +add_test(pdsch_test_multiplex1cw_p1_25 pdsch_test -x multiplex -a 2 -p 1 -n 25) +add_test(pdsch_test_multiplex1cw_p1_50 pdsch_test -x multiplex -a 2 -p 1 -n 50) +add_test(pdsch_test_multiplex1cw_p1_75 pdsch_test -x multiplex -a 2 -p 1 -n 75) +add_test(pdsch_test_multiplex1cw_p1_100 pdsch_test -x multiplex -a 2 -p 1 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 2 (1 codeword) +add_test(pdsch_test_multiplex1cw_p2_6 pdsch_test -x multiplex -a 2 -p 2 -n 6) +add_test(pdsch_test_multiplex1cw_p2_12 pdsch_test -x multiplex -a 2 -p 2 -n 12) +add_test(pdsch_test_multiplex1cw_p2_25 pdsch_test -x multiplex -a 2 -p 2 -n 25) +add_test(pdsch_test_multiplex1cw_p2_50 pdsch_test -x multiplex -a 2 -p 2 -n 50) +add_test(pdsch_test_multiplex1cw_p2_75 pdsch_test -x multiplex -a 2 -p 2 -n 75) +add_test(pdsch_test_multiplex1cw_p2_100 pdsch_test -x multiplex -a 2 -p 2 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 3 (1 codeword) +add_test(pdsch_test_multiplex1cw_p3_6 pdsch_test -x multiplex -a 2 -p 3 -n 6) +add_test(pdsch_test_multiplex1cw_p3_12 pdsch_test -x multiplex -a 2 -p 3 -n 12) +add_test(pdsch_test_multiplex1cw_p3_25 pdsch_test -x multiplex -a 2 -p 3 -n 25) +add_test(pdsch_test_multiplex1cw_p3_50 pdsch_test -x multiplex -a 2 -p 3 -n 50) +add_test(pdsch_test_multiplex1cw_p3_75 pdsch_test -x multiplex -a 2 -p 3 -n 75) +add_test(pdsch_test_multiplex1cw_p3_100 pdsch_test -x multiplex -a 2 -p 3 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (2 codeword) +add_test(pdsch_test_multiplex2cw_p0_6 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 6) +add_test(pdsch_test_multiplex2cw_p0_12 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 12) +add_test(pdsch_test_multiplex2cw_p0_25 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 25) +add_test(pdsch_test_multiplex2cw_p0_50 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 50) +add_test(pdsch_test_multiplex2cw_p0_75 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 75) +add_test(pdsch_test_multiplex2cw_p0_100 pdsch_test -x multiplex -a 2 -t 0 -p 0 -n 100) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 0 (2 codeword, swapped) +add_test(pdsch_test_multiplex2cw_p0_6_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -M 28 -n 6 -w -F 1) +add_test(pdsch_test_multiplex2cw_p0_12_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -m 28 -n 12 -w) +add_test(pdsch_test_multiplex2cw_p0_25_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -M 28 -n 25 -w) +add_test(pdsch_test_multiplex2cw_p0_50_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -m 28 -n 50 -w) +add_test(pdsch_test_multiplex2cw_p0_75_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -M 28 -n 75 -w) +add_test(pdsch_test_multiplex2cw_p0_100_swap pdsch_test -x multiplex -a 2 -t 0 -p 0 -m 28 -n 100 -w) + +# PDSCH test for Spatial Multiplex transmision mode with PMI = 1 (2 codeword) +add_test(pdsch_test_multiplex2cw_p1_6 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 6) +add_test(pdsch_test_multiplex2cw_p1_12 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 12) +add_test(pdsch_test_multiplex2cw_p1_25 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 25) +add_test(pdsch_test_multiplex2cw_p1_50 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 50) +add_test(pdsch_test_multiplex2cw_p1_75 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 75) +add_test(pdsch_test_multiplex2cw_p1_100 pdsch_test -x multiplex -a 2 -t 0 -p 1 -n 100) + +######################################################################## +# PMCH TEST +######################################################################## + + +add_executable(pmch_test pmch_test.c) +target_link_libraries(pmch_test srslte_phy) + +add_test(pmch_test_qpsk pmch_test -m 6 -n 50) +add_test(pmch_test_qam16 pmch_test -m 15 -n 100) +add_test(pmch_test_qam64 pmch_test -m 25 -n 100) + + +######################################################################## +# FILE TEST +######################################################################## + +add_executable(pbch_file_test pbch_file_test.c) +target_link_libraries(pbch_file_test srslte_phy) + +add_executable(pcfich_file_test pcfich_file_test.c) +target_link_libraries(pcfich_file_test srslte_phy) + +add_executable(phich_file_test phich_file_test.c) +target_link_libraries(phich_file_test srslte_phy) + +add_executable(pdcch_file_test pdcch_file_test.c) +target_link_libraries(pdcch_file_test srslte_phy) + +add_executable(pdsch_pdcch_file_test pdsch_pdcch_file_test.c) +target_link_libraries(pdsch_pdcch_file_test srslte_phy) + +add_executable(pmch_file_test pmch_file_test.c) +target_link_libraries(pmch_file_test srslte_phy) + +add_test(pbch_file_test pbch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.dat) +add_test(pcfich_file_test pcfich_file_test -c 150 -n 50 -p 2 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.10M.dat) +add_test(phich_file_test phich_file_test -c 150 -n 50 -p 2 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.10M.dat) +add_test(pdcch_file_test pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) +add_test(pdsch_pdcch_file_test pdsch_pdcch_file_test -c 1 -f 3 -n 6 -p 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal.1.92M.amar.dat) +add_test(pmch_file_test pmch_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/pmch_100prbs_MCS2_SR0.bin) + +######################################################################## +# PUSCH TEST +######################################################################## + +add_executable(pusch_test pusch_test.c) +target_link_libraries(pusch_test srslte_phy) + +if (NOT DEFINED TEST_EXTENSION) + set(TEST_EXTENSION Normal) +endif(NOT DEFINED TEST_EXTENSION) + +if (TEST_EXTENSION STREQUAL Paranoid) + # All valid number of PRBs for PUSCH + set(cell_n_prb_valid 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54 60 64 72 75 80 81 90 96 100) + + set(pusch_min_mcs 0) + set(pusch_max_mcs 28) + set(pusch_step_mcs 1) + + set(pusch_acks -1 0 1) + + set(pusch_cqi none wideband) + +else (TEST_EXTENSION STREQUAL Paranoid) + set(cell_n_prb_valid 6 15 25 50 100) + + set(pusch_min_mcs 0) + set(pusch_max_mcs 28) + set(pusch_step_mcs 10) + + set(pusch_acks -1 0) + + set(pusch_cqi none wideband) + +endif (TEST_EXTENSION STREQUAL Paranoid) + +foreach (cell_n_prb 6 15 25 50 75 100) + set(pusch_cell_n_prb) + foreach (n_prb ${cell_n_prb_valid}) + if (NOT (${n_prb} GREATER ${cell_n_prb})) + set(pusch_cell_n_prb ${pusch_cell_n_prb} ${n_prb}) + endif (NOT (${n_prb} GREATER ${cell_n_prb})) + endforeach (n_prb) + + foreach (n_prb ${pusch_cell_n_prb}) + foreach (mcs RANGE ${pusch_min_mcs} ${pusch_max_mcs} ${pusch_step_mcs}) + foreach (ack ${pusch_acks}) + foreach (cqi ${pusch_cqi}) + set(pusch_test_args "") + + set(pusch_test_args ${pusch_test_args} -n ${cell_n_prb}) + set(pusch_test_args ${pusch_test_args} -L ${n_prb}) + + if (NOT (${ack} EQUAL -1)) + set(pusch_test_args ${pusch_test_args} -p uci_ack ${ack}) + if (mcs EQUAL 28) + set(mcs 27) + endif (mcs EQUAL 28) + endif (NOT (${ack} EQUAL -1)) + + if (NOT (${cqi} STREQUAL none)) + set(pusch_test_args ${pusch_test_args} -p uci_cqi ${cqi}) + #if (mcs EQUAL 28) + # set(mcs 27) + #endif (mcs EQUAL 28) + endif (NOT (${cqi} STREQUAL none)) + + set(pusch_test_args ${pusch_test_args} -m ${mcs}) + + string(REGEX REPLACE "\ " "" test_name_args ${pusch_test_args}) + + add_test(pusch_test${test_name_args} pusch_test ${pusch_test_args}) + endforeach (cqi) + endforeach (ack) + endforeach (mcs) + endforeach (n_prb) +endforeach (cell_n_prb) + +######################################################################## +# PUCCH TEST +######################################################################## + +add_executable(pucch_test pucch_test.c) +target_link_libraries(pucch_test srslte_phy) + +add_test(pucch_test pucch_test) +add_test(pucch_test_uci_cqi_decoder pucch_test -q) + +######################################################################## +# PRACH TEST +######################################################################## + +add_executable(prach_test prach_test.c) +target_link_libraries(prach_test srslte_phy) + +add_test(prach prach_test) + +add_test(prach_256 prach_test -N 256) +add_test(prach_512 prach_test -N 512) +add_test(prach_1024 prach_test -N 1024) +add_test(prach_1536 prach_test -N 1536) +add_test(prach_2048 prach_test -N 2048) + +add_test(prach_f0 prach_test -f 0) +add_test(prach_f1 prach_test -f 1) +add_test(prach_f2 prach_test -f 2) +add_test(prach_f3 prach_test -f 3) + +add_test(prach_rs1 prach_test -r 1) +add_test(prach_rs2 prach_test -r 2) +add_test(prach_rs3 prach_test -r 3) + +add_test(prach_zc0 prach_test -z 0) +add_test(prach_zc2 prach_test -z 2) +add_test(prach_zc3 prach_test -z 3) + +add_executable(prach_test_multi prach_test_multi.c) +target_link_libraries(prach_test_multi srslte_phy) + +add_test(prach_test_multi prach_test_multi) + +add_test(prach_test_multi_n32 prach_test_multi -n 32) +add_test(prach_test_multi_n16 prach_test_multi -n 16) +add_test(prach_test_multi_n8 prach_test_multi -n 8) +add_test(prach_test_multi_n4 prach_test_multi -n 4) + + +if(UHD_FOUND) + add_executable(prach_test_usrp prach_test_usrp.c) + target_link_libraries(prach_test_usrp srslte_rf srslte_phy pthread) +endif(UHD_FOUND) diff --git a/lib/src/phy/phch/test/dlsch_encode_test_mex.c b/lib/src/phy/phch/test/dlsch_encode_test_mex.c new file mode 100644 index 0000000..c6181d9 --- /dev/null +++ b/lib/src/phy/phch/test/dlsch_encode_test_mex.c @@ -0,0 +1,157 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define OUTLEN prhs[2] +#define TRBLKIN prhs[3] +#define NOF_INPUTS 4 + +void help() +{ + mexErrMsgTxt + ("[cwout] = srslte_dlsch_encode(ue, chs, outlen, trblkin)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int i; + srslte_sch_t dlsch; + srslte_pdsch_cfg_t cfg; + srslte_softbuffer_tx_t softbuffers[SRSLTE_MAX_CODEWORDS]; + uint32_t nof_codewords = 1; + + memset(&dlsch, 0, sizeof(srslte_sch_t)); + memset(&cfg, 0, sizeof(srslte_pdsch_cfg_t)); + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (srslte_sch_init(&dlsch)) { + mexErrMsgTxt("Error initiating DL-SCH\n"); + return; + } + srslte_cell_t cell; + cell.nof_prb = 100; + cell.id=1; + srslte_verbose = SRSLTE_VERBOSE_NONE; + + uint8_t *trblkin_bits = NULL; + cfg.grant.nof_tb = 1; + cfg.grant.mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin_bits); + if (cfg.grant.mcs.tbs == 0) { + mexErrMsgTxt("Error trblklen is zero\n"); + return; + } + uint8_t *trblkin = srslte_vec_malloc(cfg.grant.mcs.tbs/8); + srslte_bit_pack_vector(trblkin_bits, trblkin, cfg.grant.mcs.tbs); + free(trblkin_bits); + + if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &cfg.rv)) { + mexErrMsgTxt("Field RV not found in dlsch config\n"); + return; + } + + if (mexutils_read_uint32_struct(PUSCHCFG, "NLayers", &cfg.nof_layers)) { + mexErrMsgTxt("Field NLayers not found in dlsch config\n"); + return; + } + + char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation"); + + if (!strcmp(mod_str, "QPSK")) { + cfg.grant.mcs.mod = SRSLTE_MOD_QPSK; + cfg.grant.Qm = 2; + } else if (!strcmp(mod_str, "16QAM")) { + cfg.grant.mcs.mod = SRSLTE_MOD_16QAM; + cfg.grant.Qm = 4; + } else if (!strcmp(mod_str, "64QAM")) { + cfg.grant.mcs.mod = SRSLTE_MOD_64QAM; + cfg.grant.Qm = 6; + } else { + mexErrMsgTxt("Unknown modulation\n"); + return; + } + + mxFree(mod_str); + + /* Initialise buffers */ + for (i = 0; i < nof_codewords; i++) { + if (srslte_softbuffer_tx_init(&softbuffers[i], cell.nof_prb)) { + mexErrMsgTxt("Error initiating DL-SCH soft buffer\n"); + return; + } + } + + cfg.nbits.nof_bits = mxGetScalar(OUTLEN); + uint8_t *e_bits = srslte_vec_malloc(cfg.nbits.nof_bits * sizeof(uint8_t)); + if (!e_bits) { + return; + } + if (srslte_cbsegm(&cfg.cb_segm, cfg.grant.mcs.tbs)) { + mexErrMsgTxt("Error computing CB segmentation\n"); + return; + } + uint32_t tmp_rv=cfg.rv; + if (tmp_rv) { + cfg.rv = 0; + if (srslte_dlsch_encode_multi(&dlsch, &cfg, softbuffers, &trblkin, &e_bits)) { + mexErrMsgTxt("Error encoding TB\n"); + return; + } + cfg.rv = tmp_rv; + } + if (srslte_dlsch_encode_multi(&dlsch, &cfg, softbuffers, &trblkin, &e_bits)) { + mexErrMsgTxt("Error encoding TB\n"); + return; + } + uint8_t *e_bits_unpacked = srslte_vec_malloc(cfg.nbits.nof_bits * sizeof(uint8_t)); + if (!e_bits_unpacked) { + return; + } + srslte_bit_unpack_vector(e_bits, e_bits_unpacked, cfg.nbits.nof_bits); + if (nlhs >= 1) { + mexutils_write_uint8(e_bits_unpacked, &plhs[0], cfg.nbits.nof_bits, 1); + } + + srslte_sch_free(&dlsch); + + free(trblkin); + free(e_bits); + free(e_bits_unpacked); + for (i = 0; i < nof_codewords; i++) { + srslte_softbuffer_tx_free(&softbuffers[i]); + } + return; +} + diff --git a/lib/src/phy/phch/test/pbch_file_test.c b/lib/src/phy/phch/test/pbch_file_test.c new file mode 100644 index 0000000..2f750f1 --- /dev/null +++ b/lib/src/phy/phch/test/pbch_file_test.c @@ -0,0 +1,255 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; + +srslte_cell_t cell = { + 6, // nof_prb + 2, // nof_ports + 150, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +int nof_frames = 1; + +uint8_t bch_payload_file[SRSLTE_BCH_PAYLOAD_LEN] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +#define FLEN (10*SRSLTE_SF_LEN(srslte_symbol_sz(cell.nof_prb))) + +srslte_filesource_t fsrc; +cf_t *input_buffer, *fft_buffer, *ce[SRSLTE_MAX_PORTS]; +srslte_pbch_t pbch; +srslte_ofdm_t fft; +srslte_chest_dl_t chest; + +void usage(char *prog) { + printf("Usage: %s [vcoe] -i input_file\n", prog); + printf("\t-c cell_id [Default %d]\n", cell.id); + printf("\t-p nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + + while ((opt = getopt(argc, argv, "ivcpne")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'p': + cell.nof_prb = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + int i; + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + input_buffer = malloc(FLEN * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i 0) { + // process 1st subframe only + srslte_ofdm_rx_sf(&fft); + + /* Get channel estimates for each port */ + srslte_chest_dl_estimate(&chest, fft_buffer, ce, 0); + + INFO("Decoding PBCH\n"); + + for (int i=0;i 0 && frame_cnt < nof_frames); + + base_free(); + srslte_dft_exit(); + + if (frame_cnt == 1) { + if (n == 0) { + printf("Could not decode PBCH\n"); + exit(-1); + } else { + printf("MIB decoded OK. Nof ports: %d. SFN offset: %d Payload: ", nof_tx_ports, sfn_offset); + srslte_vec_fprint_hex(stdout, bch_payload, SRSLTE_BCH_PAYLOAD_LEN); + if (nof_tx_ports == 2 && sfn_offset == 0 && !memcmp(bch_payload, bch_payload_file, SRSLTE_BCH_PAYLOAD_LEN)) { + printf("This is the signal.1.92M.dat file\n"); + exit(0); + } else { + printf("This is an unknown file\n"); + exit(-1); + } + } + } else { + printf("Decoded %d/%d MIBs\n", nof_decoded_mibs, frame_cnt); + } +} diff --git a/lib/src/phy/phch/test/pbch_test.c b/lib/src/phy/phch/test/pbch_test.c new file mode 100644 index 0000000..dd2e410 --- /dev/null +++ b/lib/src/phy/phch/test/pbch_test.c @@ -0,0 +1,156 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +void usage(char *prog) { + printf("Usage: %s [cpv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpnv")) != -1) { + switch(opt) { + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + srslte_pbch_t pbch; + uint8_t bch_payload_tx[SRSLTE_BCH_PAYLOAD_LEN], bch_payload_rx[SRSLTE_BCH_PAYLOAD_LEN]; + int i, j; + cf_t *ce[SRSLTE_MAX_PORTS]; + int nof_re; + cf_t *slot1_symbols[SRSLTE_MAX_PORTS]; + uint32_t nof_rx_ports; + + parse_args(argc,argv); + + nof_re = SRSLTE_SLOT_LEN_RE(cell.nof_prb, SRSLTE_CP_NORM); + + /* init memory */ + for (i=0;i +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("[decoded_ok, symbols, bits] = srslte_pbch(enbConfig, rxWaveform)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + int i; + srslte_cell_t cell; + srslte_pbch_t pbch; + srslte_chest_dl_t chest; + srslte_ofdm_t ofdm_rx; + cf_t *input_fft = NULL; + cf_t *ce[SRSLTE_MAX_PORTS], *ce_slot[SRSLTE_MAX_PORTS]; + + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + // Allocate memory + for (i=0;i NOF_INPUTS) { + cf_t *cearray = NULL; + mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (i=0;i NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_dl_get_noise_estimate(&chest); + } + + n = srslte_pbch_decode(&pbch, &input_fft[SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)], + ce_slot, noise_power, + NULL, &nof_ports, &sfn_offset); + } + + if (nlhs >= 1) { + if (n == 1) { + plhs[0] = mxCreateDoubleScalar(nof_ports); + } else { + plhs[0] = mxCreateDoubleScalar(0); + } + } + if (nlhs >= 2) { + mexutils_write_cf(pbch.d, &plhs[1], pbch.nof_symbols, 1); + } + if (nlhs >= 3) { + mexutils_write_f(pbch.temp, &plhs[2], 4*2*pbch.nof_symbols, 1); + } + if (nlhs >= 4) { + mexutils_write_cf(ce[0], &plhs[3], SRSLTE_SF_LEN_RE(cell.nof_prb,cell.cp)/14, 14); + } + if (nlhs >= 5) { + mexutils_write_cf(ce[1], &plhs[4], SRSLTE_SF_LEN_RE(cell.nof_prb,cell.cp)/14, 14); + } + if (nlhs >= 6) { + mexutils_write_cf(pbch.symbols[0], &plhs[5], pbch.nof_symbols, 1); + } + if (nlhs >= 7) { + mexutils_write_cf(pbch.ce[0], &plhs[6], pbch.nof_symbols, 1); + } + if (nlhs >= 8) { + plhs[7] = mxCreateDoubleScalar(sfn_offset); + } + if (nlhs >= 9) { + mexutils_write_f(pbch.rm_f, &plhs[8], 120, 1); + } + + srslte_chest_dl_free(&chest); + srslte_ofdm_rx_free(&ofdm_rx); + srslte_pbch_free(&pbch); + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; +char *matlab_file_name = NULL; + + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 0, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +int flen; + +FILE *fmatlab = NULL; + +srslte_filesource_t fsrc; +cf_t *input_buffer, *fft_buffer, *ce[SRSLTE_MAX_PORTS]; +srslte_pcfich_t pcfich; +srslte_regs_t regs; +srslte_ofdm_t fft; +srslte_chest_dl_t chest; + +void usage(char *prog) { + printf("Usage: %s [vcoe] -i input_file\n", prog); + printf("\t-o output matlab file name [Default Disabled]\n"); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "iovcenp")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'o': + matlab_file_name = argv[optind]; + break; + case 'v': + srslte_verbose++; + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + int i; + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + if (matlab_file_name) { + fmatlab = fopen(matlab_file_name, "w"); + if (!fmatlab) { + perror("fopen"); + return -1; + } + } else { + fmatlab = NULL; + } + + flen = SRSLTE_SF_LEN(srslte_symbol_sz_power2(cell.nof_prb)); + + input_buffer = srslte_vec_malloc(flen * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i 2.8 && cfi == 1) { + exit(0); + } else { + exit(-1); + } + } +} diff --git a/lib/src/phy/phch/test/pcfich_test.c b/lib/src/phy/phch/test/pcfich_test.c new file mode 100644 index 0000000..ea821a8 --- /dev/null +++ b/lib/src/phy/phch/test/pcfich_test.c @@ -0,0 +1,165 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" + + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 1000, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +void usage(char *prog) { + printf("Usage: %s [cpv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-p nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpnv")) != -1) { + switch(opt) { + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + srslte_pcfich_t pcfich; + srslte_regs_t regs; + int i, j; + cf_t *ce[SRSLTE_MAX_PORTS]; + int nof_re; + cf_t *slot_symbols[SRSLTE_MAX_PORTS]; + uint32_t cfi, cfi_rx, nsf; + int cid, max_cid; + float corr_res; + + parse_args(argc,argv); + + nof_re = SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; + + /* init memory */ + for (i=0;i +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("[cfi] = srslte_pcfich(enbConfig, rxWaveform)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int i; + srslte_cell_t cell; + srslte_pcfich_t pcfich; + srslte_chest_dl_t chest; + srslte_ofdm_t ofdm_rx; + srslte_regs_t regs; + uint32_t sf_idx; + cf_t *input_fft, *input_signal; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (mexutils_read_uint32_struct(ENBCFG, "NSubframe", &sf_idx)) { + help(); + return; + } + + if (srslte_chest_dl_init(&chest, cell)) { + mexErrMsgTxt("Error initializing equalizer\n"); + return; + } + + if (srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb)) { + mexErrMsgTxt("Error initializing FFT\n"); + return; + } + + if (srslte_regs_init(®s, cell)) { + mexErrMsgTxt("Error initiating regs\n"); + return; + } + + if (srslte_pcfich_init(&pcfich, ®s, cell)) { + mexErrMsgTxt("Error creating PBCH object\n"); + return; + } + +// Read input signal + input_signal = NULL; + int insignal_len = mexutils_read_cf(INPUT, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) { + input_fft = input_signal; + } else { + input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft); + free(input_signal); + } + + cf_t *ce[SRSLTE_MAX_PORTS]; + for (i=0;i NOF_INPUTS) { + cf_t *cearray = NULL; + mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (i=0;i NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_dl_get_noise_estimate(&chest); + } + + + uint32_t cfi; + float corr_res; + int n = srslte_pcfich_decode(&pcfich, input_fft, ce, noise_power, sf_idx, &cfi, &corr_res); + + if (nlhs >= 1) { + if (n < 0) { + plhs[0] = mxCreateDoubleScalar(-1); + } else { + plhs[0] = mxCreateDoubleScalar(cfi); + } + } + if (nlhs >= 2) { + mexutils_write_cf(pcfich.d, &plhs[1], 16, 1); + } + if (nlhs >= 3) { + mexutils_write_cf(pcfich.symbols[0], &plhs[2], 16, 1); + } + if (nlhs >= 4) { + mexutils_write_cf(ce[0], &plhs[3], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1); + } + + srslte_chest_dl_free(&chest); + srslte_ofdm_rx_free(&ofdm_rx); + srslte_pcfich_free(&pcfich); + srslte_regs_free(®s); + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; + +srslte_cell_t cell = { + 6, // cell.cell.cell.nof_prb + 1, // cell.cell.nof_ports + 0, // cell.id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +uint32_t cfi = 2; +int flen; +uint16_t rnti = SRSLTE_SIRNTI; +int max_frames = 10; + +srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1A; +srslte_filesource_t fsrc; +srslte_pdcch_t pdcch; +cf_t *input_buffer, *fft_buffer, *ce[SRSLTE_MAX_PORTS]; +srslte_regs_t regs; +srslte_ofdm_t fft; +srslte_chest_dl_t chest; + +void usage(char *prog) { + printf("Usage: %s [vcfoe] -i input_file\n", prog); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-o DCI Format [Default %s]\n", srslte_dci_format_string(dci_format)); + printf("\t-r rnti [Default SI-RNTI]\n"); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-m max_frames [Default %d]\n", max_frames); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "irvofcenmp")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'r': + rnti = strtoul(argv[optind], NULL, 0); + break; + case 'm': + max_frames = strtoul(argv[optind], NULL, 0); + break; + case 'f': + cfi = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'o': + dci_format = srslte_dci_format_from_string(argv[optind]); + if (dci_format == SRSLTE_DCI_NOF_FORMATS) { + fprintf(stderr, "Error unsupported format %s\n", argv[optind]); + exit(-1); + } + break; + case 'v': + srslte_verbose++; + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + int i; + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + flen = 2 * (SRSLTE_SLOT_LEN(srslte_symbol_sz_power2(cell.nof_prb))); + + input_buffer = malloc(flen * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + .nof_prb = 6, + .nof_ports = 1, + .id = 1, + .cp = SRSLTE_CP_NORM, + .phich_resources = SRSLTE_PHICH_R_1, + .phich_length = SRSLTE_PHICH_NORM +}; + +uint32_t cfi = 1; +uint32_t nof_rx_ant = 1; +bool print_dci_table; + +void usage(char *prog) { + printf("Usage: %s [cfpndv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-A nof_rx_ant [Default %d]\n", nof_rx_ant); + printf("\t-d Print DCI table [Default %s]\n", print_dci_table?"yes":"no"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cfpndvA")) != -1) { + switch (opt) { + case 'p': + cell.nof_ports = (uint32_t) atoi(argv[optind]); + break; + case 'f': + cfi = (uint32_t) atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = (uint32_t) atoi(argv[optind]); + break; + case 'c': + cell.id = (uint32_t) atoi(argv[optind]); + break; + case 'A': + nof_rx_ant = (uint32_t) atoi(argv[optind]); + break; + case 'd': + print_dci_table = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int test_dci_payload_size() { + int i, j; + int x[5]; + const srslte_dci_format_t formats[] = { SRSLTE_DCI_FORMAT0, SRSLTE_DCI_FORMAT1, SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1C, SRSLTE_DCI_FORMAT2A }; + const int prb[6] = { 6, 15, 25, 50, 75, 100 }; + const int dci_sz[6][5] = { { 21, 19, 21, 8, 28 }, { 22, 23, 22, 10 , 31}, { 25, 27, + 25, 12 , 36}, { 27, 31, 27, 13 , 41}, { 27, 33, 27, 14 , 42}, { 28, 39, 28, 15, 48 }}; + + + printf("Testing DCI payload sizes...\n"); + printf(" PRB\t0\t1\t1A\t1C\t2A\n"); + for (i = 0; i < 6; i++) { + int n = prb[i]; + for (j = 0; j < 5; j++) { + x[j] = srslte_dci_format_sizeof(formats[j], (uint32_t) n, 1); + if (x[j] != dci_sz[i][j]) { + fprintf(stderr, "Invalid DCI payload size for %s\n", + srslte_dci_format_string(formats[j])); + return -1; + } + } + printf(" %2d:\t%2d\t%2d\t%2d\t%2d\t%2d\n", n, x[0], x[1], x[2], x[3], x[4]); + } + printf("Ok\n"); + + if (print_dci_table) { + printf("dci_sz_table[101][4] = {\n"); + for (i=0;i<=100;i++) { + printf(" {"); + for (j=0;j<4;j++) { + printf("%d",srslte_dci_format_sizeof(formats[j], (uint32_t) i, 1)); + if (j<3) { + printf(", "); + } + } + if (i<100) { + printf("},\n"); + } else { + printf("}\n"); + } + } + printf("};\n"); + } + return 0; +} + +typedef struct { + srslte_dci_msg_t dci_tx, dci_rx; + srslte_dci_location_t dci_location; + srslte_dci_format_t dci_format; + srslte_ra_dl_dci_t ra_dl_tx; + srslte_ra_dl_dci_t ra_dl_rx; +} testcase_dci_t; + +int main(int argc, char **argv) { + srslte_pdcch_t pdcch_tx, pdcch_rx; + testcase_dci_t testcases[10]; + srslte_ra_dl_dci_t ra_dl; + srslte_regs_t regs; + int i, j, k; + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + int nof_re; + cf_t *tx_slot_symbols[SRSLTE_MAX_PORTS], *rx_slot_symbols[SRSLTE_MAX_PORTS]; + int nof_dcis; + + bzero(&testcases, sizeof(testcase_dci_t)*10); + + int ret = -1; + + parse_args(argc, argv); + + nof_re = SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; + + if (test_dci_payload_size()) { + exit(-1); + } + + /* init memory */ + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + for (j = 0; j < SRSLTE_MAX_PORTS; j++) { + ce[i][j] = malloc(sizeof(cf_t) * nof_re); + if (!ce[i][j]) { + perror("malloc"); + exit(-1); + } + for (k = 0; k < nof_re; k++) { + //ce[i][j][k] = (i == j) ? 1 : 0; + ce[i][j][k] = ((float)rand()/(float)RAND_MAX) + _Complex_I*((float)rand()/(float)RAND_MAX); + } + } + tx_slot_symbols[i] = malloc(sizeof(cf_t) * nof_re); + if (!tx_slot_symbols[i]) { + perror("malloc"); + exit(-1); + } + bzero(tx_slot_symbols[i], sizeof(cf_t) * nof_re); + + rx_slot_symbols[i] = malloc(sizeof(cf_t) * nof_re); + if (!rx_slot_symbols[i]) { + perror("malloc"); + exit(-1); + } + bzero(rx_slot_symbols[i], sizeof(cf_t) * nof_re); + } + + if (srslte_regs_init(®s, cell)) { + fprintf(stderr, "Error initiating regs\n"); + exit(-1); + } + + if (srslte_pdcch_init_enb(&pdcch_tx, cell.nof_prb)) { + fprintf(stderr, "Error creating PDCCH object\n"); + exit(-1); + } + if (srslte_pdcch_set_cell(&pdcch_tx, ®s, cell)) { + fprintf(stderr, "Error setting cell in PDCCH object\n"); + exit(-1); + } + + if (srslte_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_rx_ant)) { + fprintf(stderr, "Error creating PDCCH object\n"); + exit(-1); + } + if (srslte_pdcch_set_cell(&pdcch_rx, ®s, cell)) { + fprintf(stderr, "Error setting cell in PDCCH object\n"); + exit(-1); + } + + /* Resource allocate init */ + nof_dcis = 0; + bzero(&ra_dl, sizeof(srslte_ra_dl_dci_t)); + ra_dl.harq_process = 0; + ra_dl.mcs_idx = 5; + ra_dl.ndi = 0; + ra_dl.rv_idx = 0; + ra_dl.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + ra_dl.type0_alloc.rbg_bitmask = 0x5; + ra_dl.tb_en[0] = true; + + /* Format 1 Test case */ + testcases[nof_dcis].dci_format = SRSLTE_DCI_FORMAT1; + testcases[nof_dcis].ra_dl_tx = ra_dl; + nof_dcis++; + + /* Format 1 Test case */ + ra_dl.mcs_idx = 15; + testcases[nof_dcis].dci_format = SRSLTE_DCI_FORMAT1; + testcases[nof_dcis].ra_dl_tx = ra_dl; + nof_dcis++; + + /* Tx Diversity Test case */ + if (cell.nof_ports > 1) { + ra_dl.mcs_idx_1 = 0; + ra_dl.rv_idx_1 = 0; + ra_dl.ndi_1 = false; + ra_dl.tb_en[1] = false; + testcases[nof_dcis].dci_format = SRSLTE_DCI_FORMAT2A; + testcases[nof_dcis].ra_dl_tx = ra_dl; + nof_dcis++; + } + + /* CDD Spatial Multiplexing Test case */ + if (cell.nof_ports > 1) { + ra_dl.mcs_idx_1 = 28; + ra_dl.rv_idx_1 = 1; + ra_dl.ndi_1 = false; + ra_dl.tb_en[1] = true; + testcases[nof_dcis].dci_format = SRSLTE_DCI_FORMAT2A; + testcases[nof_dcis].ra_dl_tx = ra_dl; + nof_dcis++; + } + + /* Execute Rx */ + for (i=0;i= 1234 && crc_rem < 1234 + nof_dcis) { + crc_rem -= 1234; + } else { + printf("Received invalid DCI CRC 0x%x\n", crc_rem); + goto quit; + } + } + + /* Compare Tx and Rx */ + for (i = 0; i < nof_dcis; i++) { + if (memcmp(testcases[i].dci_tx.data, testcases[i].dci_rx.data, testcases[i].dci_tx.nof_bits)) { + printf("Error in DCI %d: Received data does not match\n", i); + goto quit; + } + if (memcmp(&testcases[i].ra_dl_tx, &testcases[i].ra_dl_rx, sizeof(srslte_ra_dl_dci_t))) { + printf("Error in RA %d: Received data does not match\n", i); + printf(" Field | Tx | Rx \n"); + printf("--------------+----------+----------\n"); + printf(" harq_process | %8d | %8d\n", testcases[i].ra_dl_tx.harq_process, testcases[i].ra_dl_rx.harq_process); + printf(" mcs_idx | %8d | %8d\n", testcases[i].ra_dl_tx.mcs_idx, testcases[i].ra_dl_rx.mcs_idx); + printf(" rv_idx | %8d | %8d\n", testcases[i].ra_dl_tx.rv_idx, testcases[i].ra_dl_rx.rv_idx); + printf(" ndi | %8d | %8d\n", testcases[i].ra_dl_tx.ndi, testcases[i].ra_dl_rx.ndi); + printf(" mcs_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.mcs_idx_1, testcases[i].ra_dl_rx.mcs_idx_1); + printf(" rv_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.rv_idx_1, testcases[i].ra_dl_rx.rv_idx_1); + printf(" ndi_1 | %8d | %8d\n", testcases[i].ra_dl_tx.ndi_1, testcases[i].ra_dl_rx.ndi_1); + printf(" tb_cw_swap | %8d | %8d\n", testcases[i].ra_dl_tx.tb_cw_swap, testcases[i].ra_dl_rx.tb_cw_swap); + printf(" sram_id | %8d | %8d\n", testcases[i].ra_dl_tx.sram_id, testcases[i].ra_dl_rx.sram_id); + printf(" pinfo | %8d | %8d\n", testcases[i].ra_dl_tx.pinfo, testcases[i].ra_dl_rx.pinfo); + printf(" pconf | %8d | %8d\n", testcases[i].ra_dl_tx.pconf, testcases[i].ra_dl_rx.pconf); + printf(" power_offset | %8d | %8d\n", testcases[i].ra_dl_tx.power_offset, testcases[i].ra_dl_rx.power_offset); + printf(" tpc_pucch | %8d | %8d\n", testcases[i].ra_dl_tx.tpc_pucch, testcases[i].ra_dl_rx.tpc_pucch); + printf(" tb_en[0] | %8d | %8d\n", testcases[i].ra_dl_tx.tb_en[0], testcases[i].ra_dl_rx.tb_en[0]); + printf(" tb_en[1] | %8d | %8d\n", testcases[i].ra_dl_tx.tb_en[1], testcases[i].ra_dl_rx.tb_en[1]); + printf(" dci_is_1a | %8d | %8d\n", testcases[i].ra_dl_tx.dci_is_1a, testcases[i].ra_dl_rx.dci_is_1a); + printf(" dci_is_1c | %8d | %8d\n", testcases[i].ra_dl_tx.dci_is_1c, testcases[i].ra_dl_rx.dci_is_1c); + goto quit; + } + } + ret = 0; + +quit: + srslte_pdcch_free(&pdcch_tx); + srslte_pdcch_free(&pdcch_rx); + srslte_regs_free(®s); + + for (i = 0; i < SRSLTE_MAX_PORTS; i++) { + for (j = 0; j < SRSLTE_MAX_PORTS; j++) { + free(ce[i][j]); + } + free(tx_slot_symbols[i]); + free(rx_slot_symbols[i]); + } + if (ret) { + printf("Error\n"); + } else { + printf("Ok\n"); + } + srslte_dft_exit(); + exit(ret); +} diff --git a/lib/src/phy/phch/test/pdcch_test_mex.c b/lib/src/phy/phch/test/pdcch_test_mex.c new file mode 100644 index 0000000..76b0bb0 --- /dev/null +++ b/lib/src/phy/phch/test/pdcch_test_mex.c @@ -0,0 +1,225 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define RNTI prhs[1] +#define AMP prhs[2] +#define INPUT prhs[3] +#define NOF_INPUTS 4 + + + +srslte_dci_format_t ue_formats[] = {SRSLTE_DCI_FORMAT1A,SRSLTE_DCI_FORMAT1,SRSLTE_DCI_FORMAT2B}; // SRSLTE_DCI_FORMAT1B should go here also +const uint32_t nof_ue_formats = 3; + +srslte_dci_format_t common_formats[] = {SRSLTE_DCI_FORMAT1A,SRSLTE_DCI_FORMAT1C}; +const uint32_t nof_common_formats = 2; + + +void help() +{ + mexErrMsgTxt + ("[decoded_ok, llr, rm, bits, symbols] = srslte_pdcch(enbConfig, RNTI, rxWaveform)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int i; + srslte_cell_t cell; + srslte_pdcch_t pdcch; + srslte_chest_dl_t chest; + srslte_ofdm_t ofdm_rx; + srslte_regs_t regs; + srslte_dci_location_t locations[MAX_CANDIDATES]; + uint32_t cfi, sf_idx; + uint16_t rnti; + cf_t *input_fft, *input_signal; + int nof_re; + uint32_t nof_formats; + srslte_dci_format_t *formats = NULL; + + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (mexutils_read_uint32_struct(ENBCFG, "CFI", &cfi)) { + help(); + return; + } + if (mexutils_read_uint32_struct(ENBCFG, "NSubframe", &sf_idx)) { + help(); + return; + } + + if (srslte_chest_dl_init(&chest, cell)) { + fprintf(stderr, "Error initializing equalizer\n"); + return; + } + + if (srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return; + } + + rnti = (uint16_t) mxGetScalar(RNTI); + + if (srslte_regs_init(®s, cell)) { + mexErrMsgTxt("Error initiating regs\n"); + return; + } + + if (srslte_regs_set_cfi(®s, cfi)) { + fprintf(stderr, "Error setting CFI\n"); + exit(-1); + } + + if (srslte_pdcch_init(&pdcch, ®s, cell)) { + mexErrMsgTxt("Error initiating channel estimator\n"); + return; + } + + // Read input signal + int insignal_len = mexutils_read_cf(INPUT, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) { + input_fft = input_signal; + mexPrintf("Input is freq domain\n"); + } else { + input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft); + free(input_signal); + mexPrintf("Input is time domain\n"); + } + + cf_t *ce[SRSLTE_MAX_PORTS]; + for (i=0;i NOF_INPUTS) { + cf_t *cearray = NULL; + nof_re = mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (i=0;i NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_dl_get_noise_estimate(&chest); + } + + float amplitude = mxGetScalar(AMP); + + srslte_viterbi_set_gain_quant(&pdcch.decoder, amplitude); + + srslte_pdcch_extract_llr(&pdcch, input_fft, ce, noise_power, sf_idx, cfi); + + uint32_t nof_locations; + if (rnti == SRSLTE_SIRNTI) { + nof_locations = srslte_pdcch_common_locations(&pdcch, locations, MAX_CANDIDATES, cfi); + formats = common_formats; + nof_formats = nof_common_formats; + } else { + nof_locations = srslte_pdcch_ue_locations(&pdcch, locations, MAX_CANDIDATES, sf_idx, cfi, rnti); + formats = ue_formats; + nof_formats = nof_ue_formats; + } + uint16_t crc_rem=0; + srslte_dci_msg_t dci_msg; + bzero(&dci_msg, sizeof(srslte_dci_msg_t)); + + for (int f=0;f= 1) { + plhs[0] = mxCreateLogicalScalar(crc_rem == rnti); + } + int nof_bits = (srslte_regs_pdcch_nregs(®s, cfi) / 9) * 72; + if (nlhs >= 2) { + mexutils_write_f(pdcch.llr, &plhs[1], nof_bits, 1); + } + if (nlhs >= 3) { + mexutils_write_cf(pdcch.symbols[0], &plhs[2], 36*pdcch.nof_cce, 1); + } + if (nlhs >= 4) { + mexutils_write_cf(pdcch.d, &plhs[3], 36*pdcch.nof_cce, 1); + } + if (nlhs >= 5) { + mexutils_write_cf(ce[0], &plhs[4], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1); + } + + srslte_chest_dl_free(&chest); + srslte_ofdm_rx_free(&ofdm_rx); + srslte_pdcch_free(&pdcch); + srslte_regs_free(®s); + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 0, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +int flen; + +uint32_t cfi = 2; +uint16_t rnti = SRSLTE_SIRNTI; + +int max_frames = 10; +uint32_t sf_idx = 0; + +srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1A; +srslte_filesource_t fsrc; +srslte_ue_dl_t ue_dl; +cf_t *input_buffer[SRSLTE_MAX_PORTS]; + +void usage(char *prog) { + printf("Usage: %s [rovfcenmps] -i input_file\n", prog); + printf("\t-o DCI format [Default %s]\n", srslte_dci_format_string(dci_format)); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-s Start subframe_idx [Default %d]\n", sf_idx); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-r rnti [Default 0x%x]\n",rnti); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-m max_frames [Default %d]\n", max_frames); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "irovfcenmps")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 's': + sf_idx = atoi(argv[optind]); + break; + case 'r': + rnti = strtoul(argv[optind], NULL, 0); + break; + case 'm': + max_frames = strtoul(argv[optind], NULL, 0); + break; + case 'f': + cfi = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'o': + dci_format = srslte_dci_format_from_string(argv[optind]); + if (dci_format == SRSLTE_DCI_NOF_FORMATS) { + fprintf(stderr, "Error unsupported format %s\n", argv[optind]); + exit(-1); + } + break; + case 'v': + srslte_verbose++; + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + flen = SRSLTE_SF_LEN(srslte_symbol_sz_power2(cell.nof_prb)); + + input_buffer[0] = malloc(flen * sizeof(cf_t)); + if (!input_buffer[0]) { + perror("malloc"); + exit(-1); + } + + if (srslte_ue_dl_init(&ue_dl, input_buffer, cell.nof_prb, 1)) { + fprintf(stderr, "Error initializing UE DL\n"); + return -1; + } + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + fprintf(stderr, "Error initializing UE DL\n"); + return -1; + } + + srslte_ue_dl_set_rnti(&ue_dl, rnti); + + DEBUG("Memory init OK\n"); + return 0; +} + +void base_free() { + srslte_filesource_free(&fsrc); + srslte_ue_dl_free(&ue_dl); + free(input_buffer[0]); +} + +int main(int argc, char **argv) { + int nof_frames; + int ret; + bool acks[SRSLTE_MAX_TB]; + bzero(acks, sizeof(bool)*SRSLTE_MAX_TB); + + if (argc < 3) { + usage(argv[0]); + exit(-1); + } + parse_args(argc,argv); + + if (base_init()) { + fprintf(stderr, "Error initializing memory\n"); + exit(-1); + } + + uint8_t *data[] = {malloc(100000)}; + if (!data[0]) { + perror("malloc"); + exit(-1); + } + + ret = -1; + nof_frames = 0; + do { + srslte_filesource_read(&fsrc, input_buffer[0], flen); + INFO("Reading %d samples sub-frame %d\n", flen, sf_idx); + + ret = srslte_ue_dl_decode(&ue_dl, data, 0, sf_idx, acks); + if(ret > 0) { + printf("PDSCH Decoded OK!\n"); + } else if (ret == 0) { + printf("No DCI grant found\n"); + } else if (ret < 0) { + printf("Error decoding PDSCH\n"); + } + sf_idx = (sf_idx+1)%10; + nof_frames++; + } while (nof_frames <= max_frames && ret == 0); + + base_free(); + srslte_dft_exit(); + if (data[0]) + free(data[0]); + if (ret > 0) { + exit(0); + } else { + exit(-1); + } +} diff --git a/lib/src/phy/phch/test/pdsch_test.c b/lib/src/phy/phch/test/pdsch_test.c new file mode 100644 index 0000000..c4f8953 --- /dev/null +++ b/lib/src/phy/phch/test/pdsch_test.c @@ -0,0 +1,591 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +// Enable to measure execution time +//#define DO_OFDM + +#ifdef DO_OFDM +#define NOF_CE_SYMBOLS SRSLTE_SF_LEN_PRB(cell.nof_prb) +#else +#define NOF_CE_SYMBOLS SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) +#endif + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 0, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_NORM, // PHICH length + SRSLTE_PHICH_R_1_6 // PHICH resources +}; + +char mimo_type_str[32] = "single"; +srslte_mimo_type_t mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; +uint32_t cfi = 1; +uint32_t mcs[SRSLTE_MAX_CODEWORDS] = {0, 0}; +uint32_t subframe = 1; +int rv_idx[SRSLTE_MAX_CODEWORDS] = {0, 1}; +uint16_t rnti = 1234; +uint32_t nof_rx_antennas = 1; +bool tb_cw_swap = false; +bool enable_coworker = false; +uint32_t pmi = 0; +char *input_file = NULL; + +void usage(char *prog) { + printf("Usage: %s [fmMcsrtRFpnwav] \n", prog); + printf("\t-f read signal from file [Default generate it with pdsch_encode()]\n"); + printf("\t-m MCS [Default %d]\n", mcs[0]); + printf("\t-M MCS2 [Default %d]\n", mcs[1]); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-s subframe [Default %d]\n", subframe); + printf("\t-r rv_idx [Default %d]\n", rv_idx[0]); + printf("\t-t rv_idx2 [Default %d]\n", rv_idx[1]); + printf("\t-R rnti [Default %d]\n", rnti); + printf("\t-F cfi [Default %d]\n", cfi); + printf("\t-x Transmission mode [single|diversity|cdd|multiplex] [Default %s]\n", mimo_type_str); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-a nof_rx_antennas [Default %d]\n", nof_rx_antennas); + printf("\t-p pmi (multiplex only) [Default %d]\n", pmi); + printf("\t-w Swap Transport Blocks\n"); + printf("\t-j Enable PDSCH decoder coworker\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "fmMcsrtRFpnawvxj")) != -1) { + switch(opt) { + case 'f': + input_file = argv[optind]; + break; + case 'm': + mcs[0] = (uint32_t) atoi(argv[optind]); + break; + case 'M': + mcs[1] = (uint32_t) atoi(argv[optind]); + break; + case 's': + subframe = atoi(argv[optind]); + break; + case 'r': + rv_idx[0] = (uint32_t) atoi(argv[optind]); + break; + case 't': + rv_idx[1] = (uint32_t) atoi(argv[optind]); + break; + case 'R': + rnti = atoi(argv[optind]); + break; + case 'F': + cfi = atoi(argv[optind]); + break; + case 'x': + strncpy(mimo_type_str, argv[optind], sizeof(mimo_type_str)-1); + mimo_type_str[sizeof(mimo_type_str)-1] = 0; + break; + case 'p': + pmi = (uint32_t) atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'a': + nof_rx_antennas = (uint32_t) atoi(argv[optind]); + break; + case 'w': + tb_cw_swap = true; + break; + case 'j': + enable_coworker = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static uint8_t *data_tx[SRSLTE_MAX_CODEWORDS] = {NULL}; +static uint8_t *data_rx[SRSLTE_MAX_CODEWORDS] = {NULL}; +cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; +cf_t *ce_dummy[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; +srslte_softbuffer_rx_t *softbuffers_rx[SRSLTE_MAX_CODEWORDS]; +srslte_ra_dl_grant_t grant; +srslte_pdsch_cfg_t pdsch_cfg; +#ifdef DO_OFDM +cf_t *tx_sf_symbols[SRSLTE_MAX_PORTS]; +cf_t *rx_sf_symbols[SRSLTE_MAX_PORTS]; +#endif /* DO_OFDM */ +cf_t *tx_slot_symbols[SRSLTE_MAX_PORTS]; +cf_t *rx_slot_symbols[SRSLTE_MAX_PORTS]; +srslte_pdsch_t pdsch_tx, pdsch_rx; +srslte_ofdm_t ofdm_tx[SRSLTE_MAX_PORTS], ofdm_rx[SRSLTE_MAX_PORTS]; +srslte_chest_dl_t chest_dl; + +int main(int argc, char **argv) { + uint32_t i, j, k; + int ret = -1; + struct timeval t[3]; + srslte_softbuffer_tx_t *softbuffers_tx[SRSLTE_MAX_CODEWORDS]; + int M=1; + bool acks[SRSLTE_MAX_CODEWORDS] = {false}; + + parse_args(argc,argv); + + /* Initialise to zeros */ + bzero(&pdsch_tx, sizeof(srslte_pdsch_t)); + bzero(&pdsch_rx, sizeof(srslte_pdsch_t)); + bzero(&pdsch_cfg, sizeof(srslte_pdsch_cfg_t)); + bzero(ce, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(ce_dummy, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(tx_slot_symbols, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(rx_slot_symbols, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + + /* Parse transmission mode */ + if (srslte_str2mimotype(mimo_type_str, &mimo_type)) { + ERROR("Wrong transmission mode."); + goto quit; + } + + if (mimo_type == SRSLTE_MIMO_TYPE_SINGLE_ANTENNA) { + cell.nof_ports = 1; + } else { + cell.nof_ports = 2; + } + + srslte_ra_dl_dci_t dci; + bzero(&dci, sizeof(srslte_ra_dl_dci_t)); + dci.type0_alloc.rbg_bitmask = 0xffffffff; + + /* If transport block 0 is enabled */ + if (mcs[0] != 0 || rv_idx[0] != 1) { + dci.mcs_idx = mcs[0]; + dci.rv_idx = rv_idx[0]; + dci.tb_en[0] = true; + } + + /* If transport block 0 is disabled */ + if (mcs[1] != 0 || rv_idx[1] != 1) { + dci.mcs_idx_1 = mcs[1]; + dci.rv_idx_1 = rv_idx[1]; + dci.tb_en[1] = true; + } + + /* Enable swap */ + if (SRSLTE_RA_DL_GRANT_NOF_TB(&dci) == SRSLTE_MAX_TB && tb_cw_swap) { + dci.tb_cw_swap = tb_cw_swap; + } + + /* Generate grant from DCI */ + if (srslte_ra_dl_dci_to_grant(&dci, cell.nof_prb, rnti, &grant)) { + fprintf(stderr, "Error computing resource allocation\n"); + return ret; + } + + /* Configure PDSCH */ + if (srslte_pdsch_cfg_mimo(&pdsch_cfg, cell, &grant, cfi, subframe, rv_idx, mimo_type, pmi)) { + fprintf(stderr, "Error configuring PDSCH\n"); + goto quit; + } + + /* init memory */ + for (i=0;i +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define PDSCHCFG prhs[1] +#define TBS prhs[2] +#define INPUT prhs[3] +#define NOF_INPUTS 4 + +void help() +{ + mexErrMsgTxt + ("[decoded_ok, llr, rm, bits, symbols] = srslte_pdsch(enbConfig, pdschConfig, trblklen, rxWaveform)\n\n"); +} + +extern int indices[2048]; + +int rv_seq[4] = {0, 2, 3, 1}; + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int i; + srslte_cell_t cell; + srslte_ofdm_t ofdm_rx; + srslte_pdsch_t pdsch; + srslte_chest_dl_t chest; + cf_t *input_fft[SRSLTE_MAX_PORTS]; + srslte_pdsch_cfg_t cfg; + srslte_softbuffer_rx_t softbuffer; + uint32_t rnti32; + uint32_t cfi; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + bzero(&cfg, sizeof(srslte_pdsch_cfg_t)); + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (mexutils_read_uint32_struct(PDSCHCFG, "RNTI", &rnti32)) { + mexErrMsgTxt("Field RNTI not found in pdsch config\n"); + return; + } + + if (mexutils_read_uint32_struct(ENBCFG, "CFI", &cfi)) { + help(); + return; + } + if (mexutils_read_uint32_struct(ENBCFG, "NSubframe", &cfg.sf_idx)) { + help(); + return; + } + + if (srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return; + } + + + const size_t ndims = mxGetNumberOfDimensions(INPUT); + uint32_t nof_antennas = 1; + if (ndims >= 3) { + const mwSize *dims = mxGetDimensions(INPUT); + nof_antennas = dims[2]; + } + + if (srslte_pdsch_init_rx_multi(&pdsch, cell, nof_antennas)) { + mexErrMsgTxt("Error initiating PDSCH\n"); + return; + } + uint16_t rnti = (uint16_t) (rnti32 & 0xffff); + srslte_pdsch_set_rnti(&pdsch, rnti); + + if (srslte_softbuffer_rx_init(&softbuffer, cell.nof_prb)) { + mexErrMsgTxt("Error initiating soft buffer\n"); + return; + } + + if (srslte_chest_dl_init(&chest, cell)) { + mexErrMsgTxt("Error initializing equalizer\n"); + return; + } + + srslte_ra_dl_grant_t grant; + grant.mcs.tbs = mxGetScalar(TBS); + if (grant.mcs.tbs == 0) { + mexErrMsgTxt("Error trblklen is zero\n"); + return; + } + if (srslte_cbsegm(&cfg.cb_segm, grant.mcs.tbs)) { + mexErrMsgTxt("Error computing CB segmentation\n"); + return; + } + + if (mexutils_read_uint32_struct(PDSCHCFG, "RV", &cfg.rv)) { + mexErrMsgTxt("Field RV not found in pdsch config\n"); + return; + } + + uint32_t max_iterations = 5; + mexutils_read_uint32_struct(PDSCHCFG, "NTurboDecIts", &max_iterations); + + char *mod_str = mexutils_get_char_struct(PDSCHCFG, "Modulation"); + + if (!strcmp(mod_str, "QPSK")) { + grant.mcs.mod = SRSLTE_MOD_QPSK; + } else if (!strcmp(mod_str, "16QAM")) { + grant.mcs.mod = SRSLTE_MOD_16QAM; + } else if (!strcmp(mod_str, "64QAM")) { + grant.mcs.mod = SRSLTE_MOD_64QAM; + } else { + mexErrMsgTxt("Unknown modulation\n"); + return; + } + + mxFree(mod_str); + + mxArray *p; + p = mxGetField(PDSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found\n"); + return; + } + + float *prbset_f; + uint64_t *prbset; + if (mxGetClassID(p) == mxDOUBLE_CLASS) { + grant.nof_prb = mexutils_read_f(p, &prbset_f); + prbset = malloc(sizeof(uint64_t)*grant.nof_prb); + for (i=0;i 1) { + cfg.rv = rv_seq[rvIdx%4]; + } + } + + // Read input signal + cf_t *input_signal = NULL; + int insignal_len = mexutils_read_cf(tmp, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + if (!(insignal_len % SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp))) { + for (int i=0;i NOF_INPUTS) { + cf_t *cearray = NULL; + mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (int k=0;k NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_dl_get_noise_estimate(&chest); + } + + r = srslte_pdsch_decode_multi(&pdsch, &cfg, &softbuffer, input_fft, ce, noise_power, rnti, data_bytes); + } + + uint8_t *data = malloc(grant.mcs.tbs); + srslte_bit_unpack_vector(data_bytes[0], data, grant.mcs.tbs); + + if (nlhs >= 1) { + plhs[0] = mxCreateLogicalScalar(r == 0); + } + if (nlhs >= 2) { + mexutils_write_uint8(data, &plhs[1], grant.mcs.tbs, 1); + } + if (nlhs >= 3) { + mexutils_write_cf(pdsch.symbols[0], &plhs[2], cfg.nbits.nof_re, 1); + } + if (nlhs >= 4) { + mexutils_write_cf(pdsch.d[0], &plhs[3], cfg.nbits.nof_re, 1); + } + if (nlhs >= 5) { + mexutils_write_s(pdsch.e[0], &plhs[4], cfg.nbits.nof_bits, 1); + } + if (nlhs >= 6) { + uint32_t len = nof_antennas*cell.nof_ports*SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp); + cf_t *cearray_ptr = srslte_vec_malloc(len*sizeof(cf_t)); + int n=0; + for (i=0;i +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; +char *matlab_file_name = NULL; + +srslte_cell_t cell = { + 50, // cell.nof_prb + 2, // cell.nof_ports + 150, // cell.id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +int flen; +int nof_ctrl_symbols = 1; +int numsubframe = 0; + +FILE *fmatlab = NULL; + +srslte_filesource_t fsrc; +cf_t *input_buffer, *fft_buffer, *ce[SRSLTE_MAX_PORTS]; +srslte_phich_t phich; +srslte_regs_t regs; +srslte_ofdm_t fft; +srslte_chest_dl_t chest; + +void usage(char *prog) { + printf("Usage: %s [vcoe] -i input_file\n", prog); + printf("\t-o output matlab file name [Default Disabled]\n"); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-f nof control symbols [Default %d]\n", nof_ctrl_symbols); + printf("\t-g phich ng factor: 1/6, 1/2, 1, 2 [Default 1]\n"); + printf("\t-e phich extended length [Default normal]\n"); + printf("\t-l extended cyclic prefix [Default normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "iovcenpfgl")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'o': + matlab_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'f': + nof_ctrl_symbols = atoi(argv[optind]); + break; + case 'g': + if (!strcmp(argv[optind], "1/6")) { + cell.phich_resources = SRSLTE_PHICH_R_1_6; + } else if (!strcmp(argv[optind], "1/2")) { + cell.phich_resources = SRSLTE_PHICH_R_1_2; + } else if (!strcmp(argv[optind], "1")) { + cell.phich_resources = SRSLTE_PHICH_R_1; + } else if (!strcmp(argv[optind], "2")) { + cell.phich_resources = SRSLTE_PHICH_R_2; + } else { + fprintf(stderr, "Invalid phich ng factor %s. Setting to default.\n", argv[optind]); + } + break; + case 'e': + cell.phich_length = SRSLTE_PHICH_EXT; + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + case 'l': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + int i; + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + if (matlab_file_name) { + fmatlab = fopen(matlab_file_name, "w"); + if (!fmatlab) { + perror("fopen"); + return -1; + } + } else { + fmatlab = NULL; + } + + flen = SRSLTE_SF_LEN(srslte_symbol_sz_power2(cell.nof_prb)); + + input_buffer = malloc(flen * sizeof(cf_t)); + if (!input_buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + if (!fft_buffer) { + perror("malloc"); + return -1; + } + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + + +srslte_cell_t cell = { + 6, // nof_prb + 1, // nof_ports + 1000, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +srslte_phich_resources_t phich_res = SRSLTE_PHICH_R_1; +srslte_phich_length_t phich_length = SRSLTE_PHICH_NORM; + +void usage(char *prog) { + printf("Usage: %s [cpvgel]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-g phich ng factor: 1/6, 1/2, 1, 2 [Default 1]\n"); + printf("\t-e phich extended length [Default normal]\n"); + printf("\t-l extended cyclic prefix [Default normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpnvgel")) != -1) { + switch(opt) { + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'g': + if (!strcmp(argv[optind], "1/6")) { + phich_res = SRSLTE_PHICH_R_1_6; + } else if (!strcmp(argv[optind], "1/2")) { + phich_res = SRSLTE_PHICH_R_1_2; + } else if (!strcmp(argv[optind], "1")) { + phich_res = SRSLTE_PHICH_R_1; + } else if (!strcmp(argv[optind], "2")) { + phich_res = SRSLTE_PHICH_R_2; + } else { + fprintf(stderr, "Invalid phich ng factor %s. Setting to default.\n", argv[optind]); + } + break; + case 'e': + phich_length = SRSLTE_PHICH_EXT; + break; + case 'l': + cell.cp = SRSLTE_CP_EXT; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + + +int main(int argc, char **argv) { + srslte_phich_t phich; + srslte_regs_t regs; + int i, j; + cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; + int nof_re; + cf_t *slot_symbols[SRSLTE_MAX_PORTS]; + uint8_t ack[50][SRSLTE_PHICH_NORM_NSEQUENCES], ack_rx; + uint32_t nsf; + float distance; + int cid, max_cid; + uint32_t ngroup, nseq, max_nseq; + + parse_args(argc,argv); + + max_nseq = SRSLTE_CP_ISNORM(cell.cp)?SRSLTE_PHICH_NORM_NSEQUENCES:SRSLTE_PHICH_EXT_NSEQUENCES; + + nof_re = SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; + + /* init memory */ + for (i=0;i +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define HIRES prhs[1] +#define INPUT prhs[2] +#define NOF_INPUTS 3 + +void help() +{ + mexErrMsgTxt + ("[hi, symbols] = srslte_phich(enbConfig, hires, input_signal, [hest, nest])\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int i; + srslte_cell_t cell; + srslte_phich_t phich; + srslte_chest_dl_t chest; + srslte_ofdm_t ofdm_rx; + srslte_regs_t regs; + uint32_t sf_idx; + cf_t *input_fft, *input_signal; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (mexutils_read_uint32_struct(ENBCFG, "NSubframe", &sf_idx)) { + help(); + return; + } + + if (srslte_chest_dl_init(&chest, cell)) { + mexErrMsgTxt("Error initializing equalizer\n"); + return; + } + + if (srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb)) { + mexErrMsgTxt("Error initializing FFT\n"); + return; + } + + if (srslte_regs_init(®s, cell)) { + mexErrMsgTxt("Error initiating regs\n"); + return; + } + + if (srslte_phich_init(&phich, ®s, cell)) { + mexErrMsgTxt("Error creating PHICH object\n"); + return; + } + +// Read input signal + input_signal = NULL; + int insignal_len = mexutils_read_cf(INPUT, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) { + input_fft = input_signal; + } else { + input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft); + free(input_signal); + } + + cf_t *ce[SRSLTE_MAX_PORTS]; + for (i=0;i NOF_INPUTS) { + cf_t *cearray = NULL; + mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (i=0;i NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_dl_get_noise_estimate(&chest); + } + + // Read hires values + float *hires = NULL; + int nhires = mexutils_read_f(HIRES, &hires); + if (nhires != 2) { + mexErrMsgTxt("Expecting 2 values for hires parameter\n"); + return; + } + uint32_t ngroup = (uint32_t) hires[0]; + uint32_t nseq = (uint32_t) hires[1]; + uint8_t ack; + float corr_res; + int n = srslte_phich_decode(&phich, input_fft, ce, noise_power, ngroup, nseq, sf_idx, &ack, &corr_res); + + if (nlhs >= 1) { + if (n < 0) { + plhs[0] = mxCreateDoubleScalar(-1); + } else { + plhs[0] = mxCreateDoubleScalar(ack); + } + } + if (nlhs >= 2) { + mexutils_write_cf(phich.z, &plhs[1], 1, SRSLTE_PHICH_NBITS); + } + + srslte_chest_dl_free(&chest); + srslte_ofdm_rx_free(&ofdm_rx); + srslte_phich_free(&phich); + srslte_regs_free(®s); + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *input_file_name = NULL; + +srslte_cell_t cell = { + 100, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_EXT, // cyclic prefix + SRSLTE_PHICH_R_1, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +int flen; + +uint32_t cfi = 2; +uint16_t rnti = SRSLTE_SIRNTI; + +int max_frames = 150; +uint32_t sf_idx = 1; + +uint8_t non_mbsfn_region = 2; +int mbsfn_area_id = 1; + +srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1A; +srslte_filesource_t fsrc; +srslte_ue_dl_t ue_dl; +cf_t *input_buffer[SRSLTE_MAX_PORTS]; + +void usage(char *prog) { + printf("Usage: %s [rovfcenmps] -i input_file\n", prog); + printf("\t-o DCI format [Default %s]\n", srslte_dci_format_string(dci_format)); + printf("\t-c cell.id [Default %d]\n", cell.id); + printf("\t-s Start subframe_idx [Default %d]\n", sf_idx); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-r rnti [Default 0x%x]\n",rnti); + printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-M mbsfn_area_id [Default %d]\n", mbsfn_area_id); + printf("\t-e Set extended prefix [Default Normal]\n"); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "irovfcenmps")) != -1) { + switch(opt) { + case 'i': + input_file_name = argv[optind]; + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 's': + sf_idx = atoi(argv[optind]); + break; + case 'r': + rnti = strtoul(argv[optind], NULL, 0); + break; + case 'f': + cfi = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'p': + cell.nof_ports = atoi(argv[optind]); + break; + case 'M': + mbsfn_area_id = atoi(argv[optind]); + break; + case 'o': + dci_format = srslte_dci_format_from_string(argv[optind]); + if (dci_format == SRSLTE_DCI_NOF_FORMATS) { + fprintf(stderr, "Error unsupported format %s\n", argv[optind]); + exit(-1); + } + break; + case 'v': + srslte_verbose++; + break; + case 'e': + cell.cp = SRSLTE_CP_EXT; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!input_file_name) { + usage(argv[0]); + exit(-1); + } +} + +int base_init() { + + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + + flen = 2 * (SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.nof_prb))); + + input_buffer[0] = malloc(flen * sizeof(cf_t)); + if (!input_buffer[0]) { + perror("malloc"); + exit(-1); + } + + if (srslte_ue_dl_init(&ue_dl, input_buffer, cell.nof_prb, 1)) { + fprintf(stderr, "Error initializing UE DL\n"); + return -1; + } + + + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + fprintf(stderr, "Error initializing UE DL\n"); + return -1; + } + + srslte_ue_dl_set_rnti(&ue_dl, rnti); + + srslte_ue_dl_set_mbsfn_area_id(&ue_dl, mbsfn_area_id); + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, non_mbsfn_region); + + + DEBUG("Memory init OK\n"); + return 0; +} + +void base_free() { + srslte_filesource_free(&fsrc); + srslte_ue_dl_free(&ue_dl); + free(input_buffer[0]); +} + +int main(int argc, char **argv) { + int ret; + + if (argc < 3) { + usage(argv[0]); + exit(-1); + } + parse_args(argc,argv); + + if (base_init()) { + fprintf(stderr, "Error initializing memory\n"); + exit(-1); + } + + uint8_t *data = malloc(100000); + if (!data) { + perror("malloc"); + exit(-1); + } + + ret = -1; + + srslte_filesource_read(&fsrc, input_buffer[0], flen); + INFO("Reading %d samples sub-frame %d\n", flen, sf_idx); + ret = srslte_ue_dl_decode_mbsfn(&ue_dl, data, sf_idx); + if(ret > 0) { + printf("PMCH Decoded OK!\n"); + } else if (ret < 0) { + printf("Error decoding PMCH\n"); + } + + base_free(); + if (data != NULL) { + free(data); + } + srslte_dft_exit(); + if (ret > 0) { + exit(0); + } else { + exit(-1); + } +} diff --git a/lib/src/phy/phch/test/pmch_test.c b/lib/src/phy/phch/test/pmch_test.c new file mode 100644 index 0000000..e4871a5 --- /dev/null +++ b/lib/src/phy/phch/test/pmch_test.c @@ -0,0 +1,489 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +// Enable to measure execution time +#define DO_OFDM + +#ifdef DO_OFDM +#define NOF_CE_SYMBOLS SRSLTE_SF_LEN_PRB(cell.nof_prb) +#else +#define NOF_CE_SYMBOLS SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) +#endif + +srslte_cell_t cell = { + 100, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_EXT, // cyclic prefix + SRSLTE_PHICH_NORM, // PHICH length + SRSLTE_PHICH_R_1_6 // PHICH resources +}; + +char mimo_type_str [32] = "single"; +srslte_mimo_type_t mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; +uint32_t cfi = 2; +uint32_t mcs_idx = 2; +uint32_t subframe = 1; +int rv_idx[SRSLTE_MAX_CODEWORDS] = {0, 1}; +uint16_t rnti = 1234; +uint32_t nof_rx_antennas = 1; +uint32_t pmi = 0; +char *input_file = NULL; +uint32_t mbsfn_area_id = 1; +uint32_t non_mbsfn_region = 2; +void usage(char *prog) { + printf("Usage: %s [fmMcsrtRFpnwav] \n", prog); + printf("\t-f read signal from file [Default generate it with pdsch_encode()]\n"); + printf("\t-m MCS [Default %d]\n", mcs_idx); + printf("\t-M mbsfn area id [Default %d]\n", mbsfn_area_id); + printf("\t-N non mbsfn region [Default %d]\n", non_mbsfn_region); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-s subframe [Default %d]\n", subframe); + printf("\t-r rv_idx [Default %d]\n", rv_idx[0]); + printf("\t-R rnti [Default %d]\n", rnti); + printf("\t-F cfi [Default %d]\n", cfi); + printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-a nof_rx_antennas [Default %d]\n", nof_rx_antennas); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "fmMcsrtRFpnavx")) != -1) { + switch(opt) { + case 'f': + input_file = argv[optind]; + break; + case 'm': + mcs_idx = (uint32_t) atoi(argv[optind]); + break; + case 's': + subframe = atoi(argv[optind]); + break; + case 'r': + rv_idx[0] = (uint32_t) atoi(argv[optind]); + break; + case 'R': + rnti = atoi(argv[optind]); + break; + case 'F': + cfi = atoi(argv[optind]); + break; + case 'x': + strncpy(mimo_type_str, argv[optind], 31); + mimo_type_str[31] = 0; + break; + case 'p': + pmi = (uint32_t) atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'a': + nof_rx_antennas = (uint32_t) atoi(argv[optind]); + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static uint8_t *data_tx[SRSLTE_MAX_CODEWORDS] = {NULL}; +static uint8_t *data_rx[SRSLTE_MAX_CODEWORDS] = {NULL}; +cf_t *ce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; +srslte_softbuffer_rx_t *softbuffers_rx[SRSLTE_MAX_CODEWORDS]; +srslte_ra_dl_grant_t grant; +#ifdef DO_OFDM +cf_t *tx_sf_symbols[SRSLTE_MAX_PORTS]; +cf_t *rx_sf_symbols[SRSLTE_MAX_PORTS]; +#endif /* DO_OFDM */ +cf_t *tx_slot_symbols[SRSLTE_MAX_PORTS]; +cf_t *rx_slot_symbols[SRSLTE_MAX_PORTS]; +srslte_pmch_t pmch_tx, pmch_rx; +srslte_pdsch_cfg_t pmch_cfg; +srslte_ofdm_t ifft_mbsfn[SRSLTE_MAX_PORTS], fft_mbsfn[SRSLTE_MAX_PORTS]; + +int main(int argc, char **argv) { + uint32_t i, j, k; + int ret = -1; + struct timeval t[3]; + srslte_softbuffer_tx_t *softbuffers_tx[SRSLTE_MAX_CODEWORDS]; + int M=1; + + parse_args(argc,argv); + /* Initialise to zeros */ + bzero(&pmch_tx, sizeof(srslte_pmch_t)); + bzero(&pmch_rx, sizeof(srslte_pmch_t)); + bzero(&pmch_cfg, sizeof(srslte_pdsch_cfg_t)); + bzero(ce, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(tx_slot_symbols, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(rx_slot_symbols, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + bzero(t, 3 * sizeof(struct timeval)); + + cell.nof_ports = 1; + + srslte_ra_dl_dci_t dci; + bzero(&dci, sizeof(srslte_ra_dl_dci_t)); + dci.type0_alloc.rbg_bitmask = 0xffffffff; + + + /* If transport block 0 is enabled */ + grant.tb_en[0] = true; + grant.tb_en[1] = false; + grant.mcs[0].idx = mcs_idx; + + grant.nof_prb = cell.nof_prb; + grant.sf_type = SRSLTE_SF_MBSFN; + + srslte_dl_fill_ra_mcs(&grant.mcs[0], cell.nof_prb); + grant.Qm[0] = srslte_mod_bits_x_symbol(grant.mcs[0].mod); + for(int i = 0; i < 2; i++){ + for(int j = 0; j < grant.nof_prb; j++){ + grant.prb_idx[i][j] = true; + } + } + + /* init memory */ + for (i=0;i +#define FORCE_POWER2_FFT +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +extern float save_corr[4096]; + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PRACHCFG prhs[1] +#define INPUT prhs[2] +#define NOF_INPUTS 3 + +void help() +{ + mexErrMsgTxt + ("[preamble, offset] = srslte_prach(ueConfig, prachConfig, signal)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + srslte_use_standard_symbol_size(true); + + uint32_t n_ul_rb = 0; + if (mexutils_read_uint32_struct(UECFG, "NULRB", &n_ul_rb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + int r = srslte_symbol_sz(n_ul_rb); + if (r < 0) { + mexErrMsgTxt("Invalid NULRB\n"); + return; + } + uint32_t N_ifft_ul = (uint32_t) r; + + uint32_t sf_idx = 0; + mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx); + uint32_t nframe = 0; + mexutils_read_uint32_struct(UECFG, "NFrame", &nframe); + + uint32_t preamble_format = 0; + mexutils_read_uint32_struct(PRACHCFG, "Format", &preamble_format); + uint32_t root_seq_idx = 0; + mexutils_read_uint32_struct(PRACHCFG, "SeqIdx", &root_seq_idx); + uint32_t seq_idx = 0; + mexutils_read_uint32_struct(PRACHCFG, "PreambleIdx", &seq_idx); + uint32_t zero_corr_zone = 0; + mexutils_read_uint32_struct(PRACHCFG, "CyclicShiftIdx", &zero_corr_zone); + uint32_t high_speed_flag = 0; + mexutils_read_uint32_struct(PRACHCFG, "HighSpeed", &high_speed_flag); + uint32_t frequency_offset = 0; + mexutils_read_uint32_struct(PRACHCFG, "FreqOffset", &frequency_offset); + + srslte_prach_t prach; + if (srslte_prach_init(&prach, N_ifft_ul, preamble_format*16, root_seq_idx, high_speed_flag, zero_corr_zone)) { + mexErrMsgTxt("Error initiating PRACH\n"); + return; + } + + cf_t *input_signal = NULL; + int nof_samples = mexutils_read_cf(INPUT, &input_signal); + + uint32_t preambles[64]; + float offsets[64]; + uint32_t nof_detected = 0; + + if (nrhs > NOF_INPUTS) { + float factor = mxGetScalar(prhs[NOF_INPUTS]); + srslte_prach_set_detect_factor(&prach, factor); + } + + mexPrintf("format=%d config=%d, N_cp=%d, ifft=%d\n", prach.f, prach.config_idx, prach.N_cp, prach.N_ifft_ul); + + if (srslte_prach_detect_offset(&prach, frequency_offset, &input_signal[prach.N_cp], nof_samples, preambles, offsets, NULL, &nof_detected)) { + mexErrMsgTxt("Error detecting PRACH\n"); + return; + } + + if (nlhs >= 1) { + mexutils_write_int((int*) preambles, &plhs[0], nof_detected, 1); + } + if (nlhs >= 2) { + mexutils_write_f(offsets, &plhs[1], nof_detected, 1); + } + if (nlhs >= 3) { + mexutils_write_f(save_corr, &plhs[2], prach.N_zc, 1); + } + + free(input_signal); + srslte_prach_free(&prach); + + return; +} + diff --git a/lib/src/phy/phch/test/prach_test.c b/lib/src/phy/phch/test/prach_test.c new file mode 100644 index 0000000..29b80a5 --- /dev/null +++ b/lib/src/phy/phch/test/prach_test.c @@ -0,0 +1,130 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +#define MAX_LEN 70176 + + +uint32_t N_ifft_ul = 1536; +uint32_t config_idx = 3; +uint32_t root_seq_idx = 0; +uint32_t zero_corr_zone = 15; + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-N Uplink IFFT size [Default %d]\n", N_ifft_ul); + printf("\t-f Preamble format [Default 0]\n"); + printf("\t-r Root sequence index [Default 0]\n"); + printf("\t-z Zero correlation zone config [Default 1]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "Nfrz")) != -1) { + switch (opt) { + case 'N': + N_ifft_ul = atoi(argv[optind]); + break; + case 'f': + config_idx = atoi(argv[optind]); + break; + case 'r': + root_seq_idx = atoi(argv[optind]); + break; + case 'z': + zero_corr_zone = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + srslte_prach_t *p = (srslte_prach_t*)malloc(sizeof(srslte_prach_t)); + + bool high_speed_flag = false; + + cf_t preamble[MAX_LEN]; + memset(preamble, 0, sizeof(cf_t)*MAX_LEN); + + srslte_prach_init(p, N_ifft_ul); + + srslte_prach_set_cell( p, + N_ifft_ul, + config_idx, + root_seq_idx, + high_speed_flag, + zero_corr_zone); + + uint32_t seq_index = 0; + uint32_t frequency_offset = 0; + + uint32_t indices[64]; + uint32_t n_indices = 0; + for(int i=0;i<64;i++) + indices[i] = 0; + + for(seq_index=0;seq_index<64;seq_index++) + { + srslte_prach_gen(p, + seq_index, + frequency_offset, + preamble); + + uint32_t prach_len = p->N_seq; + + struct timeval t[3]; + gettimeofday(&t[1], NULL); + srslte_prach_detect(p, frequency_offset, &preamble[p->N_cp], prach_len, indices, &n_indices); + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("texec=%ld us\n", t[0].tv_usec); + if(n_indices != 1 || indices[0] != seq_index) + return -1; + } + + srslte_prach_free(p); + free(p); + srslte_dft_exit(); + + printf("Done\n"); + exit(0); +} diff --git a/lib/src/phy/phch/test/prach_test_mex.c b/lib/src/phy/phch/test/prach_test_mex.c new file mode 100644 index 0000000..54af0ab --- /dev/null +++ b/lib/src/phy/phch/test/prach_test_mex.c @@ -0,0 +1,115 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#define FORCE_POWER2_FFT +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PRACHCFG prhs[1] +#define NOF_INPUTS 2 + +void help() +{ + mexErrMsgTxt + ("waveform = srslte_prach(ueConfig, prachConfig)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + uint32_t n_ul_rb = 0; + if (mexutils_read_uint32_struct(UECFG, "NULRB", &n_ul_rb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + int r = srslte_symbol_sz(n_ul_rb); + if (r < 0) { + mexErrMsgTxt("Invalid NULRB\n"); + return; + } + uint32_t N_ifft_ul = (uint32_t) r; + + uint32_t sf_idx = 0; + mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx); + uint32_t nframe = 0; + mexutils_read_uint32_struct(UECFG, "NFrame", &nframe); + + uint32_t preamble_format = 0; + mexutils_read_uint32_struct(PRACHCFG, "Format", &preamble_format); + uint32_t root_seq_idx = 0; + mexutils_read_uint32_struct(PRACHCFG, "SeqIdx", &root_seq_idx); + uint32_t seq_idx = 0; + mexutils_read_uint32_struct(PRACHCFG, "PreambleIdx", &seq_idx); + uint32_t zero_corr_zone = 0; + mexutils_read_uint32_struct(PRACHCFG, "CyclicShiftIdx", &zero_corr_zone); + uint32_t high_speed_flag = 0; + mexutils_read_uint32_struct(PRACHCFG, "HighSpeed", &high_speed_flag); + uint32_t frequency_offset = 0; + mexutils_read_uint32_struct(PRACHCFG, "FreqOffset", &frequency_offset); + + srslte_prach_t prach; + if (srslte_prach_init(&prach, N_ifft_ul, preamble_format*16, root_seq_idx, high_speed_flag, zero_corr_zone)) { + mexErrMsgTxt("Error initiating PRACH\n"); + return; + } + + uint32_t nof_samples = srslte_sampling_freq_hz(n_ul_rb) * 0.001; + + cf_t *signal = srslte_vec_malloc(sizeof(cf_t) * nof_samples); + if (!signal) { + mexErrMsgTxt("malloc"); + return; + } + bzero(signal, sizeof(cf_t) * nof_samples); + if (srslte_prach_gen(&prach, seq_idx, frequency_offset, signal)) { + mexErrMsgTxt("Error generating PRACH\n"); + return; + } + + srslte_vec_sc_prod_cfc(signal, 1.0/sqrtf(N_ifft_ul), signal, nof_samples); + + if (nlhs >= 0) { + mexutils_write_cf(signal, &plhs[0], nof_samples, 1); + } + + free(signal); + + srslte_prach_free(&prach); + + return; +} + diff --git a/lib/src/phy/phch/test/prach_test_multi.c b/lib/src/phy/phch/test/prach_test_multi.c new file mode 100644 index 0000000..7ef6f23 --- /dev/null +++ b/lib/src/phy/phch/test/prach_test_multi.c @@ -0,0 +1,144 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/prach.h" + +#define MAX_LEN 70176 + + +uint32_t N_ifft_ul = 128; +uint32_t preamble_format = 0; +uint32_t root_seq_idx = 0; +uint32_t zero_corr_zone = 1; +uint32_t n_seqs = 64; + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-N Uplink IFFT size [Default 128]\n"); + printf("\t-f Preamble format [Default 0]\n"); + printf("\t-r Root sequence index [Default 0]\n"); + printf("\t-z Zero correlation zone config [Default 1]\n"); + printf("\t-n Number of sequences used for each test [Default 64]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "Nfrzn")) != -1) { + switch (opt) { + case 'N': + N_ifft_ul = atoi(argv[optind]); + break; + case 'f': + preamble_format = atoi(argv[optind]); + break; + case 'r': + root_seq_idx = atoi(argv[optind]); + break; + case 'z': + zero_corr_zone = atoi(argv[optind]); + break; + case 'n': + n_seqs = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + srslte_prach_t *p = (srslte_prach_t*)malloc(sizeof(srslte_prach_t)); + + bool high_speed_flag = false; + + cf_t preamble[MAX_LEN]; + memset(preamble, 0, sizeof(cf_t)*MAX_LEN); + cf_t preamble_sum[MAX_LEN]; + memset(preamble_sum, 0, sizeof(cf_t)*MAX_LEN); + + srslte_prach_init(p, N_ifft_ul); + + srslte_prach_set_cell( p, + N_ifft_ul, + preamble_format, + root_seq_idx, + high_speed_flag, + zero_corr_zone); + + uint32_t seq_index = 0; + uint32_t frequency_offset = 0; + + uint32_t indices[64]; + uint32_t n_indices = 0; + for(int i=0;i<64;i++) + indices[i] = 0; + + srslte_prach_set_detect_factor(p, 10); + + for(seq_index=0;seq_indexN_cp+p->N_seq;i++) + { + preamble_sum[i] += preamble[i]; + } + } + + uint32_t prach_len = p->N_seq; + if(preamble_format == 2 || preamble_format == 3) + prach_len /= 2; + srslte_prach_detect(p, 0, &preamble_sum[p->N_cp], prach_len, indices, &n_indices); + + if(n_indices != n_seqs) + return -1; + + for(int i=0;i +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/rf/rf.h" +#include "srslte/srslte.h" + +#define MAX_LEN 70176 + + +uint32_t nof_prb = 25; +uint32_t preamble_format = 0; +uint32_t root_seq_idx = 0; +uint32_t seq_idx = 0; +uint32_t frequency_offset = 0; +uint32_t zero_corr_zone = 11; +float timeadv = 0; +uint32_t nof_frames = 20; + +float uhd_rx_gain=40, uhd_tx_gain=60, uhd_freq=2.4e9; +char *uhd_args=""; +char *output_filename = "prach_rx"; + +void usage(char *prog) { + printf("Usage: %s \n", prog); + printf("\t-a UHD args [Default %s]\n", uhd_args); + printf("\t-f UHD TX/RX frequency [Default %.2f MHz]\n", uhd_freq/1e6); + printf("\t-g UHD RX gain [Default %.1f dB]\n", uhd_rx_gain); + printf("\t-G UHD TX gain [Default %.1f dB]\n", uhd_tx_gain); + printf("\t-p Number of UL RB [Default %d]\n", nof_prb); + printf("\t-F Preamble format [Default %d]\n", preamble_format); + printf("\t-O Frequency offset [Default %d]\n", frequency_offset); + printf("\t-s sequence index [Default %d]\n", seq_idx); + printf("\t-r Root sequence index [Default %d]\n", root_seq_idx); + printf("\t-t Time advance (us) [Default %.1f us]\n", timeadv); + printf("\t-z Zero correlation zone config [Default %d]\n", zero_corr_zone); + printf("\t-o Save transmitted PRACH in file [Default no]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "apfFgGrstoPOz")) != -1) { + switch (opt) { + case 'a': + uhd_args = argv[optind]; + break; + case 'o': + output_filename = argv[optind]; + break; + case 'f': + uhd_freq = atof(argv[optind]); + break; + case 'g': + uhd_rx_gain = atof(argv[optind]); + break; + case 'G': + uhd_tx_gain = atof(argv[optind]); + break; + case 'P': + preamble_format = atoi(argv[optind]); + break; + case 'O': + frequency_offset = atoi(argv[optind]); + break; + case 't': + timeadv = atof(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + if (!srslte_nofprb_isvalid(nof_prb)) { + fprintf(stderr, "Invalid number of UL RB %d\n", nof_prb); + exit(-1); + } + break; + case 'F': + preamble_format = atoi(argv[optind]); + break; + case 'r': + root_seq_idx = atoi(argv[optind]); + break; + case 's': + seq_idx = atoi(argv[optind]); + break; + case 'z': + zero_corr_zone = atoi(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + srslte_prach_t *p = (srslte_prach_t*)malloc(sizeof(srslte_prach_t)); + + bool high_speed_flag = false; + + cf_t preamble[MAX_LEN]; + memset(preamble, 0, sizeof(cf_t)*MAX_LEN); + + srslte_prach_init(p, + srslte_symbol_sz(nof_prb)); + + srslte_prach_set_cell(p, + srslte_symbol_sz(nof_prb), + preamble_format, + root_seq_idx, + high_speed_flag, + zero_corr_zone); + + int srate = srslte_sampling_freq_hz(nof_prb); + uint32_t flen = srate/1000; + + printf("Generating PRACH\n"); + bzero(preamble, flen*sizeof(cf_t)); + srslte_prach_gen(p, + seq_idx, + frequency_offset, + preamble); + + + uint32_t prach_len = p->N_seq+p->N_cp; + + srslte_vec_save_file("generated",preamble,prach_len*sizeof(cf_t)); + + cf_t *buffer = malloc(sizeof(cf_t)*flen*nof_frames); + + // Send through UHD + srslte_rf_t rf; + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, uhd_args)) { + fprintf(stderr, "Error opening &uhd\n"); + exit(-1); + } + printf("Subframe len: %d samples\n", flen); + printf("Set RX gain: %.1f dB\n", uhd_rx_gain); + printf("Set TX gain: %.1f dB\n", uhd_tx_gain); + printf("Set TX/RX freq: %.2f MHz\n", uhd_freq/ 1000000); + + srslte_rf_set_rx_gain(&rf, uhd_rx_gain); + srslte_rf_set_tx_gain(&rf, uhd_tx_gain); + srslte_rf_set_rx_freq(&rf, uhd_freq); + srslte_rf_set_tx_freq(&rf, uhd_freq); + + if (srate > 1e6 && (srate/1000) > 0) { + if (30720%(srate/1000) == 0) { + srslte_rf_set_master_clock_rate(&rf, 30.72e6); + } else { + srslte_rf_set_master_clock_rate(&rf, 23.04e6); + } + } else { + printf("Invalid sampling rate %d Hz\n", srate); + exit(-1); + } + printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); + float srate_rf = srslte_rf_set_rx_srate(&rf, (double) srate); + if (srate_rf != srate) { + fprintf(stderr, "Could not set sampling rate\n"); + exit(-1); + } + srslte_rf_set_tx_srate(&rf, (double) srate); + sleep(1); + + cf_t *zeros = calloc(sizeof(cf_t),flen); + + srslte_timestamp_t tstamp; + + srslte_rf_start_rx_stream(&rf, false); + uint32_t nframe=0; + + while(nframeN_cp], flen, indices, offsets, NULL, &nof_detected)) { + printf("Error detecting prach\n"); + } + printf("Nof detected PRACHs: %d\n", nof_detected); + for (int i=0;i +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUCCHCFG prhs[1] +#define ACK prhs[2] +#define NOF_INPUTS 3 + +void help() +{ + mexErrMsgTxt + ("[sym, sym_with_dmrs, subframe_all]=srslte_pucch_encode(ue, chs, ack)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + + srslte_cell_t cell; + bzero(&cell, sizeof(srslte_cell_t)); + cell.nof_ports = 1; + cell.cp = SRSLTE_CP_NORM; + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + srslte_pucch_t pucch; + if (srslte_pucch_init(&pucch, cell)) { + mexErrMsgTxt("Error initiating PUSCH\n"); + return; + } + + uint32_t sf_idx = 0; + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + uint32_t rnti; + if (mexutils_read_uint32_struct(UECFG, "RNTI", &rnti)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + if (srslte_pucch_set_crnti(&pucch, (uint16_t) rnti&0xffff)) { + mexErrMsgTxt("Error setting C-RNTI\n"); + return; + } + uint32_t n_pucch; + if (mexutils_read_uint32_struct(PUCCHCFG, "ResourceIdx", &n_pucch)) { + mexErrMsgTxt("Field ResourceIdx not found in PUCCHCFG\n"); + return; + } + srslte_pucch_cfg_t pucch_cfg; + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + + if (mexutils_read_uint32_struct(PUCCHCFG, "DeltaShift", &pucch_cfg.delta_pucch_shift)) { + mexErrMsgTxt("Field DeltaShift not found in PUCCHCFG\n"); + return; + } + if (mexutils_read_uint32_struct(PUCCHCFG, "ResourceSize", &pucch_cfg.n_rb_2)) { + mexErrMsgTxt("Field DeltaShift not found in PUCCHCFG\n"); + return; + } + if (mexutils_read_uint32_struct(PUCCHCFG, "CyclicShifts", &pucch_cfg.N_cs)) { + mexErrMsgTxt("Field CyclicShifts not found in PUCCHCFG\n"); + return; + } + bool group_hopping_en = false; + char *hop = mexutils_get_char_struct(UECFG, "Hopping"); + if (hop) { + if (!strcmp(hop, "Group")) { + group_hopping_en = true; + } + mxFree(hop); + } + + pucch.shortened = false; + uint32_t sh = 0; + mexutils_read_uint32_struct(PUCCHCFG, "Shortened", &sh); + if (sh == 1) { + pucch.shortened = true; + } + + uint8_t bits[SRSLTE_PUCCH_MAX_BITS]; + uint8_t pucch2_bits[2]; + float *bits_ptr; + int n = mexutils_read_f(ACK, &bits_ptr); + + srslte_pucch_format_t format; + switch(n) { + case 0: + format = SRSLTE_PUCCH_FORMAT_1; + break; + case 1: + format = SRSLTE_PUCCH_FORMAT_1A; + break; + case 2: + format = SRSLTE_PUCCH_FORMAT_1B; + break; + case 20: + format = SRSLTE_PUCCH_FORMAT_2; + break; + case 21: + format = SRSLTE_PUCCH_FORMAT_2A; + break; + case 22: + format = SRSLTE_PUCCH_FORMAT_2B; + break; + default: + mexErrMsgTxt("Invalid number of bits in parameter ack\n"); + return; + } + if (n > 20) { + n = 20; + } + for (int i=0;i0?1:0; + } + if (format == SRSLTE_PUCCH_FORMAT_2A) { + pucch2_bits[0] = bits_ptr[20]; + } + if (format == SRSLTE_PUCCH_FORMAT_2B) { + pucch2_bits[0] = bits_ptr[20]; + pucch2_bits[1] = bits_ptr[21]; + } + free(bits_ptr); + + cf_t *sf_symbols = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)); + if (!sf_symbols) { + return; + } + bzero(sf_symbols, SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + + srslte_pucch_set_cfg(&pucch, &pucch_cfg, group_hopping_en); + + if (srslte_pucch_encode(&pucch, format, n_pucch, sf_idx, (uint16_t) rnti, bits, sf_symbols)) { + mexErrMsgTxt("Error encoding PUCCH\n"); + return; + } + + if (nlhs >= 1) { + uint32_t n_bits = srslte_pucch_nof_symbols(&pucch_cfg, format, pucch.shortened); + mexutils_write_cf(pucch.z, &plhs[0], n_bits, 1); + } + + if (nlhs >= 2) { + srslte_refsignal_ul_t pucch_dmrs; + if (srslte_refsignal_ul_init(&pucch_dmrs, cell)) { + mexErrMsgTxt("Error initiating PUCCH DMRS\n"); + return; + } + cf_t *dmrs_pucch = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_NRE*3*2); + if (!dmrs_pucch) { + return; + } + bzero(dmrs_pucch, sizeof(cf_t)*SRSLTE_NRE*3*2); + + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + pusch_cfg.group_hopping_en = group_hopping_en; + pusch_cfg.sequence_hopping_en = false; + srslte_refsignal_ul_set_cfg(&pucch_dmrs, &pusch_cfg, &pucch_cfg, NULL); + + if (srslte_refsignal_dmrs_pucch_gen(&pucch_dmrs, format, n_pucch, sf_idx, pucch2_bits, dmrs_pucch)) { + mexErrMsgTxt("Error generating PUCCH DMRS\n"); + return; + } + uint32_t n_rs = 3; + if (format >= SRSLTE_PUCCH_FORMAT_2) { + n_rs = 2; + } + mexutils_write_cf(dmrs_pucch, &plhs[1], 2*n_rs*SRSLTE_NRE, 1); + + if (nlhs >= 3) { + if (srslte_refsignal_dmrs_pucch_put(&pucch_dmrs, format, n_pucch, dmrs_pucch, sf_symbols)) { + mexErrMsgTxt("Error generating PUCCH DMRS\n"); + return; + } + mexutils_write_cf(sf_symbols, &plhs[2], SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp), 1); + } + + srslte_refsignal_ul_free(&pucch_dmrs); + free(dmrs_pucch); + } + + srslte_pucch_free(&pucch); + free(sf_symbols); + + return; +} + diff --git a/lib/src/phy/phch/test/pucch_test.c b/lib/src/phy/phch/test/pucch_test.c new file mode 100644 index 0000000..8b4506c --- /dev/null +++ b/lib/src/phy/phch/test/pucch_test.c @@ -0,0 +1,249 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + 25, // nof_prb + 1, // nof_ports + 1, // cell_id + SRSLTE_CP_NORM, // cyclic prefix + SRSLTE_PHICH_R_1_6, // PHICH resources + SRSLTE_PHICH_NORM // PHICH length +}; + +uint32_t subframe = 0; +bool test_cqi_only = false; + +void usage(char *prog) { + printf("Usage: %s [csNnv]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-s subframe [Default %d]\n", subframe); + printf("\t-n nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-q Test CQI encoding/decoding only [Default %s].\n", test_cqi_only?"yes":"no"); + printf("\t-v [set verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "csNnqv")) != -1) { + switch(opt) { + case 's': + subframe = atoi(argv[optind]); + break; + case 'n': + cell.nof_prb = atoi(argv[optind]); + break; + case 'c': + cell.id = atoi(argv[optind]); + break; + case 'q': + test_cqi_only = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int test_uci_cqi_pucch(void) { + int ret = SRSLTE_SUCCESS; + __attribute__((aligned(256))) uint8_t o_bits[SRSLTE_UCI_MAX_CQI_LEN_PUCCH] = {0}; + __attribute__((aligned(256))) uint8_t e_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) int16_t e_symb[SRSLTE_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) uint8_t d_bits[SRSLTE_UCI_MAX_CQI_LEN_PUCCH] = {0}; + + srslte_uci_cqi_pucch_t uci_cqi_pucch = {0}; + + srslte_uci_cqi_pucch_init(&uci_cqi_pucch); + + for (uint32_t nof_bits = 1; nof_bits <= SRSLTE_UCI_MAX_CQI_LEN_PUCCH-1; nof_bits++) { + for (uint32_t cqi = 0; cqi < (1 < +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUCCHCFG prhs[1] +#define N_BITS prhs[2] +#define INPUT prhs[3] +#define THRESHOLD prhs[4] +#define NOF_INPUTS 4 + +void help() +{ + mexErrMsgTxt + ("[data, symbols, ce]=srslte_pucch(ue, chs, n_bits, input)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + srslte_verbose = SRSLTE_VERBOSE_NONE; + + srslte_cell_t cell; + bzero(&cell, sizeof(srslte_cell_t)); + cell.nof_ports = 1; + cell.cp = SRSLTE_CP_NORM; + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + srslte_pucch_t pucch; + if (srslte_pucch_init(&pucch, cell)) { + mexErrMsgTxt("Error initiating PUSCH\n"); + return; + } + + uint32_t sf_idx = 0; + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &sf_idx)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + uint32_t rnti; + if (mexutils_read_uint32_struct(UECFG, "RNTI", &rnti)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + if (srslte_pucch_set_crnti(&pucch, (uint16_t) rnti&0xffff)) { + mexErrMsgTxt("Error setting C-RNTI\n"); + return; + } + uint32_t n_pucch; + if (mexutils_read_uint32_struct(PUCCHCFG, "ResourceIdx", &n_pucch)) { + mexErrMsgTxt("Field ResourceIdx not found in PUCCHCFG\n"); + return; + } + srslte_pucch_cfg_t pucch_cfg; + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + + if (mexutils_read_uint32_struct(PUCCHCFG, "DeltaShift", &pucch_cfg.delta_pucch_shift)) { + mexErrMsgTxt("Field DeltaShift not found in PUCCHCFG\n"); + return; + } + if (mexutils_read_uint32_struct(PUCCHCFG, "ResourceSize", &pucch_cfg.n_rb_2)) { + mexErrMsgTxt("Field DeltaShift not found in PUCCHCFG\n"); + return; + } + if (mexutils_read_uint32_struct(PUCCHCFG, "CyclicShifts", &pucch_cfg.N_cs)) { + mexErrMsgTxt("Field CyclicShifts not found in PUCCHCFG\n"); + return; + } + bool group_hopping_en = false; + char *hop = mexutils_get_char_struct(UECFG, "Hopping"); + if (hop) { + if (!strcmp(hop, "Group")) { + group_hopping_en = true; + } + mxFree(hop); + } + + pucch.shortened = false; + uint32_t sh = 0; + mexutils_read_uint32_struct(PUCCHCFG, "Shortened", &sh); + if (sh == 1) { + pucch.shortened = true; + } + + float *thresholds; + int th_len = 0; + + if (nrhs > NOF_INPUTS) { + th_len = mexutils_read_f(THRESHOLD, &thresholds); + if (th_len == 2) { + srslte_pucch_set_threshold(&pucch, thresholds[0], thresholds[1]); + } + } + + uint8_t bits[SRSLTE_PUCCH_MAX_BITS]; + int nof_bits = (int) mxGetScalar(N_BITS); + + srslte_pucch_format_t format; + switch(nof_bits) { + case 0: + format = SRSLTE_PUCCH_FORMAT_1; + break; + case 1: + format = SRSLTE_PUCCH_FORMAT_1A; + break; + case 2: + format = SRSLTE_PUCCH_FORMAT_1B; + break; + case 20: + format = SRSLTE_PUCCH_FORMAT_2; + break; + case 21: + format = SRSLTE_PUCCH_FORMAT_2A; + break; + case 22: + format = SRSLTE_PUCCH_FORMAT_2B; + break; + default: + mexErrMsgTxt("Invalid number of bits in parameter ack\n"); + return; + } + if (nof_bits > 20) { + nof_bits = 20; + } + + cf_t *sf_symbols = NULL; + int nof_re = mexutils_read_cf(INPUT, &sf_symbols); + if (nof_re < 0) { + mexErrMsgTxt("Error reading input\n"); + return; + } + cf_t *ce = srslte_vec_malloc(nof_re*sizeof(cf_t)); + if (!ce) { + perror("malloc"); + return; + } + bzero(ce, nof_re*sizeof(cf_t)); + srslte_chest_ul_t chest_ul; + if (srslte_chest_ul_init(&chest_ul, cell)) { + mexErrMsgTxt("Error initiating PUCCH DMRS\n"); + return; + } + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + pusch_cfg.group_hopping_en = group_hopping_en; + pusch_cfg.sequence_hopping_en = false; + srslte_chest_ul_set_cfg(&chest_ul, &pusch_cfg, &pucch_cfg, NULL); + + srslte_pucch_set_cfg(&pucch, &pucch_cfg, group_hopping_en); + + uint8_t pucch2_ack_bits[2] = {0}; + + if (srslte_chest_ul_estimate_pucch(&chest_ul, sf_symbols, ce, format, n_pucch, sf_idx, &pucch2_ack_bits)) { + mexErrMsgTxt("Error estimating PUCCH DMRS\n"); + return; + } + + if (srslte_pucch_decode(&pucch, format, n_pucch, sf_idx, (uint16_t) rnti, sf_symbols, ce, 0, bits)<0) { + mexErrMsgTxt("Error decoding PUCCH\n"); + return; + } + + if (nlhs >= 1) { + if (format != SRSLTE_PUCCH_FORMAT_1) { + mexutils_write_uint8(bits, &plhs[0], nof_bits, 1); + } else { + if (bits[0] == 1) { + mexutils_write_uint8(bits, &plhs[0], 0, 1); + } else { + mexutils_write_uint8(bits, &plhs[0], 0, 0); + } + } + } + + if (nlhs >= 2) { + mexutils_write_cf(pucch.z, &plhs[1], 10, 1); + } + + if (nlhs >= 3) { + mexutils_write_cf(pucch.z_tmp, &plhs[2], 120, 1); + } + + srslte_pucch_free(&pucch); + free(sf_symbols); + + return; +} + diff --git a/lib/src/phy/phch/test/pusch_encode_test_mex.c b/lib/src/phy/phch/test/pusch_encode_test_mex.c new file mode 100644 index 0000000..59c0bbe --- /dev/null +++ b/lib/src/phy/phch/test/pusch_encode_test_mex.c @@ -0,0 +1,251 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define TRBLKIN prhs[2] +#define CQI prhs[3] +#define RI prhs[4] +#define ACK prhs[5] +#define NOF_INPUTS 6 + +void help() +{ + mexErrMsgTxt + ("sym=srslte_pusch_encode(ue, chs, trblkin, cqi, ri, ack)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + srslte_verbose = SRSLTE_VERBOSE_NONE; + + srslte_cell_t cell; + bzero(&cell, sizeof(srslte_cell_t)); + cell.nof_ports = 1; + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + srslte_pusch_t pusch; + if (srslte_pusch_init(&pusch, cell)) { + mexErrMsgTxt("Error initiating PUSCH\n"); + return; + } + uint32_t rnti32=0; + if (mexutils_read_uint32_struct(UECFG, "RNTI", &rnti32)) { + mexErrMsgTxt("Field RNTI not found in pusch config\n"); + return; + } + + uint16_t rnti = (uint16_t) (rnti32 & 0xffff); + srslte_pusch_set_rnti(&pusch, rnti); + + + srslte_pusch_cfg_t cfg; + bzero(&cfg, sizeof(srslte_pusch_cfg_t)); + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &cfg.sf_idx)) { + mexErrMsgTxt("Field NSubframe not found in UE config\n"); + return; + } + + srslte_ra_ul_grant_t grant; + bzero(&grant, sizeof(srslte_ra_ul_grant_t)); + + char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation"); + if (!strcmp(mod_str, "QPSK")) { + grant.mcs.mod = SRSLTE_MOD_QPSK; + } else if (!strcmp(mod_str, "16QAM")) { + grant.mcs.mod = SRSLTE_MOD_16QAM; + } else if (!strcmp(mod_str, "64QAM")) { + grant.mcs.mod = SRSLTE_MOD_64QAM; + } else { + mexErrMsgTxt("Unknown modulation\n"); + return; + } + mxFree(mod_str); + + float *prbset = NULL; + mxArray *p; + p = mxGetField(PUSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found\n"); + return; + } + + uint32_t N_srs = 0; + mexutils_read_uint32_struct(PUSCHCFG, "Shortened", &N_srs); + if (N_srs == 1) { + pusch.shortened = true; + } + + grant.L_prb = mexutils_read_f(p, &prbset); + grant.n_prb[0] = prbset[0]; + grant.n_prb[1] = prbset[0]; + free(prbset); + + uint8_t *trblkin_bits = NULL; + grant.mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin_bits); + + uint8_t *trblkin = srslte_vec_malloc(grant.mcs.tbs/8); + srslte_bit_pack_vector(trblkin_bits, trblkin, grant.mcs.tbs); + free(trblkin_bits); + + grant.M_sc = grant.L_prb*SRSLTE_NRE; + grant.M_sc_init = grant.M_sc; // FIXME: What should M_sc_init be? + grant.Qm = srslte_mod_bits_x_symbol(grant.mcs.mod); + + if (srslte_pusch_cfg(&pusch, &cfg, &grant, NULL, NULL, NULL, cfg.sf_idx, cfg.rv, 0)) { + fprintf(stderr, "Error configuring PUSCH\n"); + exit(-1); + } + + mexPrintf("L_prb: %d, n_prb: %d\n", grant.L_prb, grant.n_prb[0]); + + srslte_softbuffer_tx_t softbuffer; + if (srslte_softbuffer_tx_init(&softbuffer, cell.nof_prb)) { + mexErrMsgTxt("Error initiating soft buffer\n"); + return; + } + + uint32_t nof_re = SRSLTE_NRE*cell.nof_prb*2*SRSLTE_CP_NSYMB(cell.cp); + cf_t *sf_symbols = srslte_vec_malloc(sizeof(cf_t) * nof_re); + if (!sf_symbols) { + mexErrMsgTxt("malloc"); + return; + } + bzero(sf_symbols, sizeof(cf_t) * nof_re); + + + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + uint8_t *tmp; + uci_data.uci_cqi_len = mexutils_read_uint8(CQI, &tmp); + memcpy(&uci_data.uci_cqi, tmp, uci_data.uci_cqi_len); + free(tmp); + uci_data.uci_ri_len = mexutils_read_uint8(RI, &tmp); + if (uci_data.uci_ri_len > 0) { + uci_data.uci_ri = *tmp; + } + free(tmp); + uci_data.uci_ack_len = mexutils_read_uint8(ACK, &tmp); + if (uci_data.uci_ack_len > 0) { + uci_data.uci_ack = *tmp; + } + free(tmp); + + + float beta; + if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &beta)) { + cfg.uci_cfg.I_offset_cqi = 7; + } else { + cfg.uci_cfg.I_offset_cqi = srslte_sch_find_Ioffset_cqi(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &beta)) { + cfg.uci_cfg.I_offset_ri = 2; + } else { + cfg.uci_cfg.I_offset_ri = srslte_sch_find_Ioffset_ri(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &beta)) { + cfg.uci_cfg.I_offset_ack = 0; + } else { + cfg.uci_cfg.I_offset_ack = srslte_sch_find_Ioffset_ack(beta); + } + mexPrintf("TRBL_len: %d, CQI_len: %2d, ACK_len: %d (%d), RI_len: %d (%d)\n", grant.mcs.tbs, + uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ack, uci_data.uci_ri_len, uci_data.uci_ri); + + mexPrintf("I_cqi: %2d, I_ri: %2d, I_ack=%2d\n", cfg.uci_cfg.I_offset_cqi, cfg.uci_cfg.I_offset_ri, cfg.uci_cfg.I_offset_ack); + + mexPrintf("NofRE: %3d, NofBits: %3d, TBS: %3d, N_srs=%d\n", cfg.nbits.nof_re, cfg.nbits.nof_bits, grant.mcs.tbs, N_srs); + int r = srslte_pusch_encode(&pusch, &cfg, &softbuffer, trblkin, uci_data, rnti, sf_symbols); + if (r < 0) { + mexErrMsgTxt("Error encoding PUSCH\n"); + return; + } + if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &cfg.rv)) { + mexErrMsgTxt("Field RV not found in pdsch config\n"); + return; + } + if (cfg.rv > 0) { + r = srslte_pusch_encode(&pusch, &cfg, &softbuffer, trblkin, uci_data, rnti, sf_symbols); + if (r < 0) { + mexErrMsgTxt("Error encoding PUSCH\n"); + return; + } + } + + + if (nlhs >= 1) { + + cf_t *scfdma = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_PRB(cell.nof_prb)); + bzero(scfdma, sizeof(cf_t) * SRSLTE_SF_LEN_PRB(cell.nof_prb)); + srslte_ofdm_t fft; + srslte_ofdm_tx_init(&fft, SRSLTE_CP_NORM, cell.nof_prb); + srslte_ofdm_set_normalize(&fft, true); + srslte_ofdm_set_freq_shift(&fft, 0.5); + + srslte_ofdm_tx_sf(&fft, sf_symbols, scfdma); + // Matlab toolbox expects further normalization + srslte_vec_sc_prod_cfc(scfdma, 1.0/sqrtf(srslte_symbol_sz(cell.nof_prb)), scfdma, SRSLTE_SF_LEN_PRB(cell.nof_prb)); + + mexutils_write_cf(scfdma, &plhs[0], SRSLTE_SF_LEN_PRB(cell.nof_prb), 1); + + free(scfdma); + + } + if (nlhs >= 2) { + mexutils_write_cf(sf_symbols, &plhs[1], nof_re, 1); + } + if (nlhs >= 3) { + mexutils_write_cf(pusch.z, &plhs[2], cfg.nbits.nof_re, 1); + } + if (nlhs >= 4) { + mexutils_write_uint8(pusch.q, &plhs[3], cfg.nbits.nof_bits, 1); + } + srslte_pusch_free(&pusch); + srslte_softbuffer_tx_free(&softbuffer); + free(trblkin); + free(sf_symbols); + + return; +} + diff --git a/lib/src/phy/phch/test/pusch_test.c b/lib/src/phy/phch/test/pusch_test.c new file mode 100644 index 0000000..b2b7a26 --- /dev/null +++ b/lib/src/phy/phch/test/pusch_test.c @@ -0,0 +1,472 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" + +static srslte_cell_t cell = { + .nof_prb = 6, // nof_prb + .nof_ports = 1, // nof_ports + .id = 0, // cell_id + .cp = SRSLTE_CP_NORM, // cyclic prefix + .phich_length = SRSLTE_PHICH_NORM, // PHICH length + .phich_resources = SRSLTE_PHICH_R_1_6 // PHICH resources +}; + +static srslte_uci_cfg_t uci_cfg = { + .I_offset_cqi = 6, + .I_offset_ri = 2, + .I_offset_ack = 9, +}; + +static srslte_uci_data_t uci_data_tx = { + .uci_cqi = {0}, + .uci_cqi_len = 0, + .uci_ri = 0, + .uci_ri_len = 0, + .uci_ack = 0, + .uci_ack_2 = 0, + .uci_ack_len = 0, + .ri_periodic_report = false, + .scheduling_request = false, + .channel_selection = false, + .cqi_ack = false +}; + +uint32_t cfi = 2; +uint32_t tbs = 0; +uint32_t subframe = 10; +srslte_mod_t modulation = SRSLTE_MOD_QPSK; +uint32_t rv_idx = 0; +uint32_t L_prb = 2; +uint32_t n_prb = 0; +int freq_hop = -1; +int riv = -1; +uint32_t mcs_idx = 0; +srslte_cqi_value_t cqi_value; +bool enable_64_qam = false; + +void usage(char *prog) { + printf("Usage: %s [csrnfvmtLNF] \n", prog); + printf("\n\tCell specific parameters:\n"); + printf("\t\t-n number of PRB [Default %d]\n", cell.nof_prb); + printf("\t\t-c cell id [Default %d]\n", cell.id); + + printf("\n\tGrant parameters:\n"); + printf("\t\t-m MCS index (0-28) [Default %d]\n", mcs_idx); + printf("\t\t-L L_prb [Default %d]\n", L_prb); + printf("\t\t-N n_prb [Default %d]\n", n_prb); + printf("\t\t-F frequency hopping [Default %d]\n", freq_hop); + printf("\t\t-R RIV [Default %d]\n", riv); + printf("\t\t-r rv_idx (0-3) [Default %d]\n", rv_idx); + printf("\t\t-f cfi [Default %d]\n", cfi); + + printf("\n\tCQI/RI/ACK Reporting indexes parameters:\n"); + printf("\t\t-p I_offset_cqi (0-15) [Default %d]\n", uci_cfg.I_offset_cqi); + printf("\t\t-p I_offset_ri (0-15) [Default %d]\n", uci_cfg.I_offset_ri); + printf("\t\t-p I_offset_ack (0-15) [Default %d]\n", uci_cfg.I_offset_ack); + + printf("\n\tCQI/RI/ACK Reporting contents:\n"); + printf("\t\t-p uci_cqi (none, wideband) [Default none]\n"); + printf("\t\t-p uci_ri (0-1) (zeros, ones, random) [Default none]\n"); + printf("\t\t-p uci_ack [Default none]\n"); + printf("\t\t-p uci_ack_2 [Default none]\n"); + + printf("\n\tOther parameters:\n"); + printf("\t\t-p enable_64qam [Default %s]\n", enable_64_qam ? "enabled":"disabled"); + printf("\t\t-s number of subframes [Default %d]\n", subframe); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_extensive_param(char *param, char *arg) { + int ext_code = SRSLTE_SUCCESS; + if (!strcmp(param, "I_offset_cqi")) { + uci_cfg.I_offset_cqi = (uint32_t) strtol(arg, NULL, 10); + if (uci_cfg.I_offset_cqi > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "I_offset_ri")) { + uci_cfg.I_offset_ri = (uint32_t) strtol(arg, NULL, 10); + if (uci_cfg.I_offset_ri > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "I_offset_ack")) { + uci_cfg.I_offset_ack = (uint32_t) strtol(arg, NULL, 10); + if (uci_cfg.I_offset_ack > 15) { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "uci_cqi")) { + if (!strcmp(arg, "wideband")) { + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + cqi_value.wideband.wideband_cqi = (uint8_t) (random() & 0x0f); + uci_data_tx.uci_cqi_len = (uint32_t) srslte_cqi_value_pack(&cqi_value, uci_data_tx.uci_cqi); + } else if (!strcmp(arg, "none")) { + uci_data_tx.uci_cqi_len = 0; + } else { + ext_code = SRSLTE_ERROR; + } + } else if (!strcmp(param, "uci_ri")) { + uci_data_tx.uci_ri = (uint8_t) strtol(arg, NULL, 10); + if (uci_data_tx.uci_ri > 1) { + ext_code = SRSLTE_ERROR; + } else { + uci_data_tx.uci_ri_len = 1; + } + } else if (!strcmp(param, "uci_ack")) { + uci_data_tx.uci_ack_len++; + if (uci_data_tx.uci_ack_len > 2) { + uci_data_tx.uci_ack_len = 2; + } + } else if (!strcmp(param, "uci_ack_2")) { + uci_data_tx.uci_ack_2 = (uint8_t) strtol(arg, NULL, 10); + if (uci_data_tx.uci_ack_2 > 1) { + ext_code = SRSLTE_ERROR; + } else { + uci_data_tx.uci_ack_len++; + if (uci_data_tx.uci_ack_len > 2) { + uci_data_tx.uci_ack_len = 2; + } + } + } else if (!strcmp(param, "enable_64qam")) { + enable_64_qam ^= true; + } else { + ext_code = SRSLTE_ERROR; + } + + if (ext_code) { + fprintf(stderr, "Error parsing parameter '%s' and argument '%s'\n", param, arg); + exit(ext_code); + } +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "msLNRFrncpvf")) != -1) { + switch (opt) { + case 'm': + mcs_idx = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 's': + subframe = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'f': + cfi = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'L': + L_prb = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'N': + n_prb = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'R': + riv = (int) strtol(argv[optind], NULL, 10); + break; + case 'F': + freq_hop = (int) strtol(argv[optind], NULL, 10); + break; + case 'r': + rv_idx = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'n': + cell.nof_prb = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'c': + cell.id = (uint32_t) strtol(argv[optind], NULL, 10); + break; + case 'p': + parse_extensive_param(argv[optind], argv[optind + 1]); + optind++; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + srslte_pusch_t pusch_tx; + srslte_pusch_t pusch_rx; + uint8_t *data = NULL; + uint8_t *data_rx = NULL; + cf_t *sf_symbols = NULL; + cf_t *ce = NULL; + int ret = -1; + struct timeval t[3]; + srslte_pusch_cfg_t cfg; + srslte_softbuffer_tx_t softbuffer_tx; + srslte_softbuffer_rx_t softbuffer_rx; + + parse_args(argc, argv); + + bzero(&cfg, sizeof(srslte_pusch_cfg_t)); + + srslte_dft_load(); + + srslte_ra_ul_dci_t dci; + dci.freq_hop_fl = freq_hop; + if (riv < 0) { + dci.type2_alloc.L_crb = L_prb; + dci.type2_alloc.RB_start = n_prb; + } else { + dci.type2_alloc.riv = (uint32_t) riv; + } + dci.mcs_idx = mcs_idx; + + srslte_ra_ul_grant_t grant; + if (srslte_ra_ul_dci_to_grant(&dci, cell.nof_prb, 0, &grant)) { + fprintf(stderr, "Error computing resource allocation\n"); + return ret; + } + + srslte_pusch_hopping_cfg_t ul_hopping; + ul_hopping.n_sb = 1; + ul_hopping.hopping_offset = 0; + ul_hopping.hop_mode = 1; + + if (srslte_pusch_init_ue(&pusch_tx, cell.nof_prb)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto quit; + } + if (srslte_pusch_set_cell(&pusch_tx, cell)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto quit; + } + if (srslte_pusch_init_enb(&pusch_rx, cell.nof_prb)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto quit; + } + if (srslte_pusch_set_cell(&pusch_rx, cell)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto quit; + } + + uint16_t rnti = 1234; + srslte_pusch_set_rnti(&pusch_tx, rnti); + srslte_pusch_set_rnti(&pusch_rx, rnti); + + srslte_uci_data_t uci_data_rx; + memcpy(&uci_data_rx, &uci_data_tx, sizeof(srslte_uci_data_t)); + + uint32_t nof_re = SRSLTE_NRE * cell.nof_prb * 2 * SRSLTE_CP_NSYMB(cell.cp); + sf_symbols = srslte_vec_malloc(sizeof(cf_t) * nof_re); + if (!sf_symbols) { + perror("malloc"); + exit(-1); + } + + data = srslte_vec_malloc(sizeof(uint8_t) * 150000); + if (!data) { + perror("malloc"); + exit(-1); + } + + data_rx = srslte_vec_malloc(sizeof(uint8_t) * 150000); + if (!data_rx) { + perror("malloc"); + exit(-1); + } + + if (srslte_softbuffer_tx_init(&softbuffer_tx, 100)) { + fprintf(stderr, "Error initiating soft buffer\n"); + goto quit; + } + + if (srslte_softbuffer_rx_init(&softbuffer_rx, 100)) { + fprintf(stderr, "Error initiating soft buffer\n"); + goto quit; + } + + ce = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)); + if (!ce) { + perror("srslte_vec_malloc"); + goto quit; + } + for (int j = 0; j < SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp); j++) { + ce[j] = 1; + } + + if (!enable_64_qam && grant.mcs.mod == SRSLTE_MOD_64QAM) { + grant.mcs.mod = SRSLTE_MOD_16QAM; + grant.Qm = 4; + } + + for (int n = 0; n < subframe; n++) { + ret = SRSLTE_SUCCESS; + + /* Configure PUSCH */ + if (srslte_pusch_cfg(&pusch_tx, &cfg, &grant, &uci_cfg, &ul_hopping, NULL, (uint32_t) n % 10, 0, 0)) { + fprintf(stderr, "Error configuring PDSCH\n"); + exit(-1); + } + + if (srslte_pusch_cfg(&pusch_rx, &cfg, &grant, &uci_cfg, &ul_hopping, NULL, (uint32_t) n % 10, 0, 0)) { + fprintf(stderr, "Error configuring PDSCH\n"); + exit(-1); + } + + srslte_softbuffer_tx_reset(&softbuffer_tx); + srslte_softbuffer_rx_reset(&softbuffer_rx); + + for (uint32_t i = 0; i < cfg.grant.mcs.tbs / 8; i++) { + data[i] = (uint8_t) (random() & 0xff); + } + + for (uint32_t i = 0; i < uci_data_tx.uci_cqi_len; i++) { + uci_data_tx.uci_cqi[i] = (uint8_t) (random() & 0x1); + } + + if (uci_data_tx.uci_ack_len > 0) { + uci_data_tx.uci_ack = (uint8_t) (random() & 0x1); + } + + if (uci_data_tx.uci_ack_len > 1) { + uci_data_tx.uci_ack_2 = (uint8_t) (random() & 0x1); + } + + if (srslte_pusch_encode(&pusch_tx, &cfg, &softbuffer_tx, data, uci_data_tx, rnti, sf_symbols)) { + fprintf(stderr, "Error encoding TB\n"); + exit(-1); + } + if (rv_idx > 0) { + cfg.rv = rv_idx; + if (srslte_pusch_encode(&pusch_tx, &cfg, &softbuffer_tx, data, uci_data_tx, rnti, sf_symbols)) { + fprintf(stderr, "Error encoding TB\n"); + exit(-1); + } + } + + gettimeofday(&t[1], NULL); + int r = srslte_pusch_decode(&pusch_rx, + &cfg, + &softbuffer_rx, + sf_symbols, + ce, + 0, + rnti, + data_rx, + (uci_data_tx.uci_cqi_len) ? &cqi_value : NULL, + &uci_data_rx); + gettimeofday(&t[2], NULL); + if (r) { + printf("Error returned while decoding\n"); + ret = SRSLTE_ERROR; + } + + if (memcmp(data_rx, data, (size_t) cfg.grant.mcs.tbs / 8) != 0) { + printf("Unmatched data detected\n"); + ret = SRSLTE_ERROR; + } else { + INFO("Rx Data is Ok\n"); + } + + if (uci_data_tx.uci_ack_len) { + if (uci_data_tx.uci_ack != uci_data_rx.uci_ack) { + printf("UCI ACK bit error: %d != %d\n", uci_data_tx.uci_ack, uci_data_rx.uci_ack); + ret = SRSLTE_ERROR; + } else { + INFO("Rx ACK is Ok\n"); + } + } + + if (uci_data_tx.uci_ack_len > 1) { + if (uci_data_tx.uci_ack_2 != uci_data_rx.uci_ack_2) { + printf("UCI ACK 2 bit error: %d != %d\n", uci_data_tx.uci_ack_2, uci_data_rx.uci_ack_2); + ret = SRSLTE_ERROR; + } else { + INFO("Rx ACK2 is Ok\n"); + } + } + + if (uci_data_tx.uci_ri_len) { + if (uci_data_tx.uci_ri != uci_data_rx.uci_ri) { + printf("UCI RI bit error: %d != %d\n", uci_data_tx.uci_ri, uci_data_rx.uci_ri); + ret = SRSLTE_ERROR; + } else { + INFO("Rx RI is Ok\n"); + } + } + + if (uci_data_tx.uci_cqi_len) { + if (memcmp(uci_data_tx.uci_cqi, uci_data_rx.uci_cqi, uci_data_tx.uci_cqi_len) != 0) { + printf("CQI Decode failed at subframe %d\n", n); + printf("cqi_tx="); + srslte_vec_fprint_b(stdout, uci_data_tx.uci_cqi, uci_data_tx.uci_cqi_len); + printf("cqi_rx="); + srslte_vec_fprint_b(stdout, uci_data_rx.uci_cqi, uci_data_rx.uci_cqi_len); + ret = SRSLTE_ERROR; + } else { + INFO("Rx CQI is Ok\n"); + } + } + + if (ret) { + goto quit; + } + + get_time_interval(t); + printf("DECODED OK in %d:%d (TBS: %d bits, TX: %.2f Mbps, Processing: %.2f Mbps)\n", (int) t[0].tv_sec, + (int) t[0].tv_usec, + cfg.grant.mcs.tbs, + (float) cfg.grant.mcs.tbs / 1000, + (float) cfg.grant.mcs.tbs / t[0].tv_usec); + + } + + quit: + srslte_pusch_free(&pusch_tx); + srslte_pusch_free(&pusch_rx); + srslte_softbuffer_tx_free(&softbuffer_tx); + srslte_softbuffer_rx_free(&softbuffer_rx); + + if (sf_symbols) { + free(sf_symbols); + } + if (data) { + free(data); + } + if (data_rx) { + free(data_rx); + } + if (ce) { + free(ce); + } + if (ret) { + printf("Error\n"); + } else { + printf("Ok\n"); + } + srslte_dft_exit(); + exit(ret); +} diff --git a/lib/src/phy/phch/test/pusch_test_mex.c b/lib/src/phy/phch/test/pusch_test_mex.c new file mode 100644 index 0000000..7b29075 --- /dev/null +++ b/lib/src/phy/phch/test/pusch_test_mex.c @@ -0,0 +1,309 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define TBS prhs[2] +#define INPUT prhs[3] +#define NOF_INPUTS 4 + +void help() +{ + mexErrMsgTxt + ("[decoded_ok, cqi_data, ri_data, ack_data] = srslte_pusch(ueConfig, puschConfig, trblklen, rxWaveform)\n\n"); +} + +extern int indices[2048]; + +int rv_seq[4] = {0, 2, 3, 1}; + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + srslte_ofdm_t ofdm_rx; + srslte_pusch_t pusch; + srslte_chest_ul_t chest; + cf_t *input_fft; + srslte_softbuffer_rx_t softbuffer; + uint32_t rnti32; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + srslte_verbose = SRSLTE_VERBOSE_INFO; + + srslte_cell_t cell; + bzero(&cell, sizeof(srslte_cell_t)); + cell.nof_ports = 1; + if (mexutils_read_uint32_struct(UECFG, "NCellID", &cell.id)) { + mexErrMsgTxt("Field NCellID not found in UE config\n"); + return; + } + if (mexutils_read_uint32_struct(UECFG, "NULRB", &cell.nof_prb)) { + mexErrMsgTxt("Field NULRB not found in UE config\n"); + return; + } + + srslte_pusch_cfg_t cfg; + bzero(&cfg, sizeof(srslte_pusch_cfg_t)); + if (mexutils_read_uint32_struct(UECFG, "RNTI", &rnti32)) { + mexErrMsgTxt("Field RNTI not found in pdsch config\n"); + return; + } + + if (mexutils_read_uint32_struct(UECFG, "NSubframe", &cfg.sf_idx)) { + help(); + return; + } + + if (srslte_ofdm_rx_init(&ofdm_rx, cell.cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return; + } + srslte_ofdm_set_normalize(&ofdm_rx, true); + srslte_ofdm_set_freq_shift(&ofdm_rx, 0.5); + + if (srslte_pusch_init(&pusch, cell)) { + mexErrMsgTxt("Error initiating PDSCH\n"); + return; + } + uint16_t rnti = (uint16_t) (rnti32 & 0xffff); + srslte_pusch_set_rnti(&pusch, rnti); + + if (srslte_softbuffer_rx_init(&softbuffer, cell.nof_prb)) { + mexErrMsgTxt("Error initiating soft buffer\n"); + return; + } + + if (srslte_chest_ul_init(&chest, cell)) { + mexErrMsgTxt("Error initializing equalizer\n"); + return; + } + + srslte_ra_ul_grant_t grant; + bzero(&grant, sizeof(srslte_ra_ul_grant_t)); + + char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation"); + if (!strcmp(mod_str, "QPSK")) { + grant.mcs.mod = SRSLTE_MOD_QPSK; + } else if (!strcmp(mod_str, "16QAM")) { + grant.mcs.mod = SRSLTE_MOD_16QAM; + } else if (!strcmp(mod_str, "64QAM")) { + grant.mcs.mod = SRSLTE_MOD_64QAM; + } else { + mexErrMsgTxt("Unknown modulation\n"); + return; + } + mxFree(mod_str); + + grant.mcs.tbs = mxGetScalar(TBS); + if (grant.mcs.tbs == 0) { + mexErrMsgTxt("Error trblklen is zero\n"); + return; + } + + uint32_t N_srs = 0; + mexutils_read_uint32_struct(PUSCHCFG, "Shortened", &N_srs); + if (N_srs == 1) { + pusch.shortened = true; + } + + float *prbset = NULL; + mxArray *p; + p = mxGetField(PUSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found\n"); + return; + } + + grant.L_prb = mexutils_read_f(p, &prbset); + grant.n_prb[0] = prbset[0]; + grant.n_prb[1] = prbset[0]; + free(prbset); + + grant.M_sc = grant.L_prb*SRSLTE_NRE; + grant.M_sc_init = grant.M_sc; // FIXME: What should M_sc_init be? + grant.Qm = srslte_mod_bits_x_symbol(grant.mcs.mod); + + if (srslte_cbsegm(&cfg.cb_segm, grant.mcs.tbs)) { + mexErrMsgTxt("Error computing CB segmentation\n"); + return; + } + + if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &cfg.rv)) { + mexErrMsgTxt("Field RV not found in pdsch config\n"); + return; + } + + uint32_t max_iterations = 5; + mexutils_read_uint32_struct(PUSCHCFG, "NTurboDecIts", &max_iterations); + + /* Configure rest of pdsch_cfg parameters */ + if (srslte_pusch_cfg(&pusch, &cfg, &grant, NULL, NULL, NULL, cfg.sf_idx, cfg.rv, 0)) { + fprintf(stderr, "Error configuring PDSCH\n"); + exit(-1); + } + + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + + mexutils_read_uint32_struct(PUSCHCFG, "OCQI", &uci_data.uci_cqi_len); + mexutils_read_uint32_struct(PUSCHCFG, "ORI", &uci_data.uci_ri_len); + mexutils_read_uint32_struct(PUSCHCFG, "OACK", &uci_data.uci_ack_len); + + float beta; + if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &beta)) { + cfg.uci_cfg.I_offset_cqi = 7; + } else { + cfg.uci_cfg.I_offset_cqi = srslte_sch_find_Ioffset_cqi(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &beta)) { + cfg.uci_cfg.I_offset_ri = 2; + } else { + cfg.uci_cfg.I_offset_ri = srslte_sch_find_Ioffset_ri(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &beta)) { + cfg.uci_cfg.I_offset_ack = 0; + } else { + cfg.uci_cfg.I_offset_ack = srslte_sch_find_Ioffset_ack(beta); + } + mexPrintf("TRBL_len: %d, CQI_len: %2d, ACK_len: %d (%d), RI_len: %d (%d)\n", grant.mcs.tbs, + uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ack, uci_data.uci_ri_len, uci_data.uci_ri); + + mexPrintf("I_cqi: %2d, I_ri: %2d, I_ack=%2d\n", cfg.uci_cfg.I_offset_cqi, cfg.uci_cfg.I_offset_ri, cfg.uci_cfg.I_offset_ack); + + + mexPrintf("L_prb: %d, n_prb: %d/%d, TBS=%d\n", grant.L_prb, grant.n_prb[0], grant.n_prb[1], grant.mcs.tbs); + + /** Allocate input buffers */ + int nof_retx=1; + if (mexutils_isCell(INPUT)) { + nof_retx = mexutils_getLength(INPUT); + } + + cf_t *ce = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t));; + + uint8_t *data_bytes = srslte_vec_malloc(sizeof(uint8_t) * grant.mcs.tbs/8); + if (!data_bytes) { + return; + } + srslte_sch_set_max_noi(&pusch.ul_sch, max_iterations); + + input_fft = NULL; + int r=-1; + for (int rvIdx=0;rvIdx 1) { + cfg.rv = rv_seq[rvIdx%4]; + } + } + + // Read input signal + cf_t *input_signal = NULL; + int insignal_len = mexutils_read_cf(tmp, &input_signal); + if (insignal_len < 0) { + mexErrMsgTxt("Error reading input signal\n"); + return; + } + if (insignal_len == SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp)) { + input_fft = input_signal; + mexPrintf("Input is after fft\n"); + } else { + input_fft = srslte_vec_malloc(SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp) * sizeof(cf_t)); + srslte_ofdm_rx_sf(&ofdm_rx, input_signal, input_fft); + mexPrintf("Input is before fft\n"); + free(input_signal); + } + + if (nrhs > NOF_INPUTS) { + cf_t *cearray = NULL; + mexutils_read_cf(prhs[NOF_INPUTS], &cearray); + cf_t *cearray_ptr = cearray; + for (int j=0;j NOF_INPUTS + 1) { + noise_power = mxGetScalar(prhs[NOF_INPUTS+1]); + } else if (nrhs > NOF_INPUTS) { + noise_power = 0; + } else { + noise_power = srslte_chest_ul_get_noise_estimate(&chest); + } + + r = srslte_pusch_decode(&pusch, &cfg, &softbuffer, input_fft, ce, noise_power, rnti, data_bytes, &uci_data); + } + + uint8_t *data = malloc(grant.mcs.tbs); + srslte_bit_unpack_vector(data_bytes, data, grant.mcs.tbs); + + if (nlhs >= 1) { + plhs[0] = mxCreateLogicalScalar(r == 0); + } + if (nlhs >= 2) { + mexutils_write_uint8(uci_data.uci_cqi, &plhs[1], uci_data.uci_cqi_len, 1); + } + if (nlhs >= 3 && uci_data.uci_ri_len <= 1) { + mexutils_write_uint8(&uci_data.uci_ri, &plhs[2], uci_data.uci_ri_len, 1); + } + if (nlhs >= 4 && uci_data.uci_ack_len <= 1) { + mexutils_write_uint8(&uci_data.uci_ack, &plhs[3], uci_data.uci_ack_len, 1); + } + + srslte_softbuffer_rx_free(&softbuffer); + srslte_chest_ul_free(&chest); + srslte_pusch_free(&pusch); + srslte_ofdm_rx_free(&ofdm_rx); + + free(ce); + free(data_bytes); + free(data); + if (input_fft) { + free(input_fft); + } + + return; +} + diff --git a/lib/src/phy/phch/test/signal.1.92M.amar.dat b/lib/src/phy/phch/test/signal.1.92M.amar.dat new file mode 100644 index 0000000..314a2e1 Binary files /dev/null and b/lib/src/phy/phch/test/signal.1.92M.amar.dat differ diff --git a/lib/src/phy/phch/test/signal.1.92M.dat b/lib/src/phy/phch/test/signal.1.92M.dat new file mode 100644 index 0000000..26d2caa Binary files /dev/null and b/lib/src/phy/phch/test/signal.1.92M.dat differ diff --git a/lib/src/phy/phch/test/signal.10M.dat b/lib/src/phy/phch/test/signal.10M.dat new file mode 100644 index 0000000..27a3195 Binary files /dev/null and b/lib/src/phy/phch/test/signal.10M.dat differ diff --git a/lib/src/phy/phch/test/ulsch_encode_test_mex.c b/lib/src/phy/phch/test/ulsch_encode_test_mex.c new file mode 100644 index 0000000..a744964 --- /dev/null +++ b/lib/src/phy/phch/test/ulsch_encode_test_mex.c @@ -0,0 +1,220 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +#define UECFG prhs[0] +#define PUSCHCFG prhs[1] +#define TRBLKIN prhs[2] +#define CQI prhs[3] +#define RI prhs[4] +#define ACK prhs[5] +#define NOF_INPUTS 6 + +void help() +{ + mexErrMsgTxt + ("[cwout] = srslte_pusch_encode(ue, chs, trblkin, cqi, ri, ack)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + srslte_sch_t ulsch; + + srslte_pusch_cfg_t cfg; + srslte_softbuffer_tx_t softbuffer; + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + uint32_t rv; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + bzero(&cfg, sizeof(srslte_pusch_cfg_t)); + + if (srslte_sch_init(&ulsch)) { + mexErrMsgTxt("Error initiating ULSCH\n"); + return; + } + srslte_cell_t cell; + cell.nof_prb = 100; + cell.id=1; + cell.cp=SRSLTE_CP_NORM; + + srslte_verbose = SRSLTE_VERBOSE_NONE; + + if (srslte_softbuffer_tx_init(&softbuffer, cell.nof_prb)) { + mexErrMsgTxt("Error initiating HARQ\n"); + return; + } + + uint8_t *trblkin_bits = NULL; + cfg.grant.mcs.tbs = mexutils_read_uint8(TRBLKIN, &trblkin_bits); + + uint8_t *trblkin = srslte_vec_malloc(cfg.grant.mcs.tbs/8); + srslte_bit_pack_vector(trblkin_bits, trblkin, cfg.grant.mcs.tbs); + free(trblkin_bits); + + + uint8_t *tmp; + uci_data.uci_cqi_len = mexutils_read_uint8(CQI, &tmp); + memcpy(uci_data.uci_cqi, tmp, uci_data.uci_cqi_len); + free(tmp); + uci_data.uci_ri_len = mexutils_read_uint8(RI, &tmp); + if (uci_data.uci_ri_len > 0) { + uci_data.uci_ri = *tmp; + } + free(tmp); + uci_data.uci_ack_len = mexutils_read_uint8(ACK, &tmp); + if (uci_data.uci_ack_len > 0) { + uci_data.uci_ack = *tmp; + } + free(tmp); + + mexPrintf("TRBL_len: %d, CQI_len: %d, ACK_len: %d, RI_len: %d\n", cfg.grant.mcs.tbs, + uci_data.uci_cqi_len, uci_data.uci_ack_len, uci_data.uci_ri_len); + + if (mexutils_read_uint32_struct(PUSCHCFG, "RV", &rv)) { + mexErrMsgTxt("Field RV not found in pdsch config\n"); + return; + } + + float beta; + if (mexutils_read_float_struct(PUSCHCFG, "BetaCQI", &beta)) { + cfg.uci_cfg.I_offset_cqi = 7; + } else { + cfg.uci_cfg.I_offset_cqi = srslte_sch_find_Ioffset_cqi(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaRI", &beta)) { + cfg.uci_cfg.I_offset_ri = 2; + } else { + cfg.uci_cfg.I_offset_ri = srslte_sch_find_Ioffset_ri(beta); + } + if (mexutils_read_float_struct(PUSCHCFG, "BetaACK", &beta)) { + cfg.uci_cfg.I_offset_ack = 0; + } else { + cfg.uci_cfg.I_offset_ack = srslte_sch_find_Ioffset_ack(beta); + } + + char *mod_str = mexutils_get_char_struct(PUSCHCFG, "Modulation"); + + if (!strcmp(mod_str, "QPSK")) { + cfg.grant.mcs.mod = SRSLTE_MOD_QPSK; + } else if (!strcmp(mod_str, "16QAM")) { + cfg.grant.mcs.mod = SRSLTE_MOD_16QAM; + } else if (!strcmp(mod_str, "64QAM")) { + cfg.grant.mcs.mod = SRSLTE_MOD_64QAM; + } else { + mexErrMsgTxt("Unknown modulation\n"); + return; + } + + mxFree(mod_str); + + float *prbset; + mxArray *p; + p = mxGetField(PUSCHCFG, 0, "PRBSet"); + if (!p) { + mexErrMsgTxt("Error field PRBSet not found\n"); + return; + } + + uint32_t N_srs = 0; + mexutils_read_uint32_struct(PUSCHCFG, "Shortened", &N_srs); + + + cfg.grant.L_prb = mexutils_read_f(p, &prbset); + cfg.grant.n_prb[0] = prbset[0]; + cfg.grant.n_prb[1] = prbset[0]; + free(prbset); + cfg.grant.L_prb = mexutils_read_f(p, &prbset); + cfg.grant.n_prb[0] = prbset[0]; + cfg.grant.n_prb[1] = prbset[0]; + cfg.nbits.lstart = 0; + cfg.nbits.nof_symb = 2*(SRSLTE_CP_NSYMB(cell.cp)-1) - N_srs; + cfg.grant.M_sc = cfg.grant.L_prb*SRSLTE_NRE; + cfg.grant.M_sc_init = cfg.grant.M_sc; // FIXME: What should M_sc_init be? + cfg.nbits.nof_re = cfg.nbits.nof_symb*cfg.grant.M_sc; + cfg.grant.Qm = srslte_mod_bits_x_symbol(cfg.grant.mcs.mod); + cfg.nbits.nof_bits = cfg.nbits.nof_re * cfg.grant.Qm; + + mexPrintf("Q_m: %d, NPRB: %d, RV: %d, Nsrs=%d\n", srslte_mod_bits_x_symbol(cfg.grant.mcs.mod), cfg.grant.L_prb, cfg.rv, N_srs); + + mexPrintf("I_cqi: %d, I_ri: %d, I_ack=%d\n", cfg.uci_cfg.I_offset_cqi, cfg.uci_cfg.I_offset_ri, cfg.uci_cfg.I_offset_ack); + + if (srslte_cbsegm(&cfg.cb_segm, cfg.grant.mcs.tbs)) { + mexErrMsgTxt("Error configuring HARQ process\n"); + return; + } + + uint8_t *q_bits = srslte_vec_malloc(cfg.nbits.nof_bits * sizeof(uint8_t)/8); + if (!q_bits) { + return; + } + uint8_t *q_bits_unpacked = srslte_vec_malloc(cfg.nbits.nof_bits * sizeof(uint8_t)); + if (!q_bits_unpacked) { + return; + } + uint8_t *g_bits = srslte_vec_malloc(cfg.nbits.nof_bits * sizeof(uint8_t)/8); + if (!g_bits) { + return; + } + + if (srslte_ulsch_uci_encode(&ulsch, &cfg, &softbuffer, trblkin, uci_data, g_bits, q_bits)) + { + mexErrMsgTxt("Error encoding TB\n"); + return; + } + if (rv > 0) { + cfg.rv = rv; + if (srslte_ulsch_uci_encode(&ulsch, &cfg, &softbuffer, trblkin, uci_data, g_bits, q_bits)) { + mexErrMsgTxt("Error encoding TB\n"); + return; + } + } + + srslte_bit_unpack_vector(q_bits, q_bits_unpacked, cfg.nbits.nof_bits); + + if (nlhs >= 1) { + mexutils_write_uint8(q_bits_unpacked, &plhs[0], cfg.nbits.nof_bits, 1); + } + + srslte_sch_free(&ulsch); + srslte_softbuffer_tx_free(&softbuffer); + + free(trblkin); + free(g_bits); + free(q_bits_unpacked); + free(q_bits); + + + return; +} + diff --git a/lib/src/phy/phch/uci.c b/lib/src/phy/phch/uci.c new file mode 100644 index 0000000..9d783a9 --- /dev/null +++ b/lib/src/phy/phch/uci.c @@ -0,0 +1,790 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/phch/uci.h" +#include "srslte/phy/fec/cbsegm.h" +#include "srslte/phy/fec/convcoder.h" +#include "srslte/phy/fec/crc.h" +#include "srslte/phy/fec/rm_conv.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/debug.h" + + +/* Table 5.2.2.6.4-1: Basis sequence for (32, O) code */ +static uint8_t M_basis_seq[32][11]={ + {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + {1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1 }, + {1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 }, + {1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1 }, + {1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }, + {1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1 }, + {1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 }, + {1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1 }, + {1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1 }, + {1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1 }, + {1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1 }, + {1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1 }, + {1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, + {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 }, + {1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1 }, + {1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1 }, + {1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0 }, + {1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0 }, + {1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, + {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, + {1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }, + {1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1 }, + {1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1 }, + {1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1 }, + {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0 }, + {1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1 }, + {1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0 }, + {1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0 }, + {1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0 }, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + }; + + +static uint8_t M_basis_seq_pucch[20][13]={ + {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + {1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0}, + {1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1}, + {1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1}, + {1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1}, + {1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1}, + {1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1}, + {1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1}, + {1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1}, + {1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1}, + {1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1}, + {1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1}, + {1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1}, + {1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1}, + {1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1}, + {1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1}, + {1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1}, + {1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, + }; + +void srslte_uci_cqi_pucch_init(srslte_uci_cqi_pucch_t *q) { + uint8_t word[16]; + + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; + q->cqi_table = srslte_vec_malloc(nwords * sizeof(int8_t *)); + q->cqi_table_s = srslte_vec_malloc(nwords * sizeof(int16_t *)); + + for (uint32_t w = 0; w < nwords; w++) { + q->cqi_table[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B * sizeof(int8_t)); + q->cqi_table_s[w] = srslte_vec_malloc(SRSLTE_UCI_CQI_CODED_PUCCH_B * sizeof(int16_t)); + uint8_t *ptr = word; + srslte_bit_unpack(w, &ptr, SRSLTE_UCI_MAX_CQI_LEN_PUCCH); + srslte_uci_encode_cqi_pucch(word, SRSLTE_UCI_MAX_CQI_LEN_PUCCH, q->cqi_table[w]); + for (int j = 0; j < SRSLTE_UCI_CQI_CODED_PUCCH_B; j++) { + q->cqi_table_s[w][j] = (int16_t)(2 * q->cqi_table[w][j] - 1); + } + } +} + +void srslte_uci_cqi_pucch_free(srslte_uci_cqi_pucch_t *q) { + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; + for (uint32_t w=0;wcqi_table[w]) { + free(q->cqi_table[w]); + } + if (q->cqi_table_s[w]) { + free(q->cqi_table_s[w]); + } + } + free(q->cqi_table); + free(q->cqi_table_s); +} + +/* Encode UCI CQI/PMI as described in 5.2.3.3 of 36.212 + */ +int srslte_uci_encode_cqi_pucch(uint8_t *cqi_data, uint32_t cqi_len, uint8_t b_bits[SRSLTE_UCI_CQI_CODED_PUCCH_B]) +{ + if (cqi_len <= SRSLTE_UCI_MAX_CQI_LEN_PUCCH) { + for (uint32_t i=0;icqi_table[packed], SRSLTE_UCI_CQI_CODED_PUCCH_B); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/* Decode UCI CQI/PMI over PUCCH + */ +int16_t srslte_uci_decode_cqi_pucch(srslte_uci_cqi_pucch_t *q, int16_t b_bits[32], uint8_t *cqi_data, uint32_t cqi_len) +{ + if (cqi_len < SRSLTE_UCI_MAX_CQI_LEN_PUCCH && + b_bits != NULL && + cqi_data != NULL) + { + uint32_t max_w = 0; + int32_t max_corr = INT32_MIN; + uint32_t nwords = 1 << SRSLTE_UCI_MAX_CQI_LEN_PUCCH; + for (uint32_t w=0;wcqi_table_s[w], b_bits, SRSLTE_UCI_CQI_CODED_PUCCH_B); + if (corr > max_corr) { + max_corr = corr; + max_w = w; + } + } + // Convert word to bits again + uint8_t *ptr = cqi_data; + srslte_bit_unpack(max_w, &ptr, SRSLTE_UCI_MAX_CQI_LEN_PUCCH); + + INFO("Decoded CQI: w=%d, corr=%d\n", max_w, max_corr); + return max_corr; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + + + + + + + +void encode_cqi_pusch_block(srslte_uci_cqi_pusch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t output[32]) { + for (int i=0;i<32;i++) { + output[i] = 0; + for (int n=0;ncqi_table[i] = srslte_vec_malloc(sizeof(uint8_t)*nwords*32); + q->cqi_table_s[i] = srslte_vec_malloc(sizeof(int16_t)*nwords*32); + for (uint32_t w=0;wcqi_table[i][32*w]); + for (int j=0;j<32;j++) { + q->cqi_table_s[i][32*w+j] = 2*q->cqi_table[i][32*w+j]-1; + } + } + } +} + +void cqi_pusch_pregen_free(srslte_uci_cqi_pusch_t *q) { + for (int i=0;i<11;i++) { + if (q->cqi_table[i]) { + free(q->cqi_table[i]); + } + if (q->cqi_table_s[i]) { + free(q->cqi_table_s[i]); + } + } +} + +int srslte_uci_cqi_init(srslte_uci_cqi_pusch_t *q) { + if (srslte_crc_init(&q->crc, SRSLTE_LTE_CRC8, 8)) { + return SRSLTE_ERROR; + } + int poly[3] = { 0x6D, 0x4F, 0x57 }; + if (srslte_viterbi_init(&q->viterbi, SRSLTE_VITERBI_37, poly, SRSLTE_UCI_MAX_CQI_LEN_PUSCH, true)) { + return SRSLTE_ERROR; + } + + cqi_pusch_pregen(q); + + return SRSLTE_SUCCESS; +} + +void srslte_uci_cqi_free(srslte_uci_cqi_pusch_t *q) +{ + srslte_viterbi_free(&q->viterbi); + + cqi_pusch_pregen_free(q); +} + +static uint32_t Q_prime_cqi(srslte_pusch_cfg_t *cfg, + uint32_t O, float beta, uint32_t Q_prime_ri) +{ + + uint32_t K = cfg->cb_segm.C1*cfg->cb_segm.K1 + cfg->cb_segm.C2*cfg->cb_segm.K2; + + uint32_t Q_prime = 0; + uint32_t L = (O<11)?0:8; + uint32_t x = 999999; + + if (K > 0) { + x = (uint32_t) ceilf((float) (O+L)*cfg->grant.M_sc_init*cfg->nbits.nof_symb*beta/K); + } + + Q_prime = SRSLTE_MIN(x, cfg->grant.M_sc * cfg->nbits.nof_symb - Q_prime_ri); + + return Q_prime; +} + +/* Encode UCI CQI/PMI for payloads equal or lower to 11 bits (Sec 5.2.2.6.4) + */ +int encode_cqi_short(srslte_uci_cqi_pusch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t *q_bits, uint32_t Q) +{ + if (nof_bits <= 11 && + nof_bits > 0 && + q != NULL && + data != NULL && + q_bits != NULL) + { + uint8_t *ptr = data; + uint32_t w = srslte_bit_pack(&ptr, nof_bits); + + for (int i=0;icqi_table[nof_bits-1][w*32+(i%32)]; + } + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +// For decoding the block-encoded CQI we use ML decoding +int decode_cqi_short(srslte_uci_cqi_pusch_t *q, int16_t *q_bits, uint32_t Q, uint8_t *data, uint32_t nof_bits) +{ + if (nof_bits <= 11 && + nof_bits > 0 && + q != NULL && + data != NULL && + q_bits != NULL) + { + // Accumulate all copies of the 32-length sequence + if (Q>32) { + int i=1; + for (;icqi_table_s[nof_bits-1][w*32], q_bits, SRSLTE_MIN(32, Q)); + if (corr > max_corr) { + max_corr = corr; + max_w = w; + } + } + // Convert word to bits again + uint8_t *ptr = data; + srslte_bit_unpack(max_w, &ptr, nof_bits); + + INFO("Decoded CQI: w=%d, corr=%d\n", max_w, max_corr); + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +/* Encode UCI CQI/PMI for payloads greater than 11 bits (go through CRC, conv coder and rate match) + */ +int encode_cqi_long(srslte_uci_cqi_pusch_t *q, uint8_t *data, uint32_t nof_bits, uint8_t *q_bits, uint32_t Q) +{ + srslte_convcoder_t encoder; + + if (nof_bits + 8 < SRSLTE_UCI_MAX_CQI_LEN_PUSCH && + q != NULL && + data != NULL && + q_bits != NULL) + { + int poly[3] = { 0x6D, 0x4F, 0x57 }; + encoder.K = 7; + encoder.R = 3; + encoder.tail_biting = true; + memcpy(encoder.poly, poly, 3 * sizeof(int)); + + memcpy(q->tmp_cqi, data, sizeof(uint8_t) * nof_bits); + srslte_crc_attach(&q->crc, q->tmp_cqi, nof_bits); + + DEBUG("cqi_crc_tx="); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, q->tmp_cqi, nof_bits+8); + } + + srslte_convcoder_encode(&encoder, q->tmp_cqi, q->encoded_cqi, nof_bits + 8); + + DEBUG("cconv_tx="); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, q->encoded_cqi, 3 * (nof_bits + 8)); + } + + srslte_rm_conv_tx(q->encoded_cqi, 3 * (nof_bits + 8), q_bits, Q); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int decode_cqi_long(srslte_uci_cqi_pusch_t *q, int16_t *q_bits, uint32_t Q, + uint8_t *data, uint32_t nof_bits) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (nof_bits + 8 < SRSLTE_UCI_MAX_CQI_LEN_PUSCH && + q != NULL && + data != NULL && + q_bits != NULL) + { + + srslte_rm_conv_rx_s(q_bits, Q, q->encoded_cqi_s, 3 * (nof_bits + 8)); + + DEBUG("cconv_rx="); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_s(stdout, q->encoded_cqi_s, 3 * (nof_bits + 8)); + } + + srslte_viterbi_decode_s(&q->viterbi, q->encoded_cqi_s, q->tmp_cqi, nof_bits + 8); + + DEBUG("cqi_crc_rx="); + if (SRSLTE_VERBOSE_ISDEBUG()) { + srslte_vec_fprint_b(stdout, q->tmp_cqi, nof_bits+8); + } + + ret = srslte_crc_checksum(&q->crc, q->tmp_cqi, nof_bits + 8); + if (ret == 0) { + memcpy(data, q->tmp_cqi, nof_bits*sizeof(uint8_t)); + ret = 1; + } else { + ret = 0; + } + } + return ret; +} + +/* Encode UCI CQI/PMI + */ +int srslte_uci_decode_cqi_pusch(srslte_uci_cqi_pusch_t *q, srslte_pusch_cfg_t *cfg, + int16_t *q_bits, + float beta, uint32_t Q_prime_ri, uint32_t cqi_len, + uint8_t *cqi_data, bool *cqi_ack) +{ + if (beta < 0) { + fprintf(stderr, "Error beta is reserved\n"); + return -1; + } + uint32_t Q_prime = Q_prime_cqi(cfg, cqi_len, beta, Q_prime_ri); + + int ret = SRSLTE_ERROR; + if (cqi_len <= 11) { + ret = decode_cqi_short(q, q_bits, Q_prime*cfg->grant.Qm, cqi_data, cqi_len); + } else { + ret = decode_cqi_long(q, q_bits, Q_prime*cfg->grant.Qm, cqi_data, cqi_len); + if (ret == 1) { + if (cqi_ack) { + *cqi_ack = true; + } + ret = 0; + } else if (ret == 0) { + if (cqi_ack) { + *cqi_ack = false; + } + } + } + if (ret) { + return ret; + } else { + return (int) Q_prime; + } + + return Q_prime; +} + +/* Encode UCI CQI/PMI as described in 5.2.2.6 of 36.212 + */ +int srslte_uci_encode_cqi_pusch(srslte_uci_cqi_pusch_t *q, srslte_pusch_cfg_t *cfg, + uint8_t *cqi_data, uint32_t cqi_len, + float beta, uint32_t Q_prime_ri, + uint8_t *q_bits) +{ + if (beta < 0) { + fprintf(stderr, "Error beta is reserved\n"); + return -1; + } + + uint32_t Q_prime = Q_prime_cqi(cfg, cqi_len, beta, Q_prime_ri); + + int ret = SRSLTE_ERROR; + if (cqi_len <= 11) { + ret = encode_cqi_short(q, cqi_data, cqi_len, q_bits, Q_prime*cfg->grant.Qm); + } else { + ret = encode_cqi_long(q, cqi_data, cqi_len, q_bits, Q_prime*cfg->grant.Qm); + } + if (ret) { + return ret; + } else { + return (int) Q_prime; + } +} + +static void uci_ulsch_interleave_put(srslte_uci_bit_type_t ack_coded_bits[6], uint32_t Qm, srslte_uci_bit_t *ack_bits) +{ + for(uint32_t k=0; k= 1+ack_q_bit_idx/4) { + uint32_t row = H_prime_total/N_pusch_symbs-1-ack_q_bit_idx/4; + uint32_t colidx = (3*ack_q_bit_idx)%4; + uint32_t col = SRSLTE_CP_ISNORM(cp)?ack_column_set_norm[colidx]:ack_column_set_ext[colidx]; + for(uint32_t k=0; k= 1+ri_q_bit_idx/4) { + uint32_t row = H_prime_total/N_pusch_symbs-1-ri_q_bit_idx/4; + uint32_t colidx = (3*ri_q_bit_idx)%4; + uint32_t col = SRSLTE_CP_ISNORM(cp)?ri_column_set_norm[colidx]:ri_column_set_ext[colidx]; + + for(uint32_t k=0; kcb_segm.C1*cfg->cb_segm.K1 + cfg->cb_segm.C2*cfg->cb_segm.K2; + + // If not carrying UL-SCH, get Q_prime according to 5.2.4.1 + if (K == 0) { + if (O_cqi <= 11) { + K = O_cqi; + } else { + K = O_cqi+8; + } + } + + uint32_t x = (uint32_t) ceilf((float) O*cfg->grant.M_sc_init*cfg->nbits.nof_symb*beta/K); + + uint32_t Q_prime = SRSLTE_MIN(x, 4*cfg->grant.M_sc); + + return Q_prime; +} + +static uint32_t encode_ri_ack(uint8_t data[2], uint32_t data_len, srslte_uci_bit_type_t q_encoded_bits[18], uint8_t Qm) +{ + uint32_t i = 0; + + if (data_len == 1) { + q_encoded_bits[i++] = data[0] ? UCI_BIT_1 : UCI_BIT_0; + q_encoded_bits[i++] = UCI_BIT_REPETITION; + while(i < Qm) { + q_encoded_bits[i++] = UCI_BIT_PLACEHOLDER; + } + } else if (data_len == 2) { + q_encoded_bits[i++] = data[0] ? UCI_BIT_1 : UCI_BIT_0; + q_encoded_bits[i++] = data[1] ? UCI_BIT_1 : UCI_BIT_0; + while(igrant.Qm); + + if (nof_encoded_bits > 0) { + for (uint32_t i = 0; i < Qprime; i++) { + uci_ulsch_interleave_ack_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &ack_bits[cfg->grant.Qm * i]); + uci_ulsch_interleave_put(&q_encoded_bits[(i * cfg->grant.Qm) % nof_encoded_bits], + cfg->grant.Qm, + &ack_bits[cfg->grant.Qm * i]); + } + } + return (int) Qprime; +} + +/* Encode UCI RI bits as described in 5.2.2.6 of 36.212 + * Currently only supporting 1-bit RI + */ +int srslte_uci_encode_ri(srslte_pusch_cfg_t *cfg, + uint8_t ri, + uint32_t O_cqi, float beta, uint32_t H_prime_total, + srslte_uci_bit_t *ri_bits) +{ + // FIXME: It supports RI of 1 bit only + uint8_t data[2] = {ri, 0}; + if (beta < 0) { + fprintf(stderr, "Error beta is reserved\n"); + return -1; + } + uint32_t Qprime = Q_prime_ri_ack(cfg, 1, O_cqi, beta); + srslte_uci_bit_type_t q_encoded_bits[18]; + + uint32_t nof_encoded_bits = encode_ri_ack(data, 1, q_encoded_bits, cfg->grant.Qm); + + for (uint32_t i=0;igrant.Qm, H_prime_total, cfg->nbits.nof_symb, cfg->cp, &ri_bits[cfg->grant.Qm*i]); + uci_ulsch_interleave_put(&q_encoded_bits[(i*cfg->grant.Qm)%nof_encoded_bits], cfg->grant.Qm, &ri_bits[cfg->grant.Qm*i]); + } + + return (int) Qprime; +} + +/* Encode UCI ACK/RI bits as described in 5.2.2.6 of 36.212 + * Currently only supporting 1-bit RI + */ +int srslte_uci_encode_ack_ri(srslte_pusch_cfg_t *cfg, + uint8_t *data, uint32_t data_len, + uint32_t O_cqi, float beta, uint32_t H_prime_total, + srslte_uci_bit_t *bits, bool ack_ri) { + if (beta < 0) { + fprintf(stderr, "Error beta is reserved\n"); + return -1; + } + uint32_t Qprime = Q_prime_ri_ack(cfg, data_len, O_cqi, beta); + srslte_uci_bit_type_t q_encoded_bits[18]; + + uint32_t nof_encoded_bits = encode_ri_ack(data, data_len, q_encoded_bits, cfg->grant.Qm); + + if (nof_encoded_bits > 0) { + for (uint32_t i = 0; i < Qprime; i++) { + if (ack_ri) { + uci_ulsch_interleave_ri_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &bits[cfg->grant.Qm * i]); + } else { + uci_ulsch_interleave_ack_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &bits[cfg->grant.Qm * i]); + } + uci_ulsch_interleave_put(&q_encoded_bits[(i * cfg->grant.Qm) % nof_encoded_bits], + cfg->grant.Qm, + &bits[cfg->grant.Qm * i]); + } + } + + return (int) Qprime; +} + +/* Decode UCI ACK/RI bits as described in 5.2.2.6 of 36.212 + * Currently only supporting 1-bit RI + */ +int srslte_uci_decode_ack_ri(srslte_pusch_cfg_t *cfg, int16_t *q_bits, uint8_t *c_seq, + float beta, uint32_t H_prime_total, + uint32_t O_cqi, srslte_uci_bit_t *ack_ri_bits, uint8_t data[2], uint32_t nof_bits, bool is_ri) +{ + int32_t sum[3] = {0, 0, 0}; + + if (beta < 0) { + fprintf(stderr, "Error beta is reserved\n"); + return -1; + } + + uint32_t Qprime = Q_prime_ri_ack(cfg, nof_bits, O_cqi, beta); + + for (uint32_t i = 0; i < Qprime; i++) { + if (is_ri) { + uci_ulsch_interleave_ri_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &ack_ri_bits[cfg->grant.Qm * i]); + } else { + uci_ulsch_interleave_ack_gen(i, + cfg->grant.Qm, + H_prime_total, + cfg->nbits.nof_symb, + cfg->cp, + &ack_ri_bits[cfg->grant.Qm * i]); + + } + if (nof_bits == 2 && (i % 3 == 0) && i > 0) { + decode_ri_ack_2bits(q_bits, &c_seq[0], &ack_ri_bits[cfg->grant.Qm * (i - 3)], cfg->grant.Qm, sum); + } else if (nof_bits == 1) { + sum[0] += (int32_t) decode_ri_ack_1bit(q_bits, c_seq, &ack_ri_bits[cfg->grant.Qm * i]); + } + } + + data[0] = (uint8_t) (sum[0] > 0); + if (nof_bits == 2) { + data[1] = (uint8_t) (sum[1] > 0); + } + + return (int) Qprime; +} + diff --git a/lib/src/phy/resampling/CMakeLists.txt b/lib/src/phy/resampling/CMakeLists.txt new file mode 100644 index 0000000..ef6513c --- /dev/null +++ b/lib/src/phy/resampling/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_resampling OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/resampling/decim.c b/lib/src/phy/resampling/decim.c new file mode 100644 index 0000000..eba486a --- /dev/null +++ b/lib/src/phy/resampling/decim.c @@ -0,0 +1,48 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/phy/resampling/decim.h" +#include "srslte/phy/utils/debug.h" + + +/* Performs integer linear decimation by a factor of M */ +void srslte_decim_c(cf_t *input, cf_t *output, int M, int len) { + int i; + for (i=0;i +#include +#include +#include +#include + +#include "srslte/phy/resampling/interp.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +/*************** STATIC FUNCTIONS ***********************/ + +cf_t srslte_interp_linear_onesample(cf_t input0, cf_t input1) { + return 2*input1-input0; +} + +cf_t srslte_interp_linear_onesample_cabs(cf_t input0, cf_t input1) { + float re0=0, im0=0, re1=0, im1=0, re=0, im=0; + re0 = crealf(input0); + im0 = cimagf(input1); + re1 = crealf(input0); + im1 = cimagf(input1); + re = 2*re1-re0; + im = 2*im1-im0; + return (re+im*_Complex_I); +} + + +/* Performs 1st order integer linear interpolation */ +void srslte_interp_linear_f(float *input, float *output, uint32_t M, uint32_t len) { + uint32_t i, j; + for (i=0;i 1) { + for (j=0;jdiff_vec = srslte_vec_malloc(vector_len * sizeof(cf_t)); + if (!q->diff_vec) { + perror("malloc"); + return SRSLTE_ERROR; + } + q->vector_len = vector_len; + q->max_vector_len = vector_len; + } + return ret; +} + +int srslte_interp_linear_vector_resize(srslte_interp_linsrslte_vec_t *q, uint32_t vector_len) +{ + if (vector_len <= q->max_vector_len) { + q->vector_len = vector_len; + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Error resizing interp_linear: vector_len must be lower or equal than initialized\n"); + return SRSLTE_ERROR; + } +} + +void srslte_interp_linear_vector_free(srslte_interp_linsrslte_vec_t *q) { + if (q->diff_vec) { + free(q->diff_vec); + } + + bzero(q, sizeof(srslte_interp_linsrslte_vec_t)); + +} + +void srslte_interp_linear_vector(srslte_interp_linsrslte_vec_t *q, cf_t *in0, cf_t *in1, cf_t *between, + uint32_t in1_in0_d, uint32_t M) +{ + srslte_interp_linear_vector2(q, in0, in1, NULL, between, in1_in0_d, M); +} + +void srslte_interp_linear_vector2(srslte_interp_linsrslte_vec_t *q, cf_t *in0, cf_t *in1, cf_t *start, cf_t *between, + uint32_t in1_in0_d, uint32_t M) +{ + srslte_interp_linear_vector3(q, in0, in1, start, between, in1_in0_d, M, true, q->vector_len); +} + +void srslte_interp_linear_vector3(srslte_interp_linsrslte_vec_t *q, cf_t *in0, cf_t *in1, cf_t *start, cf_t *between, + uint32_t in1_in0_d, uint32_t M, bool to_right, uint32_t len) +{ + uint32_t i; + + srslte_vec_sub_ccc(in1, in0, q->diff_vec, len); + srslte_vec_sc_prod_cfc(q->diff_vec, (float) 1/in1_in0_d, q->diff_vec, len); + + if (start) { + srslte_vec_sum_ccc(start, q->diff_vec, between, len); + } else { + srslte_vec_sum_ccc(in0, q->diff_vec, between, len); + } + for (i=0;idiff_vec, &between[q->vector_len], len); + between += q->vector_len; + } else { + between -= q->vector_len; + srslte_vec_sum_ccc(&between[q->vector_len], q->diff_vec, between, len); + } + } +} + +int srslte_interp_linear_init(srslte_interp_lin_t *q, uint32_t vector_len, uint32_t M) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q) { + bzero(q, sizeof(srslte_interp_lin_t)); + ret = SRSLTE_SUCCESS; + q->diff_vec = srslte_vec_malloc(vector_len * sizeof(cf_t)); + if (!q->diff_vec) { + perror("malloc"); + return SRSLTE_ERROR; + } + q->diff_vec2 = srslte_vec_malloc(M * vector_len * sizeof(cf_t)); + if (!q->diff_vec2) { + perror("malloc"); + free(q->diff_vec); + return SRSLTE_ERROR; + } + q->ramp = srslte_vec_malloc(M * sizeof(float)); + if (!q->ramp) { + perror("malloc"); + free(q->ramp); + free(q->diff_vec); + return SRSLTE_ERROR; + } + + for (int i=0;iramp[i] = (float) i; + } + + q->vector_len = vector_len; + q->M = M; + q->max_vector_len = vector_len; + q->max_M = M; + } + return ret; +} + +void srslte_interp_linear_free(srslte_interp_lin_t *q) { + if (q->diff_vec) { + free(q->diff_vec); + } + if (q->diff_vec2) { + free(q->diff_vec2); + } + if (q->ramp) { + free(q->ramp); + } + + bzero(q, sizeof(srslte_interp_lin_t)); + +} + + +int srslte_interp_linear_resize(srslte_interp_lin_t *q, uint32_t vector_len, uint32_t M) +{ + if (vector_len <= q->max_vector_len && M <= q->max_M) { + + for (int i=0;iramp[i] = (float) i; + } + + q->vector_len = vector_len; + q->M = M; + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Error resizing interp_linear: vector_len and M must be lower or equal than initialized\n"); + return SRSLTE_ERROR; + } +} + +void srslte_interp_linear_offset(srslte_interp_lin_t *q, cf_t *input, cf_t *output, + uint32_t off_st, uint32_t off_end) +{ + uint32_t i, j; + cf_t diff; + + i=0; + for (j=0;jM; + } + srslte_vec_sub_ccc(&input[1], input, q->diff_vec, (q->vector_len-1)); + srslte_vec_sc_prod_cfc(q->diff_vec, (float) 1/q->M, q->diff_vec, q->vector_len-1); + for (i=0;ivector_len-1;i++) { + for (j=0;jM;j++) { + output[i*q->M+j+off_st] = input[i]; + q->diff_vec2[i*q->M+j] = q->diff_vec[i]; + } + srslte_vec_prod_cfc(&q->diff_vec2[i*q->M],q->ramp,&q->diff_vec2[i*q->M],q->M); + } + srslte_vec_sum_ccc(&output[off_st], q->diff_vec2, &output[off_st], q->M*(q->vector_len-1)); + + if (q->vector_len > 1) { + diff = input[q->vector_len-1]-input[q->vector_len-2]; + for (j=0;jM+j+off_st] = input[i] + j * diff / q->M; + } + } +} + diff --git a/lib/src/phy/resampling/resample_arb.c b/lib/src/phy/resampling/resample_arb.c new file mode 100644 index 0000000..5cea28e --- /dev/null +++ b/lib/src/phy/resampling/resample_arb.c @@ -0,0 +1,182 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/phy/resampling/resample_arb.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + + + +float srslte_resample_arb_polyfilt[SRSLTE_RESAMPLE_ARB_N][SRSLTE_RESAMPLE_ARB_M] __attribute__((aligned(256))) = +{{0.000499262532685, 0.000859897001646, -0.008521087467670, 0.994530856609344, 0.017910413444042, -0.006922415923327, 0.002400347497314, 0.000000000000000 }, +{-0.001216900418513, 0.008048813790083, -0.032752435654402, 0.991047739982605, 0.046474494040012, -0.015253192745149, 0.004479591734707, -0.001903604716063 }, +{-0.002745266072452, 0.014606527984142, -0.054737392812967, 0.984105646610260, 0.077085755765438, -0.024075405672193, 0.006728826556355, -0.001750442315824 }, +{-0.004077596124262, 0.020508514717221, -0.074447125196457, 0.973753631114960, 0.109637774527073, -0.033325243741274, 0.009130494669080, -0.001807302702218 }, +{-0.005218582693487, 0.025736490264535, -0.091872826218605, 0.960064172744751, 0.144006818532944, -0.042925916612148, 0.011667170561850, -0.002005048329011 }, +{-0.006178495008498, 0.030282959342003, -0.107022657990456, 0.943133354187012, 0.180049657821655, -0.052793655544519, 0.014315498992801, -0.002303606597707 }, +{-0.006951034534723, 0.034145597368479, -0.119924493134022, 0.923079371452332, 0.217606633901596, -0.062832623720169, 0.017053086310625, -0.002673825016245 }, +{-0.007554128300399, 0.037334490567446, -0.130620718002319, 0.900041282176971, 0.256499618291855, -0.072941005229950, 0.019850222393870, -0.003099294845015 }, +{-0.007992693223059, 0.039863564074039, -0.139171004295349, 0.874178349971771, 0.296536892652512, -0.083006605505943, 0.022677028551698, -0.003564415033907 }, +{-0.008278168737888, 0.041754297912121, -0.145647943019867, 0.845668911933899, 0.337510257959366, -0.092912115156651, 0.025496333837509, -0.004060222767293 }, +{-0.008423951454461, 0.043032847344875, -0.150139778852463, 0.814707279205322, 0.379200965166092, -0.102530807256699, 0.028271337971091, -0.004574770107865 }, +{-0.008441128768027, 0.043734934180975, -0.152744457125664, 0.781503617763519, 0.421377241611481, -0.111732520163059, 0.030959306284785, -0.005100453738123 }, +{-0.008345136418939, 0.043892618268728, -0.153572276234627, 0.746281027793884, 0.463798433542252, -0.120380215346813, 0.033518772572279, -0.005625340621918 }, +{-0.008147838525474, 0.043551109731197, -0.152741402387619, 0.709274411201477, 0.506216108798981, -0.128335043787956, 0.035902760922909, -0.006140888202935 }, +{-0.007865131832659, 0.042750217020512, -0.150379851460457, 0.670727193355560, 0.548376381397247, -0.135454908013344, 0.038066454231739, -0.006634012795985 }, +{-0.007508971262723, 0.041538298130035, -0.146618664264679, 0.630890727043152, 0.590020775794983, -0.141596958041191, 0.039960436522961, -0.007094909437001 }, +{-0.007094909437001, 0.039960436522961, -0.141596958041191, 0.590020775794983, 0.630890727043152, -0.146618664264679, 0.041538298130035, -0.007508971262723 }, +{-0.006634012795985, 0.038066454231739, -0.135454908013344, 0.548376381397247, 0.670727193355560, -0.150379851460457, 0.042750217020512, -0.007865131832659 }, +{-0.006140888202935, 0.035902760922909, -0.128335043787956, 0.506216108798981, 0.709274411201477, -0.152741402387619, 0.043551109731197, -0.008147838525474 }, +{-0.005625340621918, 0.033518772572279, -0.120380215346813, 0.463798433542252, 0.746281027793884, -0.153572276234627, 0.043892618268728, -0.008345136418939 }, +{-0.005100453738123, 0.030959306284785, -0.111732520163059, 0.421377241611481, 0.781503617763519, -0.152744457125664, 0.043734934180975, -0.008441128768027 }, +{-0.004574770107865, 0.028271337971091, -0.102530807256699, 0.379200965166092, 0.814707279205322, -0.150139778852463, 0.043032847344875, -0.008423951454461 }, +{-0.004060222767293, 0.025496333837509, -0.092912115156651, 0.337510257959366, 0.845668911933899, -0.145647943019867, 0.041754297912121, -0.008278168737888 }, +{-0.003564415033907, 0.022677028551698, -0.083006605505943, 0.296536892652512, 0.874178349971771, -0.139171004295349, 0.039863564074039, -0.007992693223059 }, +{-0.003099294845015, 0.019850222393870, -0.072941005229950, 0.256499618291855, 0.900041282176971, -0.130620718002319, 0.037334490567446, -0.007554128300399 }, +{-0.002673825016245, 0.017053086310625, -0.062832623720169, 0.217606633901596, 0.923079371452332, -0.119924493134022, 0.034145597368479, -0.006951034534723 }, +{-0.002303606597707, 0.014315498992801, -0.052793655544519, 0.180049657821655, 0.943133354187012, -0.107022657990456, 0.030282959342003, -0.006178495008498 }, +{-0.002005048329011, 0.011667170561850, -0.042925916612148, 0.144006818532944, 0.960064172744751, -0.091872826218605, 0.025736490264535, -0.005218582693487 }, +{-0.001807302702218, 0.009130494669080, -0.033325243741274, 0.109637774527073, 0.973753631114960, -0.074447125196457, 0.020508514717221, -0.004077596124262 }, +{-0.001750442315824, 0.006728826556355, -0.024075405672193, 0.077085755765438, 0.984105646610260, -0.054737392812967, 0.014606527984142, -0.002745266072452 }, +{-0.001903604716063, 0.004479591734707, -0.015253192745149, 0.046474494040012, 0.991047739982605, -0.032752435654402, 0.008048813790083, -0.001216900418513 }, +{0.000000000000000, 0.002400347497314, -0.006922415923327, 0.017910413444042, 0.994530856609344, -0.008521087467670, 0.000859897001646, 0.000499262532685 }}; + + + + + float srslte_resample_arb_polyfilt_35[SRSLTE_RESAMPLE_ARB_N_35][SRSLTE_RESAMPLE_ARB_M] __attribute__((aligned(256))) = + {{0.000002955485215, 0.000657994314549, -0.033395686652146, 0.188481383863832, 0.704261032406613, 0.171322660416961, -0.032053439082436, 0.000722236729272}, +{0.000003596427925, 0.000568080243211, -0.034615802155152, 0.206204344739138, 0.702921418438421, 0.154765509342932, -0.030612377229395, 0.000764085430796}, +{0.000005121937258, 0.000449039680445, -0.035689076986744, 0.224449928603191, 0.700248311996698, 0.138842912406449, -0.029094366813032, 0.000786624971348}, +{0.000007718609465, 0.000297261794949, -0.036589488825594, 0.243172347408947, 0.696253915778072, 0.123583456372980, -0.027519775698206, 0.000792734165095}, +{0.000011575047600, 0.000109005258838, -0.037289794881918, 0.262321777662990, 0.690956433427623, 0.109011322456059, -0.025907443019580, 0.000785077624893}, +{0.000016882508098, -0.000119571475197, -0.037761641428480, 0.281844530653151, 0.684379949985066, 0.095146306185631, -0.024274662580170, 0.000766100891437}, +{0.000023834849457, -0.000392374989948, -0.037975689603840, 0.301683254255907, 0.676554274013489, 0.082003866618022, -0.022637179782782, 0.000738028846774}, +{0.000032627688404, -0.000713336731203, -0.037901757319987, 0.321777165554580, 0.667514742706306, 0.069595203590358, -0.021009201268519, 0.000702867092175}, +{0.000043456678971, -0.001086367817537, -0.037508976943232, 0.342062313218325, 0.657301991568526, 0.057927361522269, -0.019403416364306, 0.000662405963124}, +{0.000056514841640, -0.001515308860252, -0.036765968249551, 0.362471868314761, 0.645961690553484, 0.047003358086968, -0.017831029382043, 0.000618226851332}, +{0.000071988882997, -0.002003874882511, -0.035641025985104, 0.382936441958648, 0.633544248803284, 0.036822335913581, -0.016301801765629, 0.000571710504985}, +{0.000090054461223, -0.002555595491550, -0.034102321191048, 0.403384427938144, 0.620104490387788, 0.027379735343965, -0.014824103048525, 0.000524046983526}, +{0.000110870369116, -0.003173750525766, -0.032118115280935, 0.423742368211529, 0.605701303660781, 0.018667486151043, -0.013404969563367, 0.000476246951838}, +{0.000134571624077, -0.003861301468941, -0.029656985690720, 0.443935338933583, 0.590397267050922, 0.010674216032341, -0.012050169836157, 0.000429154010367}, +{0.000161261473605, -0.004620818996097, -0.026688061757620, 0.463887354454611, 0.574258254277514, 0.003385473622292, -0.010764275599988, 0.000383457772129}, +{0.000191002345063, -0.005454407088752, -0.023181269326746, 0.483521786538780, 0.557353022125450, -0.003216036280022, -0.009550737376604, 0.000339707414324}, +{0.000223805789820, -0.006363624230896, -0.019107582435436, 0.502761795874207, 0.539752784028766, -0.009150207594916, -0.008411963597610, 0.000298325451050}, +{0.000259621494011, -0.007349402269867, -0.014439280286493, 0.521530772797177, 0.521530772797177, -0.014439280286493, -0.007349402269867, 0.000259621494011}, +{0.000298325451050, -0.008411963597610, -0.009150207594916, 0.539752784028765, 0.502761795874207, -0.019107582435436, -0.006363624230896, 0.000223805789820}, +{0.000339707414324, -0.009550737376604, -0.003216036280022, 0.557353022125450, 0.483521786538780, -0.023181269326746, -0.005454407088752, 0.000191002345063}, +{0.000383457772129, -0.010764275599988, 0.003385473622292, 0.574258254277514, 0.463887354454611, -0.026688061757620, -0.004620818996097, 0.000161261473605}, +{0.000429154010367, -0.012050169836157, 0.010674216032341, 0.590397267050922, 0.443935338933583, -0.029656985690720, -0.003861301468941, 0.000134571624077}, +{0.000476246951838, -0.013404969563367, 0.018667486151043, 0.605701303660781, 0.423742368211529, -0.032118115280935, -0.003173750525766, 0.000110870369116}, +{0.000524046983526, -0.014824103048525, 0.027379735343965, 0.620104490387787, 0.403384427938144, -0.034102321191048, -0.002555595491550, 0.000090054461223}, +{0.000571710504985, -0.016301801765629, 0.036822335913581, 0.633544248803283, 0.382936441958648, -0.035641025985104, -0.002003874882511, 0.000071988882997}, +{0.000618226851332, -0.017831029382043, 0.047003358086968, 0.645961690553484, 0.362471868314761, -0.036765968249551, -0.001515308860252, 0.000056514841640}, +{0.000662405963124, -0.019403416364306, 0.057927361522269, 0.657301991568526, 0.342062313218325, -0.037508976943232, -0.001086367817537, 0.000043456678971}, +{0.000702867092175, -0.021009201268519, 0.069595203590358, 0.667514742706306, 0.321777165554580, -0.037901757319987, -0.000713336731203, 0.000032627688404}, +{0.000738028846774, -0.022637179782782, 0.082003866618022, 0.676554274013489, 0.301683254255907, -0.037975689603840, -0.000392374989948, 0.000023834849457}, +{0.000766100891437, -0.024274662580170, 0.095146306185631, 0.684379949985066, 0.281844530653151, -0.037761641428480, -0.000119571475197, 0.000016882508098}, +{0.000785077624893, -0.025907443019580, 0.109011322456059, 0.690956433427623, 0.262321777662990, -0.037289794881918, 0.000109005258838, 0.000011575047600}, +{0.000792734165095, -0.027519775698206, 0.123583456372980, 0.696253915778072, 0.243172347408947, -0.036589488825594, 0.000297261794949, 0.000007718609465}, +{0.000786624971348, -0.029094366813032, 0.138842912406449, 0.700248311996698, 0.224449928603191, -0.035689076986744, 0.000449039680445, 0.000005121937258}, +{0.000764085430796, -0.030612377229395, 0.154765509342932, 0.702921418438421, 0.206204344739138, -0.034615802155152, 0.000568080243211, 0.000003596427925}, +{0.000722236729272, -0.032053439082436, 0.171322660416961, 0.704261032406613, 0.188481383863832, -0.033395686652146, 0.000657994314549 , 0.000002955485215}}; + +static inline cf_t srslte_resample_arb_dot_prod(cf_t* x, float *y, int len){ + + cf_t res1 = srslte_vec_dot_prod_cfc(x,y,len); + return res1; +} + +// Right-shift our window of samples +void srslte_resample_arb_push(srslte_resample_arb_t *q, cf_t x){ + + memmove(&q->reg[1], &q->reg[0], (SRSLTE_RESAMPLE_ARB_M-1)*sizeof(cf_t)); + q->reg[0] = x; +} + +// Initialize our struct +void srslte_resample_arb_init(srslte_resample_arb_t *q, float rate, bool interpolate){ + + memset(q->reg, 0, SRSLTE_RESAMPLE_ARB_M*sizeof(cf_t)); + q->acc = 0.0; + q->rate = rate; + q->interpolate = interpolate; + q->step = (1/rate)*SRSLTE_RESAMPLE_ARB_N; +} + +// Resample a block of input data +int srslte_resample_arb_compute(srslte_resample_arb_t *q, cf_t *input, cf_t *output, int n_in){ + int cnt = 0; + int n_out = 0; + int idx = 0; + cf_t res1,res2; + cf_t *filter_input; + float frac = 0; + memset(q->reg, 0, SRSLTE_RESAMPLE_ARB_M*sizeof(cf_t)); + + while (cnt < n_in) { + + if(cntreg[SRSLTE_RESAMPLE_ARB_M - cnt], input, (cnt)*sizeof(cf_t)); + filter_input = q->reg; + } else{ + filter_input = &input[cnt-SRSLTE_RESAMPLE_ARB_M]; + } + + res1 = srslte_resample_arb_dot_prod(filter_input, srslte_resample_arb_polyfilt[idx], SRSLTE_RESAMPLE_ARB_M); + if(q->interpolate){ + res2 = srslte_resample_arb_dot_prod(filter_input, srslte_resample_arb_polyfilt[(idx+1)%SRSLTE_RESAMPLE_ARB_N], SRSLTE_RESAMPLE_ARB_M); + } + + if(idx == SRSLTE_RESAMPLE_ARB_N){ + *output = res1; + }else { + *output = (q->interpolate)?(res1 + (res2-res1)*frac):res1; + } + + output++; + n_out++; + q->acc += q->step; + idx = (int)(q->acc); + + while(idx >= SRSLTE_RESAMPLE_ARB_N){ + q->acc -= SRSLTE_RESAMPLE_ARB_N; + idx -= SRSLTE_RESAMPLE_ARB_N; + if(cnt < n_in){ + cnt++; + } + } + + if(q->interpolate){ + frac = q->acc - idx; + if(frac < 0) + frac = frac*(-1); + } + } + return n_out; +} diff --git a/lib/src/phy/resampling/test/CMakeLists.txt b/lib/src/phy/resampling/test/CMakeLists.txt new file mode 100644 index 0000000..f0314b6 --- /dev/null +++ b/lib/src/phy/resampling/test/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# RESAMPLING TEST +######################################################################## + +add_executable(resample_arb_test resample_arb_test.c) +target_link_libraries(resample_arb_test srslte_phy) + +add_executable(resample_arb_bench resample_arb_bench.c) +target_link_libraries(resample_arb_bench srslte_phy) + +add_test(resample resample_arb_test) + + + diff --git a/lib/src/phy/resampling/test/resample_arb_bench.c b/lib/src/phy/resampling/test/resample_arb_bench.c new file mode 100644 index 0000000..e9ef3c4 --- /dev/null +++ b/lib/src/phy/resampling/test/resample_arb_bench.c @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "srslte/phy/resampling/resample_arb.h" + + +#define ITERATIONS 10000 +int main(int argc, char **argv) { + int N=9000; + float rate = 24.0/25.0; + cf_t *in = malloc(N*sizeof(cf_t)); + cf_t *out = malloc(N*sizeof(cf_t)); + + for(int i=0;i +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "srslte/phy/resampling/resample_arb.h" + + +int main(int argc, char **argv) { + int N = 100; // Number of sinwave samples + int delay = 5; // Delay of our resampling filter + float down = 25.0; // Downsampling rate + + + + + for(int up=1;up diff && pre != post){ + printf("Interpolation failed at index %f\n", idx); + exit(-1); + } + } + + free(in); + free(out); + } + + printf("Ok\n"); + exit(0); +} diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt new file mode 100644 index 0000000..6cec5a6 --- /dev/null +++ b/lib/src/phy/rf/CMakeLists.txt @@ -0,0 +1,64 @@ +# +# Copyright 2013-2017 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/. +# + +if(RF_FOUND) + + # This library is only used by the examples + add_library(srslte_rf_utils STATIC rf_utils.c) + target_link_libraries(srslte_rf_utils srslte_phy) + + # Include common RF files + set(SOURCES_RF "") + list(APPEND SOURCES_RF rf_imp.c) + + if (UHD_FOUND) + add_definitions(-DENABLE_UHD) + list(APPEND SOURCES_RF rf_uhd_imp.c uhd_c_api.cpp) + endif (UHD_FOUND) + + if (BLADERF_FOUND) + add_definitions(-DENABLE_BLADERF) + list(APPEND SOURCES_RF rf_blade_imp.c) + endif (BLADERF_FOUND) + + if (SOAPYSDR_FOUND) + add_definitions(-DENABLE_SOAPYSDR) + list(APPEND SOURCES_RF rf_soapy_imp.c) + endif (SOAPYSDR_FOUND) + + + add_library(srslte_rf SHARED ${SOURCES_RF}) + target_link_libraries(srslte_rf srslte_rf_utils srslte_phy) + + if (UHD_FOUND) + target_link_libraries(srslte_rf ${UHD_LIBRARIES}) + endif (UHD_FOUND) + + if (BLADERF_FOUND) + target_link_libraries(srslte_rf ${BLADERF_LIBRARIES}) + endif (BLADERF_FOUND) + + if (SOAPYSDR_FOUND) + target_link_libraries(srslte_rf ${SOAPYSDR_LIBRARIES}) + endif (SOAPYSDR_FOUND) + + + INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) +endif(RF_FOUND) diff --git a/lib/src/phy/rf/rf_blade_imp.c b/lib/src/phy/rf/rf_blade_imp.c new file mode 100644 index 0000000..4f624f8 --- /dev/null +++ b/lib/src/phy/rf/rf_blade_imp.c @@ -0,0 +1,569 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "rf_blade_imp.h" +#include "srslte/phy/rf/rf.h" + + +#define CONVERT_BUFFER_SIZE 240*1024 + +typedef struct { + struct bladerf *dev; + uint32_t rx_rate; + uint32_t tx_rate; + int16_t rx_buffer[CONVERT_BUFFER_SIZE]; + int16_t tx_buffer[CONVERT_BUFFER_SIZE]; + bool rx_stream_enabled; + bool tx_stream_enabled; + srslte_rf_info_t info; +} rf_blade_handler_t; + +srslte_rf_error_handler_t blade_error_handler = NULL; + +void rf_blade_suppress_stdout(void *h) { + bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_SILENT); +} + +void rf_blade_register_error_handler(void *notused, srslte_rf_error_handler_t new_handler) +{ + new_handler = blade_error_handler; +} + +bool rf_blade_rx_wait_lo_locked(void *h) +{ + usleep(1000); + return true; +} + +const unsigned int num_buffers = 256; +const unsigned int ms_buffer_size_rx = 1024; +const unsigned int buffer_size_tx = 1024; +const unsigned int num_transfers = 32; +const unsigned int timeout_ms = 4000; + + +char* rf_blade_devname(void* h) +{ + return DEVNAME; +} + +int rf_blade_start_tx_stream(void *h) +{ + int status; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + + status = bladerf_sync_config(handler->dev, + BLADERF_MODULE_TX, + BLADERF_FORMAT_SC16_Q11_META, + num_buffers, + buffer_size_tx, + num_transfers, + timeout_ms); + if (status != 0) { + fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_enable_module(handler->dev, BLADERF_MODULE_TX, true); + if (status != 0) { + fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status)); + return status; + } + handler->tx_stream_enabled = true; + return 0; +} + +int rf_blade_start_rx_stream(void *h, bool now) +{ + int status; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + + /* Configure the device's RX module for use with the sync interface. + * SC16 Q11 samples *with* metadata are used. */ + uint32_t buffer_size_rx = ms_buffer_size_rx*(handler->rx_rate/1000/1024); + + status = bladerf_sync_config(handler->dev, + BLADERF_MODULE_RX, + BLADERF_FORMAT_SC16_Q11_META, + num_buffers, + buffer_size_rx, + num_transfers, + timeout_ms); + if (status != 0) { + fprintf(stderr, "Failed to configure RX sync interface: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_sync_config(handler->dev, + BLADERF_MODULE_TX, + BLADERF_FORMAT_SC16_Q11_META, + num_buffers, + buffer_size_tx, + num_transfers, + timeout_ms); + if (status != 0) { + fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_enable_module(handler->dev, BLADERF_MODULE_RX, true); + if (status != 0) { + fprintf(stderr, "Failed to enable RX module: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_enable_module(handler->dev, BLADERF_MODULE_TX, true); + if (status != 0) { + fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status)); + return status; + } + handler->rx_stream_enabled = true; + return 0; +} + +int rf_blade_stop_rx_stream(void *h) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + int status = bladerf_enable_module(handler->dev, BLADERF_MODULE_RX, false); + if (status != 0) { + fprintf(stderr, "Failed to enable RX module: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_enable_module(handler->dev, BLADERF_MODULE_TX, false); + if (status != 0) { + fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status)); + return status; + } + handler->rx_stream_enabled = false; + handler->tx_stream_enabled = false; + return 0; +} + +void rf_blade_flush_buffer(void *h) +{ +} + +bool rf_blade_has_rssi(void *h) +{ + return false; +} + +float rf_blade_get_rssi(void *h) +{ + return 0; +} + +int rf_blade_open_multi(char *args, void **h, uint32_t nof_channels) +{ + return rf_blade_open(args, h); +} + +int rf_blade_open(char *args, void **h) +{ + *h = NULL; + + rf_blade_handler_t *handler = (rf_blade_handler_t*) malloc(sizeof(rf_blade_handler_t)); + if (!handler) { + perror("malloc"); + return -1; + } + *h = handler; + + printf("Opening bladeRF...\n"); + int status = bladerf_open(&handler->dev, args); + if (status) { + fprintf(stderr, "Unable to open device: %s\n", bladerf_strerror(status)); + return status; + } + + //bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE); + + /* Configure the gains of the RX LNA and RX VGA1*/ + status = bladerf_set_lna_gain(handler->dev, BLADERF_LNA_GAIN_MAX); + if (status != 0) { + fprintf(stderr, "Failed to set RX LNA gain: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_set_rxvga1(handler->dev, 27); + if (status != 0) { + fprintf(stderr, "Failed to set RX VGA1 gain: %s\n", bladerf_strerror(status)); + return status; + } + status = bladerf_set_txvga1(handler->dev, BLADERF_TXVGA1_GAIN_MAX); + if (status != 0) { + fprintf(stderr, "Failed to set TX VGA1 gain: %s\n", bladerf_strerror(status)); + return status; + } + handler->rx_stream_enabled = false; + handler->tx_stream_enabled = false; + + /* Set info structure */ + handler->info.min_tx_gain = BLADERF_TXVGA2_GAIN_MIN; + handler->info.max_tx_gain = BLADERF_TXVGA2_GAIN_MAX; + handler->info.min_rx_gain = BLADERF_RXVGA2_GAIN_MIN; + handler->info.max_rx_gain = BLADERF_RXVGA2_GAIN_MAX; + + return 0; +} + + +int rf_blade_close(void *h) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + bladerf_close(handler->dev); + return 0; +} + +void rf_blade_set_master_clock_rate(void *h, double rate) +{ +} + +bool rf_blade_is_master_clock_dynamic(void *h) +{ + return true; +} + +double rf_blade_set_rx_srate(void *h, double freq) +{ + uint32_t bw; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + int status = bladerf_set_sample_rate(handler->dev, BLADERF_MODULE_RX, (uint32_t) freq, &handler->rx_rate); + if (status != 0) { + fprintf(stderr, "Failed to set samplerate = %u: %s\n", (uint32_t) freq, bladerf_strerror(status)); + return -1; + } + if (handler->rx_rate < 2000000) { + status = bladerf_set_bandwidth(handler->dev, BLADERF_MODULE_RX, handler->rx_rate, &bw); + if (status != 0) { + fprintf(stderr, "Failed to set bandwidth = %u: %s\n", handler->rx_rate, bladerf_strerror(status)); + return -1; + } + } else { + status = bladerf_set_bandwidth(handler->dev, BLADERF_MODULE_RX, handler->rx_rate*0.8, &bw); + if (status != 0) { + fprintf(stderr, "Failed to set bandwidth = %u: %s\n", handler->rx_rate, bladerf_strerror(status)); + return -1; + } + } + printf("Set RX sampling rate %.2f Mhz, filter BW: %.2f Mhz\n", (float) handler->rx_rate/1e6, (float) bw/1e6); + return (double) handler->rx_rate; +} + +double rf_blade_set_tx_srate(void *h, double freq) +{ + uint32_t bw; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + int status = bladerf_set_sample_rate(handler->dev, BLADERF_MODULE_TX, (uint32_t) freq, &handler->tx_rate); + if (status != 0) { + fprintf(stderr, "Failed to set samplerate = %u: %s\n", (uint32_t) freq, bladerf_strerror(status)); + return -1; + } + status = bladerf_set_bandwidth(handler->dev, BLADERF_MODULE_TX, handler->tx_rate, &bw); + if (status != 0) { + fprintf(stderr, "Failed to set bandwidth = %u: %s\n", handler->tx_rate, bladerf_strerror(status)); + return -1; + } + return (double) handler->tx_rate; +} + +double rf_blade_set_rx_gain(void *h, double gain) +{ + int status; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + status = bladerf_set_rxvga2(handler->dev, (int) gain); + if (status != 0) { + fprintf(stderr, "Failed to set RX VGA2 gain: %s\n", bladerf_strerror(status)); + return -1; + } + return rf_blade_get_rx_gain(h); +} + +double rf_blade_set_tx_gain(void *h, double gain) +{ + int status; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + status = bladerf_set_txvga2(handler->dev, (int) gain); + if (status != 0) { + fprintf(stderr, "Failed to set TX VGA2 gain: %s\n", bladerf_strerror(status)); + return -1; + } + return rf_blade_get_tx_gain(h); +} + +double rf_blade_get_rx_gain(void *h) +{ + int status; + int gain; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + status = bladerf_get_rxvga2(handler->dev, &gain); + if (status != 0) { + fprintf(stderr, "Failed to get RX VGA2 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + return gain; // Add rxvga1 and LNA +} + +double rf_blade_get_tx_gain(void *h) +{ + int status; + int gain; + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + status = bladerf_get_txvga2(handler->dev, &gain); + if (status != 0) { + fprintf(stderr, "Failed to get TX VGA2 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + return gain; // Add txvga1 +} + +srslte_rf_info_t *rf_blade_get_info(void *h) +{ + + srslte_rf_info_t *info = NULL; + + if (h) { + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + + info = &handler->info; + } + return info; +} + +double rf_blade_set_rx_freq(void *h, double freq) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + uint32_t f_int = (uint32_t) round(freq); + int status = bladerf_set_frequency(handler->dev, BLADERF_MODULE_RX, f_int); + if (status != 0) { + fprintf(stderr, "Failed to set samplerate = %u: %s\n", + (uint32_t) freq, bladerf_strerror(status)); + return -1; + } + f_int=0; + bladerf_get_frequency(handler->dev, BLADERF_MODULE_RX, &f_int); + printf("set RX frequency to %u\n", f_int); + + return freq; +} + +double rf_blade_set_tx_freq(void *h, double freq) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + uint32_t f_int = (uint32_t) round(freq); + int status = bladerf_set_frequency(handler->dev, BLADERF_MODULE_TX, f_int); + if (status != 0) { + fprintf(stderr, "Failed to set samplerate = %u: %s\n", + (uint32_t) freq, bladerf_strerror(status)); + return -1; + } + + f_int=0; + bladerf_get_frequency(handler->dev, BLADERF_MODULE_TX, &f_int); + printf("set TX frequency to %u\n", f_int); + return freq; +} + +void rf_blade_set_tx_cal(void *h, srslte_rf_cal_t *cal) { + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_FPGA_PHASE, cal->dc_gain); + bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_FPGA_GAIN, cal->dc_phase); + bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_LMS_DCOFF_I, cal->iq_i); + bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_LMS_DCOFF_Q, cal->iq_q); +} + +void rf_blade_set_rx_cal(void *h, srslte_rf_cal_t *cal) { + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_PHASE, cal->dc_gain); + bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_GAIN, cal->dc_phase); + bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_I, cal->iq_i); + bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_Q, cal->iq_q); +} + + +static void timestamp_to_secs(uint32_t rate, uint64_t timestamp, time_t *secs, double *frac_secs) { + double totalsecs = (double) timestamp/rate; + time_t secs_i = (time_t) totalsecs; + if (secs) { + *secs = secs_i; + } + if (frac_secs) { + *frac_secs = totalsecs-secs_i; + } +} + +static void secs_to_timestamps(uint32_t rate, time_t secs, double frac_secs, uint64_t *timestamp) { + double totalsecs = (double) secs + frac_secs; + if (timestamp) { + *timestamp = rate * totalsecs; + } +} + +void rf_blade_get_time(void *h, time_t *secs, double *frac_secs) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + struct bladerf_metadata meta; + + int status = bladerf_get_timestamp(handler->dev, BLADERF_MODULE_RX, &meta.timestamp); + if (status != 0) { + fprintf(stderr, "Failed to get current RX timestamp: %s\n", + bladerf_strerror(status)); + } + timestamp_to_secs(handler->rx_rate, meta.timestamp, secs, frac_secs); +} + + +int rf_blade_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return rf_blade_recv_with_time(h, *data, nsamples, blocking, secs, frac_secs); +} + +int rf_blade_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + struct bladerf_metadata meta; + int status; + + memset(&meta, 0, sizeof(meta)); + meta.flags = BLADERF_META_FLAG_RX_NOW; + + if (2*nsamples > CONVERT_BUFFER_SIZE) { + fprintf(stderr, "RX failed: nsamples exceeds buffer size (%d>%d)\n", nsamples, CONVERT_BUFFER_SIZE); + return -1; + } + status = bladerf_sync_rx(handler->dev, handler->rx_buffer, nsamples, &meta, 2000); + if (status) { + fprintf(stderr, "RX failed: %s\n\n", bladerf_strerror(status)); + return -1; + } else if (meta.status & BLADERF_META_STATUS_OVERRUN) { + if (blade_error_handler) { + srslte_rf_error_t error; + error.opt = meta.actual_count; + error.type = SRSLTE_RF_ERROR_OVERFLOW; + blade_error_handler(error); + } else { + fprintf(stderr, "Overrun detected in scheduled RX. " + "%u valid samples were read.\n\n", meta.actual_count); + } + } + + timestamp_to_secs(handler->rx_rate, meta.timestamp, secs, frac_secs); + srslte_vec_convert_if(handler->rx_buffer, 2048, data, 2*nsamples); + + return nsamples; +} + +int rf_blade_send_timed_multi(void *h, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + return rf_blade_send_timed(h, data[0], nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, + is_end_of_burst); +} + +int rf_blade_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_blade_handler_t *handler = (rf_blade_handler_t*) h; + struct bladerf_metadata meta; + int status; + + if (!handler->tx_stream_enabled) { + rf_blade_start_tx_stream(h); + } + + if (2*nsamples > CONVERT_BUFFER_SIZE) { + fprintf(stderr, "TX failed: nsamples exceeds buffer size (%d>%d)\n", nsamples, CONVERT_BUFFER_SIZE); + return -1; + } + + srslte_vec_convert_fi(data, 2048, handler->tx_buffer, 2*nsamples); + + memset(&meta, 0, sizeof(meta)); + if (is_start_of_burst) { + if (has_time_spec) { + secs_to_timestamps(handler->tx_rate, secs, frac_secs, &meta.timestamp); + } else { + meta.flags |= BLADERF_META_FLAG_TX_NOW; + } + meta.flags |= BLADERF_META_FLAG_TX_BURST_START; + } + if (is_end_of_burst) { + meta.flags |= BLADERF_META_FLAG_TX_BURST_END; + } + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + + status = bladerf_sync_tx(handler->dev, handler->tx_buffer, nsamples, &meta, 2000); + if (status == BLADERF_ERR_TIME_PAST) { + if (blade_error_handler) { + error.type = SRSLTE_RF_ERROR_LATE; + blade_error_handler(error); + } else { + fprintf(stderr, "TX failed: %s\n", bladerf_strerror(status)); + } + } else if (status) { + fprintf(stderr, "TX failed: %s\n", bladerf_strerror(status)); + return status; + } else if (meta.status == BLADERF_META_STATUS_UNDERRUN) { + if (blade_error_handler) { + error.type = SRSLTE_RF_ERROR_UNDERFLOW; + blade_error_handler(error); + } else { + fprintf(stderr, "TX warning: underflow detected.\n"); + } + } + + return nsamples; +} + diff --git a/lib/src/phy/rf/rf_blade_imp.h b/lib/src/phy/rf/rf_blade_imp.h new file mode 100644 index 0000000..6c8c04c --- /dev/null +++ b/lib/src/phy/rf/rf_blade_imp.h @@ -0,0 +1,136 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/config.h" +#include "srslte/phy/rf/rf.h" + +#define DEVNAME "bladerf" + +SRSLTE_API int rf_blade_open(char *args, + void **handler); + +SRSLTE_API int rf_blade_open_multi(char *args, + void **handler, uint32_t nof_channels); + +SRSLTE_API char* rf_blade_devname(void *h); + +SRSLTE_API int rf_blade_close(void *h); + +SRSLTE_API void rf_blade_set_tx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API void rf_blade_set_rx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API int rf_blade_start_rx_stream(void *h, bool now); + +SRSLTE_API int rf_blade_start_rx_stream_nsamples(void *h, + uint32_t nsamples); + +SRSLTE_API int rf_blade_stop_rx_stream(void *h); + +SRSLTE_API void rf_blade_flush_buffer(void *h); + +SRSLTE_API bool rf_blade_has_rssi(void *h); + +SRSLTE_API float rf_blade_get_rssi(void *h); + +SRSLTE_API bool rf_blade_rx_wait_lo_locked(void *h); + +SRSLTE_API void rf_blade_set_master_clock_rate(void *h, + double rate); + +SRSLTE_API bool rf_blade_is_master_clock_dynamic(void *h); + +SRSLTE_API double rf_blade_set_rx_srate(void *h, + double freq); + +SRSLTE_API double rf_blade_set_rx_gain(void *h, + double gain); + +SRSLTE_API float rf_blade_get_rx_gain_offset(void *h); + +SRSLTE_API double rf_blade_get_rx_gain(void *h); + +SRSLTE_API double rf_blade_get_tx_gain(void *h); + +SRSLTE_API srslte_rf_info_t *rf_blade_get_info(void *h); + +SRSLTE_API void rf_blade_suppress_stdout(void *h); + +SRSLTE_API void rf_blade_register_error_handler(void *h, + srslte_rf_error_handler_t error_handler); + +SRSLTE_API double rf_blade_set_rx_freq(void *h, + double freq); + +SRSLTE_API int rf_blade_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_blade_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double rf_blade_set_tx_srate(void *h, + double freq); + +SRSLTE_API double rf_blade_set_tx_gain(void *h, + double gain); + +SRSLTE_API double rf_blade_set_tx_freq(void *h, + double freq); + +SRSLTE_API void rf_blade_get_time(void *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_blade_send_timed_multi(void *h, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSLTE_API int rf_blade_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + + diff --git a/lib/src/phy/rf/rf_dev.h b/lib/src/phy/rf/rf_dev.h new file mode 100644 index 0000000..80b5125 --- /dev/null +++ b/lib/src/phy/rf/rf_dev.h @@ -0,0 +1,248 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + + +/* RF frontend API */ +typedef struct { + const char *name; + char* (*srslte_rf_devname) (void *h); + bool (*srslte_rf_rx_wait_lo_locked) (void *h); + int (*srslte_rf_start_rx_stream)(void *h, bool now); + int (*srslte_rf_stop_rx_stream)(void *h); + void (*srslte_rf_flush_buffer)(void *h); + bool (*srslte_rf_has_rssi)(void *h); + float (*srslte_rf_get_rssi)(void *h); + void (*srslte_rf_suppress_stdout)(void *h); + void (*srslte_rf_register_error_handler)(void *h, srslte_rf_error_handler_t error_handler); + int (*srslte_rf_open)(char *args, void **h); + int (*srslte_rf_open_multi)(char *args, void **h, uint32_t nof_channels); + int (*srslte_rf_close)(void *h); + void (*srslte_rf_set_master_clock_rate)(void *h, double rate); + bool (*srslte_rf_is_master_clock_dynamic)(void *h); + double (*srslte_rf_set_rx_srate)(void *h, double freq); + double (*srslte_rf_set_rx_gain)(void *h, double gain); + double (*srslte_rf_set_tx_gain)(void *h, double gain); + double (*srslte_rf_get_rx_gain)(void *h); + double (*srslte_rf_get_tx_gain)(void *h); + srslte_rf_info_t *(*srslte_rf_get_info)(void *h); + double (*srslte_rf_set_rx_freq)(void *h, double freq); + double (*srslte_rf_set_tx_srate)(void *h, double freq); + double (*srslte_rf_set_tx_freq)(void *h, double freq); + void (*srslte_rf_get_time)(void *h, time_t *secs, double *frac_secs); + int (*srslte_rf_recv_with_time)(void *h, void *data, uint32_t nsamples, + bool blocking, time_t *secs,double *frac_secs); + int (*srslte_rf_recv_with_time_multi)(void *h, void **data, uint32_t nsamples, + bool blocking, time_t *secs,double *frac_secs); + int (*srslte_rf_send_timed)(void *h, void *data, int nsamples, + time_t secs, double frac_secs, bool has_time_spec, + bool blocking, bool is_start_of_burst, bool is_end_of_burst); + int (*srslte_rf_send_timed_multi)(void *h, void *data[4], int nsamples, + time_t secs, double frac_secs, bool has_time_spec, + bool blocking, bool is_start_of_burst, bool is_end_of_burst); + void (*srslte_rf_set_tx_cal)(void *h, srslte_rf_cal_t *cal); + + void (*srslte_rf_set_rx_cal)(void *h, srslte_rf_cal_t *cal); + +} rf_dev_t; + +/* Define implementation for UHD */ +#ifdef ENABLE_UHD + +#include "rf_uhd_imp.h" + +static rf_dev_t dev_uhd = { + "UHD", + rf_uhd_devname, + rf_uhd_rx_wait_lo_locked, + rf_uhd_start_rx_stream, + rf_uhd_stop_rx_stream, + rf_uhd_flush_buffer, + rf_uhd_has_rssi, + rf_uhd_get_rssi, + rf_uhd_suppress_stdout, + rf_uhd_register_error_handler, + rf_uhd_open, + .srslte_rf_open_multi = rf_uhd_open_multi, + rf_uhd_close, + rf_uhd_set_master_clock_rate, + rf_uhd_is_master_clock_dynamic, + rf_uhd_set_rx_srate, + rf_uhd_set_rx_gain, + rf_uhd_set_tx_gain, + rf_uhd_get_rx_gain, + rf_uhd_get_tx_gain, + rf_uhd_get_info, + rf_uhd_set_rx_freq, + rf_uhd_set_tx_srate, + rf_uhd_set_tx_freq, + rf_uhd_get_time, + rf_uhd_recv_with_time, + rf_uhd_recv_with_time_multi, + rf_uhd_send_timed, + .srslte_rf_send_timed_multi = rf_uhd_send_timed_multi, + rf_uhd_set_tx_cal, + rf_uhd_set_rx_cal +}; +#endif + +/* Define implementation for bladeRF */ +#ifdef ENABLE_BLADERF + +#include "rf_blade_imp.h" + +static rf_dev_t dev_blade = { + "bladeRF", + rf_blade_devname, + rf_blade_rx_wait_lo_locked, + rf_blade_start_rx_stream, + rf_blade_stop_rx_stream, + rf_blade_flush_buffer, + rf_blade_has_rssi, + rf_blade_get_rssi, + rf_blade_suppress_stdout, + rf_blade_register_error_handler, + rf_blade_open, + .srslte_rf_open_multi = rf_blade_open_multi, + rf_blade_close, + rf_blade_set_master_clock_rate, + rf_blade_is_master_clock_dynamic, + rf_blade_set_rx_srate, + rf_blade_set_rx_gain, + rf_blade_set_tx_gain, + rf_blade_get_rx_gain, + rf_blade_get_tx_gain, + rf_blade_get_info, + rf_blade_set_rx_freq, + rf_blade_set_tx_srate, + rf_blade_set_tx_freq, + rf_blade_get_time, + rf_blade_recv_with_time, + rf_blade_recv_with_time_multi, + rf_blade_send_timed, + .srslte_rf_send_timed_multi = rf_blade_send_timed_multi, + rf_blade_set_tx_cal, + rf_blade_set_rx_cal +}; +#endif + +#ifdef ENABLE_SOAPYSDR + +#include "rf_soapy_imp.h" + +static rf_dev_t dev_soapy = { + "soapy", + rf_soapy_devname, + rf_soapy_rx_wait_lo_locked, + rf_soapy_start_rx_stream, + rf_soapy_stop_rx_stream, + rf_soapy_flush_buffer, + rf_soapy_has_rssi, + rf_soapy_get_rssi, + rf_soapy_suppress_stdout, + rf_soapy_register_error_handler, + rf_soapy_open, + rf_soapy_open_multi, + rf_soapy_close, + rf_soapy_set_master_clock_rate, + rf_soapy_is_master_clock_dynamic, + rf_soapy_set_rx_srate, + rf_soapy_set_rx_gain, + rf_soapy_set_tx_gain, + rf_soapy_get_rx_gain, + rf_soapy_get_tx_gain, + rf_soapy_get_info, + rf_soapy_set_rx_freq, + rf_soapy_set_tx_srate, + rf_soapy_set_tx_freq, + rf_soapy_get_time, + rf_soapy_recv_with_time, + rf_soapy_recv_with_time_multi, + rf_soapy_send_timed, + .srslte_rf_send_timed_multi = rf_soapy_send_timed_multi, + rf_soapy_set_tx_cal, + rf_soapy_set_rx_cal +}; + +#endif + +//#define ENABLE_DUMMY_DEV + +#ifdef ENABLE_DUMMY_DEV +int dummy_rcv() { + usleep(100000); + return 1; +} +void dummy_fnc() { + +} + +static rf_dev_t dev_dummy = { + "dummy", + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_fnc, + dummy_rcv, + dummy_fnc, + dummy_fnc, + dummy_fnc +}; +#endif + +static rf_dev_t *available_devices[] = { + +#ifdef ENABLE_UHD + &dev_uhd, +#endif +#ifdef ENABLE_SOAPYSDR + &dev_soapy, +#endif +#ifdef ENABLE_BLADERF + &dev_blade, +#endif +#ifdef ENABLE_DUMMY_DEV + &dev_dummy, +#endif + NULL +}; diff --git a/lib/src/phy/rf/rf_helper.h b/lib/src/phy/rf/rf_helper.h new file mode 100644 index 0000000..7cc721d --- /dev/null +++ b/lib/src/phy/rf/rf_helper.h @@ -0,0 +1,52 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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/. + * + */ + +// A bunch of helper functions to process device arguments + + +#define REMOVE_SUBSTRING_WITHCOMAS(S, TOREMOVE) \ + remove_substring(S, TOREMOVE ",");\ + remove_substring(S, TOREMOVE ", ");\ + remove_substring(S, "," TOREMOVE);\ + remove_substring(S, ", " TOREMOVE);\ + remove_substring(S, TOREMOVE) + +static void remove_substring(char *s,const char *toremove) { + while((s=strstr(s,toremove))) { + memmove(s,s+strlen(toremove),1+strlen(s+strlen(toremove))); + } +} + +static void copy_subdev_string(char *dst, char *src) { + int n = 0; + size_t len = strlen(src); + /* Copy until end of string or comma */ + while (n < len && src[n] != '\0' && src[n] != ',') { + dst[n] = src[n]; + n++; + } + dst[n] = '\0'; +} \ No newline at end of file diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c new file mode 100644 index 0000000..eec44ef --- /dev/null +++ b/lib/src/phy/rf/rf_imp.c @@ -0,0 +1,366 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 + +#include "srslte/phy/rf/rf.h" +#include "srslte/srslte.h" +#include "rf_dev.h" + +int rf_get_available_devices(char **devnames, int max_strlen) { + int i=0; + while(available_devices[i]->name) { + strncpy(devnames[i], available_devices[i]->name, max_strlen); + i++; + } + return i; +} + +double srslte_rf_set_rx_gain_th(srslte_rf_t *rf, double gain) +{ + if (gain > rf->cur_rx_gain + 2 || gain < rf->cur_rx_gain - 2){ + pthread_mutex_lock(&rf->mutex); + rf->new_rx_gain = gain; + pthread_cond_signal(&rf->cond); + pthread_mutex_unlock(&rf->mutex); + } + return rf->cur_rx_gain; +} + +void srslte_rf_set_tx_rx_gain_offset(srslte_rf_t *rf, double offset) { + rf->tx_rx_gain_offset = offset; +} + +/* This thread listens for set_rx_gain commands to the USRP */ +static void* thread_gain_fcn(void *h) { + srslte_rf_t* rf = (srslte_rf_t*) h; + + while(1) { + pthread_mutex_lock(&rf->mutex); + while(rf->cur_rx_gain == rf->new_rx_gain) + { + pthread_cond_wait(&rf->cond, &rf->mutex); + } + if (rf->new_rx_gain != rf->cur_rx_gain) { + srslte_rf_set_rx_gain(h, rf->new_rx_gain); + rf->cur_rx_gain = srslte_rf_get_rx_gain(h); + rf->new_rx_gain = rf->cur_rx_gain; + } + if (rf->tx_gain_same_rx) { + printf("setting also tx\n"); + srslte_rf_set_tx_gain(h, rf->cur_rx_gain+rf->tx_rx_gain_offset); + } + pthread_mutex_unlock(&rf->mutex); + } + return NULL; +} + + +/* Create auxiliary thread and mutexes for AGC */ +int srslte_rf_start_gain_thread(srslte_rf_t *rf, bool tx_gain_same_rx) { + rf->tx_gain_same_rx = tx_gain_same_rx; + rf->tx_rx_gain_offset = 0.0; + if (pthread_mutex_init(&rf->mutex, NULL)) { + return -1; + } + if (pthread_cond_init(&rf->cond, NULL)) { + return -1; + } + if (pthread_create(&rf->thread_gain, NULL, thread_gain_fcn, rf)) { + perror("pthread_create"); + return -1; + } + return 0; +} + +const char* srslte_rf_get_devname(srslte_rf_t *rf) { + return ((rf_dev_t*) rf->dev)->name; +} + +int srslte_rf_open_devname(srslte_rf_t *rf, char *devname, char *args, uint32_t nof_channels) { + /* Try to open the device if name is provided */ + if (devname) { + if (devname[0] != '\0') { + int i=0; + while(available_devices[i] != NULL) { + if (!strcmp(available_devices[i]->name, devname)) { + rf->dev = available_devices[i]; + return available_devices[i]->srslte_rf_open_multi(args, &rf->handler, nof_channels); + } + i++; + } + printf("Device %s not found. Switching to auto mode\n", devname); + } + } + + /* If in auto mode or provided device not found, try to open in order of apperance in available_devices[] array */ + int i=0; + while(available_devices[i] != NULL) { + if (!available_devices[i]->srslte_rf_open_multi(args, &rf->handler, nof_channels)) { + rf->dev = available_devices[i]; + return 0; + } + i++; + } + fprintf(stderr, "No compatible RF frontend found\n"); + return -1; +} + +void srslte_rf_set_tx_cal(srslte_rf_t *rf, srslte_rf_cal_t *cal) { + return ((rf_dev_t*) rf->dev)->srslte_rf_set_tx_cal(rf->handler, cal); +} + +void srslte_rf_set_rx_cal(srslte_rf_t *rf, srslte_rf_cal_t *cal) { + return ((rf_dev_t*) rf->dev)->srslte_rf_set_rx_cal(rf->handler, cal); +} + + +const char* srslte_rf_name(srslte_rf_t *rf) { + return ((rf_dev_t*) rf->dev)->srslte_rf_devname(rf->handler); +} + +bool srslte_rf_rx_wait_lo_locked(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_rx_wait_lo_locked(rf->handler); +} + +int srslte_rf_start_rx_stream(srslte_rf_t *rf, bool now) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_start_rx_stream(rf->handler, now); +} + +int srslte_rf_stop_rx_stream(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_stop_rx_stream(rf->handler); +} + +void srslte_rf_flush_buffer(srslte_rf_t *rf) +{ + ((rf_dev_t*) rf->dev)->srslte_rf_flush_buffer(rf->handler); +} + +bool srslte_rf_has_rssi(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_has_rssi(rf->handler); +} + +float srslte_rf_get_rssi(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_get_rssi(rf->handler); +} + +void srslte_rf_suppress_stdout(srslte_rf_t *rf) +{ + ((rf_dev_t*) rf->dev)->srslte_rf_suppress_stdout(rf->handler); +} + +void srslte_rf_register_error_handler(srslte_rf_t *rf, srslte_rf_error_handler_t error_handler) +{ + ((rf_dev_t*) rf->dev)->srslte_rf_register_error_handler(rf->handler, error_handler); +} + +int srslte_rf_open(srslte_rf_t *h, char *args) +{ + return srslte_rf_open_devname(h, NULL, args, 1); +} + +int srslte_rf_open_multi(srslte_rf_t *h, char *args, uint32_t nof_channels) +{ + return srslte_rf_open_devname(h, NULL, args, nof_channels); +} + +int srslte_rf_close(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_close(rf->handler); +} + +void srslte_rf_set_master_clock_rate(srslte_rf_t *rf, double rate) +{ + ((rf_dev_t*) rf->dev)->srslte_rf_set_master_clock_rate(rf->handler, rate); +} + +bool srslte_rf_is_master_clock_dynamic(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_is_master_clock_dynamic(rf->handler); +} + +double srslte_rf_set_rx_srate(srslte_rf_t *rf, double freq) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_rx_srate(rf->handler, freq); +} + +double srslte_rf_set_rx_gain(srslte_rf_t *rf, double gain) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_rx_gain(rf->handler, gain); +} + +double srslte_rf_get_rx_gain(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_get_rx_gain(rf->handler); +} + +double srslte_rf_get_tx_gain(srslte_rf_t *rf) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_get_tx_gain(rf->handler); +} + +srslte_rf_info_t *srslte_rf_get_info(srslte_rf_t *rf) { + srslte_rf_info_t *ret = NULL; + if (((rf_dev_t*) rf->dev)->srslte_rf_get_info) { + ret = ((rf_dev_t*) rf->dev)->srslte_rf_get_info(rf->handler); + } + return ret; +} + + +double srslte_rf_set_rx_freq(srslte_rf_t *rf, double freq) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_rx_freq(rf->handler, freq); +} + + +int srslte_rf_recv(srslte_rf_t *rf, void *data, uint32_t nsamples, bool blocking) +{ + return srslte_rf_recv_with_time(rf, data, nsamples, blocking, NULL, NULL); +} + +int srslte_rf_recv_multi(srslte_rf_t *rf, void **data, uint32_t nsamples, bool blocking) +{ + return srslte_rf_recv_with_time_multi(rf, data, nsamples, blocking, NULL, NULL); +} + +int srslte_rf_recv_with_time(srslte_rf_t *rf, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_recv_with_time(rf->handler, data, nsamples, blocking, secs, frac_secs); +} + +int srslte_rf_recv_with_time_multi(srslte_rf_t *rf, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_recv_with_time_multi(rf->handler, data, nsamples, blocking, secs, frac_secs); +} + +double srslte_rf_set_tx_gain(srslte_rf_t *rf, double gain) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_tx_gain(rf->handler, gain); +} + +double srslte_rf_set_tx_srate(srslte_rf_t *rf, double freq) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_tx_srate(rf->handler, freq); +} + +double srslte_rf_set_tx_freq(srslte_rf_t *rf, double freq) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_set_tx_freq(rf->handler, freq); +} + +void srslte_rf_get_time(srslte_rf_t *rf, time_t *secs, double *frac_secs) +{ + return ((rf_dev_t*) rf->dev)->srslte_rf_get_time(rf->handler, secs, frac_secs); +} + + +int srslte_rf_send_timed3(srslte_rf_t *rf, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + + return ((rf_dev_t*) rf->dev)->srslte_rf_send_timed(rf->handler, data, nsamples, secs, frac_secs, + has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int srslte_rf_send_timed_multi(srslte_rf_t *rf, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + + return ((rf_dev_t*) rf->dev)->srslte_rf_send_timed_multi(rf->handler, data, nsamples, secs, frac_secs, + true, blocking, is_start_of_burst, is_end_of_burst); +} + +int srslte_rf_send_multi(srslte_rf_t *rf, + void *data[4], + int nsamples, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + + return ((rf_dev_t*) rf->dev)->srslte_rf_send_timed_multi(rf->handler, data, nsamples, 0, 0, + false, blocking, is_start_of_burst, is_end_of_burst); +} + +int srslte_rf_send(srslte_rf_t *rf, void *data, uint32_t nsamples, bool blocking) +{ + return srslte_rf_send2(rf, data, nsamples, blocking, true, true); +} + +int srslte_rf_send2(srslte_rf_t *rf, void *data, uint32_t nsamples, bool blocking, bool start_of_burst, bool end_of_burst) +{ + return srslte_rf_send_timed3(rf, data, nsamples, 0, 0, false, blocking, start_of_burst, end_of_burst); +} + + +int srslte_rf_send_timed(srslte_rf_t *rf, + void *data, + int nsamples, + time_t secs, + double frac_secs) +{ + return srslte_rf_send_timed2(rf, data, nsamples, secs, frac_secs, true, true); +} + +int srslte_rf_send_timed2(srslte_rf_t *rf, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool is_start_of_burst, + bool is_end_of_burst) +{ + return srslte_rf_send_timed3(rf, data, nsamples, secs, frac_secs, true, true, is_start_of_burst, is_end_of_burst); +} diff --git a/lib/src/phy/rf/rf_soapy_imp.c b/lib/src/phy/rf/rf_soapy_imp.c new file mode 100644 index 0000000..a0dcc65 --- /dev/null +++ b/lib/src/phy/rf/rf_soapy_imp.c @@ -0,0 +1,925 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include + +#include "srslte/srslte.h" +#include "rf_soapy_imp.h" +#include "rf_helper.h" + +#include +#include +#include +#include +#include + +#define HAVE_ASYNC_THREAD 1 + +#define USE_TX_MTU 0 +#define SET_RF_BW 1 + +#define PRINT_RX_STATS 0 +#define PRINT_TX_STATS 0 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +typedef struct { + char *devname; + SoapySDRKwargs args; + SoapySDRDevice *device; + SoapySDRRange *ranges; + SoapySDRStream *rxStream; + SoapySDRStream *txStream; + bool tx_stream_active; + bool rx_stream_active; + srslte_rf_info_t info; + double tx_rate; + size_t rx_mtu, tx_mtu; + + srslte_rf_error_handler_t soapy_error_handler; + + bool async_thread_running; + pthread_t async_thread; + + uint32_t num_time_errors; + uint32_t num_lates; + uint32_t num_overflows; + uint32_t num_underflows; + uint32_t num_other_errors; + uint32_t num_stream_curruption; +} rf_soapy_handler_t; + + +cf_t zero_mem[64*1024]; + + +static void log_overflow(rf_soapy_handler_t *h) { + if (h->soapy_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = SRSLTE_RF_ERROR_OVERFLOW; + h->soapy_error_handler(error); + } else { + h->num_overflows++; + } +} + +static void log_late(rf_soapy_handler_t *h, bool is_rx) { + if (h->soapy_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.opt = is_rx?1:0; + error.type = SRSLTE_RF_ERROR_LATE; + h->soapy_error_handler(error); + } else { + h->num_lates++; + } +} + +static void log_underflow(rf_soapy_handler_t *h) { + if (h->soapy_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = SRSLTE_RF_ERROR_UNDERFLOW; + h->soapy_error_handler(error); + } else { + h->num_underflows++; + } +} + + +#if HAVE_ASYNC_THREAD +static void* async_thread(void *h) { + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + + while(handler->async_thread_running) { + int ret = 0; + size_t chanMask = 0; + int flags = 0; + const long timeoutUs = 400000; // arbitrarily chosen + long long timeNs; + + ret = SoapySDRDevice_readStreamStatus(handler->device, handler->txStream, &chanMask, &flags, &timeNs, timeoutUs); + if (ret == SOAPY_SDR_TIME_ERROR) { + // this is a late + log_late(handler, false); + } else if (ret == SOAPY_SDR_UNDERFLOW) { + log_underflow(handler); + } else if (ret == SOAPY_SDR_OVERFLOW) { + log_overflow(handler); + } else if (ret == SOAPY_SDR_TIMEOUT) { + // this is a timeout of the readStreamStatus call, ignoring it .. + } else if (ret == SOAPY_SDR_NOT_SUPPORTED) { + // stopping async thread + fprintf(stderr, "Receiving async metadata not supported by device. Exiting thread.\n"); + handler->async_thread_running = false; + } else { + fprintf(stderr, "Error while receiving aync metadata: %s (%d), flags=%d, channel=%zu, timeNs=%lld\n", SoapySDR_errToStr(ret), ret, flags, chanMask, timeNs); + handler->async_thread_running = false; + } + } + return NULL; +} +#endif + + +int soapy_error(void *h) +{ + return 0; +} + + +void rf_soapy_get_freq_range(void *h) +{ + // not supported +} + + +void rf_soapy_suppress_handler(const char *x) +{ + // not supported +} + + +void rf_soapy_msg_handler(const char *msg) +{ + // not supported +} + + +void rf_soapy_suppress_stdout(void *h) +{ + // not supported +} + + +void rf_soapy_register_error_handler(void *h, srslte_rf_error_handler_t new_handler) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + handler->soapy_error_handler = new_handler; +} + + +char* rf_soapy_devname(void* h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + return handler->devname; +} + + +bool rf_soapy_rx_wait_lo_locked(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*)h; + char *ret = SoapySDRDevice_readChannelSensor(handler->device, SOAPY_SDR_RX, 0, "lo_locked"); + if (ret != NULL) { + return (strcmp(ret, "true") == 0 ? true : false); + } + return true; +} + + +void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + double actual_bw = SoapySDRDevice_getBandwidth(handler->device, SOAPY_SDR_TX, 0); + char str_buf[25]; + snprintf(str_buf, sizeof(str_buf), "%f", actual_bw); + if (SoapySDRDevice_writeSetting(handler->device, "CALIBRATE_TX", str_buf)) { + printf("Error calibrating Rx\n"); + } +} + + +void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal) +{ + // not supported +} + + +int rf_soapy_start_rx_stream(void *h, bool now) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if(handler->rx_stream_active == false){ + if(SoapySDRDevice_activateStream(handler->device, handler->rxStream, 0, 0, 0) != 0) + return SRSLTE_ERROR; + handler->rx_stream_active = true; + } + return SRSLTE_SUCCESS; +} + + +int rf_soapy_start_tx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if(handler->tx_stream_active == false){ + if(SoapySDRDevice_activateStream(handler->device, handler->txStream, 0, 0, 0) != 0) + return SRSLTE_ERROR; + handler->tx_stream_active = true; + } + return SRSLTE_SUCCESS; +} + + +int rf_soapy_stop_rx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_deactivateStream(handler->device, handler->rxStream, 0, 0) != 0) + return SRSLTE_ERROR; + + handler->rx_stream_active = false; + return SRSLTE_SUCCESS; +} + + +int rf_soapy_stop_tx_stream(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if(SoapySDRDevice_deactivateStream(handler->device, handler->txStream, 0, 0) != 0) + return SRSLTE_ERROR; + + handler->tx_stream_active = false; + return SRSLTE_SUCCESS; +} + + +void rf_soapy_flush_buffer(void *h) +{ + int n; + cf_t tmp1[1024]; + cf_t tmp2[1024]; + void *data[2] = {tmp1, tmp2}; + do { + n = rf_soapy_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + + +bool rf_soapy_has_rssi(void *h) +{ + // TODO: implement rf_soapy_has_rssi() + return false; +} + + +float rf_soapy_get_rssi(void *h) +{ + printf("TODO: implement rf_soapy_get_rssi()\n"); + return 0.0; +} + + +//TODO: add multi-channel support +int rf_soapy_open_multi(char *args, void **h, uint32_t nof_rx_antennas) +{ + size_t length; + const SoapySDRKwargs *soapy_args = SoapySDRDevice_enumerate(NULL, &length); + + if (length == 0) { + printf("No Soapy devices found.\n"); + return SRSLTE_ERROR; + } + char* devname = DEVNAME_NONE; + for (size_t i = 0; i < length; i++) { + printf("Soapy has found device #%d: ", (int)i); + for (size_t j = 0; j < soapy_args[i].size; j++) { + printf("%s=%s, ", soapy_args[i].keys[j], soapy_args[i].vals[j]); + if(!strcmp(soapy_args[i].keys[j],"name") && !strcmp(soapy_args[i].vals[j], "LimeSDR-USB")){ + devname = DEVNAME_LIME; + } else if (!strcmp(soapy_args[i].keys[j],"name") && !strcmp(soapy_args[i].vals[j], "LimeSDR Mini")){ + devname = DEVNAME_LIME_MINI; + } + } + printf("\n"); + } + + SoapySDRDevice *sdr = SoapySDRDevice_make(&(soapy_args[0])); + if (sdr == NULL) { + printf("Failed to create Soapy object\n"); + return SRSLTE_ERROR; + } + + // create handler + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) malloc(sizeof(rf_soapy_handler_t)); + bzero(handler, sizeof(rf_soapy_handler_t)); + *h = handler; + handler->device = sdr; + handler->tx_stream_active = false; + handler->rx_stream_active = false; + handler->devname = devname; + + // init rx/tx rate to lowest LTE rate to avoid decimation warnings + rf_soapy_set_rx_srate(handler, 1.92e6); + rf_soapy_set_tx_srate(handler, 1.92e6); + + if(SoapySDRDevice_getNumChannels(handler->device, SOAPY_SDR_RX) > 0){ + printf("Setting up RX stream\n"); + if(SoapySDRDevice_setupStream(handler->device, &(handler->rxStream), SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) { + printf("Rx setupStream fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + handler->rx_mtu = SoapySDRDevice_getStreamMTU(handler->device, handler->rxStream); + } + + if(SoapySDRDevice_getNumChannels(handler->device, SOAPY_SDR_TX) > 0){ + printf("Setting up TX stream\n"); + if (SoapySDRDevice_setupStream(handler->device, &(handler->txStream), SOAPY_SDR_TX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0) { + printf("Tx setupStream fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + handler->tx_mtu = SoapySDRDevice_getStreamMTU(handler->device, handler->txStream); + } + + // list device sensors + size_t list_length; + char** list; + list = SoapySDRDevice_listSensors(handler->device, &list_length); + printf("Available device sensors: \n"); + for(int i = 0; i < list_length; i++) { + printf(" - %s\n", list[i]); + } + + // list channel sensors + list = SoapySDRDevice_listChannelSensors(handler->device, SOAPY_SDR_RX, 0, &list_length); + printf("Available sensors for RX channel 0: \n"); + for(int i = 0; i < list_length; i++) { + printf(" - %s\n", list[i]); + } + + // Set static radio info + SoapySDRRange tx_range = SoapySDRDevice_getGainRange(handler->device, SOAPY_SDR_TX, 0); + SoapySDRRange rx_range = SoapySDRDevice_getGainRange(handler->device, SOAPY_SDR_RX, 0); + handler->info.min_tx_gain = tx_range.minimum; + handler->info.max_tx_gain = tx_range.maximum; + handler->info.min_rx_gain = rx_range.minimum; + handler->info.max_rx_gain = rx_range.maximum; + + // Check device arguments + if (args) { + // config file + const char config_arg[] = "config="; + char config_str[64] = {0}; + char *config_ptr = strstr(args, config_arg); + if (config_ptr) { + copy_subdev_string(config_str, config_ptr + strlen(config_arg)); + printf("Loading config file %s\n", config_str); + SoapySDRDevice_writeSetting(handler->device, "LOAD_CONFIG", config_str); + remove_substring(args, config_arg); + remove_substring(args, config_str); + } + + // rx antenna + const char rx_ant_arg[] = "rxant="; + char rx_ant_str[64] = {0}; + char *rx_ant_ptr = strstr(args, rx_ant_arg); + if (rx_ant_ptr) { + copy_subdev_string(rx_ant_str, rx_ant_ptr + strlen(rx_ant_arg)); + printf("Setting Rx antenna to %s\n", rx_ant_str); + if (SoapySDRDevice_setAntenna(handler->device, SOAPY_SDR_RX, 0, rx_ant_str) != 0) { + fprintf(stderr, "Failed to set Rx antenna.\n"); + } + remove_substring(args, rx_ant_arg); + remove_substring(args, rx_ant_str); + } + + // tx antenna + const char tx_ant_arg[] = "txant="; + char tx_ant_str[64] = {0}; + char *tx_ant_ptr = strstr(args, tx_ant_arg); + if (tx_ant_ptr) { + copy_subdev_string(tx_ant_str, tx_ant_ptr + strlen(tx_ant_arg)); + printf("Setting Tx antenna to %s\n", tx_ant_str); + if (SoapySDRDevice_setAntenna(handler->device, SOAPY_SDR_TX, 0, tx_ant_str) != 0) { + fprintf(stderr, "Failed to set Tx antenna.\n"); + } + remove_substring(args, tx_ant_arg); + remove_substring(args, tx_ant_str); + } + + // log level + const char loglevel_arg[] = "loglevel="; + char loglevel_str[64] = {0}; + char *loglevel_ptr = strstr(args, loglevel_arg); + if (loglevel_ptr) { + copy_subdev_string(loglevel_str, loglevel_ptr + strlen(loglevel_arg)); + if (strcmp(loglevel_str, "error") == 0) { + SoapySDR_setLogLevel(SOAPY_SDR_ERROR); + } + remove_substring(args, loglevel_arg); + remove_substring(args, loglevel_str); + } + } + +#if HAVE_ASYNC_THREAD + bool start_async_thread = true; + if (strstr(args, "silent")) { + REMOVE_SUBSTRING_WITHCOMAS(args, "silent"); + start_async_thread = false; + } +#endif + + // receive one subframe to allow for transceiver calibration + if (strstr(devname, "lime")) { + // set default tx gain and leave some time to calibrate tx + rf_soapy_set_tx_gain(handler, 45); + rf_soapy_set_rx_gain(handler, 35); + + cf_t dummy_buffer[1920]; + cf_t *dummy_buffer_array[SRSLTE_MAX_PORTS]; + dummy_buffer_array[0] = dummy_buffer; + rf_soapy_start_rx_stream(handler, true); + rf_soapy_recv_with_time_multi(handler, (void**)dummy_buffer_array, 1920, false, NULL, NULL); + rf_soapy_stop_rx_stream(handler); + + usleep(10000); + } + + // list gains and AGC mode + bool has_agc = SoapySDRDevice_hasGainMode(handler->device, SOAPY_SDR_RX, 0); + list = SoapySDRDevice_listGains(handler->device, SOAPY_SDR_RX, 0, &list_length); + printf("State of gain elements for Rx channel 0 (AGC %s):\n", has_agc ? "supported":"not supported"); + for(int i = 0; i < list_length; i++) { + printf(" - %s: %.2f dB\n", list[i], SoapySDRDevice_getGainElement(handler->device, SOAPY_SDR_RX, 0, list[i])); + } + + has_agc = SoapySDRDevice_hasGainMode(handler->device, SOAPY_SDR_TX, 0); + printf("State of gain elements for Tx channel 0 (AGC %s):\n", has_agc ? "supported":"not supported"); + for(int i = 0; i < list_length; i++) { + printf(" - %s: %.2f dB\n", list[i], SoapySDRDevice_getGainElement(handler->device, SOAPY_SDR_TX, 0, list[i])); + } + + // print actual antenna configuration + char *ant = SoapySDRDevice_getAntenna(handler->device, SOAPY_SDR_RX, 0); + printf("Rx antenna set to %s\n", ant); + + ant = SoapySDRDevice_getAntenna(handler->device, SOAPY_SDR_TX, 0); + printf("Tx antenna set to %s\n", ant); + +#if HAVE_ASYNC_THREAD + if (start_async_thread) { + // Start low priority thread to receive async commands + handler->async_thread_running = true; + if (pthread_create(&handler->async_thread, NULL, async_thread, handler)) { + perror("pthread_create"); + return -1; + } + } +#endif + + return SRSLTE_SUCCESS; +} + + +int rf_soapy_open(char *args, void **h) +{ + return rf_soapy_open_multi(args, h, 1); +} + + +int rf_soapy_close(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + +#if HAVE_ASYNC_THREAD + if (handler->async_thread_running) { + handler->async_thread_running = false; + pthread_join(handler->async_thread, NULL); + } +#endif + + if (handler->tx_stream_active) { + rf_soapy_stop_tx_stream(handler); + SoapySDRDevice_closeStream(handler->device, handler->txStream); + } + + if (handler->rx_stream_active) { + rf_soapy_stop_rx_stream(handler); + SoapySDRDevice_closeStream(handler->device, handler->rxStream); + } + + SoapySDRDevice_unmake(handler->device); + free(handler); + + // print statistics + if (handler->num_lates) printf("#lates=%d\n", handler->num_lates); + if (handler->num_overflows) printf("#overflows=%d\n", handler->num_overflows); + if (handler->num_underflows) printf("#underflows=%d\n", handler->num_underflows); + if (handler->num_time_errors) printf("#time_errors=%d\n", handler->num_time_errors); + if (handler->num_other_errors) printf("#other_errors=%d\n", handler->num_other_errors); + + return SRSLTE_SUCCESS; +} + +void rf_soapy_set_master_clock_rate(void *h, double rate) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setMasterClockRate(handler->device, rate) != 0) { + printf("rf_soapy_set_master_clock_rate Rx fail: %s\n", SoapySDRDevice_lastError()); + } + + printf("Set master clock rate to %.2f MHz\n", SoapySDRDevice_getMasterClockRate(handler->device)/1e6); +} + + +bool rf_soapy_is_master_clock_dynamic(void *h) +{ + printf("TODO: implement rf_soapy_is_master_clock_dynamic()\n"); + return false; +} + + +double rf_soapy_set_rx_srate(void *h, double rate) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + + // Restart streaming, as the Lime seems to have problems reconfiguring the sample rate during streaming + bool rx_stream_active = handler->rx_stream_active; + if (rx_stream_active) { + rf_soapy_stop_rx_stream(handler); + } + + if (SoapySDRDevice_setSampleRate(handler->device, SOAPY_SDR_RX, 0, rate) != 0) { + printf("setSampleRate Rx fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + +#if SET_RF_BW + // Set bandwidth close to current rate + size_t bw_length; + SoapySDRRange *bw_range = SoapySDRDevice_getBandwidthRange(handler->device, SOAPY_SDR_RX, 0, &bw_length); + double bw = rate * 0.75; + bw = MIN(bw, bw_range->maximum); + bw = MAX(bw, bw_range->minimum); + bw = MAX(bw, 2.5e6); // For the Lime to avoid warnings + if (SoapySDRDevice_setBandwidth(handler->device, SOAPY_SDR_RX, 0, bw) != 0) { + printf("setBandwidth fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + printf("Set Rx bandwidth to %.2f MHz\n", SoapySDRDevice_getBandwidth(handler->device, SOAPY_SDR_RX, 0)/1e6); +#endif + + if (rx_stream_active) { + rf_soapy_start_rx_stream(handler, true); + } + + return SoapySDRDevice_getSampleRate(handler->device, SOAPY_SDR_RX,0); +} + +double rf_soapy_set_tx_srate(void *h, double rate) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + + // stop/start streaming during rate reconfiguration + bool rx_stream_active = handler->rx_stream_active; + if (handler->rx_stream_active) { + rf_soapy_stop_rx_stream(handler); + } + + if (SoapySDRDevice_setSampleRate(handler->device, SOAPY_SDR_TX, 0, rate) != 0) { + printf("setSampleRate Tx fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + +#if SET_RF_BW + size_t bw_length; + SoapySDRRange *bw_range = SoapySDRDevice_getBandwidthRange(handler->device, SOAPY_SDR_TX, 0, &bw_length); + // try to set the BW a bit narrower than sampling rate to prevent aliasing but make sure to stay within device boundaries + double bw = rate * 0.75; + bw = MAX(bw, bw_range->minimum); + bw = MIN(bw, bw_range->maximum); + if (SoapySDRDevice_setBandwidth(handler->device, SOAPY_SDR_TX, 0, bw) != 0) { + printf("setBandwidth fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + printf("Set Tx bandwidth to %.2f MHz\n", SoapySDRDevice_getBandwidth(handler->device, SOAPY_SDR_TX, 0)/1e6); +#endif + + if (rx_stream_active) { + rf_soapy_start_rx_stream(handler, true); + } + + handler->tx_rate = SoapySDRDevice_getSampleRate(handler->device, SOAPY_SDR_TX, 0); + return handler->tx_rate; +} + + +double rf_soapy_set_rx_gain(void *h, double gain) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setGain(handler->device, SOAPY_SDR_RX, 0, gain) != 0) + { + printf("setGain fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + return rf_soapy_get_rx_gain(h); +} + + +double rf_soapy_set_tx_gain(void *h, double gain) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setGain(handler->device, SOAPY_SDR_TX, 0, gain) != 0) + { + printf("setGain fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + return rf_soapy_get_tx_gain(h); +} + + +double rf_soapy_get_rx_gain(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + return SoapySDRDevice_getGain(handler->device, SOAPY_SDR_RX, 0); +} + + +double rf_soapy_get_tx_gain(void *h) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + return SoapySDRDevice_getGain(handler->device, SOAPY_SDR_TX, 0); +} + + +srslte_rf_info_t * rf_soapy_get_info(void *h) +{ + srslte_rf_info_t *info = NULL; + if (h) { + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + info = &handler->info; + } + return info; +} + + +double rf_soapy_set_rx_freq(void *h, double freq) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setFrequency(handler->device, SOAPY_SDR_RX, 0, freq, NULL) != 0) + { + printf("setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + printf("Tuned Rx to %.2f MHz\n", SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_RX, 0)/1e6); + + // wait until LO is locked + rf_soapy_rx_wait_lo_locked(handler); + + return SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_RX, 0); +} + +double rf_soapy_set_tx_freq(void *h, double freq) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + if (SoapySDRDevice_setFrequency(handler->device, SOAPY_SDR_TX, 0, freq, NULL) != 0) + { + printf("setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return SRSLTE_ERROR; + } + + printf("Tuned Tx to %.2f MHz\n", SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_TX, 0)/1e6); + + return SoapySDRDevice_getFrequency(handler->device, SOAPY_SDR_TX, 0); +} + + +void rf_soapy_get_time(void *h, time_t *secs, double *frac_secs) +{ + printf("Todo: implement rf_soapy_get_time()\n"); +} + + +//TODO: add multi-channel support +int rf_soapy_recv_with_time_multi(void *h, + void *data[SRSLTE_MAX_PORTS], + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; + int flags; //flags set by receive operation + int num_channels = 1; // temp + const long timeoutUs = 400000; // arbitrarily chosen + + int trials = 0; + int ret = 0; + long long timeNs; //timestamp for receive buffer + int n = 0; + +#if PRINT_RX_STATS + printf("rx: nsamples=%d rx_mtu=%zd\n", nsamples, handler->rx_mtu); +#endif + + do { + size_t rx_samples = MIN(nsamples - n, handler->rx_mtu); +#if PRINT_RX_STATS + printf(" - rx_samples=%zd\n", rx_samples); +#endif + + void *buffs_ptr[4]; + for (int i=0; idevice, handler->rxStream, buffs_ptr, rx_samples, &flags, &timeNs, timeoutUs); + if (ret == SOAPY_SDR_OVERFLOW || (ret > 0 && (flags & SOAPY_SDR_END_ABRUPT) != 0)) { + log_overflow(handler); + continue; + } else + if (ret == SOAPY_SDR_TIMEOUT) { + log_late(handler, true); + continue; + } else + if (ret < 0) { + // unspecific error + printf("SoapySDRDevice_readStream returned %d: %s\n", ret, SoapySDR_errToStr(ret)); + handler->num_other_errors++; + } + + // update rx time only for first segment + if (secs != NULL && frac_secs != NULL && n == 0) { + *secs = timeNs / 1e9; + *frac_secs = (timeNs % 1000000000)/1e9; + //printf("rx_time: secs=%lld, frac_secs=%lf timeNs=%llu\n", *secs, *frac_secs, timeNs); + } + +#if PRINT_RX_STATS + printf(" - rx: %d/%zd\n", ret, rx_samples); +#endif + + n += ret; + trials++; + } while (n < nsamples && trials < 100); + + return n; +} + + +int rf_soapy_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return rf_soapy_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + + +int rf_soapy_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + void *_data[SRSLTE_MAX_PORTS]= {data, zero_mem, zero_mem, zero_mem}; + return rf_soapy_send_timed_multi(h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + + +// Todo: Check correct handling of flags, use RF metrics API, fix timed transmissions +int rf_soapy_send_timed_multi(void *h, + void *data[SRSLTE_MAX_PORTS], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_soapy_handler_t *handler = (rf_soapy_handler_t *) h; + int flags = 0; + const long timeoutUs = 100000; // arbitrarily chosen + long long timeNs = 0; + int trials = 0; + int ret = 0; + int n = 0; + +#if PRINT_TX_STATS + printf("tx: namples=%d, mtu=%zd\n", nsamples, handler->tx_mtu); +#endif + + if (!handler->tx_stream_active) { + rf_soapy_start_tx_stream(h); + } + + // Convert initial tx time + if (has_time_spec) { + timeNs = secs * 1000000000; + timeNs = timeNs + (frac_secs * 1000000000); + } + + do { +#if USE_TX_MTU + size_t tx_samples = MIN(nsamples - n, handler->tx_mtu); +#else + size_t tx_samples = nsamples; + if (tx_samples > nsamples - n) { + tx_samples = nsamples - n; + } +#endif + + // (re-)set stream flags + flags = 0; + if (is_start_of_burst && is_end_of_burst) { + flags |= SOAPY_SDR_ONE_PACKET; + } + + if (is_end_of_burst) { + flags |= SOAPY_SDR_END_BURST; + } + + // only set time flag for first tx + if(has_time_spec && n == 0) { + flags |= SOAPY_SDR_HAS_TIME; + } + +#if PRINT_TX_STATS + printf(" - tx_samples=%zd at timeNs=%llu flags=%d\n", tx_samples, timeNs, flags); +#endif + + ret = SoapySDRDevice_writeStream(handler->device, handler->txStream, (const void *)data, tx_samples, &flags, timeNs, timeoutUs); + if (ret >= 0) { + // Tx was ok +#if PRINT_TX_STATS + printf(" - tx: %d/%zd\n", ret, tx_samples); +#endif + // Advance tx time + if (has_time_spec && ret < nsamples) { + long long adv = SoapySDR_ticksToTimeNs(ret, handler->tx_rate); +#if PRINT_TX_STATS + printf(" - tx: timeNs_old=%llu, adv=%llu, timeNs_new=%llu, tx_rate=%f\n", timeNs, adv, timeNs+adv, handler->tx_rate); +#endif + timeNs += adv; + } + n += ret; + } + else + if (ret < 0) { + // An error has occured + switch (ret) { + case SOAPY_SDR_TIMEOUT: + log_late(handler, false); + printf("L"); + break; + case SOAPY_SDR_STREAM_ERROR: + handler->num_stream_curruption++; + printf("E"); + break; + case SOAPY_SDR_TIME_ERROR: + handler->num_time_errors++; + printf("T"); + break; + case SOAPY_SDR_UNDERFLOW: + log_underflow(handler); + printf("U"); + break; + default: + fprintf(stderr, "Error during writeStream\n"); + exit(-1); + return SRSLTE_ERROR; + } + } + trials++; + } while (n < nsamples && trials < 100); + + if (n != nsamples) { + fprintf(stderr, "Couldn't write all samples after %d trials.\n", trials); + } + + return n; +} diff --git a/lib/src/phy/rf/rf_soapy_imp.h b/lib/src/phy/rf/rf_soapy_imp.h new file mode 100644 index 0000000..5b17873 --- /dev/null +++ b/lib/src/phy/rf/rf_soapy_imp.h @@ -0,0 +1,131 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/config.h" +#include "srslte/phy/rf/rf.h" +#define DEVNAME_NONE "none" +#define DEVNAME_LIME "lime" +#define DEVNAME_LIME_MINI "lime_mini" + +SRSLTE_API int rf_soapy_open(char *args, + void **handler); + +SRSLTE_API int rf_soapy_open_multi(char *args, + void **handler, + uint32_t nof_rx_antennas); + +SRSLTE_API char* rf_soapy_devname(void *h); + +SRSLTE_API int rf_soapy_close(void *h); + +SRSLTE_API void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API int rf_soapy_start_rx_stream(void *h, bool now); + +SRSLTE_API int rf_soapy_stop_rx_stream(void *h); + +SRSLTE_API void rf_soapy_flush_buffer(void *h); + +SRSLTE_API bool rf_soapy_has_rssi(void *h); + +SRSLTE_API float rf_soapy_get_rssi(void *h); + +SRSLTE_API bool rf_soapy_rx_wait_lo_locked(void *h); + +SRSLTE_API void rf_soapy_set_master_clock_rate(void *h, + double rate); + +SRSLTE_API bool rf_soapy_is_master_clock_dynamic(void *h); + +SRSLTE_API double rf_soapy_set_rx_srate(void *h, + double freq); + +SRSLTE_API double rf_soapy_set_rx_gain(void *h, + double gain); + +SRSLTE_API double rf_soapy_get_rx_gain(void *h); + +SRSLTE_API double rf_soapy_set_tx_gain(void *h, + double gain); + +SRSLTE_API double rf_soapy_get_tx_gain(void *h); + +SRSLTE_API srslte_rf_info_t *rf_soapy_get_info(void *h); + +SRSLTE_API void rf_soapy_suppress_stdout(void *h); + +SRSLTE_API void rf_soapy_register_error_handler(void *h, srslte_rf_error_handler_t error_handler); + +SRSLTE_API double rf_soapy_set_rx_freq(void *h, + double freq); + +SRSLTE_API int rf_soapy_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_soapy_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double rf_soapy_set_tx_srate(void *h, + double freq); + +SRSLTE_API double rf_soapy_set_tx_freq(void *h, + double freq); + +SRSLTE_API void rf_soapy_get_time(void *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_soapy_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +int rf_soapy_send_timed_multi(void *h, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); diff --git a/lib/src/phy/rf/rf_uhd_imp.c b/lib/src/phy/rf/rf_uhd_imp.c new file mode 100644 index 0000000..7fe2914 --- /dev/null +++ b/lib/src/phy/rf/rf_uhd_imp.c @@ -0,0 +1,945 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "rf_uhd_imp.h" +#include "rf_helper.h" +#include "uhd_c_api.h" + +#define HAVE_ASYNC_THREAD 1 + +typedef struct { + char *devname; + uhd_usrp_handle usrp; + uhd_rx_streamer_handle rx_stream; + uhd_tx_streamer_handle tx_stream; + + uhd_rx_metadata_handle rx_md, rx_md_first; + uhd_tx_metadata_handle tx_md; + + srslte_rf_info_t info; + size_t rx_nof_samples; + size_t tx_nof_samples; + double tx_rate; + bool dynamic_rate; + bool has_rssi; + uint32_t nof_rx_channels; + int nof_tx_channels; + + srslte_rf_error_handler_t uhd_error_handler; + + float current_master_clock; + + bool async_thread_running; + pthread_t async_thread; + + pthread_mutex_t tx_mutex; +} rf_uhd_handler_t; + +void suppress_handler(const char *x) +{ + // do nothing +} + +cf_t zero_mem[64*1024]; + +static void log_overflow(rf_uhd_handler_t *h) { + if (h->uhd_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = SRSLTE_RF_ERROR_OVERFLOW; + h->uhd_error_handler(error); + } +} + +static void log_late(rf_uhd_handler_t *h, bool is_rx) { + if (h->uhd_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.opt = is_rx?1:0; + error.type = SRSLTE_RF_ERROR_LATE; + h->uhd_error_handler(error); + } +} + +#if HAVE_ASYNC_THREAD +static void log_underflow(rf_uhd_handler_t *h) { + if (h->uhd_error_handler) { + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = SRSLTE_RF_ERROR_UNDERFLOW; + h->uhd_error_handler(error); + } +} +#endif + +static void log_rx_error(rf_uhd_handler_t *h) { + if (h->uhd_error_handler) { + char error_string[512]; + uhd_usrp_last_error(h->usrp, error_string, 512); + fprintf(stderr, "USRP reported the following error: %s\n", error_string); + + srslte_rf_error_t error; + bzero(&error, sizeof(srslte_rf_error_t)); + error.type = SRSLTE_RF_ERROR_RX; + h->uhd_error_handler(error); + } +} + +#if HAVE_ASYNC_THREAD +static void* async_thread(void *h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + uhd_async_metadata_handle md; + uhd_async_metadata_make(&md); + while(handler->async_thread_running) { + bool valid; + uhd_error err = uhd_tx_streamer_recv_async_msg(handler->tx_stream, &md, 0.5, &valid); + if (err == UHD_ERROR_NONE) { + if (valid) { + uhd_async_metadata_event_code_t event_code; + uhd_async_metadata_event_code(md, &event_code); + if (event_code == UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW || + event_code == UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW_IN_PACKET) { + log_underflow(handler); + } else if (event_code == UHD_ASYNC_METADATA_EVENT_CODE_TIME_ERROR) { + log_late(handler, false); + } + } + } else { + fprintf(stderr, "Error while receiving aync metadata: 0x%x\n", err); + return NULL; + } + } + uhd_async_metadata_free(&md); + return NULL; +} +#endif + +void rf_uhd_suppress_stdout(void *h) { + rf_uhd_register_msg_handler_c(suppress_handler); +} + +void rf_uhd_register_error_handler(void *h, srslte_rf_error_handler_t new_handler) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + handler->uhd_error_handler = new_handler; +} + +static bool find_string(uhd_string_vector_handle h, char *str) +{ + char buff[128]; + size_t n; + uhd_string_vector_size(h, &n); + for (int i=0;iusrp, sensor_name, 0, value_h); + } else { + uhd_usrp_get_mboard_sensor(handler->usrp, sensor_name, 0, value_h); + } + uhd_sensor_value_to_bool(*value_h, &val_out); + } else { + usleep(500); + val_out = true; + } + + return val_out; +} + +char* rf_uhd_devname(void* h) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + return handler->devname; +} + +bool rf_uhd_rx_wait_lo_locked(void *h) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + + uhd_string_vector_handle mb_sensors; + uhd_string_vector_handle rx_sensors; + char *sensor_name; + uhd_sensor_value_handle value_h; + uhd_string_vector_make(&mb_sensors); + uhd_string_vector_make(&rx_sensors); + uhd_sensor_value_make_from_bool(&value_h, "", true, "True", "False"); + uhd_usrp_get_mboard_sensor_names(handler->usrp, 0, &mb_sensors); + uhd_usrp_get_rx_sensor_names(handler->usrp, 0, &rx_sensors); + + /*if (find_string(rx_sensors, "lo_locked")) { + sensor_name = "lo_locked"; + } else */if (find_string(mb_sensors, "ref_locked")) { + sensor_name = "ref_locked"; + } else { + sensor_name = NULL; + } + + double report = 0.0; + while (!isLocked(handler, sensor_name, false, &value_h) && report < 30.0) { + report += 0.1; + usleep(1000); + } + + bool val = isLocked(handler, sensor_name, false, &value_h); + + uhd_string_vector_free(&mb_sensors); + uhd_string_vector_free(&rx_sensors); + uhd_sensor_value_free(&value_h); + + return val; +} + +void rf_uhd_set_tx_cal(void *h, srslte_rf_cal_t *cal) +{ + +} + +void rf_uhd_set_rx_cal(void *h, srslte_rf_cal_t *cal) +{ + +} + + +int rf_uhd_start_rx_stream(void *h, bool now) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + + uhd_stream_cmd_t stream_cmd = { + .stream_mode = UHD_STREAM_MODE_START_CONTINUOUS, + .stream_now = now + }; + if (!now) { + uhd_usrp_get_time_now(handler->usrp, 0, &stream_cmd.time_spec_full_secs, &stream_cmd.time_spec_frac_secs); + stream_cmd.time_spec_frac_secs += 0.2; + if (stream_cmd.time_spec_frac_secs > 1) { + stream_cmd.time_spec_frac_secs -= 1; + stream_cmd.time_spec_full_secs += 1; + } + } + uhd_rx_streamer_issue_stream_cmd(handler->rx_stream, &stream_cmd); + return 0; +} + +int rf_uhd_stop_rx_stream(void *h) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + uhd_stream_cmd_t stream_cmd = { + .stream_mode = UHD_STREAM_MODE_STOP_CONTINUOUS, + .stream_now = true + }; + uhd_rx_streamer_issue_stream_cmd(handler->rx_stream, &stream_cmd); + return 0; +} + +void rf_uhd_flush_buffer(void *h) +{ + int n; + cf_t tmp1[1024]; + cf_t tmp2[1024]; + void *data[2] = {tmp1, tmp2}; + do { + n = rf_uhd_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + +bool rf_uhd_has_rssi(void *h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + return handler->has_rssi; +} + +bool get_has_rssi(void *h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + uhd_string_vector_handle rx_sensors; + uhd_string_vector_make(&rx_sensors); + uhd_usrp_get_rx_sensor_names(handler->usrp, 0, &rx_sensors); + bool ret = find_string(rx_sensors, "rssi"); + uhd_string_vector_free(&rx_sensors); + return ret; +} + +float rf_uhd_get_rssi(void *h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + if (handler->has_rssi) { + double val_out; + + uhd_sensor_value_handle rssi_value; + uhd_sensor_value_make_from_realnum(&rssi_value, "rssi", 0, "dBm", "%f"); + uhd_usrp_get_rx_sensor(handler->usrp, "rssi", 0, &rssi_value); + uhd_sensor_value_to_realnum(rssi_value, &val_out); + uhd_sensor_value_free(&rssi_value); + + return val_out; + } else { + return 0.0; + } +} + +int rf_uhd_open(char *args, void **h) +{ + return rf_uhd_open_multi(args, h, 1); +} + +int rf_uhd_open_multi(char *args, void **h, uint32_t nof_channels) +{ + if (h) { + *h = NULL; + + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) malloc(sizeof(rf_uhd_handler_t)); + if (!handler) { + perror("malloc"); + return -1; + } + bzero(handler, sizeof(rf_uhd_handler_t)); + *h = handler; + + /* Set priority to UHD threads */ + uhd_set_thread_priority(uhd_default_thread_priority, true); + + /* Find available devices */ + uhd_string_vector_handle devices_str; + uhd_string_vector_make(&devices_str); + uhd_usrp_find("", &devices_str); + + char args2[512]; + + handler->dynamic_rate = true; + + // Allow NULL parameter + if (args == NULL) { + args = ""; + } + handler->devname = NULL; + + pthread_mutex_init(&handler->tx_mutex, NULL); + + // Initialize handler + handler->uhd_error_handler = NULL; + + bzero(zero_mem, sizeof(cf_t)*64*1024); + + // Check external clock argument + enum {DEFAULT, EXTERNAL, GPSDO} clock_src; + if (strstr(args, "clock=external")) { + REMOVE_SUBSTRING_WITHCOMAS(args, "clock=external"); + clock_src = EXTERNAL; + } else if (strstr(args, "clock=gpsdo")) { + printf("Using GPSDO clock\n"); + REMOVE_SUBSTRING_WITHCOMAS(args, "clock=gpsdo"); + clock_src = GPSDO; + } else { + clock_src = DEFAULT; + } + + bool start_async_thread = true; + + if (strstr(args, "silent")) { + REMOVE_SUBSTRING_WITHCOMAS(args, "silent"); + start_async_thread = false; + } + + // Set over the wire format + char *otw_format = "sc16"; + if (strstr(args, "otw_format=sc12")) { + REMOVE_SUBSTRING_WITHCOMAS(args, "otw_format=sc12"); + otw_format = "sc12"; + } else if (strstr(args, "otw_format=sc16")) { + REMOVE_SUBSTRING_WITHCOMAS(args, "otw_format=sc16"); + /* Do nothing */ + } else if (strstr(args, "otw_format=")) { + fprintf(stderr, "Wrong over the wire format. Valid formats: sc12, sc16\n"); + return -1; + } + + // Set transmitter subdevice spec string + const char tx_subdev_arg[] = "tx_subdev_spec="; + char tx_subdev_str[64] = {0}; + char *tx_subdev_ptr = strstr(args, tx_subdev_arg); + if (tx_subdev_ptr) { + copy_subdev_string(tx_subdev_str, tx_subdev_ptr + strlen(tx_subdev_arg)); + } + + // Set receiver subdevice spec string + const char rx_subdev_arg[] = "rx_subdev_spec="; + char rx_subdev_str[64] = {0}; + char *rx_subdev_ptr = strstr(args, rx_subdev_arg); + if (rx_subdev_ptr) { + copy_subdev_string(rx_subdev_str, rx_subdev_ptr + strlen(rx_subdev_arg)); + } + + if (tx_subdev_ptr) { + remove_substring(args, tx_subdev_arg); + remove_substring(args, tx_subdev_str); + } + + if (rx_subdev_ptr) { + remove_substring(args, rx_subdev_arg); + remove_substring(args, rx_subdev_str); + } + + /* If device type or name not given in args, choose a B200 */ + if (args[0]=='\0') { + if (find_string(devices_str, "type=b200") && !strstr(args, "recv_frame_size")) { + // If B200 is available, use it + args = "type=b200,master_clock_rate=30.72e6"; + handler->current_master_clock = 30720000; + handler->devname = DEVNAME_B200; + } else if (find_string(devices_str, "type=x300")) { + // Else if X300 is available, set master clock rate now (can't be changed later) + args = "type=x300,master_clock_rate=184.32e6"; + handler->current_master_clock = 184320000; + handler->dynamic_rate = false; + handler->devname = DEVNAME_X300; + } else if (find_string(devices_str, "type=e3x0")) { + // Else if E3X0 is available, set master clock rate now (can't be changed later) + args = "type=e3x0,master_clock_rate=30.72e6"; + handler->dynamic_rate = false; + handler->devname = DEVNAME_E3X0; + } else if (find_string(devices_str, "type=n3xx")) { + args = "type=n3xx,master_clock_rate=122.88e6"; + handler->current_master_clock = 122880000; + handler->dynamic_rate = false; + handler->devname = DEVNAME_N300; + srslte_use_standard_symbol_size(true); + } + } else { + // If args is set and x300 type is specified, make sure master_clock_rate is defined + if (strstr(args, "type=x300") && !strstr(args, "master_clock_rate")) { + sprintf(args2, "%s,master_clock_rate=184.32e6",args); + args = args2; + handler->current_master_clock = 184320000; + handler->dynamic_rate = false; + handler->devname = DEVNAME_X300; + } else if (strstr(args, "type=n3xx")) { + sprintf(args2, "%s,master_clock_rate=122.88e6", args); + args = args2; + handler->current_master_clock = 122880000; + handler->dynamic_rate = false; + handler->devname = DEVNAME_N300; + srslte_use_standard_symbol_size(true); + } else if (strstr(args, "type=e3x0")) { + snprintf(args2, sizeof(args2), "%s,master_clock_rate=30.72e6", args); + args = args2; + handler->devname = DEVNAME_E3X0; + } else { + snprintf(args2, sizeof(args2), "%s,master_clock_rate=30.72e6", args); + args = args2; + handler->current_master_clock = 30720000; + handler->devname = DEVNAME_B200; + } + } + + uhd_string_vector_free(&devices_str); + + /* Create UHD handler */ + printf("Opening USRP with args: %s\n", args); + uhd_error error = uhd_usrp_make(&handler->usrp, args); + if (error) { + fprintf(stderr, "Error opening UHD: code %d\n", error); + return -1; + } + + /* Set transmitter subdev spec if specified */ + if (strlen(tx_subdev_str)) { + uhd_subdev_spec_handle subdev_spec_handle = {0}; + + printf("Setting tx_subdev_spec to '%s'\n", tx_subdev_str); + + uhd_subdev_spec_make(&subdev_spec_handle, tx_subdev_str); + uhd_usrp_set_tx_subdev_spec(handler->usrp, subdev_spec_handle, 0); + uhd_subdev_spec_free(&subdev_spec_handle); + } + + /* Set receiver subdev spec if specified */ + if (strlen(rx_subdev_str)) { + uhd_subdev_spec_handle subdev_spec_handle = {0}; + + printf("Setting rx_subdev_spec to '%s'\n", rx_subdev_str); + + uhd_subdev_spec_make(&subdev_spec_handle, rx_subdev_str); + uhd_usrp_set_rx_subdev_spec(handler->usrp, subdev_spec_handle, 0); + uhd_subdev_spec_free(&subdev_spec_handle); + } + + if (!handler->devname) { + char dev_str[1024]; + uhd_usrp_get_mboard_name(handler->usrp, 0, dev_str, 1024); + if (strstr(dev_str, "B2") || strstr(dev_str, "B2")) { + handler->devname = DEVNAME_B200; + } else if (strstr(dev_str, "X3") || strstr(dev_str, "X3")) { + handler->devname = DEVNAME_X300; + } else if (strstr(dev_str, "n3xx")) { + handler->devname = DEVNAME_N300; + } + } + if (!handler->devname) { + handler->devname = "uhd_unknown"; + } + + // Set external clock reference + if (clock_src == EXTERNAL) { + uhd_usrp_set_clock_source(handler->usrp, "external", 0); + } else if (clock_src == GPSDO) { + uhd_usrp_set_clock_source(handler->usrp, "gpsdo", 0); + } + + handler->has_rssi = get_has_rssi(handler); + + size_t channel[4] = {0, 1, 2, 3}; + uhd_stream_args_t stream_args = { + .cpu_format = "fc32", + .otw_format = otw_format, + .args = "", + .channel_list = channel, + .n_channels = nof_channels, + }; + + handler->nof_rx_channels = nof_channels; + handler->nof_tx_channels = nof_channels; + + /* Set default rate to avoid decimation warnings */ + for (int i=0;iusrp, 1.92e6, i); + uhd_usrp_set_tx_rate(handler->usrp, 1.92e6, i); + } + + if (nof_channels > 1) + uhd_usrp_set_time_unknown_pps(handler->usrp, 0, 0.0); + + /* Initialize rx and tx stremers */ + uhd_rx_streamer_make(&handler->rx_stream); + error = uhd_usrp_get_rx_stream(handler->usrp, &stream_args, handler->rx_stream); + if (error) { + fprintf(stderr, "Error opening RX stream: %d\n", error); + return -1; + } + uhd_tx_streamer_make(&handler->tx_stream); + error = uhd_usrp_get_tx_stream(handler->usrp, &stream_args, handler->tx_stream); + if (error) { + fprintf(stderr, "Error opening TX stream: %d\n", error); + return -1; + } + + uhd_rx_streamer_max_num_samps(handler->rx_stream, &handler->rx_nof_samples); + uhd_tx_streamer_max_num_samps(handler->tx_stream, &handler->tx_nof_samples); + + uhd_meta_range_handle rx_gain_range = NULL; + uhd_meta_range_make(&rx_gain_range); + uhd_usrp_get_rx_gain_range(handler->usrp, "", 0, rx_gain_range); + uhd_meta_range_start(rx_gain_range, &handler->info.min_rx_gain); + uhd_meta_range_stop(rx_gain_range, &handler->info.max_rx_gain); + uhd_meta_range_free(&rx_gain_range); + + uhd_meta_range_handle tx_gain_range = NULL; + uhd_meta_range_make(&tx_gain_range); + uhd_usrp_get_tx_gain_range(handler->usrp, "", 0, tx_gain_range); + uhd_meta_range_start(tx_gain_range, &handler->info.min_tx_gain); + uhd_meta_range_stop(tx_gain_range, &handler->info.max_tx_gain); + uhd_meta_range_free(&tx_gain_range); + + // Make metadata objects for RX/TX + uhd_rx_metadata_make(&handler->rx_md); + uhd_rx_metadata_make(&handler->rx_md_first); + uhd_tx_metadata_make(&handler->tx_md, false, 0, 0, false, false); + + // Set starting gain to half maximum in case of using AGC + rf_uhd_set_rx_gain(handler, handler->info.max_rx_gain*0.7); + +#if HAVE_ASYNC_THREAD + if (start_async_thread) { + // Start low priority thread to receive async commands + handler->async_thread_running = true; + if (pthread_create(&handler->async_thread, NULL, async_thread, handler)) { + perror("pthread_create"); + return -1; + } + } +#endif + + /* Restore priorities */ + uhd_set_thread_priority(0, false); + + return 0; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + +int rf_uhd_close(void *h) +{ + rf_uhd_stop_rx_stream(h); + + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + + uhd_tx_metadata_free(&handler->tx_md); + uhd_rx_metadata_free(&handler->rx_md_first); + uhd_rx_metadata_free(&handler->rx_md); + handler->async_thread_running = false; + pthread_join(handler->async_thread, NULL); + + uhd_tx_streamer_free(&handler->tx_stream); + uhd_rx_streamer_free(&handler->rx_stream); + uhd_usrp_free(&handler->usrp); + + free(handler); + + /** Something else to close the USRP?? */ + return SRSLTE_SUCCESS; +} + +void rf_uhd_set_master_clock_rate(void *h, double rate) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + if (rate != handler->current_master_clock) { + if (handler->dynamic_rate) { + uhd_usrp_set_master_clock_rate(handler->usrp, rate, 0); + } + handler->current_master_clock = rate; + } +} + +bool rf_uhd_is_master_clock_dynamic(void *h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + return handler->dynamic_rate; +} + +double rf_uhd_set_rx_srate(void *h, double freq) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + if (handler->nof_rx_channels > 1) { + time_t full; + double frac; + uhd_usrp_get_time_now(handler->usrp, 0, &full, &frac); + frac += 0.100; + if (frac >= 1.0) { full++; frac -= 1.0; }; + uhd_usrp_set_command_time(handler->usrp, full, frac, 0); + for (int i=0;inof_rx_channels;i++) + uhd_usrp_set_rx_rate(handler->usrp, freq, i); + usleep(100000); + } else { + uhd_usrp_set_rx_rate(handler->usrp, freq, 0); + } + return freq; +} + +double rf_uhd_set_tx_srate(void *h, double freq) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + if (handler->nof_tx_channels > 1) { + time_t full; + double frac; + uhd_usrp_get_time_now(handler->usrp, 0, &full, &frac); + frac += 0.100; + if (frac >= 1.0) { full++; frac -= 1.0; }; + uhd_usrp_set_command_time(handler->usrp, full, frac, 0); + for (int i=0;inof_tx_channels;i++) + uhd_usrp_set_tx_rate(handler->usrp, freq, i); + usleep(100000); + } else { + uhd_usrp_set_tx_rate(handler->usrp, freq, 0); + } + handler->tx_rate = freq; + return freq; +} + +double rf_uhd_set_rx_gain(void *h, double gain) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + for (int i=0;inof_rx_channels;i++) { + uhd_usrp_set_rx_gain(handler->usrp, gain, i, ""); + } + return gain; +} + +double rf_uhd_set_tx_gain(void *h, double gain) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + for (int i=0;inof_tx_channels;i++) { + uhd_usrp_set_tx_gain(handler->usrp, gain, i, ""); + } + return gain; +} + +double rf_uhd_get_rx_gain(void *h) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + double gain; + uhd_usrp_get_rx_gain(handler->usrp, 0, "", &gain); + return gain; +} + +double rf_uhd_get_tx_gain(void *h) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + double gain; + uhd_usrp_get_tx_gain(handler->usrp, 0, "", &gain); + return gain; +} + +srslte_rf_info_t *rf_uhd_get_info(void *h) +{ + srslte_rf_info_t *info = NULL; + if (h) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + info = &handler->info; + } + return info; +} + +double rf_uhd_set_rx_freq(void *h, double freq) +{ + uhd_tune_request_t tune_request = { + .target_freq = freq, + .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + }; + uhd_tune_result_t tune_result; + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + for (int i=0;inof_rx_channels;i++) { + uhd_usrp_set_rx_freq(handler->usrp, &tune_request, i, &tune_result); + } + return freq; +} + +double rf_uhd_set_tx_freq(void *h, double freq) +{ + uhd_tune_request_t tune_request = { + .target_freq = freq, + .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + }; + uhd_tune_result_t tune_result; + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + for (int i=0;inof_tx_channels;i++) { + uhd_usrp_set_tx_freq(handler->usrp, &tune_request, i, &tune_result); + } + return freq; +} + + +void rf_uhd_get_time(void *h, time_t *secs, double *frac_secs) { + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + uhd_usrp_get_time_now(handler->usrp, 0, secs, frac_secs); +} + +int rf_uhd_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + return rf_uhd_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + +int rf_uhd_recv_with_time_multi(void *h, + void *data[SRSLTE_MAX_PORTS], + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs) +{ + rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; + uhd_rx_metadata_handle *md = &handler->rx_md_first; + size_t rxd_samples = 0; + int trials = 0; + if (blocking) { + int n = 0; + while (n < nsamples && trials < 100) { + void *buffs_ptr[4]; + for (int i=0;inof_rx_channels;i++) { + cf_t *data_c = (cf_t*) data[i]; + buffs_ptr[i] = &data_c[n]; + } + + size_t num_samps_left = nsamples - n; + size_t num_rx_samples = (num_samps_left > handler->rx_nof_samples) ? handler->rx_nof_samples : num_samps_left; + + rxd_samples = 0; + uhd_error error = uhd_rx_streamer_recv(handler->rx_stream, buffs_ptr, + num_rx_samples, md, 0.5, false, &rxd_samples); + if (error) { + fprintf(stderr, "Error receiving from UHD: %d\n", error); + log_rx_error(handler); + return -1; + } + + uhd_rx_metadata_error_code_t error_code = 0; + uhd_rx_metadata_error_code(*md, &error_code); + + md = &handler->rx_md; + n += rxd_samples; + trials++; + + if (error_code == UHD_RX_METADATA_ERROR_CODE_OVERFLOW) { + log_overflow(handler); + } else if (error_code == UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND) { + log_late(handler, true); + } else if (error_code == UHD_RX_METADATA_ERROR_CODE_TIMEOUT) { + fprintf(stderr, "Error timed out while receiving samples from UHD.\n"); + return -1; + } else if (error_code != UHD_RX_METADATA_ERROR_CODE_NONE ) { + fprintf(stderr, "Error code 0x%x was returned during streaming. Aborting.\n", error_code); + } + } + } else { + uhd_error error = uhd_rx_streamer_recv(handler->rx_stream, data, nsamples, md, 0.0, false, &rxd_samples); + if (error) { + fprintf(stderr, "Error receiving from UHD: %d\n", error); + log_rx_error(handler); + return -1; + } + } + if (secs && frac_secs) { + uhd_rx_metadata_time_spec(handler->rx_md_first, secs, frac_secs); + } + return nsamples; +} + +int rf_uhd_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + void *_data[SRSLTE_MAX_PORTS]= {data, zero_mem, zero_mem, zero_mem}; + + return rf_uhd_send_timed_multi(h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int rf_uhd_send_timed_multi(void *h, + void *data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_uhd_handler_t* handler = (rf_uhd_handler_t*) h; + + pthread_mutex_lock(&handler->tx_mutex); + int ret = -1; + + /* Resets the USRP time FIXME: this might cause problems for burst transmissions */ + if (!has_time_spec && is_start_of_burst && handler->nof_tx_channels > 1) { + uhd_usrp_set_time_now(handler->usrp, 0, 0, 0); + uhd_tx_metadata_set_time_spec(&handler->tx_md, 0, 0.1); + } + + size_t txd_samples; + int trials = 0; + if (blocking) { + if (has_time_spec) { + uhd_tx_metadata_set_time_spec(&handler->tx_md, secs, frac_secs); + } + int n = 0; + cf_t *data_c[4]; + for (int i = 0; i < 4; i++) { + data_c[i] = data[i] ? data[i] : zero_mem; + } + do { + size_t tx_samples = handler->tx_nof_samples; + + // First packet is start of burst if so defined, others are never + if (n == 0) { + uhd_tx_metadata_set_start(&handler->tx_md, is_start_of_burst); + } else { + uhd_tx_metadata_set_start(&handler->tx_md, false); + } + + // middle packets are never end of burst, last one as defined + if (nsamples - n > tx_samples) { + uhd_tx_metadata_set_end(&handler->tx_md, false); + } else { + tx_samples = nsamples - n; + uhd_tx_metadata_set_end(&handler->tx_md, is_end_of_burst); + } + + const void *buffs_ptr[4]; + for (int i = 0; i < 4; i++) { + void *buff = (void*) &data_c[i][n]; + buffs_ptr[i] = buff; + } + uhd_error error = uhd_tx_streamer_send(handler->tx_stream, buffs_ptr, + tx_samples, &handler->tx_md, 0.5, &txd_samples); + if (error) { + fprintf(stderr, "Error sending to UHD: %d\n", error); + goto unlock; + } + // Increase time spec + uhd_tx_metadata_add_time_spec(&handler->tx_md, txd_samples/handler->tx_rate); + n += txd_samples; + trials++; + } while (n < nsamples && trials < 100); + + ret = nsamples; + + } else { + + const void *buffs_ptr[4]; + for (int i = 0; i < 4; i++) { + buffs_ptr[i] = data[i]; + } + uhd_tx_metadata_set_has_time_spec(&handler->tx_md, is_start_of_burst); + uhd_tx_metadata_set_start(&handler->tx_md, is_start_of_burst); + uhd_tx_metadata_set_end(&handler->tx_md, is_end_of_burst); + uhd_error error = uhd_tx_streamer_send(handler->tx_stream, buffs_ptr, nsamples, &handler->tx_md, 0.0, &txd_samples); + if (error) { + fprintf(stderr, "Error sending to UHD: %d\n", error); + goto unlock; + } + + ret = txd_samples; + + } +unlock: + pthread_mutex_unlock(&handler->tx_mutex); + return ret; +} + diff --git a/lib/src/phy/rf/rf_uhd_imp.h b/lib/src/phy/rf/rf_uhd_imp.h new file mode 100644 index 0000000..e0ad09b --- /dev/null +++ b/lib/src/phy/rf/rf_uhd_imp.h @@ -0,0 +1,140 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "srslte/config.h" +#include "srslte/phy/rf/rf.h" + +#define DEVNAME_B200 "uhd_b200" +#define DEVNAME_X300 "uhd_x300" +#define DEVNAME_N300 "uhd_n300" +#define DEVNAME_E3X0 "uhd_e3x0" + + +SRSLTE_API int rf_uhd_open(char *args, + void **handler); + +SRSLTE_API int rf_uhd_open_multi(char *args, + void **handler, + uint32_t nof_channels); + +SRSLTE_API char* rf_uhd_devname(void *h); + +SRSLTE_API int rf_uhd_close(void *h); + +SRSLTE_API void rf_uhd_set_tx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API void rf_uhd_set_rx_cal(void *h, srslte_rf_cal_t *cal); + +SRSLTE_API int rf_uhd_start_rx_stream(void *h, + bool now); + +SRSLTE_API int rf_uhd_start_rx_stream_nsamples(void *h, + uint32_t nsamples); + +SRSLTE_API int rf_uhd_stop_rx_stream(void *h); + +SRSLTE_API void rf_uhd_flush_buffer(void *h); + +SRSLTE_API bool rf_uhd_has_rssi(void *h); + +SRSLTE_API float rf_uhd_get_rssi(void *h); + +SRSLTE_API bool rf_uhd_rx_wait_lo_locked(void *h); + +SRSLTE_API void rf_uhd_set_master_clock_rate(void *h, + double rate); + +SRSLTE_API bool rf_uhd_is_master_clock_dynamic(void *h); + +SRSLTE_API double rf_uhd_set_rx_srate(void *h, + double freq); + +SRSLTE_API double rf_uhd_set_rx_gain(void *h, + double gain); + +SRSLTE_API double rf_uhd_get_rx_gain(void *h); + +SRSLTE_API double rf_uhd_get_tx_gain(void *h); + +SRSLTE_API srslte_rf_info_t *rf_uhd_get_info(void *h); + +SRSLTE_API void rf_uhd_suppress_stdout(void *h); + +SRSLTE_API void rf_uhd_register_error_handler(void *h, srslte_rf_error_handler_t error_handler); + +SRSLTE_API double rf_uhd_set_rx_freq(void *h, + double freq); + +SRSLTE_API int rf_uhd_recv_with_time(void *h, + void *data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_uhd_recv_with_time_multi(void *h, + void **data, + uint32_t nsamples, + bool blocking, + time_t *secs, + double *frac_secs); + +SRSLTE_API double rf_uhd_set_tx_srate(void *h, + double freq); + +SRSLTE_API double rf_uhd_set_tx_gain(void *h, + double gain); + +SRSLTE_API double rf_uhd_set_tx_freq(void *h, + double freq); + +SRSLTE_API void rf_uhd_get_time(void *h, + time_t *secs, + double *frac_secs); + +SRSLTE_API int rf_uhd_send_timed(void *h, + void *data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSLTE_API int rf_uhd_send_timed_multi(void *h, + void *data[SRSLTE_MAX_PORTS], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + diff --git a/lib/src/phy/rf/rf_utils.c b/lib/src/phy/rf/rf_utils.c new file mode 100644 index 0000000..f12054e --- /dev/null +++ b/lib/src/phy/rf/rf_utils.c @@ -0,0 +1,249 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" + +#include "srslte/phy/rf/rf.h" +#include "srslte/phy/rf/rf_utils.h" + +int rf_rssi_scan(srslte_rf_t *rf, float *freqs, float *rssi, int nof_bands, double fs, int nsamp) { + int i, j; + int ret = -1; + cf_t *buffer; + double f; + + buffer = calloc(nsamp, sizeof(cf_t)); + if (!buffer) { + goto free_and_exit; + } + + srslte_rf_set_rx_gain(rf, 20.0); + srslte_rf_set_rx_srate(rf, fs); + + for (i=0;iid, cell->cp)) { + fprintf(stderr, "Error initiating srslte_ue_mib_sync\n"); + goto clean_exit; + } + + int srate = srslte_sampling_freq_hz(SRSLTE_UE_MIB_NOF_PRB); + INFO("Setting sampling frequency %.2f MHz for PSS search\n", (float) srate/1000000); + srslte_rf_set_rx_srate(rf, (float) srate); + + INFO("Starting receiver...\n"); + srslte_rf_start_rx_stream(rf, false); + + // Copy CFO estimate if provided and disable CP estimation during find + if (cfo) { + ue_mib.ue_sync.cfo_current_value = *cfo/15000; + ue_mib.ue_sync.cfo_is_copied = true; + ue_mib.ue_sync.cfo_correct_enable_find = true; + srslte_sync_set_cfo_cp_enable(&ue_mib.ue_sync.sfind, false, 0); + } + + /* Find and decode MIB */ + ret = srslte_ue_mib_sync_decode(&ue_mib, config->max_frames_pbch, bch_payload, &cell->nof_ports, NULL); + if (ret < 0) { + fprintf(stderr, "Error decoding MIB\n"); + goto clean_exit; + } + if (ret == 1) { + srslte_pbch_mib_unpack(bch_payload, cell, NULL); + } + + // Save CFO + if (cfo) { + *cfo = srslte_ue_sync_get_cfo(&ue_mib.ue_sync); + } + +clean_exit: + + srslte_rf_stop_rx_stream(rf); + srslte_ue_mib_sync_free(&ue_mib); + + return ret; +} + +/** This function is simply a wrapper to the ue_cell_search module for rf devices + */ +int rf_cell_search(srslte_rf_t *rf, uint32_t nof_rx_antennas, + cell_search_cfg_t *config, + int force_N_id_2, srslte_cell_t *cell, float *cfo) +{ + int ret = SRSLTE_ERROR; + srslte_ue_cellsearch_t cs; + srslte_ue_cellsearch_result_t found_cells[3]; + + bzero(found_cells, 3*sizeof(srslte_ue_cellsearch_result_t)); + + if (srslte_ue_cellsearch_init_multi(&cs, config->max_frames_pss, srslte_rf_recv_wrapper_cs, nof_rx_antennas, (void*) rf)) { + fprintf(stderr, "Error initiating UE cell detect\n"); + return SRSLTE_ERROR; + } + if (config->nof_valid_pss_frames) { + srslte_ue_cellsearch_set_nof_valid_frames(&cs, config->nof_valid_pss_frames); + } + + INFO("Setting sampling frequency %.2f MHz for PSS search\n", SRSLTE_CS_SAMP_FREQ/1000000); + srslte_rf_set_rx_srate(rf, SRSLTE_CS_SAMP_FREQ); + + INFO("Starting receiver...\n"); + srslte_rf_start_rx_stream(rf, false); + + /* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */ + uint32_t max_peak_cell = 0; + if (force_N_id_2 >= 0) { + ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); + max_peak_cell = force_N_id_2; + } else { + ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell); + } + if (ret < 0) { + srslte_rf_stop_rx_stream(rf); + fprintf(stderr, "Error searching cell\n"); + return SRSLTE_ERROR; + } else if (ret == 0) { + srslte_rf_stop_rx_stream(rf); + fprintf(stderr, "Could not find any cell in this frequency\n"); + return SRSLTE_SUCCESS; + } + + for (int i=0;i<3;i++) { + if (i == max_peak_cell) { + printf("*"); + } else { + printf(" "); + } + printf("Found Cell_id: %3d CP: %s, DetectRatio=%2.0f%% PSR=%.2f, Power=%.1f dBm\n", + found_cells[i].cell_id, srslte_cp_string(found_cells[i].cp), + found_cells[i].mode*100, + found_cells[i].psr, 20*log10(found_cells[i].peak*1000)); + } + + // Save result + if (cell) { + cell->id = found_cells[max_peak_cell].cell_id; + cell->cp = found_cells[max_peak_cell].cp; + } + + // Save CFO + if (cfo) { + *cfo = found_cells[max_peak_cell].cfo; + } + + srslte_rf_stop_rx_stream(rf); + srslte_ue_cellsearch_free(&cs); + + return ret; +} + + +/* Finds a cell and decodes MIB from the PBCH. + * Returns 1 if the cell is found and MIB is decoded successfully. + * 0 if no cell was found or MIB could not be decoded, + * -1 on error + */ +int rf_search_and_decode_mib(srslte_rf_t *rf, uint32_t nof_rx_antennas, cell_search_cfg_t *config, int force_N_id_2, srslte_cell_t *cell, float *cfo) +{ + int ret = SRSLTE_ERROR; + + printf("Searching for cell...\n"); + ret = rf_cell_search(rf, nof_rx_antennas, config, force_N_id_2, cell, cfo); + if (ret > 0) { + printf("Decoding PBCH for cell %d (N_id_2=%d)\n", cell->id, cell->id%3); + ret = rf_mib_decoder(rf, nof_rx_antennas, config, cell, cfo); + if (ret < 0) { + fprintf(stderr, "Could not decode PBCH from CELL ID %d\n", cell->id); + return SRSLTE_ERROR; + } + } + return ret; +} + diff --git a/lib/src/phy/rf/uhd_c_api.cpp b/lib/src/phy/rf/uhd_c_api.cpp new file mode 100644 index 0000000..d98c3c9 --- /dev/null +++ b/lib/src/phy/rf/uhd_c_api.cpp @@ -0,0 +1,56 @@ + + +/* This file implements a few features not currently provided by the UHD C-API */ +#include +#include + +extern "C" { +#include "srslte/phy/rf/rf.h" +#include "uhd_c_api.h" +} + +#if UHD_VERSION < 31100 +static void (*handler)(const char*); + +void translate_handler(uhd::msg::type_t type, const std::string & msg) +{ + if(handler) + handler(msg.c_str()); +} +#endif + +void rf_uhd_register_msg_handler_c(void (*new_handler)(const char*)) +{ +#if UHD_VERSION < 31100 + handler = new_handler; + uhd::msg::register_handler(translate_handler); +#endif +} + +void uhd_tx_metadata_set_time_spec(uhd_tx_metadata_handle *md, time_t secs, double frac_secs) +{ + (*md)->tx_metadata_cpp.time_spec = uhd::time_spec_t(secs, frac_secs); + (*md)->tx_metadata_cpp.has_time_spec = true; +} + +void uhd_tx_metadata_set_start(uhd_tx_metadata_handle *md, bool is_start_of_burst) +{ + (*md)->tx_metadata_cpp.start_of_burst = is_start_of_burst; +} + +void uhd_tx_metadata_set_has_time_spec(uhd_tx_metadata_handle *md, bool has_time_spec) +{ + (*md)->tx_metadata_cpp.has_time_spec = has_time_spec; +} + + +void uhd_tx_metadata_set_end(uhd_tx_metadata_handle *md, bool is_end_of_burst) +{ + (*md)->tx_metadata_cpp.end_of_burst = is_end_of_burst; +} + +void uhd_tx_metadata_add_time_spec(uhd_tx_metadata_handle *md, double frac_secs) +{ + (*md)->tx_metadata_cpp.time_spec += frac_secs; +} + diff --git a/lib/src/phy/rf/uhd_c_api.h b/lib/src/phy/rf/uhd_c_api.h new file mode 100644 index 0000000..8f6cb27 --- /dev/null +++ b/lib/src/phy/rf/uhd_c_api.h @@ -0,0 +1,37 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/config.h" +#include "srslte/phy/rf/rf.h" + +/* Declare functions not currently provided by the C-API */ +SRSLTE_API void rf_uhd_register_msg_handler_c(void (*new_handler)(const char*)); +SRSLTE_API void uhd_tx_metadata_set_time_spec(uhd_tx_metadata_handle *md, time_t secs, double frac_secs); +SRSLTE_API void uhd_tx_metadata_set_start(uhd_tx_metadata_handle *md, bool is_start_of_burst); +SRSLTE_API void uhd_tx_metadata_set_has_time_spec(uhd_tx_metadata_handle *md, bool has_time_spec); +SRSLTE_API void uhd_tx_metadata_set_end(uhd_tx_metadata_handle *md, bool is_end_of_burst); +SRSLTE_API void uhd_tx_metadata_add_time_spec(uhd_tx_metadata_handle *md, double frac_secs); diff --git a/lib/src/phy/scrambling/CMakeLists.txt b/lib/src/phy/scrambling/CMakeLists.txt new file mode 100644 index 0000000..b8c4941 --- /dev/null +++ b/lib/src/phy/scrambling/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_scrambling OBJECT ${SOURCES}) +add_subdirectory(test) diff --git a/lib/src/phy/scrambling/scrambling.c b/lib/src/phy/scrambling/scrambling.c new file mode 100644 index 0000000..ca03429 --- /dev/null +++ b/lib/src/phy/scrambling/scrambling.c @@ -0,0 +1,103 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/scrambling/scrambling.h" + +void srslte_scrambling_f(srslte_sequence_t *s, float *data) { + srslte_scrambling_f_offset(s, data, 0, s->cur_len); +} + +void srslte_scrambling_f_offset(srslte_sequence_t *s, float *data, int offset, int len) { + assert (len + offset <= s->cur_len); + srslte_vec_prod_fff(data, &s->c_float[offset], data, len); +} + +void srslte_scrambling_s(srslte_sequence_t *s, short *data) { + srslte_scrambling_s_offset(s, data, 0, s->cur_len); +} + +void srslte_scrambling_s_offset(srslte_sequence_t *s, short *data, int offset, int len) { + assert (len + offset <= s->cur_len); + srslte_vec_prod_sss(data, &s->c_short[offset], data, len); +} + +void srslte_scrambling_c(srslte_sequence_t *s, cf_t *data) { + srslte_scrambling_c_offset(s, data, 0, s->cur_len); +} + +void srslte_scrambling_c_offset(srslte_sequence_t *s, cf_t *data, int offset, int len) { + assert (len + offset <= s->cur_len); + srslte_vec_prod_cfc(data, &s->c_float[offset], data, len); +} + +void scrambling_b(uint8_t *c, uint8_t *data, int len) { + + srslte_vec_xor_bbb((int8_t*)c,(int8_t*)data,(int8_t*)data,len); +} + +void scrambling_b_word(uint8_t *c, uint8_t *data, int len) { + // Do XOR every 64 bits + // FIXME: Use 32-bit in 32-bit machines + uint64_t *x = (uint64_t*) data; + uint64_t *y = (uint64_t*) c; + for (int i=0;ic, data, s->cur_len); +} + +void srslte_scrambling_b_offset(srslte_sequence_t *s, uint8_t *data, int offset, int len) { + if (offset%8) { + // Do not load words if offset is not word-aligned + scrambling_b(&s->c[offset], data, len); + } else { + scrambling_b_word(&s->c[offset], data, len); + } +} + +void srslte_scrambling_bytes(srslte_sequence_t *s, uint8_t *data, int len) { + scrambling_b_word(s->c_bytes, data, len/8); + // Scramble last bits + if (len%8) { + uint8_t tmp_bits[8]; + srslte_bit_unpack_vector(&data[len/8], tmp_bits, len%8); + scrambling_b(&s->c[8*(len/8)], tmp_bits, len%8); + srslte_bit_pack_vector(tmp_bits, &data[len/8], len%8); + } +} diff --git a/lib/src/phy/scrambling/test/CMakeLists.txt b/lib/src/phy/scrambling/test/CMakeLists.txt new file mode 100644 index 0000000..8dd63d4 --- /dev/null +++ b/lib/src/phy/scrambling/test/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# SCRAMBLING TEST +######################################################################## + +add_executable(scrambling_test scrambling_test.c) +target_link_libraries(scrambling_test srslte_phy) + +add_test(scrambling_pbch_bit scrambling_test -s PBCH -c 50) +add_test(scrambling_pbch_float scrambling_test -s PBCH -c 50 -f) +add_test(scrambling_pbch_e_bit scrambling_test -s PBCH -c 50 -e) +add_test(scrambling_pbch_e_float scrambling_test -s PBCH -c 50 -f -e) + + + diff --git a/lib/src/phy/scrambling/test/scrambling_test.c b/lib/src/phy/scrambling/test/scrambling_test.c new file mode 100644 index 0000000..40722f0 --- /dev/null +++ b/lib/src/phy/scrambling/test/scrambling_test.c @@ -0,0 +1,184 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +char *srslte_sequence_name = NULL; +bool do_floats = false; +srslte_cp_t cp = SRSLTE_CP_NORM; +int cell_id = -1; +int nof_bits = 100; + +void usage(char *prog) { + printf("Usage: %s [ef] -c cell_id -s [PBCH, PDSCH, PDCCH, PMCH, PUCCH]\n", prog); + printf("\t -l nof_bits [Default %d]\n", nof_bits); + printf("\t -e CP extended [Default CP Normal]\n"); + printf("\t -f scramble floats [Default bits]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "csefl")) != -1) { + switch (opt) { + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'l': + nof_bits = atoi(argv[optind]); + break; + case 'e': + cp = SRSLTE_CP_EXT; + break; + case 'f': + do_floats = true; + break; + case 's': + srslte_sequence_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (cell_id == -1) { + usage(argv[0]); + exit(-1); + } + if (!srslte_sequence_name) { + usage(argv[0]); + exit(-1); + } +} + +int init_sequence(srslte_sequence_t *seq, char *name) { + if (!strcmp(name, "PBCH")) { + bzero(seq, sizeof(srslte_sequence_t)); + return srslte_sequence_pbch(seq, cp, cell_id); + } else if (!strcmp(name, "PDSCH")) { + bzero(seq, sizeof(srslte_sequence_t)); + return srslte_sequence_pdsch(seq, 1234, 0, 0, cell_id, nof_bits); + } else { + fprintf(stderr, "Unsupported sequence name %s\n", name); + return -1; + } +} + + +int main(int argc, char **argv) { + int i; + srslte_sequence_t seq; + uint8_t *input_b, *scrambled_b; + float *input_f, *scrambled_f; + struct timeval t[3]; + + parse_args(argc, argv); + + if (init_sequence(&seq, srslte_sequence_name) == -1) { + fprintf(stderr, "Error initiating sequence %s\n", srslte_sequence_name); + exit(-1); + } + + if (!do_floats) { + input_b = malloc(sizeof(uint8_t) * seq.cur_len); + if (!input_b) { + perror("malloc"); + exit(-1); + } + scrambled_b = malloc(sizeof(uint8_t) * seq.cur_len); + if (!scrambled_b) { + perror("malloc"); + exit(-1); + } + + for (i=0;i +#include +#include +#include + +#include "srslte/phy/utils/cexptab.h" +#include "srslte/phy/sync/cfo.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +/* Set next macro to 1 for using table generated CFO compensation */ +#define SRSLTE_CFO_USE_EXP_TABLE 0 + +int srslte_cfo_init(srslte_cfo_t *h, uint32_t nsamples) { +#if SRSLTE_CFO_USE_EXP_TABLE + int ret = SRSLTE_ERROR; + bzero(h, sizeof(srslte_cfo_t)); + + if (srslte_cexptab_init(&h->tab, SRSLTE_CFO_CEXPTAB_SIZE)) { + goto clean; + } + h->cur_cexp = srslte_vec_malloc(sizeof(cf_t) * nsamples); + if (!h->cur_cexp) { + goto clean; + } + h->tol = 0; + h->last_freq = 0; + h->nsamples = nsamples; + h->max_samples = nsamples; + srslte_cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); + + ret = SRSLTE_SUCCESS; +clean: + if (ret == SRSLTE_ERROR) { + srslte_cfo_free(h); + } + return ret; +#else /* SRSLTE_CFO_USE_EXP_TABLE */ + h->nsamples = nsamples; + return SRSLTE_SUCCESS; +#endif /* SRSLTE_CFO_USE_EXP_TABLE */ +} + +void srslte_cfo_free(srslte_cfo_t *h) { +#if SRSLTE_CFO_USE_EXP_TABLE + srslte_cexptab_free(&h->tab); + if (h->cur_cexp) { + free(h->cur_cexp); + } +#endif /* SRSLTE_CFO_USE_EXP_TABLE */ + bzero(h, sizeof(srslte_cfo_t)); +} + +void srslte_cfo_set_tol(srslte_cfo_t *h, float tol) { + h->tol = tol; +} + +int srslte_cfo_resize(srslte_cfo_t *h, uint32_t samples) { +#if SRSLTE_CFO_USE_EXP_TABLE + if (samples <= h->max_samples) { + srslte_cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, samples); + h->nsamples = samples; + } else { + fprintf(stderr, "Error in cfo_resize(): nof_samples must be lower than initialized\n"); + return SRSLTE_ERROR; + } +#endif /* SRSLTE_CFO_USE_EXP_TABLE */ + return SRSLTE_SUCCESS; +} + +void srslte_cfo_correct(srslte_cfo_t *h, const cf_t *input, cf_t *output, float freq) { +#if SRSLTE_CFO_USE_EXP_TABLE + if (fabs(h->last_freq - freq) > h->tol) { + h->last_freq = freq; + srslte_cexptab_gen(&h->tab, h->cur_cexp, h->last_freq, h->nsamples); + DEBUG("CFO generating new table for frequency %.4fe-6\n", freq*1e6); + } + srslte_vec_prod_ccc(h->cur_cexp, input, output, h->nsamples); +#else /* SRSLTE_CFO_USE_EXP_TABLE */ + srslte_vec_apply_cfo(input, freq, output, h->nsamples); +#endif /* SRSLTE_CFO_USE_EXP_TABLE */ +} diff --git a/lib/src/phy/sync/cp.c b/lib/src/phy/sync/cp.c new file mode 100644 index 0000000..80d0a21 --- /dev/null +++ b/lib/src/phy/sync/cp.c @@ -0,0 +1,92 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include + +#include "srslte/phy/sync/cp.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/debug.h" + +int srslte_cp_synch_init(srslte_cp_synch_t *q, uint32_t symbol_sz) +{ + q->symbol_sz = symbol_sz; + q->max_symbol_sz = symbol_sz; + + q->corr = srslte_vec_malloc(sizeof(cf_t) * q->symbol_sz); + if (!q->corr) { + perror("malloc"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +void srslte_cp_synch_free(srslte_cp_synch_t *q) +{ + if (q->corr) { + free(q->corr); + } +} + +int srslte_cp_synch_resize(srslte_cp_synch_t *q, uint32_t symbol_sz) +{ + if (symbol_sz > q->max_symbol_sz) { + fprintf(stderr, "Error in cp_synch_resize(): symbol_sz must be lower than initialized\n"); + return SRSLTE_ERROR; + } + q->symbol_sz = symbol_sz; + + return SRSLTE_SUCCESS; +} + + +uint32_t srslte_cp_synch(srslte_cp_synch_t *q, const cf_t *input, uint32_t max_offset, uint32_t nof_symbols, uint32_t cp_len) +{ + if (max_offset > q->symbol_sz) { + max_offset = q->symbol_sz; + } + for (int i=0;icorr[i] = 0; + const cf_t *inputPtr = input; + for (int n=0;ncorr[i] += srslte_vec_dot_prod_conj_ccc(&inputPtr[i], &inputPtr[i+q->symbol_sz], cplen)/nof_symbols; + inputPtr += q->symbol_sz+cplen; + } + } + uint32_t max_idx = srslte_vec_max_abs_ci(q->corr, max_offset); + return max_idx; +} + +cf_t srslte_cp_synch_corr_output(srslte_cp_synch_t *q, uint32_t offset) +{ + if (offset < q->symbol_sz) { + return q->corr[offset]; + } else { + return 0; + } +} + diff --git a/lib/src/phy/sync/find_sss.c b/lib/src/phy/sync/find_sss.c new file mode 100644 index 0000000..6695d70 --- /dev/null +++ b/lib/src/phy/sync/find_sss.c @@ -0,0 +1,203 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/sync/sss.h" + +#define MAX_M 3 + + +static void corr_all_zs(cf_t z[SRSLTE_SSS_N], float s[SRSLTE_SSS_N][SRSLTE_SSS_N-1], float output[SRSLTE_SSS_N]) { + uint32_t m; + cf_t tmp[SRSLTE_SSS_N]; + + for (m = 0; m < SRSLTE_SSS_N; m++) { + tmp[m] = srslte_vec_dot_prod_cfc(z, s[m], SRSLTE_SSS_N - 1); + } + srslte_vec_abs_square_cf(tmp, output, SRSLTE_SSS_N); +} + +static void corr_all_sz_partial(cf_t z[SRSLTE_SSS_N], float s[SRSLTE_SSS_N][SRSLTE_SSS_N], uint32_t M, float output[SRSLTE_SSS_N]) { + uint32_t Nm = SRSLTE_SSS_N/M; + cf_t tmp[SRSLTE_SSS_N]; + float tmp_abs[MAX_M-1][SRSLTE_SSS_N]; + int j, m; + float *ptr; + + for (j=0;jdftp_input, input, input_fft); + + if (ce) { + srslte_vec_div_ccc(&input_fft[q->fft_size/2-SRSLTE_SSS_N], ce, + &input_fft[q->fft_size/2-SRSLTE_SSS_N], 2*SRSLTE_SSS_N); + } + + for (int i = 0; i < SRSLTE_SSS_N; i++) { + y[0][i] = input_fft[q->fft_size/2-SRSLTE_SSS_N + 2 * i]; + y[1][i] = input_fft[q->fft_size/2-SRSLTE_SSS_N + 2 * i + 1]; + } + + srslte_vec_prod_cfc(y[0], q->fc_tables[q->N_id_2].c[0], y[0], SRSLTE_SSS_N); + srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].c[1], y[1], SRSLTE_SSS_N); + +} + +int srslte_sss_m0m1_diff(srslte_sss_t *q, const cf_t *input, uint32_t *m0, float *m0_value, + uint32_t *m1, float *m1_value) +{ + return srslte_sss_m0m1_diff_coh(q, input, NULL, m0, m0_value, m1, m1_value); +} + +/* Differential SSS estimation. + * Returns m0 and m1 estimates + * + * Source: "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" + * Jung-In Kim, Jung-Su Han, Hee-Jin Roh and Hyung-Jin Choi + + * + */ +int srslte_sss_m0m1_diff_coh(srslte_sss_t *q, const cf_t *input, cf_t ce[2*SRSLTE_SSS_N], uint32_t *m0, float *m0_value, + uint32_t *m1, float *m1_value) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL && + m0 != NULL && + m1 != NULL) + { + + cf_t yprod[SRSLTE_SSS_N]; + cf_t y[2][SRSLTE_SSS_N]; + + extract_pair_sss(q, input, ce, y); + + srslte_vec_prod_conj_ccc(&y[0][1], y[0], yprod, SRSLTE_SSS_N - 1); + corr_all_zs(yprod, q->fc_tables[q->N_id_2].sd, q->corr_output_m0); + *m0 = srslte_vec_max_fi(q->corr_output_m0, SRSLTE_SSS_N); + if (m0_value) { + *m0_value = q->corr_output_m0[*m0]; + } + + srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].z1[*m0], y[1], SRSLTE_SSS_N); + srslte_vec_prod_conj_ccc(&y[1][1], y[1], yprod, SRSLTE_SSS_N - 1); + corr_all_zs(yprod, q->fc_tables[q->N_id_2].sd, q->corr_output_m1); + *m1 = srslte_vec_max_fi(q->corr_output_m1, SRSLTE_SSS_N); + if (m1_value) { + *m1_value = q->corr_output_m1[*m1]; + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Partial correlation SSS estimation. + * Returns m0 and m1 estimates + * + * Source: "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" + * Jung-In Kim, Jung-Su Han, Hee-Jin Roh and Hyung-Jin Choi + + */ +int srslte_sss_m0m1_partial(srslte_sss_t *q, const cf_t *input, uint32_t M, cf_t ce[2*SRSLTE_SSS_N], uint32_t *m0, float *m0_value, + uint32_t *m1, float *m1_value) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL && + m0 != NULL && + m1 != NULL && + M <= MAX_M) + { + cf_t y[2][SRSLTE_SSS_N]; + + extract_pair_sss(q, input, ce, y); + + corr_all_sz_partial(y[0], q->fc_tables[q->N_id_2].s, M, q->corr_output_m0); + *m0 = srslte_vec_max_fi(q->corr_output_m0, SRSLTE_SSS_N); + if (m0_value) { + *m0_value = q->corr_output_m0[*m0]; + } + srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].z1[*m0], y[1], SRSLTE_SSS_N); + corr_all_sz_partial(y[1], q->fc_tables[q->N_id_2].s, M, q->corr_output_m1); + *m1 = srslte_vec_max_fi(q->corr_output_m1, SRSLTE_SSS_N); + if (m1_value) { + *m1_value = q->corr_output_m1[*m1]; + } + ret = SRSLTE_SUCCESS; + } + return ret; +} + +void convert_tables(srslte_sss_fc_tables_t *fc_tables, srslte_sss_tables_t *in) { + uint32_t i, j; + + for (i = 0; i < SRSLTE_SSS_N; i++) { + for (j = 0; j < SRSLTE_SSS_N; j++) { + fc_tables->z1[i][j] = (float) in->z1[i][j]; + } + } + for (i = 0; i < SRSLTE_SSS_N; i++) { + for (j = 0; j < SRSLTE_SSS_N; j++) { + fc_tables->s[i][j] = (float) in->s[i][j]; + } + } + for (i = 0; i < SRSLTE_SSS_N; i++) { + for (j = 0; j < SRSLTE_SSS_N - 1; j++) { + fc_tables->sd[i][j] = (float) in->s[i][j + 1] * in->s[i][j]; + } + } + for (i = 0; i < 2; i++) { + for (j = 0; j < SRSLTE_SSS_N; j++) { + fc_tables->c[i][j] = (float) in->c[i][j]; + } + } +} diff --git a/lib/src/phy/sync/gen_sss.c b/lib/src/phy/sync/gen_sss.c new file mode 100644 index 0000000..421d9e9 --- /dev/null +++ b/lib/src/phy/sync/gen_sss.c @@ -0,0 +1,162 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 + +#include "srslte/phy/sync/sss.h" + +/** + * @brief Function documentation: initSSStables() + * This function generates the scrambling sequences required for generation of + * SSS sequence according with 3GPP TS 36.211 version 10.5.0 Release 10. + */ +void generate_zsc_tilde(int *z_tilde, int *s_tilde, int *c_tilde) { + + int i; + int x[SRSLTE_SSS_N]; + bzero(x, sizeof(int) * SRSLTE_SSS_N); + x[4] = 1; + + for (i = 0; i < 26; i++) + x[i + 5] = (x[i + 2] + x[i]) % 2; + for (i = 0; i < SRSLTE_SSS_N; i++) + s_tilde[i] = 1 - 2 * x[i]; + + for (i = 0; i < 26; i++) + x[i + 5] = (x[i + 3] + x[i]) % 2; + for (i = 0; i < SRSLTE_SSS_N; i++) + c_tilde[i] = 1 - 2 * x[i]; + + for (i = 0; i < 26; i++) + x[i + 5] = (x[i + 4] + x[i + 2] + x[i + 1] + x[i]) % 2; + for (i = 0; i < SRSLTE_SSS_N; i++) + z_tilde[i] = 1 - 2 * x[i]; +} + +void generate_m0m1(uint32_t N_id_1, uint32_t *m0, uint32_t *m1) { + uint32_t q_prime = N_id_1 / (SRSLTE_SSS_N - 1); + uint32_t q = (N_id_1 + (q_prime * (q_prime + 1) / 2)) / (SRSLTE_SSS_N - 1); + uint32_t m_prime = N_id_1 + (q * (q + 1) / 2); + *m0 = m_prime % SRSLTE_SSS_N; + *m1 = (*m0 + m_prime / SRSLTE_SSS_N + 1) % SRSLTE_SSS_N; +} + + +/* table[m0][m1-1]=N_id_1 */ +void generate_N_id_1_table(uint32_t table[30][30]) { + uint32_t m0, m1; + uint32_t N_id_1; + for (N_id_1=0;N_id_1<168;N_id_1++) { + generate_m0m1(N_id_1, &m0, &m1); + table[m0][m1-1] = N_id_1; + } +} + + +void generate_s(int *s, int *s_tilde, uint32_t m0_m1) { + uint32_t i; + for (i = 0; i < SRSLTE_SSS_N; i++) { + s[i] = s_tilde[(i + m0_m1) % SRSLTE_SSS_N]; + } +} + +void generate_s_all(int s[SRSLTE_SSS_N][SRSLTE_SSS_N], int *s_tilde) { + uint32_t i; + for (i = 0; i < SRSLTE_SSS_N; i++) { + generate_s(s[i], s_tilde, i); + } +} + +void generate_c(int *c, int *c_tilde, uint32_t N_id_2, bool is_c0) { + uint32_t i; + for (i = 0; i < SRSLTE_SSS_N; i++) { + c[i] = c_tilde[(i + N_id_2 + (is_c0 ? 3 : 0)) % SRSLTE_SSS_N]; + } +} + +void generate_z(int *z, int *z_tilde, uint32_t m0_m1) { + uint32_t i; + for (i = 0; i < SRSLTE_SSS_N; i++) { + z[i] = z_tilde[(i + (m0_m1 % 8)) % SRSLTE_SSS_N]; + } +} + +void generate_z_all(int z[SRSLTE_SSS_N][SRSLTE_SSS_N], int *z_tilde) { + uint32_t i; + for (i = 0; i < SRSLTE_SSS_N; i++) { + generate_z(z[i], z_tilde, i); + } +} + +void generate_sss_all_tables(srslte_sss_tables_t *tables, uint32_t N_id_2) { + uint32_t i; + int s_t[SRSLTE_SSS_N], c_t[SRSLTE_SSS_N], z_t[SRSLTE_SSS_N]; + + generate_zsc_tilde(z_t, s_t, c_t); + generate_s_all(tables->s, s_t); + generate_z_all(tables->z1, z_t); + for (i = 0; i < 2; i++) { + generate_c(tables->c[i], c_t, N_id_2, i != 0); + } +} + +void srslte_sss_generate(float *signal0, float *signal5, uint32_t cell_id) { + + uint32_t i; + uint32_t id1 = cell_id / 3; + uint32_t id2 = cell_id % 3; + uint32_t m0; + uint32_t m1; + int s_t[SRSLTE_SSS_N], c_t[SRSLTE_SSS_N], z_t[SRSLTE_SSS_N]; + int s0[SRSLTE_SSS_N], s1[SRSLTE_SSS_N], c0[SRSLTE_SSS_N], c1[SRSLTE_SSS_N], z1_0[SRSLTE_SSS_N], z1_1[SRSLTE_SSS_N]; + + generate_m0m1(id1, &m0, &m1); + generate_zsc_tilde(z_t, s_t, c_t); + + generate_s(s0, s_t, m0); + generate_s(s1, s_t, m1); + + generate_c(c0, c_t, id2, 0); + generate_c(c1, c_t, id2, 1); + + generate_z(z1_0, z_t, m0); + generate_z(z1_1, z_t, m1); + + for (i = 0; i < SRSLTE_SSS_N; i++) { + /** Even Resource Elements: Sub-frame 0*/ + signal0[2 * i] = (float) (s0[i] * c0[i]); + /** Odd Resource Elements: Sub-frame 0*/ + signal0[2 * i + 1] = (float) (s1[i] * c1[i] * z1_0[i]); + } + for (i = 0; i < SRSLTE_SSS_N; i++) { + /** Even Resource Elements: Sub-frame 5*/ + signal5[2 * i] = (float) (s1[i] * c0[i]); + /** Odd Resource Elements: Sub-frame 5*/ + signal5[2 * i + 1] = (float) (s0[i] * c1[i] * z1_1[i]); + } +} + diff --git a/lib/src/phy/sync/pss.c b/lib/src/phy/sync/pss.c new file mode 100644 index 0000000..ac54264 --- /dev/null +++ b/lib/src/phy/sync/pss.c @@ -0,0 +1,628 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/sync/pss.h" +#include "srslte/phy/utils/debug.h" + + +int srslte_pss_init_N_id_2(cf_t *pss_signal_freq, cf_t *pss_signal_time, + uint32_t N_id_2, uint32_t fft_size, int cfo_i) { + srslte_dft_plan_t plan; + cf_t pss_signal_pad[2048]; + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (srslte_N_id_2_isvalid(N_id_2) && + fft_size <= 2048) + { + + srslte_pss_generate(pss_signal_freq, N_id_2); + + bzero(pss_signal_pad, fft_size * sizeof(cf_t)); + bzero(pss_signal_time, fft_size * sizeof(cf_t)); + memcpy(&pss_signal_pad[(fft_size-SRSLTE_PSS_LEN)/2+cfo_i], pss_signal_freq, SRSLTE_PSS_LEN * sizeof(cf_t)); + + /* Convert signal into the time domain */ + if (srslte_dft_plan(&plan, fft_size, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + return SRSLTE_ERROR; + } + + srslte_dft_plan_set_mirror(&plan, true); + srslte_dft_plan_set_dc(&plan, true); + srslte_dft_plan_set_norm(&plan, true); + srslte_dft_run_c(&plan, pss_signal_pad, pss_signal_time); + + srslte_vec_conj_cc(pss_signal_time, pss_signal_time, fft_size); + srslte_vec_sc_prod_cfc(pss_signal_time, 1.0/SRSLTE_PSS_LEN, pss_signal_time, fft_size); + + srslte_dft_plan_free(&plan); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* Initializes the PSS synchronization object with fft_size=128 + */ +int srslte_pss_init(srslte_pss_t *q, uint32_t frame_size) { + return srslte_pss_init_fft(q, frame_size, 128); +} + +int srslte_pss_init_fft(srslte_pss_t *q, uint32_t frame_size, uint32_t fft_size) { + return srslte_pss_init_fft_offset(q, frame_size, fft_size, 0); +} + +int srslte_pss_init_fft_offset(srslte_pss_t *q, uint32_t frame_size, uint32_t fft_size, int offset) { + return srslte_pss_init_fft_offset_decim(q, frame_size, fft_size, offset, 1); +} + +/* Initializes the PSS synchronization object. + * + * It correlates a signal of frame_size samples with the PSS sequence in the frequency + * domain. The PSS sequence is transformed using fft_size samples. + */ +int srslte_pss_init_fft_offset_decim(srslte_pss_t *q, + uint32_t max_frame_size, uint32_t max_fft_size, + int offset, int decimate) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) { + + ret = SRSLTE_ERROR; + + uint32_t N_id_2; + uint32_t buffer_size; + bzero(q, sizeof(srslte_pss_t)); + + q->N_id_2 = 10; + q->ema_alpha = 0.2; + + q->max_fft_size = max_fft_size; + q->max_frame_size = max_frame_size; + + q->decimate = decimate; + uint32_t fft_size = max_fft_size/q->decimate; + uint32_t frame_size = max_frame_size/q->decimate; + + q->fft_size = fft_size; + q->frame_size = frame_size; + + buffer_size = fft_size + frame_size + 1; + + q->filter_pss_enable = false; + q->chest_on_filter = false; + + if(q->decimate > 1) { + int filter_order = 3; + srslte_filt_decim_cc_init(&q->filter,q->decimate,filter_order); + q->filter.filter_output = srslte_vec_malloc((buffer_size) * sizeof(cf_t)); + q->filter.downsampled_input = srslte_vec_malloc((buffer_size + filter_order) * sizeof(cf_t)); + printf("decimation for the PSS search is %d \n",q->decimate); + } + + if (srslte_dft_plan(&q->dftp_input, fft_size, SRSLTE_DFT_FORWARD, SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error creating DFT plan \n"); + goto clean_and_exit; + } + srslte_dft_plan_set_mirror(&q->dftp_input, true); + srslte_dft_plan_set_dc(&q->dftp_input, true); + srslte_dft_plan_set_norm(&q->dftp_input, false); + + if (srslte_dft_plan(&q->idftp_input, fft_size, SRSLTE_DFT_BACKWARD, SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error creating DFT plan \n"); + goto clean_and_exit; + } + srslte_dft_plan_set_mirror(&q->idftp_input, true); + srslte_dft_plan_set_dc(&q->idftp_input, true); + srslte_dft_plan_set_norm(&q->idftp_input, false); + + bzero(q->tmp_fft2, sizeof(cf_t)*SRSLTE_SYMBOL_SZ_MAX); + + q->tmp_input = srslte_vec_malloc((buffer_size + frame_size*(q->decimate - 1)) * sizeof(cf_t)); + if (!q->tmp_input) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + + bzero(&q->tmp_input[q->frame_size], q->fft_size * sizeof(cf_t)); + + q->conv_output = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->conv_output) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output, sizeof(cf_t) * buffer_size); + q->conv_output_avg = srslte_vec_malloc(buffer_size * sizeof(float)); + if (!q->conv_output_avg) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output_avg, sizeof(float) * buffer_size); +#ifdef SRSLTE_PSS_ACCUMULATE_ABS + q->conv_output_abs = srslte_vec_malloc(buffer_size * sizeof(float)); + if (!q->conv_output_abs) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + bzero(q->conv_output_abs, sizeof(float) * buffer_size); +#endif + + for (N_id_2=0;N_id_2<3;N_id_2++) { + q->pss_signal_time[N_id_2] = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + if (!q->pss_signal_time[N_id_2]) { + fprintf(stderr, "Error allocating memory\n"); + goto clean_and_exit; + } + /* The PSS is translated into the time domain for each N_id_2 */ + if (srslte_pss_init_N_id_2(q->pss_signal_freq[N_id_2], q->pss_signal_time[N_id_2], N_id_2, fft_size, offset)) { + fprintf(stderr, "Error initiating PSS detector for N_id_2=%d fft_size=%d\n", N_id_2, fft_size); + goto clean_and_exit; + } + bzero(&q->pss_signal_time[N_id_2][q->fft_size], q->frame_size * sizeof(cf_t)); + } + #ifdef CONVOLUTION_FFT + + + if (srslte_conv_fft_cc_init(&q->conv_fft, frame_size, fft_size)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + goto clean_and_exit; + } + for(N_id_2=0; N_id_2<3; N_id_2++) { + q->pss_signal_freq_full[N_id_2] = srslte_vec_malloc(buffer_size * sizeof(cf_t)); + srslte_dft_run_c(&q->conv_fft.filter_plan, q->pss_signal_time[N_id_2], q->pss_signal_freq_full[N_id_2]); + } + + #endif + + srslte_pss_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_and_exit: + if (ret == SRSLTE_ERROR) { + srslte_pss_free(q); + } + return ret; + +} + + +/* Initializes the PSS synchronization object. + * + * It correlates a signal of frame_size samples with the PSS sequence in the frequency + * domain. The PSS sequence is transformed using fft_size samples. + */ +int srslte_pss_resize(srslte_pss_t *q, uint32_t frame_size, uint32_t fft_size, int offset) { + + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q != NULL) { + + ret = SRSLTE_ERROR; + + if (fft_size > q->max_fft_size || frame_size > q->max_frame_size) { + fprintf(stderr, "Error in pss_config(): fft_size and frame_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + uint32_t N_id_2; + uint32_t buffer_size; + + q->N_id_2 = 10; + q->ema_alpha = 0.2; + + fft_size = fft_size/q->decimate; + frame_size = frame_size/q->decimate; + + q->fft_size = fft_size; + q->frame_size = frame_size; + + buffer_size = fft_size + frame_size + 1; + + if (srslte_dft_replan(&q->dftp_input, fft_size)) { + fprintf(stderr, "Error creating DFT plan \n"); + return SRSLTE_ERROR; + } + + if (srslte_dft_replan(&q->idftp_input, fft_size)) { + fprintf(stderr, "Error creating DFT plan \n"); + return SRSLTE_ERROR; + } + + bzero(q->tmp_fft2, sizeof(cf_t)*SRSLTE_SYMBOL_SZ_MAX); + + bzero(&q->tmp_input[q->frame_size], q->fft_size * sizeof(cf_t)); + bzero(q->conv_output, sizeof(cf_t) * buffer_size); + bzero(q->conv_output_avg, sizeof(float) * buffer_size); + +#ifdef SRSLTE_PSS_ACCUMULATE_ABS + bzero(q->conv_output_abs, sizeof(float) * buffer_size); +#endif + + // Generate PSS sequences for this FFT size + for (N_id_2=0;N_id_2<3;N_id_2++) { + if (srslte_pss_init_N_id_2(q->pss_signal_freq[N_id_2], q->pss_signal_time[N_id_2], N_id_2, fft_size, offset)) { + fprintf(stderr, "Error initiating PSS detector for N_id_2=%d fft_size=%d\n", N_id_2, fft_size); + return SRSLTE_ERROR; + } + bzero(&q->pss_signal_time[N_id_2][q->fft_size], q->frame_size * sizeof(cf_t)); + } +#ifdef CONVOLUTION_FFT + + if (srslte_conv_fft_cc_replan(&q->conv_fft, frame_size, fft_size)) { + fprintf(stderr, "Error initiating convolution FFT\n"); + return SRSLTE_ERROR; + } + for(int i =0; i< 3; i++) { + srslte_dft_run_c(&q->conv_fft.filter_plan, q->pss_signal_time[i], q->pss_signal_freq_full[i]); + } + +#endif + + srslte_pss_reset(q); + + ret = SRSLTE_SUCCESS; + } + return ret; + +} + +void srslte_pss_free(srslte_pss_t *q) { + uint32_t i; + + if (q) { + for (i=0;i<3;i++) { + if (q->pss_signal_time[i]) { + free(q->pss_signal_time[i]); + } + if(q->pss_signal_freq_full[i]){ + free(q->pss_signal_freq_full[i]); + } + } + #ifdef CONVOLUTION_FFT + srslte_conv_fft_cc_free(&q->conv_fft); + + #endif + if (q->tmp_input) { + free(q->tmp_input); + } + if (q->conv_output) { + free(q->conv_output); + } + if (q->conv_output_abs) { + free(q->conv_output_abs); + } + if (q->conv_output_avg) { + free(q->conv_output_avg); + } + + srslte_dft_plan_free(&q->dftp_input); + srslte_dft_plan_free(&q->idftp_input); + + if(q->decimate > 1) + { + srslte_filt_decim_cc_free(&q->filter); + free(q->filter.filter_output); + free(q->filter.downsampled_input); + } + + + bzero(q, sizeof(srslte_pss_t)); + } +} + +void srslte_pss_reset(srslte_pss_t *q) { + uint32_t buffer_size = q->fft_size + q->frame_size + 1; + bzero(q->conv_output_avg, sizeof(float) * buffer_size); +} + +/** + * This function calculates the Zadoff-Chu sequence. + * @param signal Output array. + */ +int srslte_pss_generate(cf_t *signal, uint32_t N_id_2) { + int i; + float arg; + const float root_value[] = { 25.0, 29.0, 34.0 }; + int root_idx; + + int sign = -1; + + if (N_id_2 > 2) { + fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); + return -1; + } + + root_idx = N_id_2; + + for (i = 0; i < SRSLTE_PSS_LEN / 2; i++) { + arg = (float) sign * M_PI * root_value[root_idx] + * ((float) i * ((float) i + 1.0)) / 63.0; + __real__ signal[i] = cosf(arg); + __imag__ signal[i] = sinf(arg); + } + for (i = SRSLTE_PSS_LEN / 2; i < SRSLTE_PSS_LEN; i++) { + arg = (float) sign * M_PI * root_value[root_idx] + * (((float) i + 2.0) * ((float) i + 1.0)) / 63.0; + __real__ signal[i] = cosf(arg); + __imag__ signal[i] = sinf(arg); + } + return 0; +} + +/** 36.211 10.3 section 6.11.1.2 + */ +void srslte_pss_put_slot(cf_t *pss_signal, cf_t *slot, uint32_t nof_prb, srslte_cp_t cp) { + int k; + k = (SRSLTE_CP_NSYMB(cp) - 1) * nof_prb * SRSLTE_NRE + nof_prb * SRSLTE_NRE / 2 - 31; + memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); + memcpy(&slot[k], pss_signal, SRSLTE_PSS_LEN * sizeof(cf_t)); + memset(&slot[k + SRSLTE_PSS_LEN], 0, 5 * sizeof(cf_t)); +} + +void srslte_pss_get_slot(cf_t *slot, cf_t *pss_signal, uint32_t nof_prb, srslte_cp_t cp) { + int k; + k = (SRSLTE_CP_NSYMB(cp) - 1) * nof_prb * SRSLTE_NRE + nof_prb * SRSLTE_NRE / 2 - 31; + memcpy(pss_signal, &slot[k], SRSLTE_PSS_LEN * sizeof(cf_t)); +} + + +/** Sets the current N_id_2 value. Returns -1 on error, 0 otherwise + */ +int srslte_pss_set_N_id_2(srslte_pss_t *q, uint32_t N_id_2) { + if (!srslte_N_id_2_isvalid((N_id_2))) { + fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); + return -1; + } else { + q->N_id_2 = N_id_2; + return 0; + } +} + +/* Sets the weight factor alpha for the exponential moving average of the PSS correlation output + */ +void srslte_pss_set_ema_alpha(srslte_pss_t *q, float alpha) { + q->ema_alpha = alpha; +} + +float compute_peak_sidelobe(srslte_pss_t *q, uint32_t corr_peak_pos, uint32_t conv_output_len) +{ + // Find end of peak lobe to the right + int pl_ub = corr_peak_pos+1; + while(q->conv_output_avg[pl_ub+1] <= q->conv_output_avg[pl_ub] && pl_ub < conv_output_len) { + pl_ub ++; + } + // Find end of peak lobe to the left + int pl_lb; + if (corr_peak_pos > 2) { + pl_lb = corr_peak_pos-1; + while(q->conv_output_avg[pl_lb-1] <= q->conv_output_avg[pl_lb] && pl_lb > 1) { + pl_lb --; + } + } else { + pl_lb = 0; + } + + int sl_distance_right = conv_output_len-1-pl_ub; + if (sl_distance_right < 0) { + sl_distance_right = 0; + } + int sl_distance_left = pl_lb; + + int sl_right = pl_ub+srslte_vec_max_fi(&q->conv_output_avg[pl_ub], sl_distance_right); + int sl_left = srslte_vec_max_fi(q->conv_output_avg, sl_distance_left); + float side_lobe_value = SRSLTE_MAX(q->conv_output_avg[sl_right], q->conv_output_avg[sl_left]); + + return q->conv_output_avg[corr_peak_pos]/side_lobe_value; +} + +/** Performs time-domain PSS correlation. + * Returns the index of the PSS correlation peak in a subframe. + * The frame starts at corr_peak_pos-subframe_size/2. + * The value of the correlation is stored in corr_peak_value. + * + * Input buffer must be subframe_size long. + */ +int srslte_pss_find_pss(srslte_pss_t *q, const cf_t *input, float *corr_peak_value) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL) + { + + uint32_t corr_peak_pos; + uint32_t conv_output_len; + + if (!srslte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Error finding PSS peak, Must set N_id_2 first\n"); + return SRSLTE_ERROR; + } + + /* Correlate input with PSS sequence + * + * We do not reverse time-domain PSS signal because it's conjugate is symmetric. + * The conjugate operation on pss_signal_time has been done in srslte_pss_init_N_id_2 + * This is why we can use FFT-based convolution + */ + if (q->frame_size >= q->fft_size) { + #ifdef CONVOLUTION_FFT + memcpy(q->tmp_input, input, (q->frame_size * q->decimate) * sizeof(cf_t)); + if(q->decimate > 1) { + srslte_filt_decim_cc_execute(&(q->filter), q->tmp_input, q->filter.downsampled_input, q->filter.filter_output , (q->frame_size * q->decimate)); + conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->filter.filter_output,q->pss_signal_freq_full[q->N_id_2], q->conv_output); + } else { + conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->tmp_input, q->pss_signal_freq_full[q->N_id_2], q->conv_output); + } + + #else + conv_output_len = srslte_conv_cc(input, q->pss_signal_time[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); + #endif + } else { + for (int i=0;iframe_size;i++) { + q->conv_output[i] = srslte_vec_dot_prod_ccc(q->pss_signal_time[q->N_id_2], &input[i], q->fft_size); + } + conv_output_len = q->frame_size; + } + + // Compute modulus square + srslte_vec_abs_square_cf(q->conv_output, q->conv_output_abs, conv_output_len-1); + + // If enabled, average the absolute value from previous calls + if (q->ema_alpha < 1.0 && q->ema_alpha > 0.0) { + srslte_vec_sc_prod_fff(q->conv_output_abs, q->ema_alpha, q->conv_output_abs, conv_output_len-1); + srslte_vec_sc_prod_fff(q->conv_output_avg, 1-q->ema_alpha, q->conv_output_avg, conv_output_len-1); + + srslte_vec_sum_fff(q->conv_output_abs, q->conv_output_avg, q->conv_output_avg, conv_output_len-1); + } else { + memcpy(q->conv_output_avg, q->conv_output_abs, sizeof(float)*(conv_output_len-1)); + } + + /* Find maximum of the absolute value of the correlation */ + corr_peak_pos = srslte_vec_max_fi(q->conv_output_avg, conv_output_len-1); + + // save absolute value + q->peak_value = q->conv_output_avg[corr_peak_pos]; + +#ifdef SRSLTE_PSS_RETURN_PSR + if (corr_peak_value) { + *corr_peak_value = compute_peak_sidelobe(q, corr_peak_pos, conv_output_len); + } +#else + if (corr_peak_value) { + *corr_peak_value = q->conv_output_avg[corr_peak_pos]; + } +#endif + + if(q->decimate >1) { + int decimation_correction = (q->filter.num_taps - 2); + corr_peak_pos = corr_peak_pos - decimation_correction; + corr_peak_pos = corr_peak_pos*q->decimate; + } + + if (q->frame_size >= q->fft_size) { + ret = (int) corr_peak_pos; + } else { + ret = (int) corr_peak_pos + q->fft_size; + } + } + return ret; +} + +/* Computes frequency-domain channel estimation of the PSS symbol + * input signal is in the time-domain. + * ce is the returned frequency-domain channel estimates. + */ +int srslte_pss_chest(srslte_pss_t *q, const cf_t *input, cf_t ce[SRSLTE_PSS_LEN]) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + input != NULL) + { + + if (!srslte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Error finding PSS peak, Must set N_id_2 first\n"); + return SRSLTE_ERROR; + } + + /* Transform to frequency-domain */ + srslte_dft_run_c(&q->dftp_input, input, q->tmp_fft); + + /* Compute channel estimate taking the PSS sequence as reference */ + srslte_vec_prod_conj_ccc(&q->tmp_fft[(q->fft_size-SRSLTE_PSS_LEN)/2], q->pss_signal_freq[q->N_id_2], ce, SRSLTE_PSS_LEN); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +/* input points to beginning of last OFDM symbol of slot 0 of subframe 0 or 5 + * It must be called after calling srslte_pss_cfo_compute() with filter enabled + */ +void srslte_pss_sic(srslte_pss_t *q, cf_t *input) { + if (q->chest_on_filter) { + + bzero(q->tmp_fft, sizeof(cf_t)*q->fft_size); + + // Pass transmitted PSS sequence through the channel + srslte_vec_prod_ccc(q->pss_signal_freq[q->N_id_2], q->tmp_ce, &q->tmp_fft[(q->fft_size-SRSLTE_PSS_LEN)/2], SRSLTE_PSS_LEN); + + // Get time-domain version of the received PSS + srslte_dft_run_c(&q->idftp_input, q->tmp_fft, q->tmp_fft2); + + // Substract received PSS from this N_id_2 from the input signal + srslte_vec_sc_prod_cfc(q->tmp_fft2, 1.0/q->fft_size, q->tmp_fft2, q->fft_size); + srslte_vec_sub_ccc(input, q->tmp_fft2, input, q->fft_size); + + } else { + fprintf(stderr, "Error calling srslte_pss_sic(): need to enable channel estimation on filtering\n"); + } +} + +// Frequency-domain filtering of the central 64 sub-carriers +void srslte_pss_filter(srslte_pss_t *q, const cf_t *input, cf_t *output) +{ + srslte_dft_run_c(&q->dftp_input, input, q->tmp_fft); + + memcpy(&q->tmp_fft2[q->fft_size/2-SRSLTE_PSS_LEN/2], + &q->tmp_fft[q->fft_size/2-SRSLTE_PSS_LEN/2], + sizeof(cf_t)*SRSLTE_PSS_LEN); + + if (q->chest_on_filter) { + srslte_vec_prod_conj_ccc(&q->tmp_fft[(q->fft_size-SRSLTE_PSS_LEN)/2], q->pss_signal_freq[q->N_id_2], q->tmp_ce, SRSLTE_PSS_LEN); + } + + srslte_dft_run_c(&q->idftp_input, q->tmp_fft2, output); +} + +/* Returns the CFO estimation given a PSS received sequence + * + * Source: An Efficient CFO Estimation Algorithm for the Downlink of 3GPP-LTE + * Feng Wang and Yu Zhu + */ +float srslte_pss_cfo_compute(srslte_pss_t* q, const cf_t *pss_recv) { + cf_t y0, y1; + + const cf_t *pss_ptr = pss_recv; + + if (q->filter_pss_enable) { + srslte_pss_filter(q, pss_recv, q->tmp_fft); + pss_ptr = (const cf_t*) q->tmp_fft; + } + + y0 = srslte_vec_dot_prod_ccc(q->pss_signal_time[q->N_id_2], pss_ptr, q->fft_size/2); + y1 = srslte_vec_dot_prod_ccc(&q->pss_signal_time[q->N_id_2][q->fft_size/2], &pss_ptr[q->fft_size/2], q->fft_size/2); + return carg(conjf(y0) * y1)/M_PI; +} + diff --git a/lib/src/phy/sync/sfo.c b/lib/src/phy/sync/sfo.c new file mode 100644 index 0000000..7e63003 --- /dev/null +++ b/lib/src/phy/sync/sfo.c @@ -0,0 +1,56 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/phy/sync/sfo.h" + +/* Estimate SFO based on the array of time estimates t0 + * of length len. The parameter period is the time between t0 samples + */ +float srslte_sfo_estimate(int *t0, int len, float period) { + int i; + float sfo=0.0; + for (i=1;i +#include +#include +#include +#include + +#include "srslte/phy/sync/sss.h" +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/utils/convolution.h" +#include "srslte/phy/utils/vector.h" + +void generate_sss_all_tables(srslte_sss_tables_t *tables, uint32_t N_id_2); +void convert_tables(srslte_sss_fc_tables_t *fc_tables, srslte_sss_tables_t *in); +void generate_N_id_1_table(uint32_t table[30][30]); + +int srslte_sss_init(srslte_sss_t *q, uint32_t fft_size) { + + if (q != NULL && + fft_size <= 2048) + { + uint32_t N_id_2; + srslte_sss_tables_t sss_tables; + + bzero(q, sizeof(srslte_sss_t)); + + if (srslte_dft_plan(&q->dftp_input, fft_size, SRSLTE_DFT_FORWARD, SRSLTE_DFT_COMPLEX)) { + srslte_sss_free(q); + return SRSLTE_ERROR; + } + srslte_dft_plan_set_mirror(&q->dftp_input, true); + srslte_dft_plan_set_dc(&q->dftp_input, true); + + q->fft_size = fft_size; + q->max_fft_size = fft_size; + + generate_N_id_1_table(q->N_id_1_table); + + for (N_id_2=0;N_id_2<3;N_id_2++) { + generate_sss_all_tables(&sss_tables, N_id_2); + convert_tables(&q->fc_tables[N_id_2], &sss_tables); + } + q->N_id_2 = 0; + return SRSLTE_SUCCESS; + } + return SRSLTE_ERROR_INVALID_INPUTS; +} + +int srslte_sss_resize(srslte_sss_t *q, uint32_t fft_size) { + if (q != NULL && + fft_size <= 2048) + { + if (fft_size > q->max_fft_size) { + fprintf(stderr, "Error in sss_synch_resize(): fft_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + if (srslte_dft_replan(&q->dftp_input, fft_size)) { + srslte_sss_free(q); + return SRSLTE_ERROR; + } + q->fft_size = fft_size; + return SRSLTE_SUCCESS; + } + return SRSLTE_ERROR_INVALID_INPUTS; +} + +void srslte_sss_free(srslte_sss_t *q) { + srslte_dft_plan_free(&q->dftp_input); + bzero(q, sizeof(srslte_sss_t)); +} + +/** Sets the N_id_2 to search for */ +int srslte_sss_set_N_id_2(srslte_sss_t *q, uint32_t N_id_2) { + if (!srslte_N_id_2_isvalid(N_id_2)) { + fprintf(stderr, "Invalid N_id_2 %d\n", N_id_2); + return SRSLTE_ERROR; + } else { + q->N_id_2 = N_id_2; + return SRSLTE_SUCCESS; + } +} + +/** 36.211 10.3 section 6.11.2.2 + */ +void srslte_sss_put_slot(float *sss, cf_t *slot, uint32_t nof_prb, srslte_cp_t cp) { + uint32_t i, k; + + k = (SRSLTE_CP_NSYMB(cp) - 2) * nof_prb * SRSLTE_NRE + nof_prb * SRSLTE_NRE / 2 - 31; + + if (k > 5) { + memset(&slot[k - 5], 0, 5 * sizeof(cf_t)); + for (i = 0; i < SRSLTE_SSS_LEN; i++) { + __real__ slot[k + i] = sss[i]; + __imag__ slot[k + i] = 0; + } + memset(&slot[k + SRSLTE_SSS_LEN], 0, 5 * sizeof(cf_t)); + } +} + +/** Sets the SSS correlation peak detection threshold */ +void srslte_sss_set_threshold(srslte_sss_t *q, float threshold) { + q->corr_peak_threshold = threshold; +} + +/** Returns the subframe index based on the m0 and m1 values */ +uint32_t srslte_sss_subframe(uint32_t m0, uint32_t m1) { + if (m1 > m0) { + return 0; + } else { + return 5; + } +} + +/** Returns the N_id_1 value based on the m0 and m1 values */ +int srslte_sss_N_id_1(srslte_sss_t *q, uint32_t m0, uint32_t m1) { + int N_id_1 = -1; + if (m1 > m0) { + if (m0 < 30 && m1 - 1 < 30) { + N_id_1 = q->N_id_1_table[m0][m1 - 1]; + } + } else { + if (m1 < 30 && m0 - 1 < 30) { + N_id_1 = q->N_id_1_table[m1][m0 - 1]; + } + } + return N_id_1; +} diff --git a/lib/src/phy/sync/sync.c b/lib/src/phy/sync/sync.c new file mode 100644 index 0000000..39bd981 --- /dev/null +++ b/lib/src/phy/sync/sync.c @@ -0,0 +1,688 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/common/phy_common.h" +#include "srslte/phy/sync/sync.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/sync/cfo.h" + +#define CFO_EMA_ALPHA 0.1 +#define CP_EMA_ALPHA 0.1 + +#define DEFAULT_CFO_TOL 0.0 // Hz + +#define MAX_CFO_PSS_OFFSET 7000 + +static bool fft_size_isvalid(uint32_t fft_size) { + if (fft_size >= SRSLTE_SYNC_FFT_SZ_MIN && fft_size <= SRSLTE_SYNC_FFT_SZ_MAX && (fft_size%64) == 0) { + return true; + } else { + return false; + } +} + +int srslte_sync_init(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size) +{ + return srslte_sync_init_decim(q, frame_size, max_offset, fft_size, 1); +} +int srslte_sync_init_decim(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size, int decimate) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + fft_size_isvalid(fft_size)) + { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_sync_t)); + + q->N_id_2 = 1000; + q->N_id_1 = 1000; + + q->cfo_ema_alpha = CFO_EMA_ALPHA; + q->sss_alg = SSS_FULL; + + q->detect_cp = true; + q->sss_en = true; + q->cfo_pss_enable = false; + q->cfo_cp_enable = false; + q->cfo_i_initiated = false; + q->pss_filtering_enabled = false; + + q->cfo_cp_nsymbols = 3; + q->fft_size = fft_size; + q->frame_size = frame_size; + q->max_offset = max_offset; + q->max_frame_size = frame_size; + + srslte_sync_cfo_reset(q); + + if (srslte_cfo_init(&q->cfo_corr_frame, q->frame_size)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + if (srslte_cfo_init(&q->cfo_corr_symbol, q->fft_size)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + // Set default CFO tolerance + srslte_sync_set_cfo_tol(q, DEFAULT_CFO_TOL); + + for (int i=0;i<2;i++) { + q->cfo_i_corr[i] = srslte_vec_malloc(sizeof(cf_t)*q->frame_size); + if (!q->cfo_i_corr[i]) { + perror("malloc"); + goto clean_exit; + } + } + + q->temp = srslte_vec_malloc(sizeof(cf_t)*2*q->frame_size); + if (!q->temp) { + perror("malloc"); + goto clean_exit; + } + + srslte_sync_set_cp(q, SRSLTE_CP_NORM); + q->decimate = decimate; + if(!decimate) { + decimate = 1; + } + + if (srslte_pss_init_fft_offset_decim(&q->pss, max_offset, fft_size, 0, decimate)) { + fprintf(stderr, "Error initializing PSS object\n"); + goto clean_exit; + } + if (srslte_sss_init(&q->sss, fft_size)) { + fprintf(stderr, "Error initializing SSS object\n"); + goto clean_exit; + } + + if (srslte_cp_synch_init(&q->cp_synch, fft_size)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + DEBUG("SYNC init with frame_size=%d, max_offset=%d and fft_size=%d\n", frame_size, max_offset, fft_size); + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters frame_size: %d, fft_size: %d\n", frame_size, fft_size); + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_sync_free(q); + } + return ret; +} + +void srslte_sync_free(srslte_sync_t *q) +{ + if (q) { + srslte_pss_free(&q->pss); + srslte_sss_free(&q->sss); + srslte_cfo_free(&q->cfo_corr_frame); + srslte_cfo_free(&q->cfo_corr_symbol); + srslte_cp_synch_free(&q->cp_synch); + + for (int i = 0; i < 2; i++) { + if (q->cfo_i_corr[i]) { + free(q->cfo_i_corr[i]); + } + srslte_pss_free(&q->pss_i[i]); + } + + if (q->temp) { + free(q->temp); + } + } +} + +int srslte_sync_resize(srslte_sync_t *q, uint32_t frame_size, uint32_t max_offset, uint32_t fft_size) { + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + fft_size_isvalid(fft_size)) + { + if (frame_size > q->max_frame_size) { + fprintf(stderr, "Error in sync_resize(): frame_size must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + q->fft_size = fft_size; + q->frame_size = frame_size; + q->max_offset = max_offset; + + if (srslte_pss_resize(&q->pss, q->max_offset, q->fft_size, 0)) { + fprintf(stderr, "Error resizing PSS object\n"); + return SRSLTE_ERROR; + } + if (srslte_sss_resize(&q->sss, q->fft_size)) { + fprintf(stderr, "Error resizing SSS object\n"); + return SRSLTE_ERROR; + } + + if (srslte_cp_synch_resize(&q->cp_synch, q->fft_size)) { + fprintf(stderr, "Error resizing CFO\n"); + return SRSLTE_ERROR; + } + + if (srslte_cfo_resize(&q->cfo_corr_frame, q->frame_size)) { + fprintf(stderr, "Error resizing CFO\n"); + return SRSLTE_ERROR; + } + + if (srslte_cfo_resize(&q->cfo_corr_symbol, q->fft_size)) { + fprintf(stderr, "Error resizing CFO\n"); + return SRSLTE_ERROR; + } + + if (q->cfo_i_initiated) { + for (int i=0;i<2;i++) { + int offset=(i==0)?-1:1; + if (srslte_pss_resize(&q->pss_i[i], q->max_offset, q->fft_size, offset)) { + fprintf(stderr, "Error initializing PSS object\n"); + } + for (int t=0;tframe_size;t++) { + q->cfo_i_corr[i][t] = cexpf(-2*_Complex_I*M_PI*offset*(float) t/q->fft_size); + } + } + } + + // Update CFO tolerance + srslte_sync_set_cfo_tol(q, q->current_cfo_tol); + + DEBUG("SYNC init with frame_size=%d, max_offset=%d and fft_size=%d\n", frame_size, max_offset, fft_size); + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters frame_size: %d, fft_size: %d\n", frame_size, fft_size); + } + + return ret; +} + +void srslte_sync_set_cfo_tol(srslte_sync_t *q, float tol) { + q->current_cfo_tol = tol; + srslte_cfo_set_tol(&q->cfo_corr_frame, tol/(15000.0*q->fft_size)); + srslte_cfo_set_tol(&q->cfo_corr_symbol, tol/(15000.0*q->fft_size)); +} + +void srslte_sync_set_threshold(srslte_sync_t *q, float threshold) { + q->threshold = threshold; +} + +void srslte_sync_sss_en(srslte_sync_t *q, bool enabled) { + q->sss_en = enabled; +} + +bool srslte_sync_sss_detected(srslte_sync_t *q) { + return srslte_N_id_1_isvalid(q->N_id_1); +} + +int srslte_sync_get_cell_id(srslte_sync_t *q) { + if (srslte_N_id_2_isvalid(q->N_id_2) && srslte_N_id_1_isvalid(q->N_id_1)) { + return q->N_id_1*3 + q->N_id_2; + } else { + return -1; + } +} + +int srslte_sync_set_N_id_2(srslte_sync_t *q, uint32_t N_id_2) { + if (srslte_N_id_2_isvalid(N_id_2)) { + q->N_id_2 = N_id_2; + return SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid N_id_2=%d\n", N_id_2); + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +uint32_t srslte_sync_get_sf_idx(srslte_sync_t *q) { + return q->sf_idx; +} + +float srslte_sync_get_cfo(srslte_sync_t *q) { + return q->cfo_cp_mean + q->cfo_pss_mean + q->cfo_i_value; +} + +void srslte_sync_cfo_reset(srslte_sync_t *q) +{ + q->cfo_cp_mean = 0; + q->cfo_cp_is_set = false; + q->cfo_pss_mean = 0; + q->cfo_pss_is_set = false; +} + +void srslte_sync_copy_cfo(srslte_sync_t *q, srslte_sync_t *src_obj) { + q->cfo_cp_mean = src_obj->cfo_cp_mean; + q->cfo_pss_mean = src_obj->cfo_pss_mean; + q->cfo_i_value = src_obj->cfo_i_value; + q->cfo_cp_is_set = false; + q->cfo_pss_is_set = false; +} + +void srslte_sync_set_cfo_i_enable(srslte_sync_t *q, bool enable) { + q->cfo_i_enable = enable; + if (q->cfo_i_enable && !q->cfo_i_initiated) { + for (int i=0;i<2;i++) { + int offset=(i==0)?-1:1; + if (srslte_pss_init_fft_offset(&q->pss_i[i], q->max_offset, q->fft_size, offset)) { + fprintf(stderr, "Error initializing PSS object\n"); + } + for (int t=0;tframe_size;t++) { + q->cfo_i_corr[i][t] = cexpf(-2*_Complex_I*M_PI*offset*(float) t/q->fft_size); + } + } + q->cfo_i_initiated = true; + } +} + +void srslte_sync_set_sss_eq_enable(srslte_sync_t *q, bool enable) { + q->sss_channel_equalize = enable; + if (enable) { + q->pss_filtering_enabled = true; + q->pss.chest_on_filter = true; + } +} + +void srslte_sync_set_pss_filt_enable(srslte_sync_t *q, bool enable) { + q->pss_filtering_enabled = enable; +} + +void srslte_sync_set_cfo_cp_enable(srslte_sync_t *q, bool enable, uint32_t nof_symbols) { + q->cfo_cp_enable = enable; + q->cfo_cp_nsymbols = nof_symbols; +} + +void srslte_sync_set_cfo_pss_enable(srslte_sync_t *q, bool enable) { + q->cfo_pss_enable = enable; +} + +void srslte_sync_set_cfo_ema_alpha(srslte_sync_t *q, float alpha) { + q->cfo_ema_alpha = alpha; +} + +float srslte_sync_get_peak_value(srslte_sync_t *q) { + return q->peak_value; +} + +void srslte_sync_cp_en(srslte_sync_t *q, bool enabled) { + q->detect_cp = enabled; +} + +void srslte_sync_set_em_alpha(srslte_sync_t *q, float alpha) +{ + srslte_pss_set_ema_alpha(&q->pss, alpha); +} + +srslte_cp_t srslte_sync_get_cp(srslte_sync_t *q) +{ + return q->cp; +} +void srslte_sync_set_cp(srslte_sync_t *q, srslte_cp_t cp) +{ + q->cp = cp; + q->cp_len = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(1,q->fft_size):SRSLTE_CP_LEN_EXT(q->fft_size); + if (q->frame_size < q->fft_size) { + q->nof_symbols = 1; + } else { + q->nof_symbols = q->frame_size/(q->fft_size+q->cp_len)-1; + } +} + +void srslte_sync_set_sss_algorithm(srslte_sync_t *q, sss_alg_t alg) +{ + q->sss_alg = alg; +} + +/* CP detection algorithm taken from: + * "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" + * by Jung-In Kim et al. + */ +srslte_cp_t srslte_sync_detect_cp(srslte_sync_t *q, const cf_t *input, uint32_t peak_pos) +{ + float R_norm=0, R_ext=0, C_norm=0, C_ext=0; + float M_norm=0, M_ext=0; + + uint32_t cp_norm_len = SRSLTE_CP_LEN_NORM(7, q->fft_size); + uint32_t cp_ext_len = SRSLTE_CP_LEN_EXT(q->fft_size); + + uint32_t nof_symbols = peak_pos/(q->fft_size+cp_ext_len); + + if (nof_symbols > 3) { + nof_symbols = 3; + } + + if (nof_symbols > 0) { + + const cf_t *input_cp_norm = &input[peak_pos-nof_symbols*(q->fft_size+cp_norm_len)]; + const cf_t *input_cp_ext = &input[peak_pos-nof_symbols*(q->fft_size+cp_ext_len)]; + + for (int i=0;ifft_size], input_cp_norm, cp_norm_len)); + C_norm += cp_norm_len * srslte_vec_avg_power_cf(input_cp_norm, cp_norm_len); + input_cp_norm += q->fft_size+cp_norm_len; + } + if (C_norm > 0) { + M_norm = R_norm/C_norm; + } + + q->M_norm_avg = SRSLTE_VEC_EMA(M_norm/nof_symbols, q->M_norm_avg, CP_EMA_ALPHA); + + for (int i=0;ifft_size], input_cp_ext, cp_ext_len)); + C_ext += cp_ext_len * srslte_vec_avg_power_cf(input_cp_ext, cp_ext_len); + input_cp_ext += q->fft_size+cp_ext_len; + } + if (C_ext > 0) { + M_ext = R_ext/C_ext; + } + + q->M_ext_avg = SRSLTE_VEC_EMA(M_ext/nof_symbols, q->M_ext_avg, CP_EMA_ALPHA); + + if (q->M_norm_avg > q->M_ext_avg) { + return SRSLTE_CP_NORM; + } else if (q->M_norm_avg < q->M_ext_avg) { + return SRSLTE_CP_EXT; + } else { + if (R_norm > R_ext) { + return SRSLTE_CP_NORM; + } else { + return SRSLTE_CP_EXT; + } + } + } else { + return SRSLTE_CP_NORM; + } +} + +/* Returns 1 if the SSS is found, 0 if not and -1 if there is not enough space + * to correlate + */ +int sync_sss_symbol(srslte_sync_t *q, const cf_t *input) +{ + int ret; + + srslte_sss_set_N_id_2(&q->sss, q->N_id_2); + + switch(q->sss_alg) { + case SSS_DIFF: + srslte_sss_m0m1_diff(&q->sss, input, &q->m0, &q->m0_value, &q->m1, &q->m1_value); + break; + case SSS_PARTIAL_3: + srslte_sss_m0m1_partial(&q->sss, input, 3, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); + break; + case SSS_FULL: + srslte_sss_m0m1_partial(&q->sss, input, 1, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); + break; + } + + q->sf_idx = srslte_sss_subframe(q->m0, q->m1); + ret = srslte_sss_N_id_1(&q->sss, q->m0, q->m1); + if (ret >= 0) { + q->N_id_1 = (uint32_t) ret; + DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", + q->N_id_1, q->sf_idx, SRSLTE_CP_ISNORM(q->cp)?"Normal":"Extended"); + return 1; + } else { + q->N_id_1 = 1000; + return SRSLTE_SUCCESS; + } +} + +srslte_pss_t* srslte_sync_get_cur_pss_obj(srslte_sync_t *q) +{ + srslte_pss_t *pss_obj[3] = {&q->pss_i[0], &q->pss, &q->pss_i[1]}; + return pss_obj[q->cfo_i_value+1]; +} + +static float cfo_cp_estimate(srslte_sync_t *q, const cf_t *input) +{ + uint32_t cp_offset = 0; + cp_offset = srslte_cp_synch(&q->cp_synch, input, q->max_offset, q->cfo_cp_nsymbols, SRSLTE_CP_LEN_NORM(1,q->fft_size)); + cf_t cp_corr_max = srslte_cp_synch_corr_output(&q->cp_synch, cp_offset); + float cfo = -carg(cp_corr_max) / M_PI / 2; + return cfo; +} + +static int cfo_i_estimate(srslte_sync_t *q, const cf_t *input, int find_offset, int *peak_pos, int *cfo_i) +{ + float peak_value; + float max_peak_value = -99; + int max_cfo_i = 0; + srslte_pss_t *pss_obj[3] = {&q->pss_i[0], &q->pss, &q->pss_i[1]}; + for (int cfo_i=0;cfo_i<3;cfo_i++) { + srslte_pss_set_N_id_2(pss_obj[cfo_i], q->N_id_2); + int p = srslte_pss_find_pss(pss_obj[cfo_i], &input[find_offset], &peak_value); + if (p < 0) { + return -1; + } + if (peak_value > max_peak_value) { + max_peak_value = peak_value; + if (peak_pos) { + *peak_pos = p; + } + q->peak_value = peak_value; + max_cfo_i = cfo_i-1; + } + } + if (cfo_i) { + *cfo_i = max_cfo_i; + } + return 0; +} + +/** Finds the PSS sequence previously defined by a call to srslte_sync_set_N_id_2() + * around the position find_offset in the buffer input. + * + * Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold() + * or 0 otherwise. Returns a negative number on error (if N_id_2 has not been set) + * + * The input signal is not modified. Any CFO correction is done in internal buffers + * + * The maximum of the correlation peak is always stored in *peak_position + */ +srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uint32_t find_offset, uint32_t *peak_position) +{ + srslte_sync_find_ret_t ret = SRSLTE_SYNC_ERROR; + int peak_pos = 0; + + if (!q) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + if (input != NULL && + srslte_N_id_2_isvalid(q->N_id_2) && + fft_size_isvalid(q->fft_size)) + { + + if (peak_position) { + *peak_position = 0; + } + + const cf_t *input_ptr = input; + + /* First CFO estimation stage is integer. + * Finds max PSS correlation for shifted +1/0/-1 integer versions. + * This should only used once N_id_2 is set + */ + if (q->cfo_i_enable) { + if (cfo_i_estimate(q, input_ptr, find_offset, &peak_pos, &q->cfo_i_value) < 0) { + fprintf(stderr, "Error calling finding PSS sequence at : %d \n", peak_pos); + return SRSLTE_ERROR; + } + // Correct it using precomputed signal and store in buffer (don't modify input signal) + if (q->cfo_i_value != 0) { + srslte_vec_prod_ccc((cf_t*) input_ptr, q->cfo_i_corr[q->cfo_i_value<0?0:1], q->temp, q->frame_size); + INFO("Compensating cfo_i=%d\n", q->cfo_i_value); + input_ptr = q->temp; + } + } + + /* Second stage is coarse fractional CFO estimation using CP. + * In case of multi-cell, this can lead to incorrect estimations if CFO from different cells is different + */ + if (q->cfo_cp_enable) { + float cfo_cp = cfo_cp_estimate(q, input_ptr); + + if (!q->cfo_cp_is_set) { + q->cfo_cp_mean = cfo_cp; + q->cfo_cp_is_set = true; + } else { + /* compute exponential moving average CFO */ + q->cfo_cp_mean = SRSLTE_VEC_EMA(cfo_cp, q->cfo_cp_mean, q->cfo_ema_alpha); + } + + INFO("CP-CFO: estimated=%f, mean=%f\n", cfo_cp, q->cfo_cp_mean); + + /* Correct CFO with the averaged CFO estimation */ + srslte_cfo_correct(&q->cfo_corr_frame, input_ptr, q->temp, -q->cfo_cp_mean / q->fft_size); + input_ptr = q->temp; + } + + /* Find maximum of PSS correlation. If Integer CFO is enabled, correlation is already done + */ + if (!q->cfo_i_enable) { + srslte_pss_set_N_id_2(&q->pss, q->N_id_2); + peak_pos = srslte_pss_find_pss(&q->pss, &input_ptr[find_offset], q->threshold>0?&q->peak_value:NULL); + if (peak_pos < 0) { + fprintf(stderr, "Error calling finding PSS sequence at : %d \n", peak_pos); + return SRSLTE_ERROR; + } + } + + INFO("PSS: id=%d, peak_pos=%d, peak_value=%f\n", q->N_id_2, peak_pos, q->peak_value); + + // Save peak position + if (peak_position) { + *peak_position = (uint32_t) peak_pos; + } + + // In case of decimation, this compensates for the constant time shift caused by the low pass filter + if(q->decimate && peak_pos < 0) { + peak_pos = 0 ;//peak_pos + q->decimate*(2);// replace 2 with q->filter_size -2; + } + + /* If peak is over threshold, compute CFO and SSS */ + if (q->peak_value >= q->threshold || q->threshold == 0) { + + if (q->cfo_pss_enable && peak_pos >= q->fft_size) { + + // Filter central bands before PSS-based CFO estimation + const cf_t *pss_ptr = &input_ptr[find_offset + peak_pos - q->fft_size]; + if (q->pss_filtering_enabled) { + srslte_pss_filter(&q->pss, pss_ptr, q->pss_filt); + pss_ptr = q->pss_filt; + } + + // PSS-based CFO estimation + q->cfo_pss = srslte_pss_cfo_compute(&q->pss, pss_ptr); + if (!q->cfo_pss_is_set) { + q->cfo_pss_mean = q->cfo_pss; + q->cfo_pss_is_set = true; + } else if (15000*fabsf(q->cfo_pss) < MAX_CFO_PSS_OFFSET) { + q->cfo_pss_mean = SRSLTE_VEC_EMA(q->cfo_pss, q->cfo_pss_mean, q->cfo_ema_alpha); + } + + INFO("PSS-CFO: filter=%s, estimated=%f, mean=%f\n", + q->pss_filtering_enabled?"yes":"no", q->cfo_pss, q->cfo_pss_mean); + + } + + // If there is enough space for CP and SSS estimation + if (peak_pos + find_offset >= 2 * (q->fft_size + SRSLTE_CP_LEN_EXT(q->fft_size))) { + + // If SSS search is enabled, correlate SSS sequence + if (q->sss_en) { + + // Set an invalid N_id_1 indicating SSS is yet to be detected + q->N_id_1 = 1000; + + int sss_idx = find_offset + peak_pos - 2 * q->fft_size - + SRSLTE_CP_LEN(q->fft_size, (SRSLTE_CP_ISNORM(q->cp) ? SRSLTE_CP_NORM_LEN : SRSLTE_CP_EXT_LEN)); + + const cf_t *sss_ptr = &input_ptr[sss_idx]; + + // Correct CFO if detected in PSS + if (q->cfo_pss_enable) { + srslte_cfo_correct(&q->cfo_corr_symbol, sss_ptr, q->sss_filt, -q->cfo_pss_mean / q->fft_size); + // Equalize channel if estimated in PSS + if (q->sss_channel_equalize && q->pss.chest_on_filter && q->pss_filtering_enabled) { + srslte_vec_prod_ccc(&q->sss_filt[q->fft_size/2-SRSLTE_PSS_LEN/2], q->pss.tmp_ce, + &q->sss_filt[q->fft_size/2-SRSLTE_PSS_LEN/2], SRSLTE_PSS_LEN); + } + sss_ptr = q->sss_filt; + } + + if (sync_sss_symbol(q, sss_ptr) < 0) { + fprintf(stderr, "Error correlating SSS\n"); + return -1; + } + } + + // Detect CP length + if (q->detect_cp) { + srslte_sync_set_cp(q, srslte_sync_detect_cp(q, input_ptr, peak_pos + find_offset)); + } else { + DEBUG("Not enough room to detect CP length. Peak position: %d\n", peak_pos); + } + + ret = SRSLTE_SYNC_FOUND; + } else { + ret = SRSLTE_SYNC_FOUND_NOSPACE; + } + } else { + ret = SRSLTE_SYNC_NOFOUND; + } + + DEBUG("SYNC ret=%d N_id_2=%d find_offset=%d frame_len=%d, pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f kHz\n", + ret, q->N_id_2, find_offset, q->frame_size, peak_pos, q->peak_value, + q->threshold, q->sf_idx, 15*(srslte_sync_get_cfo(q))); + + } else if (srslte_N_id_2_isvalid(q->N_id_2)) { + fprintf(stderr, "Must call srslte_sync_set_N_id_2() first!\n"); + } + + return ret; +} + +void srslte_sync_reset(srslte_sync_t *q) { + q->M_ext_avg = 0; + q->M_norm_avg = 0; + srslte_pss_reset(&q->pss); +} diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt new file mode 100644 index 0000000..35407ca --- /dev/null +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -0,0 +1,75 @@ +# +# Copyright 2013-2017 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/. +# + +find_package(SRSGUI) + +######################################################################## +# PROGRAM TO DEBUG PSS FROM USRP +######################################################################## + +add_executable(pss_file pss_file.c) +target_link_libraries(pss_file srslte_phy) + +if(UHD_FOUND) + add_executable(pss_usrp pss_usrp.c) + target_link_libraries(pss_usrp srslte_phy srslte_rf) +endif(UHD_FOUND) + + +if(SRSGUI_FOUND) + include_directories(${SRSGUI_INCLUDE_DIRS}) + target_link_libraries(pss_file ${SRSGUI_LIBRARIES}) + if(UHD_FOUND) + target_link_libraries(pss_usrp ${SRSGUI_LIBRARIES}) + endif(UHD_FOUND) +else(SRSGUI_FOUND) + add_definitions(-DDISABLE_GRAPHICS) +endif(SRSGUI_FOUND) + +######################################################################## +# SYNC TEST +######################################################################## + +add_executable(sync_test sync_test.c) +target_link_libraries(sync_test srslte_phy) + +add_test(sync_test_100 sync_test -o 100 -c 501) +add_test(sync_test_400 sync_test -o 400 -c 2) +add_test(sync_test_100_e sync_test -o 100 -e -c 150) +add_test(sync_test_400_e sync_test -o 400 -e -c 151) + +add_test(sync_test_100 sync_test -o 100 -p 50 -c 501) +add_test(sync_test_400 sync_test -o 400 -p 50 -c 500) +add_test(sync_test_100_e sync_test -o 100 -e -p 50 -c 133) +add_test(sync_test_400_e sync_test -o 400 -e -p 50 -c 123) + +######################################################################## +# CFO TEST +######################################################################## + +add_executable(cfo_test cfo_test.c) +target_link_libraries(cfo_test srslte_phy) + +add_test(cfo_test_1 cfo_test -f 0.12345 -n 1000) +add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) + + + + diff --git a/lib/src/phy/sync/test/cfo_test.c b/lib/src/phy/sync/test/cfo_test.c new file mode 100644 index 0000000..3764d74 --- /dev/null +++ b/lib/src/phy/sync/test/cfo_test.c @@ -0,0 +1,119 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +#define MAX_MSE 0.1 + +float freq = 0; +int num_samples = 1000; + +void usage(char *prog) { + printf("Usage: %s -f freq -n num_samples\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nf")) != -1) { + switch (opt) { + case 'n': + num_samples = atoi(argv[optind]); + break; + case 'f': + freq = atof(argv[optind]); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int i; + cf_t *input, *output; + srslte_cfo_t cfocorr; + float mse; + + if (argc < 5) { + usage(argv[0]); + exit(-1); + } + + parse_args(argc, argv); + + input = malloc(sizeof(cf_t) * num_samples); + if (!input) { + perror("malloc"); + exit(-1); + } + output = malloc(sizeof(cf_t) * num_samples); + if (!output) { + perror("malloc"); + exit(-1); + } + + for (i=0;i MAX_MSE) { + printf("MSE too large\n"); + exit(-1); + } else { + printf("Ok\n"); + exit(0); + } +} diff --git a/lib/src/phy/sync/test/cp_mex.c b/lib/src/phy/sync/test/cp_mex.c new file mode 100644 index 0000000..01579ba --- /dev/null +++ b/lib/src/phy/sync/test/cp_mex.c @@ -0,0 +1,93 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define NOF_INPUTS 2 + + +void help() +{ + mexErrMsgTxt + ("[offset,corr] = srslte_cp_synch(enbConfig, inputSignal)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_cell_t cell; + srslte_cp_synch_t cp_synch; + cf_t *input_symbols; + int frame_len; + + if (nrhs != NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + /** Allocate input buffers */ + frame_len = mexutils_read_cf(INPUT, &input_symbols); + if (frame_len < 0) { + mexErrMsgTxt("Error reading input symbols\n"); + return; + } + + uint32_t symbol_sz = srslte_symbol_sz(cell.nof_prb); + if (srslte_cp_synch_init(&cp_synch, symbol_sz)) { + fprintf(stderr, "Error initiating CP\n"); + return; + } + + uint32_t cp_len = SRSLTE_CP_LEN_NORM(1, symbol_sz); + uint32_t nsymbols = frame_len/(symbol_sz+cp_len)-1; + uint32_t peak_idx = srslte_cp_synch(&cp_synch, input_symbols, symbol_sz, nsymbols, cp_len); + + if (nlhs >= 1) { + plhs[0] = mxCreateDoubleScalar(peak_idx); + } + if (nlhs >= 2) { + mexutils_write_cf(cp_synch.corr, &plhs[1], symbol_sz, 1); + } + + srslte_cp_synch_free(&cp_synch); + free(input_symbols); + + return; +} + diff --git a/lib/src/phy/sync/test/pss_file.c b/lib/src/phy/sync/test/pss_file.c new file mode 100644 index 0000000..d1ec2a1 --- /dev/null +++ b/lib/src/phy/sync/test/pss_file.c @@ -0,0 +1,407 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" + + +#ifndef DISABLE_GRAPHICS +void init_plots(); +void do_plots(float *corr, float energy, uint32_t size, cf_t ce[SRSLTE_PSS_LEN]); +void do_plots_sss(float *corr_m0, float *corr_m1); +void destroy_plots(); +#endif + + +bool disable_plots = false; +char *input_file_name; +int cell_id = -1; +int nof_frames = -1; +uint32_t fft_size=64; +float threshold = 0.4; +int N_id_2_sync = -1; +srslte_cp_t cp=SRSLTE_CP_NORM; +int file_offset = 0; + +void usage(char *prog) { + printf("Usage: %s [nlestodv] -i cell_id -f input_file_name\n", prog); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l N_id_2 to sync [Default use cell_id]\n"); + printf("\t-e Extended CP [Default Normal]\n"); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); + printf("\t-o file read offset [Default %d]\n", file_offset); +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "nlestdvoif")) != -1) { + switch (opt) { + case 'f': + input_file_name = argv[optind]; + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'e': + cp = SRSLTE_CP_EXT; + break; + case 'i': + cell_id = atoi(argv[optind]); + break; + case 'o': + file_offset = atoi(argv[optind]); + break; + case 'l': + N_id_2_sync = atoi(argv[optind]); + break; + case 's': + fft_size = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'd': + disable_plots = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (cell_id < 0) { + usage(argv[0]); + exit(-1); + } +} + float m0_value, m1_value; + +int main(int argc, char **argv) { + srslte_filesource_t fsrc; + cf_t *buffer; + int frame_cnt, n; + srslte_pss_t pss; + srslte_cfo_t cfocorr, cfocorr64; + srslte_sss_t sss; + int32_t flen; + int peak_idx, last_peak; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + cf_t ce[SRSLTE_PSS_LEN]; + + parse_args(argc, argv); + + if (N_id_2_sync == -1) { + N_id_2_sync = cell_id%3; + } + uint32_t N_id_2 = cell_id%3; + uint32_t N_id_1 = cell_id/3; + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + init_plots(); +#endif + + flen = fft_size*15*5; + + buffer = malloc(sizeof(cf_t) * flen * 2); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (srslte_pss_init_fft(&pss, flen, fft_size)) { + fprintf(stderr, "Error initiating PSS\n"); + exit(-1); + } + + if (srslte_pss_set_N_id_2(&pss, N_id_2_sync)) { + fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2_sync); + exit(-1); + } + + srslte_cfo_init(&cfocorr, flen); + srslte_cfo_init(&cfocorr64, flen); + + if (srslte_sss_init(&sss, fft_size)) { + fprintf(stderr, "Error initializing SSS object\n"); + return SRSLTE_ERROR; + } + + srslte_sss_set_N_id_2(&sss, N_id_2); + + printf("Opening file...\n"); + if (srslte_filesource_init(&fsrc, input_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", input_file_name); + exit(-1); + } + printf("N_id_2: %d\n", N_id_2); + + printf("Frame length %d samples\n", flen); + printf("PSS detection threshold: %.2f\n", threshold); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + last_peak = 0; + mean_peak = 0; + int peak_offset = 0; + float cfo; + float mean_cfo = 0; + uint32_t m0, m1; + uint32_t sss_error1 = 0, sss_error2 = 0, sss_error3 = 0; + uint32_t cp_is_norm = 0; + + srslte_sync_t ssync; + bzero(&ssync, sizeof(srslte_sync_t)); + ssync.fft_size = fft_size; + + n = srslte_filesource_read(&fsrc, buffer, file_offset); + + while(frame_cnt < nof_frames || nof_frames == -1) { + n = srslte_filesource_read(&fsrc, buffer, flen - peak_offset); + if (n < 0) { + fprintf(stderr, "Error reading samples\n"); + exit(-1); + } + if (n < flen - peak_offset) { + fprintf(stdout, "End of file (n=%d, flen=%d, peak=%d)\n", n, flen, peak_offset); + break; + } + + peak_idx = srslte_pss_find_pss(&pss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding PSS peak\n"); + exit(-1); + } + + mean_peak = SRSLTE_VEC_CMA(peak_value, mean_peak, frame_cnt); + + if (peak_value >= threshold) { + nof_det++; + + if (peak_idx >= fft_size) { + + // Estimate CFO + cfo = srslte_pss_cfo_compute(&pss, &buffer[peak_idx-fft_size]); + mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, frame_cnt); + + // Correct CFO + srslte_cfo_correct(&cfocorr, buffer, buffer, -mean_cfo / fft_size); + + // Estimate channel + if (srslte_pss_chest(&pss, &buffer[peak_idx-fft_size], ce)) { + fprintf(stderr, "Error computing channel estimation\n"); + exit(-1); + } + + // Find SSS + int sss_idx = peak_idx-2*fft_size-(SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN(fft_size, SRSLTE_CP_NORM_LEN):SRSLTE_CP_LEN(fft_size, SRSLTE_CP_EXT_LEN)); + if (sss_idx >= 0 && sss_idx < flen-fft_size) { + srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 3, NULL, &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error2++; + } + INFO("sf_idx = %d\n", srslte_sss_subframe(m0, m1)); + INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + srslte_sss_m0m1_diff(&sss, &buffer[sss_idx], &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error3++; + } + INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error1++; + } + INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + } + + // Estimate CP + if (peak_idx > 2*(fft_size + SRSLTE_CP_LEN_EXT(fft_size))) { + srslte_cp_t cp = srslte_sync_detect_cp(&ssync, buffer, peak_idx); + if (SRSLTE_CP_ISNORM(cp)) { + cp_is_norm++; + } + } + + } else { + INFO("No space for CFO computation. Frame starts at \n"); + } + + if(srslte_sss_subframe(m0,m1) == 0) + { +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + do_plots_sss(sss.corr_output_m0, sss.corr_output_m1); +#endif + } + + } else { + nof_nodet++; + } + + if (frame_cnt > 100) { + if (abs(last_peak-peak_idx) > 4) { + if (peak_value >= threshold) { + nof_nopeakdet++; + } + nof_nopeak++; + } + } + + frame_cnt++; + + printf("[%5d]: Pos: %5d, PSR: %4.1f (~%4.1f) Pdet: %4.2f, " + "FA: %4.2f, CFO: %+4.1f kHz SSSmiss: %4.2f/%4.2f/%4.2f CPNorm: %.0f%%\r", + frame_cnt, + peak_idx - flen/10, + peak_value, mean_peak, + (float) nof_det/frame_cnt, + (float) nof_nopeakdet/frame_cnt, mean_cfo*15, + (float) sss_error1/nof_det,(float) sss_error2/nof_det,(float) sss_error3/nof_det, + (float) cp_is_norm/nof_det * 100); + + if (SRSLTE_VERBOSE_ISINFO()) { + printf("\n"); + } + + usleep(10000); + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + do_plots(pss.conv_output_avg, pss.conv_output_avg[peak_idx], pss.fft_size+pss.frame_size-1, ce); +#endif + + last_peak = peak_idx; + + } + + srslte_pss_free(&pss); + free(buffer); + srslte_filesource_free(&fsrc); +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + destroy_plots(); +#endif + + printf("Ok\n"); + exit(0); +} + +extern cf_t *tmp2; + + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + + +#include "srsgui/srsgui.h" +plot_real_t pssout; +//plot_complex_t pce; + +plot_real_t psss1;//, psss2; + +float tmp[1000000]; +cf_t tmpce[SRSLTE_PSS_LEN]; + + +void init_plots() { + sdrgui_init(); + plot_real_init(&pssout); + plot_real_setTitle(&pssout, "PSS xCorr"); + plot_real_setLabels(&pssout, "Index", "Absolute value"); + plot_real_setYAxisScale(&pssout, 0, 1); + + /* + plot_complex_init(&pce); + plot_complex_setTitle(&pce, "Channel Estimates"); + plot_complex_setYAxisScale(&pce, Ip, -2, 2); + plot_complex_setYAxisScale(&pce, Q, -2, 2); + plot_complex_setYAxisScale(&pce, Magnitude, 0, 2); + plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI); + */ + + plot_real_init(&psss1); + plot_real_setTitle(&psss1, "SSS xCorr m0"); + plot_real_setLabels(&psss1, "Index", "Absolute value"); + plot_real_setYAxisScale(&psss1, 0, 1); + + /* + plot_real_init(&psss2); + plot_real_setTitle(&psss2, "SSS xCorr m1"); + plot_real_setLabels(&psss2, "Index", "Absolute value"); + plot_real_setYAxisScale(&psss2, 0, 1); + */ + + +} + +void do_plots(float *corr, float energy, uint32_t size, cf_t ce[SRSLTE_PSS_LEN]) { + srslte_vec_sc_prod_fff(corr,1./energy,tmp, size); + plot_real_setNewData(&pssout, tmp, size); + +// float norm = srslte_vec_avg_power_cf(ce, SRSLTE_PSS_LEN); + // srslte_vec_sc_prod_cfc(ce, 1.0/sqrt(norm), tmpce, SRSLTE_PSS_LEN); + + //plot_complex_setNewData(&pce, tmpce, SRSLTE_PSS_LEN); +} + +void do_plots_sss(float *corr_m0, float *corr_m1) { + if (m0_value > 0) + srslte_vec_sc_prod_fff(corr_m0,1./m0_value,corr_m0, SRSLTE_SSS_N); + plot_real_setNewData(&psss1, corr_m0, SRSLTE_SSS_N); + +// if (m1_value > 0) +// srslte_vec_sc_prod_fff(corr_m1,1./m1_value,corr_m1, SRSLTE_SSS_N); +// plot_real_setNewData(&psss2, corr_m1, SRSLTE_SSS_N); +} + +void destroy_plots() { + sdrgui_exit(); +} + + +#endif diff --git a/lib/src/phy/sync/test/pss_mex.c b/lib/src/phy/sync/test/pss_mex.c new file mode 100644 index 0000000..59cc0a8 --- /dev/null +++ b/lib/src/phy/sync/test/pss_mex.c @@ -0,0 +1,101 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define NOF_INPUTS 2 + + +void help() +{ + mexErrMsgTxt + ("[offset,corr] = srslte_pss(enbConfig, inputSignal)\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_cell_t cell; + srslte_pss_t pss; + cf_t *input_symbols; + int frame_len; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + srslte_use_standard_symbol_size(true); + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + /* Allocate input buffers */ + frame_len = mexutils_read_cf(INPUT, &input_symbols); + if (frame_len < 0) { + mexErrMsgTxt("Error reading input symbols\n"); + return; + } + + if (nrhs == NOF_INPUTS+1) { + frame_len = (int) mxGetScalar(prhs[NOF_INPUTS]); + } + + if (srslte_pss_init_fft(&pss, frame_len, srslte_symbol_sz(cell.nof_prb))) { + fprintf(stderr, "Error initiating PSS\n"); + exit(-1); + } + if (srslte_pss_set_N_id_2(&pss, cell.id%3)) { + fprintf(stderr, "Error setting N_id_2=%d\n",cell.id%3); + exit(-1); + } + srslte_pss_set_ema_alpha(&pss, 1.0); + + int peak_idx = srslte_pss_find_pss(&pss, input_symbols, NULL); + + if (nlhs >= 1) { + plhs[0] = mxCreateDoubleScalar(peak_idx); + } + if (nlhs >= 2) { + mexutils_write_cf(pss.conv_output, &plhs[1], frame_len, 1); + } + + srslte_pss_free(&pss); + free(input_symbols); + + return; +} + diff --git a/lib/src/phy/sync/test/pss_usrp.c b/lib/src/phy/sync/test/pss_usrp.c new file mode 100644 index 0000000..08afdd0 --- /dev/null +++ b/lib/src/phy/sync/test/pss_usrp.c @@ -0,0 +1,424 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" +#include "srslte/phy/rf/rf.h" + + +#ifndef DISABLE_GRAPHICS +void init_plots(); +void do_plots_pss(float *corr, float energy, uint32_t size); +void do_plots_sss(float *corr_m0, float *corr_m1); +#endif + + +bool disable_plots = false; +int cell_id = -1; +char *rf_args=""; +float rf_gain=40.0, rf_freq=-1.0; +int nof_frames = -1; +uint32_t fft_size=64; +float threshold = 0.4; +int N_id_2_sync = -1; +srslte_cp_t cp=SRSLTE_CP_NORM; + +void usage(char *prog) { + printf("Usage: %s [aedgtvnp] -f rx_frequency_hz -i cell_id\n", prog); + printf("\t-a RF args [Default %s]\n", rf_args); + printf("\t-g RF Gain [Default %.2f dB]\n", rf_gain); + printf("\t-n nof_frames [Default %d]\n", nof_frames); + printf("\t-l N_id_2 to sync [Default use cell_id]\n"); + printf("\t-e Extended CP [Default Normal]\n"); + printf("\t-s symbol_sz [Default %d]\n", fft_size); + printf("\t-t threshold [Default %.2f]\n", threshold); +#ifndef DISABLE_GRAPHICS + printf("\t-d disable plots [Default enabled]\n"); +#else + printf("\t plots are disabled. Graphics library not available\n"); +#endif + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "adgetvnsfil")) != -1) { + switch (opt) { + case 'a': + rf_args = argv[optind]; + break; + case 'g': + rf_gain = atof(argv[optind]); + break; + case 'f': + rf_freq = atof(argv[optind]); + break; + case 't': + threshold = atof(argv[optind]); + break; + case 'e': + cp = SRSLTE_CP_EXT; + break; + case 'i': + cell_id = atoi(argv[optind]); + break; + case 'l': + N_id_2_sync = atoi(argv[optind]); + break; + case 's': + fft_size = atoi(argv[optind]); + break; + case 'n': + nof_frames = atoi(argv[optind]); + break; + case 'd': + disable_plots = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (cell_id < 0 || rf_freq < 0) { + usage(argv[0]); + exit(-1); + } +} + float m0_value, m1_value; + +int main(int argc, char **argv) { + cf_t *buffer; + int frame_cnt, n; + srslte_rf_t rf; + srslte_pss_t pss; + srslte_cfo_t cfocorr, cfocorr64; + srslte_sss_t sss; + int32_t flen; + int peak_idx, last_peak; + float peak_value; + float mean_peak; + uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; + cf_t ce[SRSLTE_PSS_LEN]; + float sfo = 0; + + parse_args(argc, argv); + + if (N_id_2_sync == -1) { + N_id_2_sync = cell_id%3; + } + uint32_t N_id_2 = cell_id%3; + uint32_t N_id_1 = cell_id/3; + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + init_plots(); +#endif + + float srate = 15000.0*fft_size; + + flen = srate*5/1000; + + printf("Opening RF device...\n"); + if (srslte_rf_open(&rf, rf_args)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + if (srate < 10e6) { + srslte_rf_set_master_clock_rate(&rf, 4*srate); + } else { + srslte_rf_set_master_clock_rate(&rf, srate); + } + + printf("Set RX rate: %.2f MHz\n", srslte_rf_set_rx_srate(&rf, srate) / 1000000); + printf("Set RX gain: %.1f dB\n", srslte_rf_set_rx_gain(&rf, rf_gain)); + printf("Set RX freq: %.2f MHz\n", srslte_rf_set_rx_freq(&rf, rf_freq) / 1000000); + srslte_rf_rx_wait_lo_locked(&rf); + + buffer = malloc(sizeof(cf_t) * flen * 2); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + if (srslte_pss_init_fft(&pss, flen, fft_size)) { + fprintf(stderr, "Error initiating PSS\n"); + exit(-1); + } + + if (srslte_pss_set_N_id_2(&pss, N_id_2_sync)) { + fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2_sync); + exit(-1); + } + + srslte_cfo_init(&cfocorr, flen); + srslte_cfo_init(&cfocorr64, flen); + + if (srslte_sss_init(&sss, fft_size)) { + fprintf(stderr, "Error initializing SSS object\n"); + exit(-1); + } + + srslte_sss_set_N_id_2(&sss, N_id_2); + + printf("N_id_2: %d\n", N_id_2); + + srslte_rf_start_rx_stream(&rf, false); + + printf("Frame length %d samples\n", flen); + printf("PSS detection threshold: %.2f\n", threshold); + + nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; + frame_cnt = 0; + last_peak = 0; + mean_peak = 0; + int peak_offset = 0; + float cfo; + float mean_cfo = 0; + uint32_t m0, m1; + uint32_t sss_error1 = 0, sss_error2 = 0, sss_error3 = 0; + uint32_t cp_is_norm = 0; + + srslte_sync_t ssync; + bzero(&ssync, sizeof(srslte_sync_t)); + ssync.fft_size = fft_size; + + uint32_t max_peak = 0; + uint32_t max_peak_ = 0; + uint32_t min_peak = fft_size; + uint32_t min_peak_ = fft_size; + + pss.filter_pss_enable = true; + + while(frame_cnt < nof_frames || nof_frames == -1) { + n = srslte_rf_recv(&rf, buffer, flen - peak_offset, 1); + if (n < 0) { + fprintf(stderr, "Error receiving samples\n"); + exit(-1); + } + + peak_idx = srslte_pss_find_pss(&pss, buffer, &peak_value); + if (peak_idx < 0) { + fprintf(stderr, "Error finding PSS peak\n"); + exit(-1); + } + + mean_peak = SRSLTE_VEC_CMA(peak_value, mean_peak, frame_cnt); + + if (peak_value >= threshold) { + nof_det++; + + if (peak_idx >= fft_size) { + + // Estimate CFO + cfo = srslte_pss_cfo_compute(&pss, &buffer[peak_idx-fft_size]); + mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, frame_cnt); + + // Correct CFO + srslte_cfo_correct(&cfocorr, buffer, buffer, -mean_cfo / fft_size); + + // Estimate channel + if (srslte_pss_chest(&pss, &buffer[peak_idx-fft_size], ce)) { + fprintf(stderr, "Error computing channel estimation\n"); + exit(-1); + } + + // Find SSS + int sss_idx = peak_idx-2*fft_size-(SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN(fft_size, SRSLTE_CP_NORM_LEN):SRSLTE_CP_LEN(fft_size, SRSLTE_CP_EXT_LEN)); + if (sss_idx >= 0 && sss_idx < flen-fft_size) { + + // Filter SSS + srslte_pss_filter(&pss, &buffer[sss_idx], &buffer[sss_idx]); + + INFO("Full N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, ce, &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error2++; + } + INFO("Partial N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + srslte_sss_m0m1_diff_coh(&sss, &buffer[sss_idx], ce, &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error3++; + } + INFO("Diff N_id_1: %d\n", srslte_sss_N_id_1(&sss, m0, m1)); + } + srslte_sss_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); + if (srslte_sss_N_id_1(&sss, m0, m1) != N_id_1) { + sss_error1++; + } + + // Estimate CP + if (peak_idx > 2*(fft_size + SRSLTE_CP_LEN_EXT(fft_size))) { + srslte_cp_t cp = srslte_sync_detect_cp(&ssync, buffer, peak_idx); + if (SRSLTE_CP_ISNORM(cp)) { + cp_is_norm++; + } + } + + } else { + INFO("No space for CFO computation. Frame starts at \n"); + } + + if(srslte_sss_subframe(m0,m1) == 0) + { +#ifndef DISABLE_GRAPHICS + if (!disable_plots) + do_plots_sss(sss.corr_output_m0, sss.corr_output_m1); +#endif + } + + } else { + nof_nodet++; + } + + printf("[%5d]: Pos: %5d (%d-%d), PSR: %4.1f (~%4.1f) Pdet: %4.2f, " + "FA: %4.2f, CFO: %+7.1f Hz, SFO: %+.2f Hz SSSmiss: %4.2f/%4.2f/%4.2f CPNorm: %.0f%%\r", + frame_cnt, + peak_idx, min_peak_, max_peak_, + peak_value, mean_peak, + (float) nof_det/frame_cnt, + (float) nof_nopeakdet/frame_cnt, mean_cfo*15000, sfo, + (float) sss_error1/nof_det,(float) sss_error2/nof_det,(float) sss_error3/nof_det, + (float) cp_is_norm/nof_det * 100); + + if (frame_cnt > 100) { + if (abs(last_peak-peak_idx) > 4) { + if (peak_value >= threshold) { + nof_nopeakdet++; + } + nof_nopeak++; + } + + sfo = SRSLTE_VEC_CMA((peak_idx - last_peak)/5e-3, sfo, frame_cnt); + + int frame_idx = frame_cnt % 200; + uint32_t peak_offset_symbol = peak_idx%fft_size; + if (peak_offset_symbol > max_peak) { + max_peak = peak_offset_symbol; + } + if (peak_offset_symbol < min_peak) { + min_peak = peak_offset_symbol; + } + if (!frame_idx) { + max_peak_ = max_peak; + min_peak_ = min_peak; + max_peak = 0; + min_peak = fft_size; + } + } + + frame_cnt++; + + if (SRSLTE_VERBOSE_ISINFO()) { + printf("\n"); + } + +#ifndef DISABLE_GRAPHICS + if (!disable_plots) { + do_plots_pss(pss.conv_output_avg, pss.conv_output_avg[peak_idx], pss.fft_size+pss.frame_size-1); + } +#endif + + last_peak = peak_idx; + + } + + srslte_sss_free(&sss); + srslte_pss_free(&pss); + free(buffer); + srslte_rf_close(&rf); + + + printf("Ok\n"); + exit(0); +} + +extern cf_t *tmp2; + + +/********************************************************************** + * Plotting Functions + ***********************************************************************/ +#ifndef DISABLE_GRAPHICS + + +#include "srsgui/srsgui.h" +plot_real_t pssout; +//plot_complex_t pce; + +plot_real_t psss1; + +float tmp[1000000]; +cf_t tmpce[SRSLTE_PSS_LEN]; + + +void init_plots() { + sdrgui_init(); + plot_real_init(&pssout); + plot_real_setTitle(&pssout, "PSS xCorr"); + plot_real_setLabels(&pssout, "Index", "Absolute value"); + plot_real_setYAxisScale(&pssout, 0, 1); + + /* + plot_complex_init(&pce); + plot_complex_setTitle(&pce, "Channel Estimates"); + plot_complex_setYAxisScale(&pce, Ip, -2, 2); + plot_complex_setYAxisScale(&pce, Q, -2, 2); + plot_complex_setYAxisScale(&pce, Magnitude, 0, 2); + plot_complex_setYAxisScale(&pce, Phase, -M_PI, M_PI); + */ + + plot_real_init(&psss1); + plot_real_setTitle(&psss1, "SSS xCorr m0"); + plot_real_setLabels(&psss1, "Index", "Absolute value"); + plot_real_setYAxisScale(&psss1, 0, 1); +} + +void do_plots_pss(float *corr, float peak, uint32_t size) { + srslte_vec_sc_prod_fff(corr,1./peak,tmp, size); + plot_real_setNewData(&pssout, tmp, size); +} + +void do_plots_sss(float *corr_m0, float *corr_m1) { + if (m0_value > 0) + srslte_vec_sc_prod_fff(corr_m0,1./m0_value,corr_m0, SRSLTE_SSS_N); + plot_real_setNewData(&psss1, corr_m0, SRSLTE_SSS_N); +} + + +#endif diff --git a/lib/src/phy/sync/test/sss_mex.c b/lib/src/phy/sync/test/sss_mex.c new file mode 100644 index 0000000..56165fe --- /dev/null +++ b/lib/src/phy/sync/test/sss_mex.c @@ -0,0 +1,127 @@ +/** + * + * \section COPYRIGHT + * +* Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include "srslte/srslte.h" +#include "srslte/mex/mexutils.h" + +/** MEX function to be called from MATLAB to test the channel estimator + */ + +#define ENBCFG prhs[0] +#define INPUT prhs[1] +#define ALGO prhs[2] +#define NOF_INPUTS 2 + + +void help() +{ + mexErrMsgTxt + ("[N_id_1,sf_idx,corr_output_m0,corr_output_m1] = srslte_sss(enbConfig, inputSignal, [Algorithm])\n" + "\tinputSignal must be aligned to the subframe. CP length is assumed Normal.\n" + "\tAlgorithm is an optional parameter: Can be 'partial','diff','full'\n\n"); +} + +/* the gateway function */ +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + srslte_cell_t cell; + srslte_sss_t sss; + cf_t *input_symbols; + int frame_len; + uint32_t m0, m1; + float m0_value, m1_value; + char alg[64]; + + if (nrhs < NOF_INPUTS) { + help(); + return; + } + + if (mexutils_read_cell(ENBCFG, &cell)) { + help(); + return; + } + + if (nrhs > NOF_INPUTS) { + mxGetString(ALGO, alg, (mwSize)sizeof(alg)); + } else { + strcpy(alg, "full"); + } + + /** Allocate input buffers */ + frame_len = mexutils_read_cf(INPUT, &input_symbols); + if (frame_len < 0) { + mexErrMsgTxt("Error reading input symbols\n"); + return; + } + + if (srslte_sss_init(&sss, srslte_symbol_sz(cell.nof_prb))) { + mexErrMsgTxt("Error initializing SSS object\n"); + return; + } + + srslte_sss_set_N_id_2(&sss, cell.id%3); + + // Find SSS + uint32_t sss_idx = SRSLTE_SLOT_IDX_CPNORM(5,srslte_symbol_sz(cell.nof_prb)); + if (sss_idx > frame_len) { + mexErrMsgTxt("Error too few samples provided.\n"); + return; + } + //mexPrintf("SSS begins at %d/%d. Running algorithm %s\n", sss_idx, frame_len, alg); + if (!strcmp(alg, "partial")) { + srslte_sss_m0m1_partial(&sss, &input_symbols[sss_idx], 3, NULL, &m0, &m0_value, &m1, &m1_value); + } else if (!strcmp(alg, "diff")) { + srslte_sss_m0m1_diff(&sss, &input_symbols[sss_idx], &m0, &m0_value, &m1, &m1_value); + } else if (!strcmp(alg, "full")) { + srslte_sss_m0m1_partial(&sss, &input_symbols[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); + } else { + mexErrMsgTxt("Unsupported algorithm type\n"); + return; + } + + //mexPrintf("m0: %d, m1: %d, N_id_1: %d\n", m0, m1, srslte_sss_N_id_1(&sss, m0, m1)); + + if (nlhs >= 1) { + plhs[0] = mxCreateDoubleScalar(srslte_sss_N_id_1(&sss, m0, m1)); + } + if (nlhs >= 2) { + plhs[1] = mxCreateDoubleScalar(srslte_sss_subframe(m0, m1)); + } + if (nlhs >= 3) { + mexutils_write_f(sss.corr_output_m0, &plhs[2], SRSLTE_SSS_N, 1); + } + if (nlhs >= 4) { + mexutils_write_f(sss.corr_output_m1, &plhs[3], SRSLTE_SSS_N, 1); + } + srslte_sss_free(&sss); + free(input_symbols); + + return; +} + diff --git a/lib/src/phy/sync/test/sync_test.c b/lib/src/phy/sync/test/sync_test.c new file mode 100644 index 0000000..7c6d02e --- /dev/null +++ b/lib/src/phy/sync/test/sync_test.c @@ -0,0 +1,193 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "srslte/srslte.h" + +int cell_id = -1, offset = 0; +srslte_cp_t cp = SRSLTE_CP_NORM; +uint32_t nof_prb=6; + +#define FLEN SRSLTE_SF_LEN(fft_size) + +void usage(char *prog) { + printf("Usage: %s [cpoev]\n", prog); + printf("\t-c cell_id [Default check for all]\n"); + printf("\t-p nof_prb [Default %d]\n", nof_prb); + printf("\t-o offset [Default %d]\n", offset); + printf("\t-e extended CP [Default normal]\n"); + printf("\t-v srslte_verbose\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cpoev")) != -1) { + switch (opt) { + case 'c': + cell_id = atoi(argv[optind]); + break; + case 'p': + nof_prb = atoi(argv[optind]); + break; + case 'o': + offset = atoi(argv[optind]); + break; + case 'e': + cp = SRSLTE_CP_EXT; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char **argv) { + int N_id_2, sf_idx, find_sf; + cf_t *buffer, *fft_buffer; + cf_t pss_signal[SRSLTE_PSS_LEN]; + float sss_signal0[SRSLTE_SSS_LEN]; // for subframe 0 + float sss_signal5[SRSLTE_SSS_LEN]; // for subframe 5 + int cid, max_cid; + uint32_t find_idx; + srslte_sync_t syncobj; + srslte_ofdm_t ifft; + int fft_size; + + parse_args(argc, argv); + + fft_size = srslte_symbol_sz(nof_prb); + if (fft_size < 0) { + fprintf(stderr, "Invalid nof_prb=%d\n", nof_prb); + exit(-1); + } + + buffer = malloc(sizeof(cf_t) * FLEN); + if (!buffer) { + perror("malloc"); + exit(-1); + } + + fft_buffer = malloc(sizeof(cf_t) * FLEN * 2); + if (!fft_buffer) { + perror("malloc"); + exit(-1); + } + + if (srslte_ofdm_tx_init(&ifft, cp, buffer, fft_buffer, nof_prb)) { + fprintf(stderr, "Error creating iFFT object\n"); + exit(-1); + } + + if (srslte_sync_init(&syncobj, FLEN, FLEN, fft_size)) { + fprintf(stderr, "Error initiating PSS/SSS\n"); + return -1; + } + + srslte_sync_set_cp(&syncobj, cp); + + /* Set a very high threshold to make sure the correlation is ok */ + srslte_sync_set_threshold(&syncobj, 5.0); + srslte_sync_set_sss_algorithm(&syncobj, SSS_PARTIAL_3); + + if (cell_id == -1) { + cid = 0; + max_cid = 49; + } else { + cid = cell_id; + max_cid = cell_id; +} + while(cid <= max_cid) { + N_id_2 = cid%3; + + /* Generate PSS/SSS signals */ + srslte_pss_generate(pss_signal, N_id_2); + srslte_sss_generate(sss_signal0, sss_signal5, cid); + + srslte_sync_set_N_id_2(&syncobj, N_id_2); + + // SF1 is SF5 + for (sf_idx=0;sf_idx<2;sf_idx++) { + memset(buffer, 0, sizeof(cf_t) * FLEN); + srslte_pss_put_slot(pss_signal, buffer, nof_prb, cp); + srslte_sss_put_slot(sf_idx?sss_signal5:sss_signal0, buffer, nof_prb, cp); + + /* Transform to OFDM symbols */ + memset(fft_buffer, 0, sizeof(cf_t) * FLEN); + srslte_ofdm_tx_sf(&ifft); + + /* Apply sample offset */ + for (int i = 0; i < FLEN; i++) { + fft_buffer[FLEN - i - 1 + offset] = fft_buffer[FLEN - i - 1]; + } + bzero(fft_buffer, sizeof(cf_t) * offset); + + if (srslte_sync_find(&syncobj, fft_buffer, 0, &find_idx) < 0) { + fprintf(stderr, "Error running srslte_sync_find\n"); + exit(-1); + } + find_sf = srslte_sync_get_sf_idx(&syncobj); + printf("cell_id: %d find: %d, offset: %d, ns=%d find_ns=%d\n", cid, find_idx, offset, + sf_idx, find_sf); + if (find_idx != offset + FLEN/2) { + printf("offset != find_offset: %d != %d\n", find_idx, offset + FLEN/2); + exit(-1); + } + if (sf_idx*5 != find_sf) { + printf("ns != find_ns\n"); + exit(-1); + } + if (srslte_sync_get_cp(&syncobj) != cp) { + printf("Detected CP should be %s\n", SRSLTE_CP_ISNORM(cp)?"Normal":"Extended"); + exit(-1); + } + } + cid++; + } + + free(fft_buffer); + free(buffer); + + srslte_sync_free(&syncobj); + srslte_ofdm_tx_free(&ifft); + srslte_dft_exit(); + + printf("Ok\n"); + exit(0); +} + diff --git a/lib/src/phy/ue/CMakeLists.txt b/lib/src/phy/ue/CMakeLists.txt new file mode 100644 index 0000000..3072b3a --- /dev/null +++ b/lib/src/phy/ue/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_ue OBJECT ${SOURCES}) diff --git a/lib/src/phy/ue/ue_cell_search.c b/lib/src/phy/ue/ue_cell_search.c new file mode 100644 index 0000000..133aad1 --- /dev/null +++ b/lib/src/phy/ue/ue_cell_search.c @@ -0,0 +1,343 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include + +#include "srslte/phy/ue/ue_cell_search.h" + +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +int srslte_ue_cellsearch_init(srslte_ue_cellsearch_t * q, uint32_t max_frames, + int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*), + void *stream_handler) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) { + ret = SRSLTE_ERROR; + srslte_cell_t cell; + + bzero(q, sizeof(srslte_ue_cellsearch_t)); + + bzero(&cell, sizeof(srslte_cell_t)); + cell.id = SRSLTE_CELL_ID_UNKNOWN; + cell.nof_prb = SRSLTE_CS_NOF_PRB; + + if (srslte_ue_sync_init(&q->ue_sync, cell.nof_prb, true, recv_callback, stream_handler)) { + fprintf(stderr, "Error initiating ue_sync\n"); + goto clean_exit; + } + + if (srslte_ue_sync_set_cell(&q->ue_sync, cell)) { + fprintf(stderr, "Error initiating ue_sync\n"); + goto clean_exit; + } + + q->sf_buffer[0] = srslte_vec_malloc(3*sizeof(cf_t)*SRSLTE_SF_LEN_PRB(100)); + q->nof_rx_antennas = 1; + + q->candidates = calloc(sizeof(srslte_ue_cellsearch_result_t), max_frames); + if (!q->candidates) { + perror("malloc"); + goto clean_exit; + } + q->mode_ntimes = calloc(sizeof(uint32_t), max_frames); + if (!q->mode_ntimes) { + perror("malloc"); + goto clean_exit; + } + q->mode_counted = calloc(sizeof(uint8_t), max_frames); + if (!q->mode_counted) { + perror("malloc"); + goto clean_exit; + } + + q->max_frames = max_frames; + q->nof_valid_frames = max_frames; + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_cellsearch_free(q); + } + return ret; +} + +int srslte_ue_cellsearch_init_multi(srslte_ue_cellsearch_t * q, uint32_t max_frames, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t,srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && nof_rx_antennas > 0) { + ret = SRSLTE_ERROR; + srslte_cell_t cell; + + bzero(q, sizeof(srslte_ue_cellsearch_t)); + + bzero(&cell, sizeof(srslte_cell_t)); + cell.id = SRSLTE_CELL_ID_UNKNOWN; + cell.nof_prb = SRSLTE_CS_NOF_PRB; + + if (srslte_ue_sync_init_multi(&q->ue_sync, cell.nof_prb, true, recv_callback, nof_rx_antennas, stream_handler)) { + fprintf(stderr, "Error initiating ue_sync\n"); + goto clean_exit; + } + if (srslte_ue_sync_set_cell(&q->ue_sync, cell)) { + fprintf(stderr, "Error setting cell in ue_sync\n"); + goto clean_exit; + } + + for (int i=0;isf_buffer[i] = srslte_vec_malloc(3*sizeof(cf_t)*SRSLTE_SF_LEN_PRB(100)); + } + q->nof_rx_antennas = nof_rx_antennas; + + q->candidates = calloc(sizeof(srslte_ue_cellsearch_result_t), max_frames); + if (!q->candidates) { + perror("malloc"); + goto clean_exit; + } + q->mode_ntimes = calloc(sizeof(uint32_t), max_frames); + if (!q->mode_ntimes) { + perror("malloc"); + goto clean_exit; + } + q->mode_counted = calloc(sizeof(uint8_t), max_frames); + if (!q->mode_counted) { + perror("malloc"); + goto clean_exit; + } + + q->max_frames = max_frames; + q->nof_valid_frames = max_frames; + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_cellsearch_free(q); + } + return ret; +} + +void srslte_ue_cellsearch_free(srslte_ue_cellsearch_t * q) +{ + for (int i=0;inof_rx_antennas;i++) { + if (q->sf_buffer[i]) { + free(q->sf_buffer[i]); + } + } + if (q->candidates) { + free(q->candidates); + } + if (q->mode_counted) { + free(q->mode_counted); + } + if (q->mode_ntimes) { + free(q->mode_ntimes); + } + srslte_ue_sync_free(&q->ue_sync); + + bzero(q, sizeof(srslte_ue_cellsearch_t)); + +} + +int srslte_ue_cellsearch_set_nof_valid_frames(srslte_ue_cellsearch_t * q, uint32_t nof_frames) +{ + if (nof_frames <= q->max_frames) { + q->nof_valid_frames = nof_frames; + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +/* Decide the most likely cell based on the mode */ +static void get_cell(srslte_ue_cellsearch_t * q, uint32_t nof_detected_frames, srslte_ue_cellsearch_result_t *found_cell) +{ + uint32_t i, j; + + bzero(q->mode_counted, nof_detected_frames); + bzero(q->mode_ntimes, sizeof(uint32_t) * nof_detected_frames); + + /* First find mode of CELL IDs */ + for (i = 0; i < nof_detected_frames; i++) { + uint32_t cnt = 1; + for (j=i+1;jcandidates[j].cell_id == q->candidates[i].cell_id && !q->mode_counted[j]) { + q->mode_counted[j]=1; + cnt++; + } + } + q->mode_ntimes[i] = cnt; + } + uint32_t max_times=0, mode_pos=0; + for (i=0;imode_ntimes[i] > max_times) { + max_times = q->mode_ntimes[i]; + mode_pos = i; + } + } + found_cell->cell_id = q->candidates[mode_pos].cell_id; + /* Now in all these cell IDs, find most frequent CP */ + uint32_t nof_normal = 0; + found_cell->peak = 0; + for (i=0;icandidates[i].cell_id == found_cell->cell_id) { + if (SRSLTE_CP_ISNORM(q->candidates[i].cp)) { + nof_normal++; + } + } + // average absolute peak value + found_cell->peak += q->candidates[i].peak; + } + found_cell->peak /= nof_detected_frames; + + if (nof_normal > q->mode_ntimes[mode_pos]/2) { + found_cell->cp = SRSLTE_CP_NORM; + } else { + found_cell->cp = SRSLTE_CP_EXT; + } + found_cell->mode = (float) q->mode_ntimes[mode_pos]/nof_detected_frames; + + // PSR is already averaged so take the last value + found_cell->psr = q->candidates[nof_detected_frames-1].psr; + + // CFO is also already averaged + found_cell->cfo = q->candidates[nof_detected_frames-1].cfo; +} + +/** Finds up to 3 cells, one per each N_id_2=0,1,2 and stores ID and CP in the structure pointed by found_cell. + * Each position in found_cell corresponds to a different N_id_2. + * Saves in the pointer max_N_id_2 the N_id_2 index of the cell with the highest PSR + * Returns the number of found cells or a negative number if error + */ +int srslte_ue_cellsearch_scan(srslte_ue_cellsearch_t * q, + srslte_ue_cellsearch_result_t found_cells[3], + uint32_t *max_N_id_2) +{ + int ret = 0; + float max_peak_value = -1.0; + uint32_t nof_detected_cells = 0; + + for (uint32_t N_id_2=0;N_id_2<3 && ret >= 0;N_id_2++) { + ret = srslte_ue_cellsearch_scan_N_id_2(q, N_id_2, &found_cells[N_id_2]); + if (ret < 0) { + fprintf(stderr, "Error searching cell\n"); + return ret; + } + nof_detected_cells += ret; + if (max_N_id_2) { + if (found_cells[N_id_2].peak > max_peak_value) { + max_peak_value = found_cells[N_id_2].peak; + *max_N_id_2 = N_id_2; + } + } + } + return nof_detected_cells; +} + +/** Finds a cell for a given N_id_2 and stores ID and CP in the structure pointed by found_cell. + * Returns 1 if the cell is found, 0 if not or -1 on error + */ +int srslte_ue_cellsearch_scan_N_id_2(srslte_ue_cellsearch_t * q, + uint32_t N_id_2, + srslte_ue_cellsearch_result_t *found_cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t nof_detected_frames = 0; + uint32_t nof_scanned_frames = 0; + + if (q != NULL) + { + ret = SRSLTE_SUCCESS; + + bzero(q->candidates, sizeof(srslte_ue_cellsearch_result_t)*q->max_frames); + bzero(q->mode_ntimes, sizeof(uint32_t)*q->max_frames); + bzero(q->mode_counted, sizeof(uint8_t)*q->max_frames); + + srslte_ue_sync_set_N_id_2(&q->ue_sync, N_id_2); + srslte_ue_sync_reset(&q->ue_sync); + srslte_ue_sync_cfo_reset(&q->ue_sync); + + do { + + ret = srslte_ue_sync_zerocopy_multi(&q->ue_sync, q->sf_buffer); + if (ret < 0) { + fprintf(stderr, "Error calling srslte_ue_sync_work()\n"); + return -1; + } else if (ret == 1) { + /* This means a peak was found and ue_sync is now in tracking state */ + ret = srslte_sync_get_cell_id(&q->ue_sync.strack); + if (ret >= 0) { + /* Save cell id, cp and peak */ + q->candidates[nof_detected_frames].cell_id = (uint32_t) ret; + q->candidates[nof_detected_frames].cp = srslte_sync_get_cp(&q->ue_sync.strack); + q->candidates[nof_detected_frames].peak = q->ue_sync.strack.pss.peak_value; + q->candidates[nof_detected_frames].psr = srslte_sync_get_peak_value(&q->ue_sync.strack); + q->candidates[nof_detected_frames].cfo = srslte_ue_sync_get_cfo(&q->ue_sync); + DEBUG + ("CELL SEARCH: [%3d/%3d/%d]: Found peak PSR=%.3f, Cell_id: %d CP: %s\n", + nof_detected_frames, nof_scanned_frames, q->nof_valid_frames, + q->candidates[nof_detected_frames].psr, q->candidates[nof_detected_frames].cell_id, + srslte_cp_string(q->candidates[nof_detected_frames].cp)); + + nof_detected_frames++; + } + } else if (ret == 0) { + /* This means a peak is not yet found and ue_sync is in find state + * Do nothing, just wait and increase nof_scanned_frames counter. + */ + } + + nof_scanned_frames++; + + } while (nof_scanned_frames < q->max_frames && nof_detected_frames < q->nof_valid_frames); + + /* In either case, check if the mean PSR is above the minimum threshold */ + if (nof_detected_frames > 0) { + ret = 1; // A cell has been found. + if (found_cell) { + get_cell(q, nof_detected_frames, found_cell); + } + } else { + ret = 0; // A cell was not found. + } + } + + return ret; +} diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c new file mode 100644 index 0000000..558f8b5 --- /dev/null +++ b/lib/src/phy/ue/ue_dl.c @@ -0,0 +1,991 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/phy/ue/ue_dl.h" + +#include +#include + + +#define CURRENT_FFTSIZE srslte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SRSLTE_SF_LEN(CURRENT_FFTSIZE) + +#define CURRENT_SLOTLEN_RE SRSLTE_SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + +#define MAX_SFLEN_RE SRSLTE_SF_LEN_RE(max_prb, q->cell.cp) + +const static srslte_dci_format_t ue_dci_formats[8][2] = { + /* Mode 1 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1}, + /* Mode 2 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1}, + /* Mode 3 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT2A}, + /* Mode 4 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT2}, + /* Mode 5 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1D}, + /* Mode 6 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1B}, + /* Mode 7 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT1}, + /* Mode 8 */ {SRSLTE_DCI_FORMAT1A, SRSLTE_DCI_FORMAT2B} +}; + +static srslte_dci_format_t common_formats[] = {SRSLTE_DCI_FORMAT1A,SRSLTE_DCI_FORMAT1C}; +const uint32_t nof_common_formats = 2; + +int srslte_ue_dl_init(srslte_ue_dl_t *q, + cf_t *in_buffer[SRSLTE_MAX_PORTS], + uint32_t max_prb, + uint32_t nof_rx_antennas) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + nof_rx_antennas <= SRSLTE_MAX_PORTS) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_ue_dl_t)); + + q->pdsch_pkt_errors = 0; + q->pdsch_pkts_total = 0; + q->pmch_pkt_errors = 0; + q->pmch_pkts_total = 0; + q->pending_ul_dci_rnti = 0; + q->nof_rx_antennas = nof_rx_antennas; + + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + q->sf_symbols_m[j] = srslte_vec_malloc(MAX_SFLEN_RE * sizeof(cf_t)); + if (!q->sf_symbols_m[j]) { + perror("malloc"); + goto clean_exit; + } + for (uint32_t i=0;ice_m[i][j] = srslte_vec_malloc(MAX_SFLEN_RE * sizeof(cf_t)); + if (!q->ce_m[i][j]) { + perror("malloc"); + goto clean_exit; + } + bzero(q->ce_m[i][j], MAX_SFLEN_RE * sizeof(cf_t)); + } + } + + q->sf_symbols = q->sf_symbols_m[0]; + for (int i=0;ice[i] = q->ce_m[i][0]; + } + + for (int i = 0; i < nof_rx_antennas; i++) { + if (srslte_ofdm_rx_init(&q->fft[i], SRSLTE_CP_NORM, in_buffer[i], q->sf_symbols_m[i], max_prb)) { + fprintf(stderr, "Error initiating FFT\n"); + goto clean_exit; + } + } + + if (srslte_ofdm_rx_init_mbsfn(&q->fft_mbsfn, SRSLTE_CP_EXT, in_buffer[0], q->sf_symbols_m[0], max_prb)) { + fprintf(stderr, "Error initiating FFT for MBSFN subframes \n"); + goto clean_exit; + } + srslte_ofdm_set_non_mbsfn_region(&q->fft_mbsfn, 2); // Set a default to init + + if (srslte_chest_dl_init(&q->chest, max_prb)) { + fprintf(stderr, "Error initiating channel estimator\n"); + goto clean_exit; + } + if (srslte_pcfich_init(&q->pcfich, nof_rx_antennas)) { + fprintf(stderr, "Error creating PCFICH object\n"); + goto clean_exit; + } + if (srslte_phich_init(&q->phich, nof_rx_antennas)) { + fprintf(stderr, "Error creating PHICH object\n"); + goto clean_exit; + } + + if (srslte_pdcch_init_ue(&q->pdcch, max_prb, nof_rx_antennas)) { + fprintf(stderr, "Error creating PDCCH object\n"); + goto clean_exit; + } + + if (srslte_pdsch_init_ue(&q->pdsch, max_prb, nof_rx_antennas)) { + fprintf(stderr, "Error creating PDSCH object\n"); + goto clean_exit; + } + + if (srslte_pmch_init_multi(&q->pmch, max_prb, nof_rx_antennas)) { + fprintf(stderr, "Error creating PMCH object\n"); + goto clean_exit; + } + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + q->softbuffers[i] = srslte_vec_malloc(sizeof(srslte_softbuffer_rx_t)); + if (!q->softbuffers[i]) { + fprintf(stderr, "Error allocating soft buffer\n"); + goto clean_exit; + } + + if (srslte_softbuffer_rx_init(q->softbuffers[i], max_prb)) { + fprintf(stderr, "Error initiating soft buffer\n"); + goto clean_exit; + } + } + + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parametres\n"); + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_dl_free(q); + } + return ret; +} + +void srslte_ue_dl_free(srslte_ue_dl_t *q) { + if (q) { + for (int port = 0; port < SRSLTE_MAX_PORTS; port++) { + srslte_ofdm_rx_free(&q->fft[port]); + } + srslte_ofdm_rx_free(&q->fft_mbsfn); + srslte_chest_dl_free(&q->chest); + srslte_regs_free(&q->regs); + srslte_pcfich_free(&q->pcfich); + srslte_phich_free(&q->phich); + srslte_pdcch_free(&q->pdcch); + srslte_pdsch_free(&q->pdsch); + srslte_pmch_free(&q->pmch); + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + srslte_softbuffer_rx_free(q->softbuffers[i]); + if (q->softbuffers[i]) { + free(q->softbuffers[i]); + } + } + for (int j = 0; j < SRSLTE_MAX_PORTS; j++) { + if (q->sf_symbols_m[j]) { + free(q->sf_symbols_m[j]); + } + for (uint32_t i=0;ice_m[i][j]) { + free(q->ce_m[i][j]); + } + } + } + bzero(q, sizeof(srslte_ue_dl_t)); + } +} + +int srslte_ue_dl_set_cell(srslte_ue_dl_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_cell_isvalid(&cell)) + { + q->pdsch_pkt_errors = 0; + q->pdsch_pkts_total = 0; + q->pmch_pkt_errors = 0; + q->pmch_pkts_total = 0; + q->pending_ul_dci_rnti = 0; + + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + if (q->cell.nof_prb != 0) { + srslte_regs_free(&q->regs); + } + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + if (srslte_regs_init(&q->regs, q->cell)) { + fprintf(stderr, "Error resizing REGs\n"); + return SRSLTE_ERROR; + } + for (int port = 0; port < q->nof_rx_antennas; port++) { + if (srslte_ofdm_rx_set_prb(&q->fft[port], q->cell.cp, q->cell.nof_prb)) { + fprintf(stderr, "Error resizing FFT\n"); + return SRSLTE_ERROR; + } + } + + if (srslte_ofdm_rx_set_prb(&q->fft_mbsfn, SRSLTE_CP_EXT, q->cell.nof_prb)) { + fprintf(stderr, "Error resizing MBSFN FFT\n"); + return SRSLTE_ERROR; + } + + if (srslte_chest_dl_set_cell(&q->chest, q->cell)) { + fprintf(stderr, "Error resizing channel estimator\n"); + return SRSLTE_ERROR; + } + if (srslte_pcfich_set_cell(&q->pcfich, &q->regs, q->cell)) { + fprintf(stderr, "Error resizing PCFICH object\n"); + return SRSLTE_ERROR; + } + if (srslte_phich_set_cell(&q->phich, &q->regs, q->cell)) { + fprintf(stderr, "Error resizing PHICH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pdcch_set_cell(&q->pdcch, &q->regs, q->cell)) { + fprintf(stderr, "Error resizing PDCCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pdsch_set_cell(&q->pdsch, q->cell)) { + fprintf(stderr, "Error resizing PDSCH object\n"); + return SRSLTE_ERROR; + } + + if (srslte_pmch_set_cell(&q->pmch, q->cell)) { + fprintf(stderr, "Error resizing PMCH object\n"); + return SRSLTE_ERROR; + } + + q->current_rnti = 0; + } + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid cell properties ue_dl: Id=%d, Ports=%d, PRBs=%d\n", + cell.id, cell.nof_ports, cell.nof_prb); + } + return ret; +} + +/* Precalculate the PDSCH scramble sequences for a given RNTI. This function takes a while + * to execute, so shall be called once the final C-RNTI has been allocated for the session. + * For the connection procedure, use srslte_pusch_encode_rnti() or srslte_pusch_decode_rnti() functions + */ +void srslte_ue_dl_set_rnti(srslte_ue_dl_t *q, uint16_t rnti) { + + srslte_pdsch_set_rnti(&q->pdsch, rnti); + + // Compute UE-specific and Common search space for this RNTI + for (int cfi=0;cfi<3;cfi++) { + for (int sf_idx=0;sf_idx<10;sf_idx++) { + q->current_ss_ue[cfi][sf_idx].nof_locations = srslte_pdcch_ue_locations(&q->pdcch, q->current_ss_ue[cfi][sf_idx].loc, MAX_CANDIDATES_UE, sf_idx, cfi+1, rnti); + } + q->current_ss_common[cfi].nof_locations = srslte_pdcch_common_locations(&q->pdcch, q->current_ss_common[cfi].loc, MAX_CANDIDATES_COM, cfi+1); + } + + q->current_rnti = rnti; +} +/* Set the area ID on pmch and chest_dl to generate scrambling sequence and reference + * signals. + */ +int srslte_ue_dl_set_mbsfn_area_id(srslte_ue_dl_t *q, + uint16_t mbsfn_area_id) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if(q != NULL) { + ret = SRSLTE_ERROR; + if(srslte_chest_dl_set_mbsfn_area_id(&q->chest, mbsfn_area_id)) { + fprintf(stderr, "Error setting MBSFN area ID \n"); + return ret; + } + if(srslte_pmch_set_area_id(&q->pmch, mbsfn_area_id)) { + fprintf(stderr, "Error setting MBSFN area ID \n"); + return ret; + } + q->current_mbsfn_area_id = mbsfn_area_id; + ret = SRSLTE_SUCCESS; + } + return ret; +} + +void srslte_ue_dl_set_non_mbsfn_region(srslte_ue_dl_t *q, + uint8_t non_mbsfn_region_length) { + srslte_ofdm_set_non_mbsfn_region(&q->fft_mbsfn, non_mbsfn_region_length); +} + +void srslte_ue_dl_set_power_alloc(srslte_ue_dl_t *q, float rho_a, float rho_b) { + if (q) { + srslte_pdsch_set_power_allocation(&q->pdsch, rho_a); + q->rho_b = rho_b; + + uint32_t nof_symbols_slot = SRSLTE_CP_NSYMB(q->cell.cp); + uint32_t nof_re_symbol = SRSLTE_NRE * q->cell.nof_prb; + + /* Apply rho_b if required according to 3GPP 36.213 Table 5.2-2 */ + if (rho_b != 0.0f && rho_b != 1.0f) { + float scaling = 1.0f / rho_b; + for (uint32_t i = 0; i < q->nof_rx_antennas; i++) { + for (uint32_t j = 0; j < 2; j++) { + cf_t *ptr; + ptr = q->sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 0); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + if (q->cell.cp == SRSLTE_CP_NORM) { + ptr = q->sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 4); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } else { + ptr = q->sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 3); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + if (q->cell.nof_ports == 4) { + ptr = q->sf_symbols_m[i] + nof_re_symbol * (j * nof_symbols_slot + 1); + srslte_vec_sc_prod_cfc(ptr, scaling, ptr, nof_re_symbol); + } + } + } + } + } +} + +void srslte_ue_dl_reset(srslte_ue_dl_t *q) { + for(int i = 0; i < SRSLTE_MAX_CODEWORDS; i++){ + srslte_softbuffer_rx_reset(q->softbuffers[i]); + } + bzero(&q->pdsch_cfg, sizeof(srslte_pdsch_cfg_t)); +} + +/** Applies the following operations to a subframe of synchronized samples: + * - OFDM demodulation + * - Channel estimation + * - PCFICH decoding + * - PDCCH decoding: Find DCI for RNTI given by previous call to srslte_ue_dl_set_rnti() + * - PDSCH decoding: Decode TB scrambling with RNTI given by srslte_ue_dl_set_rnti() + */ +int srslte_ue_dl_decode(srslte_ue_dl_t *q, uint8_t *data[SRSLTE_MAX_CODEWORDS], + uint32_t tm, uint32_t tti, bool acks[SRSLTE_MAX_CODEWORDS]) { + return srslte_ue_dl_decode_rnti(q, data, tm, tti, q->current_rnti, acks); +} + + +int srslte_ue_dl_decode_fft_estimate(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t *cfi){ + + return srslte_ue_dl_decode_fft_estimate_mbsfn(q, sf_idx, cfi, SRSLTE_SF_NORM); +} + +int srslte_ue_dl_decode_fft_estimate_mbsfn(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t *cfi, srslte_sf_t sf_type) +{ + if (q && cfi && sf_idx < SRSLTE_NSUBFRAMES_X_FRAME) { + + /* Run FFT for all subframe data */ + for (int j=0;jnof_rx_antennas;j++) { + if(sf_type == SRSLTE_SF_MBSFN ) { + srslte_ofdm_rx_sf(&q->fft_mbsfn); + }else{ + srslte_ofdm_rx_sf(&q->fft[j]); + } + } + return srslte_ue_dl_decode_estimate_mbsfn(q, sf_idx, cfi, sf_type); + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_ue_dl_decode_fft_estimate_noguru(srslte_ue_dl_t *q, cf_t *input[SRSLTE_MAX_PORTS], uint32_t sf_idx, uint32_t *cfi) +{ + if (input && q && sf_idx < SRSLTE_NSUBFRAMES_X_FRAME) { + + /* Run FFT for all subframe data */ + for (int j=0;jnof_rx_antennas;j++) { + srslte_ofdm_rx_sf_ng(&q->fft[j], input[j], q->sf_symbols_m[j]); + } + return srslte_ue_dl_decode_estimate_mbsfn(q, sf_idx, cfi, SRSLTE_SF_NORM); + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + +int srslte_ue_dl_decode_estimate(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t *cfi) { + + return srslte_ue_dl_decode_estimate_mbsfn(q, sf_idx, cfi, SRSLTE_SF_NORM); +} + + +int srslte_ue_dl_decode_estimate_mbsfn(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t *cfi, srslte_sf_t sf_type) { + float cfi_corr; + if (q && cfi && sf_idx < SRSLTE_NSUBFRAMES_X_FRAME) { + + /* Get channel estimates for each port */ + if(sf_type == SRSLTE_SF_MBSFN){ + srslte_chest_dl_estimate_multi_mbsfn(&q->chest, q->sf_symbols_m, q->ce_m, sf_idx, q->nof_rx_antennas, q->current_mbsfn_area_id); + }else{ + srslte_chest_dl_estimate_multi(&q->chest, q->sf_symbols_m, q->ce_m, sf_idx, q->nof_rx_antennas); + } + + + /* First decode PCFICH and obtain CFI */ + if (srslte_pcfich_decode_multi(&q->pcfich, q->sf_symbols_m, q->ce_m, + srslte_chest_dl_get_noise_estimate(&q->chest), + sf_idx, cfi, &cfi_corr)<0) { + fprintf(stderr, "Error decoding PCFICH\n"); + return SRSLTE_ERROR; + } + + INFO("Decoded CFI=%d with correlation %.2f, sf_idx=%d\n", *cfi, cfi_corr, sf_idx); + + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR_INVALID_INPUTS; + } +} + + +int srslte_ue_dl_cfg_grant(srslte_ue_dl_t *q, srslte_ra_dl_grant_t *grant, uint32_t cfi, uint32_t sf_idx, + int rvidx[SRSLTE_MAX_CODEWORDS], srslte_mimo_type_t mimo_type) { + uint32_t pmi = 0; + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(grant); + + /* Translates Precoding Information (pinfo) to Precoding matrix Index (pmi) as 3GPP 36.212 Table 5.3.3.1.5-4 */ + if (mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + if (nof_tb == 1) { + if (grant->pinfo > 0 && grant->pinfo < 5) { + pmi = grant->pinfo - 1; + } else { + ERROR("Not Implemented (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + pmi = grant->pinfo % 4; + } + } else { + if (grant->pinfo == 2) { + ERROR("Not implemented codebook index (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + } else if (grant->pinfo > 2) { + ERROR("Reserved codebook index (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); + } + pmi = grant->pinfo % 2; + } + } + if(SRSLTE_SF_MBSFN == grant->sf_type) { + return srslte_pmch_cfg(&q->pmch_cfg, q->cell, grant, cfi, sf_idx); + } else { + return srslte_pdsch_cfg_mimo(&q->pdsch_cfg, q->cell, grant, cfi, sf_idx, rvidx, mimo_type, pmi); + } +} + +int srslte_ue_dl_decode_rnti(srslte_ue_dl_t *q, + uint8_t *data[SRSLTE_MAX_CODEWORDS], uint32_t tm, uint32_t tti, uint16_t rnti, + bool acks[SRSLTE_MAX_CODEWORDS]) { + srslte_mimo_type_t mimo_type; + srslte_dci_msg_t dci_msg; + srslte_ra_dl_dci_t dci_unpacked; + srslte_ra_dl_grant_t grant; + int ret = SRSLTE_ERROR; + uint32_t cfi; + uint32_t sf_idx = tti%10; + + if ((ret = srslte_ue_dl_decode_fft_estimate_mbsfn(q, sf_idx, &cfi, SRSLTE_SF_NORM)) < 0) { + return ret; + } + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&q->chest); + // Uncoment next line to do ZF by default in pdsch_ue example + //float noise_estimate = 0; + + if (srslte_pdcch_extract_llr_multi(&q->pdcch, q->sf_symbols_m, q->ce_m, noise_estimate, sf_idx, cfi)) { + fprintf(stderr, "Error extracting LLRs\n"); + return SRSLTE_ERROR; + } + + int found_dci = srslte_ue_dl_find_dl_dci(q, tm, cfi, sf_idx, rnti, &dci_msg); + if (found_dci == 1) { + + INFO("PDCCH: DL DCI %s rnti=0x%x, cce_index=%d, L=%d, tti=%d\n", srslte_dci_format_string(dci_msg.format), + q->current_rnti, q->last_location.ncce, (1<last_location.L), tti); + + if (srslte_dci_msg_to_dl_grant(&dci_msg, rnti, q->cell.nof_prb, q->cell.nof_ports, &dci_unpacked, &grant)) { + fprintf(stderr, "Error unpacking DCI\n"); + return SRSLTE_ERROR; + } + + /* ===== These lines of code are supposed to be MAC functionality === */ + + + int rvidx[SRSLTE_MAX_CODEWORDS] = {1}; + if (dci_unpacked.rv_idx < 0) { + uint32_t sfn = tti / 10; + uint32_t k = (sfn / 2) % 4; + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (grant.tb_en[i]) { + rvidx[i] = ((uint32_t) ceilf((float) 1.5 * k)) % 4; + srslte_softbuffer_rx_reset_tbs(q->softbuffers[i], (uint32_t) grant.mcs[i].tbs); + } + } + } else { + for (int i = 0; i < SRSLTE_MAX_CODEWORDS; i++) { + if (grant.tb_en[i]) { + switch (i) { + case 0: + rvidx[i] = (uint32_t) dci_unpacked.rv_idx; + break; + case 1: + rvidx[i] = (uint32_t) dci_unpacked.rv_idx_1; + break; + default: + ERROR("Wrong number of transport blocks"); + return SRSLTE_ERROR; + } + srslte_softbuffer_rx_reset_tbs(q->softbuffers[i], (uint32_t) grant.mcs[i].tbs); + } + } + } + + switch(dci_msg.format) { + case SRSLTE_DCI_FORMAT1: + case SRSLTE_DCI_FORMAT1A: + case SRSLTE_DCI_FORMAT1C: + if (q->cell.nof_ports == 1) { + mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + } else { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } + break; + case SRSLTE_DCI_FORMAT2: + if (SRSLTE_RA_DL_GRANT_NOF_TB(&grant) == 1 && dci_unpacked.pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } + break; + case SRSLTE_DCI_FORMAT2A: + if (SRSLTE_RA_DL_GRANT_NOF_TB(&grant) == 1 && dci_unpacked.pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_CDD; + } + break; + + /* Not implemented formats */ + case SRSLTE_DCI_FORMAT0: + case SRSLTE_DCI_FORMAT1B: + case SRSLTE_DCI_FORMAT1D: + case SRSLTE_DCI_FORMAT2B: + default: + ERROR("Transmission mode not supported."); + return SRSLTE_ERROR; + } + + if (srslte_ue_dl_cfg_grant(q, &grant, cfi, sf_idx, rvidx, mimo_type)) { + ERROR("Configuing PDSCH"); + return SRSLTE_ERROR; + } + + /* ===== End of MAC functionality ========== */ + + q->nof_detected++; + + + if (q->pdsch_cfg.grant.mcs[0].mod > 0 && q->pdsch_cfg.grant.mcs[0].tbs >= 0) { + ret = srslte_pdsch_decode(&q->pdsch, &q->pdsch_cfg, q->softbuffers, + q->sf_symbols_m, q->ce_m, + noise_estimate, + rnti, data, acks); + + + for (int tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (grant.tb_en[tb]) { + if (!acks[tb]) { + q->pdsch_pkt_errors++; + } + q->pdsch_pkts_total++; + } + } + + if (ret == SRSLTE_ERROR) { + } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling srslte_pdsch_decode()\n"); + } + + /* If we are in TM4 (Closed-Loop MIMO), compute condition number */ + + } + + /* + printf("Saving signal...\n"); + srslte_vec_save_file("input", input, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + srslte_ue_dl_save_signal(q, &q->softbuffer, sf_idx, rvidx, rnti, cfi); + //exit(-1); + */ + + } + + if (found_dci == 1 && ret == SRSLTE_SUCCESS) { + return q->pdsch_cfg.grant.mcs[0].tbs; + } else { + return 0; + } +} + + + +int srslte_ue_dl_decode_mbsfn(srslte_ue_dl_t * q, + uint8_t *data, + uint32_t tti) +{ + srslte_ra_dl_grant_t grant; + int ret = SRSLTE_ERROR; + uint32_t cfi; + uint32_t sf_idx = tti%10; + + if ((ret = srslte_ue_dl_decode_fft_estimate_mbsfn(q, sf_idx, &cfi, SRSLTE_SF_MBSFN)) < 0) { + return ret; + } + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&q->chest); + // Uncoment next line to do ZF by default in pdsch_ue example + //float noise_estimate = 0; + + grant.sf_type = SRSLTE_SF_MBSFN; + grant.mcs[0].idx = 2; + grant.tb_en[0] = true; + grant.tb_en[1] = false; + grant.nof_prb = q->pmch.cell.nof_prb; + srslte_dl_fill_ra_mcs(&grant.mcs[0], grant.nof_prb); + srslte_softbuffer_rx_reset_tbs(q->softbuffers[0], (uint32_t) grant.mcs[0].tbs); + for(int j = 0; j < 2; j++){ + for(int f = 0; f < grant.nof_prb; f++){ + grant.prb_idx[j][f] = true; + } + } + grant.Qm[0] = srslte_mod_bits_x_symbol(grant.mcs[0].mod); + + // redundancy version is set to 0 for the PMCH + if (srslte_ue_dl_cfg_grant(q, &grant, cfi, sf_idx, SRSLTE_PMCH_RV, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA)) { + return SRSLTE_ERROR; + } + + if (q->pmch_cfg.grant.mcs[0].mod > 0 && q->pmch_cfg.grant.mcs[0].tbs >= 0) { + ret = srslte_pmch_decode_multi(&q->pmch, &q->pmch_cfg, q->softbuffers[0], + q->sf_symbols_m, q->ce_m, + noise_estimate, + q->current_mbsfn_area_id, data); + + + if (ret == SRSLTE_ERROR) { + q->pmch_pkt_errors++; + } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { + fprintf(stderr, "Error calling srslte_pmch_decode()\n"); + } + } + + q->pmch_pkts_total++; + + if (ret == SRSLTE_SUCCESS) { + return q->pmch_cfg.grant.mcs[0].tbs; + } else { + return 0; + } +} + + +/* Compute the Rank Indicator (RI) and Precoder Matrix Indicator (PMI) by computing the Signal to Interference plus + * Noise Ratio (SINR), valid for TM4 */ +int srslte_ue_dl_ri_pmi_select(srslte_ue_dl_t *q, uint8_t *ri, uint8_t *pmi, float *current_sinr) { + float noise_estimate = srslte_chest_dl_get_noise_estimate(&q->chest); + float best_sinr = -INFINITY; + uint8_t best_pmi = 0, best_ri = 0; + + if (q->cell.nof_ports < 2) { + /* Do nothing */ + return SRSLTE_SUCCESS; + } else { + if (srslte_pdsch_pmi_select(&q->pdsch, &q->pdsch_cfg, q->ce_m, noise_estimate, + SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp), q->pmi, q->sinr)) { + DEBUG("SINR calculation error"); + return SRSLTE_ERROR; + } + + /* Select the best Rank indicator (RI) and Precoding Matrix Indicator (PMI) */ + for (uint32_t nof_layers = 1; nof_layers <= SRSLTE_MAX_LAYERS; nof_layers++) { + float _sinr = q->sinr[nof_layers - 1][q->pmi[nof_layers - 1]] * nof_layers * nof_layers; + /* Find best SINR, force maximum number of layers if SNR is higher than 30 dB */ + if (_sinr > best_sinr + 0.1 || _sinr > 1.0e+3) { + best_sinr = _sinr; + best_pmi = (uint8_t) q->pmi[nof_layers - 1]; + best_ri = (uint8_t) (nof_layers - 1); + } + } + } + + /* Print Trace */ + if (ri != NULL && pmi != NULL && current_sinr != NULL) { + INFO("PDSCH Select RI=%d; PMI=%d; Current SINR=%.1fdB (nof_layers=%d, codebook_idx=%d)\n", *ri, *pmi, + 10*log10(*current_sinr), q->pdsch_cfg.nof_layers, q->pdsch_cfg.codebook_idx); + } + + /* Set RI */ + q->ri = best_ri; + if (ri != NULL) { + *ri = best_ri; + } + + /* Set PMI */ + if (pmi != NULL) { + *pmi = best_pmi; + } + + /* Set current SINR */ + if (current_sinr != NULL && q->pdsch_cfg.mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + if (q->pdsch_cfg.nof_layers == 1) { + *current_sinr = q->sinr[0][q->pdsch_cfg.codebook_idx]; + } else if (q->pdsch_cfg.nof_layers == 2) { + *current_sinr = q->sinr[1][q->pdsch_cfg.codebook_idx - 1]; + } else { + ERROR("Not implemented number of layers (%d)", q->pdsch_cfg.nof_layers); + return SRSLTE_ERROR; + } + } + + return SRSLTE_SUCCESS; +} + + +/* Compute the Rank Indicator (RI) by computing the condition number, valid for TM3 */ +int srslte_ue_dl_ri_select(srslte_ue_dl_t *q, uint8_t *ri, float *cn) { + float _cn; + int ret = srslte_pdsch_cn_compute(&q->pdsch, q->ce_m, SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp), &_cn); + + /* Set Condition number */ + if (cn) { + *cn = _cn; + } + + q->ri = (uint8_t)((_cn < 17.0f)? 1:0); + /* Set rank indicator */ + if (!ret && ri) { + *ri = (uint8_t) q->ri; + } + + return ret; +} + + +uint32_t srslte_ue_dl_get_ncce(srslte_ue_dl_t *q) { + return q->last_location.ncce; +} + +static int dci_blind_search(srslte_ue_dl_t *q, dci_blind_search_t *search_space, uint16_t rnti, uint32_t cfi, srslte_dci_msg_t *dci_msg) +{ + int ret = SRSLTE_ERROR; + uint16_t crc_rem = 0; + if (rnti) { + ret = 0; + int i=0; + while (!ret && i < search_space->nof_locations) { + DEBUG("Searching format %s in %d,%d (%d/%d)\n", + srslte_dci_format_string(search_space->format), search_space->loc[i].ncce, search_space->loc[i].L, + i, search_space->nof_locations); + + if (srslte_pdcch_decode_msg(&q->pdcch, dci_msg, &search_space->loc[i], search_space->format, cfi, &crc_rem)) { + fprintf(stderr, "Error decoding DCI msg\n"); + return SRSLTE_ERROR; + } + if (crc_rem == rnti) { + // If searching for Format1A but found Format0 save it for later + if (dci_msg->format == SRSLTE_DCI_FORMAT0 && search_space->format == SRSLTE_DCI_FORMAT1A) + { + if (!q->pending_ul_dci_rnti) { + q->pending_ul_dci_rnti = crc_rem; + memcpy(&q->pending_ul_dci_msg, dci_msg, sizeof(srslte_dci_msg_t)); + memcpy(&q->last_location_ul, &search_space->loc[i], sizeof(srslte_dci_location_t)); + } + // Else if we found it, save location and leave + } else if (dci_msg->format == search_space->format) { + ret = 1; + if (dci_msg->format == SRSLTE_DCI_FORMAT0) { + memcpy(&q->last_location_ul, &search_space->loc[i], sizeof(srslte_dci_location_t)); + } else { + memcpy(&q->last_location, &search_space->loc[i], sizeof(srslte_dci_location_t)); + } + } + } + i++; + } + } else { + fprintf(stderr, "RNTI not specified\n"); + } + return ret; +} + +int srslte_ue_dl_find_ul_dci(srslte_ue_dl_t *q, uint32_t cfi, uint32_t sf_idx, uint16_t rnti, srslte_dci_msg_t *dci_msg) +{ + if (rnti && cfi > 0 && cfi < 4) { + /* Do not search if an UL DCI is already pending */ + if (q->pending_ul_dci_rnti == rnti) { + q->pending_ul_dci_rnti = 0; + memcpy(dci_msg, &q->pending_ul_dci_msg, sizeof(srslte_dci_msg_t)); + return 1; + } + + // Configure and run DCI blind search + dci_blind_search_t search_space; + search_space.nof_locations = 0; + dci_blind_search_t *current_ss = &search_space; + if (q->current_rnti == rnti) { + current_ss = &q->current_ss_ue[cfi-1][sf_idx]; + } else { + // If locations are not pre-generated, generate them now + current_ss->nof_locations = srslte_pdcch_ue_locations(&q->pdcch, current_ss->loc, MAX_CANDIDATES_UE, sf_idx, cfi, rnti); + } + + current_ss->format = SRSLTE_DCI_FORMAT0; + INFO("Searching UL C-RNTI in %d ue locations\n", search_space.nof_locations); + return dci_blind_search(q, current_ss, rnti, cfi, dci_msg); + } else { + return 0; + } +} + +int srslte_ue_dl_find_dl_dci(srslte_ue_dl_t *q, uint32_t tm, uint32_t cfi, uint32_t sf_idx, uint16_t rnti, srslte_dci_msg_t *dci_msg) +{ + srslte_rnti_type_t rnti_type; + if (rnti == SRSLTE_SIRNTI) { + rnti_type = SRSLTE_RNTI_SI; + } else if (rnti == SRSLTE_PRNTI) { + rnti_type = SRSLTE_RNTI_PCH; + } else if (rnti <= SRSLTE_RARNTI_END) { + rnti_type = SRSLTE_RNTI_RAR; + } else { + rnti_type = SRSLTE_RNTI_USER; + } + return srslte_ue_dl_find_dl_dci_type(q, tm, cfi, sf_idx, rnti, rnti_type, dci_msg); +} + +// Blind search for SI/P/RA-RNTI +static int find_dl_dci_type_siprarnti(srslte_ue_dl_t *q, uint32_t cfi, uint16_t rnti, srslte_dci_msg_t *dci_msg) +{ + int ret = 0; + // Configure and run DCI blind search + dci_blind_search_t search_space; + search_space.nof_locations = srslte_pdcch_common_locations(&q->pdcch, search_space.loc, MAX_CANDIDATES_COM, cfi); + INFO("Searching SI/P/RA-RNTI in %d common locations, %d formats\n", search_space.nof_locations, nof_common_formats); + // Search for RNTI only if there is room for the common search space + if (search_space.nof_locations > 0) { + for (int f=0;f 3) { + ERROR("CFI must be 1 ≤ cfi ≤ 3 (cfi=%d)", cfi); + return SRSLTE_ERROR; + } + + // Search UE-specific search space + if (q->current_rnti == rnti) { + current_ss = &q->current_ss_ue[cfi-1][sf_idx]; + } else { + // If locations are not pre-generated, generate them now + current_ss->nof_locations = srslte_pdcch_ue_locations(&q->pdcch, current_ss->loc, MAX_CANDIDATES_UE, sf_idx, cfi, rnti); + } + + for (int f = 0; f < 2; f++) { + srslte_dci_format_t format = ue_dci_formats[tm][f]; + + INFO("Searching DL C-RNTI %s in %d ue locations\n", srslte_dci_format_string(format), + current_ss->nof_locations); + + current_ss->format = format; + if ((ret = dci_blind_search(q, current_ss, rnti, cfi, dci_msg))) { + return ret; + } + } + + // Search Format 1A in the Common SS also + if (q->current_rnti == rnti) { + current_ss = &q->current_ss_common[cfi-1]; + } else { + // If locations are not pre-generated, generate them now + current_ss->nof_locations = srslte_pdcch_common_locations(&q->pdcch, current_ss->loc, MAX_CANDIDATES_COM, cfi); + } + + // Search for RNTI only if there is room for the common search space + if (current_ss->nof_locations > 0) { + current_ss->format = SRSLTE_DCI_FORMAT1A; + INFO("Searching DL C-RNTI in %d ue locations, format 1A\n", current_ss->nof_locations); + return dci_blind_search(q, current_ss, rnti, cfi, dci_msg); + } + return SRSLTE_SUCCESS; +} + +int srslte_ue_dl_find_dl_dci_type(srslte_ue_dl_t *q, uint32_t tm, uint32_t cfi, uint32_t sf_idx, + uint16_t rnti, srslte_rnti_type_t rnti_type, srslte_dci_msg_t *dci_msg) +{ + if (rnti_type == SRSLTE_RNTI_SI || rnti_type == SRSLTE_RNTI_PCH || rnti_type == SRSLTE_RNTI_RAR) { + return find_dl_dci_type_siprarnti(q, cfi, rnti, dci_msg); + } else { + return find_dl_dci_type_crnti(q, tm, cfi, sf_idx, rnti, dci_msg); + } +} + +bool srslte_ue_dl_decode_phich(srslte_ue_dl_t *q, uint32_t sf_idx, uint32_t n_prb_lowest, uint32_t n_dmrs) +{ + uint8_t ack_bit; + float distance; + uint32_t ngroup, nseq; + srslte_phich_calc(&q->phich, n_prb_lowest, n_dmrs, &ngroup, &nseq); + INFO("Decoding PHICH sf_idx=%d, n_prb_lowest=%d, n_dmrs=%d, n_group=%d, n_seq=%d, Ngroups=%d, Nsf=%d\n", + sf_idx, n_prb_lowest, n_dmrs, ngroup, nseq, + srslte_phich_ngroups(&q->phich), srslte_phich_nsf(&q->phich)); + + if (!srslte_phich_decode(&q->phich, q->sf_symbols_m, q->ce_m, 0, ngroup, nseq, sf_idx, &ack_bit, &distance)) { + q->last_phich_corr = distance; + INFO("Decoded PHICH %d with distance %f\n", ack_bit, distance); + } else { + fprintf(stderr, "Error decoding PHICH\n"); + return false; + } + if (ack_bit) { + return true; + } else { + return false; + } +} + +void srslte_ue_dl_save_signal(srslte_ue_dl_t *q, srslte_softbuffer_rx_t *softbuffer, uint32_t tti, uint32_t rv_idx, uint16_t rnti, uint32_t cfi) { + srslte_vec_save_file("sf_symbols", q->sf_symbols, SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + printf("%d samples\n", SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)); + srslte_vec_save_file("ce0", q->ce[0], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + if (q->cell.nof_ports > 1) { + srslte_vec_save_file("ce1", q->ce[1], SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)*sizeof(cf_t)); + } + srslte_vec_save_file("pcfich_ce0", q->pcfich.ce[0], q->pcfich.nof_symbols*sizeof(cf_t)); + srslte_vec_save_file("pcfich_ce1", q->pcfich.ce[1], q->pcfich.nof_symbols*sizeof(cf_t)); + srslte_vec_save_file("pcfich_symbols", q->pcfich.symbols[0], q->pcfich.nof_symbols*sizeof(cf_t)); + srslte_vec_save_file("pcfich_eq_symbols", q->pcfich.d, q->pcfich.nof_symbols*sizeof(cf_t)); + srslte_vec_save_file("pcfich_llr", q->pcfich.data_f, PCFICH_CFI_LEN*sizeof(float)); + + srslte_vec_save_file("pdcch_ce0", q->pdcch.ce[0], q->pdcch.nof_cce[cfi-1]*36*sizeof(cf_t)); + srslte_vec_save_file("pdcch_ce1", q->pdcch.ce[1], q->pdcch.nof_cce[cfi-1]*36*sizeof(cf_t)); + srslte_vec_save_file("pdcch_symbols", q->pdcch.symbols[0], q->pdcch.nof_cce[cfi-1]*36*sizeof(cf_t)); + srslte_vec_save_file("pdcch_eq_symbols", q->pdcch.d, q->pdcch.nof_cce[cfi-1]*36*sizeof(cf_t)); + srslte_vec_save_file("pdcch_llr", q->pdcch.llr, q->pdcch.nof_cce[cfi-1]*72*sizeof(float)); + + + srslte_vec_save_file("pdsch_symbols", q->pdsch.d[0], q->pdsch_cfg.nbits[0].nof_re*sizeof(cf_t)); + srslte_vec_save_file("llr", q->pdsch.e[0], q->pdsch_cfg.nbits[0].nof_bits*sizeof(cf_t)); + int cb_len = q->pdsch_cfg.cb_segm[0].K1; + for (int i=0;ipdsch_cfg.cb_segm[0].C;i++) { + char tmpstr[64]; + snprintf(tmpstr,64,"rmout_%d.dat",i); + srslte_vec_save_file(tmpstr, softbuffer->buffer_f[i], (3*cb_len+12)*sizeof(int16_t)); + } + printf("Saved files for tti=%d, sf=%d, cfi=%d, mcs=%d, tbs=%d, rv=%d, rnti=0x%x\n", tti, tti%10, cfi, + q->pdsch_cfg.grant.mcs[0].idx, q->pdsch_cfg.grant.mcs[0].tbs, rv_idx, rnti); +} + + + diff --git a/lib/src/phy/ue/ue_mib.c b/lib/src/phy/ue/ue_mib.c new file mode 100644 index 0000000..c003a2b --- /dev/null +++ b/lib/src/phy/ue/ue_mib.c @@ -0,0 +1,293 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/ue/ue_mib.h" + +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + +int srslte_ue_mib_init(srslte_ue_mib_t * q, + cf_t *in_buffer[SRSLTE_MAX_PORTS], + uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_ue_mib_t)); + + if (srslte_pbch_init(&q->pbch)) { + fprintf(stderr, "Error initiating PBCH\n"); + goto clean_exit; + } + + q->sf_symbols = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->sf_symbols) { + perror("malloc"); + goto clean_exit; + } + + for (int i=0;ice[i] = srslte_vec_malloc(SRSLTE_SF_LEN_RE(max_prb, SRSLTE_CP_NORM) * sizeof(cf_t)); + if (!q->ce[i]) { + perror("malloc"); + goto clean_exit; + } + } + + if (srslte_ofdm_rx_init(&q->fft, SRSLTE_CP_NORM, in_buffer[0], q->sf_symbols, max_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + goto clean_exit; + } + if (srslte_chest_dl_init(&q->chest, max_prb)) { + fprintf(stderr, "Error initializing reference signal\n"); + goto clean_exit; + } + srslte_ue_mib_reset(q); + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_mib_free(q); + } + return ret; +} + +void srslte_ue_mib_free(srslte_ue_mib_t * q) +{ + if (q->sf_symbols) { + free(q->sf_symbols); + } + for (int i=0;ice[i]) { + free(q->ce[i]); + } + } + srslte_sync_free(&q->sfind); + srslte_chest_dl_free(&q->chest); + srslte_pbch_free(&q->pbch); + srslte_ofdm_rx_free(&q->fft); + + bzero(q, sizeof(srslte_ue_mib_t)); + +} + +int srslte_ue_mib_set_cell(srslte_ue_mib_t * q, + srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + cell.nof_ports <= SRSLTE_MAX_PORTS) + { + if (srslte_pbch_set_cell(&q->pbch, cell)) { + fprintf(stderr, "Error initiating PBCH\n"); + return SRSLTE_ERROR; + } + if (srslte_ofdm_rx_set_prb(&q->fft, cell.cp, cell.nof_prb)) { + fprintf(stderr, "Error initializing FFT\n"); + return SRSLTE_ERROR; + } + + if (cell.nof_ports == 0) { + cell.nof_ports = SRSLTE_MAX_PORTS; + } + + if (srslte_chest_dl_set_cell(&q->chest, cell)) { + fprintf(stderr, "Error initializing reference signal\n"); + return SRSLTE_ERROR; + } + srslte_ue_mib_reset(q); + + ret = SRSLTE_SUCCESS; + } + return ret; +} + + +void srslte_ue_mib_reset(srslte_ue_mib_t * q) +{ + q->frame_cnt = 0; + srslte_pbch_decode_reset(&q->pbch); +} + +int srslte_ue_mib_decode(srslte_ue_mib_t * q, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], uint32_t *nof_tx_ports, int *sfn_offset) +{ + int ret = SRSLTE_SUCCESS; + cf_t *ce_slot1[SRSLTE_MAX_PORTS]; + + /* Run FFT for the slot symbols */ + srslte_ofdm_rx_sf(&q->fft); + + /* Get channel estimates of sf idx #0 for each port */ + ret = srslte_chest_dl_estimate(&q->chest, q->sf_symbols, q->ce, 0); + if (ret < 0) { + return SRSLTE_ERROR; + } + /* Reset decoder if we missed a frame */ + if (q->frame_cnt > 8) { + INFO("Resetting PBCH decoder after %d frames\n", q->frame_cnt); + srslte_ue_mib_reset(q); + } + + for (int i=0;ice[i][SRSLTE_SLOT_LEN_RE(q->chest.cell.nof_prb, q->chest.cell.cp)]; + } + + /* Decode PBCH */ + ret = srslte_pbch_decode(&q->pbch, &q->sf_symbols[SRSLTE_SLOT_LEN_RE(q->chest.cell.nof_prb, q->chest.cell.cp)], + ce_slot1, 0, + bch_payload, nof_tx_ports, sfn_offset); + + + if (ret < 0) { + fprintf(stderr, "Error decoding PBCH (%d)\n", ret); + } else if (ret == 1) { + INFO("MIB decoded: %u\n", q->frame_cnt); + srslte_ue_mib_reset(q); + ret = SRSLTE_UE_MIB_FOUND; + } else { + ret = SRSLTE_UE_MIB_NOTFOUND; + INFO("MIB not decoded: %u\n", q->frame_cnt); + q->frame_cnt++; + } + + return ret; +} + +int srslte_ue_mib_sync_init_multi(srslte_ue_mib_sync_t *q, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t, srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler) +{ + for (int i=0;isf_buffer[i] = srslte_vec_malloc(3*sizeof(cf_t)*SRSLTE_SF_LEN_PRB(SRSLTE_UE_MIB_NOF_PRB)); + } + q->nof_rx_antennas = nof_rx_antennas; + + if (srslte_ue_mib_init(&q->ue_mib, q->sf_buffer, SRSLTE_UE_MIB_NOF_PRB)) { + fprintf(stderr, "Error initiating ue_mib\n"); + return SRSLTE_ERROR; + } + if (srslte_ue_sync_init_multi(&q->ue_sync, SRSLTE_UE_MIB_NOF_PRB, false, recv_callback, nof_rx_antennas, stream_handler)) { + fprintf(stderr, "Error initiating ue_sync\n"); + srslte_ue_mib_free(&q->ue_mib); + return SRSLTE_ERROR; + } + srslte_ue_sync_decode_sss_on_track(&q->ue_sync, true); + return SRSLTE_SUCCESS; +} + +int srslte_ue_mib_sync_set_cell(srslte_ue_mib_sync_t *q, + uint32_t cell_id, + srslte_cp_t cp) +{ + srslte_cell_t cell; + // If the ports are set to 0, ue_mib goes through 1, 2 and 4 ports to blindly detect nof_ports + cell.nof_ports = 0; + cell.id = cell_id; + cell.cp = cp; + cell.nof_prb = SRSLTE_UE_MIB_NOF_PRB; + + if (srslte_ue_mib_set_cell(&q->ue_mib, cell)) { + fprintf(stderr, "Error initiating ue_mib\n"); + return SRSLTE_ERROR; + } + if (srslte_ue_sync_set_cell(&q->ue_sync, cell)) { + fprintf(stderr, "Error initiating ue_sync\n"); + srslte_ue_mib_free(&q->ue_mib); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + + +void srslte_ue_mib_sync_free(srslte_ue_mib_sync_t *q) { + for (int i=0;inof_rx_antennas;i++) { + if (q->sf_buffer[i]) { + free(q->sf_buffer[i]); + } + } + srslte_ue_mib_free(&q->ue_mib); + srslte_ue_sync_free(&q->ue_sync); +} + +void srslte_ue_mib_sync_reset(srslte_ue_mib_sync_t * q) { + srslte_ue_mib_reset(&q->ue_mib); + srslte_ue_sync_reset(&q->ue_sync); +} + +int srslte_ue_mib_sync_decode(srslte_ue_mib_sync_t * q, + uint32_t max_frames_timeout, + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN], + uint32_t *nof_tx_ports, + int *sfn_offset) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t nof_frames = 0; + int mib_ret = SRSLTE_UE_MIB_NOTFOUND; + + if (q != NULL) + { + srslte_ue_mib_sync_reset(q); + + ret = SRSLTE_SUCCESS; + do { + mib_ret = SRSLTE_UE_MIB_NOTFOUND; + ret = srslte_ue_sync_zerocopy_multi(&q->ue_sync, q->sf_buffer); + if (ret < 0) { + fprintf(stderr, "Error calling srslte_ue_sync_work()\n"); + return -1; + } else if (srslte_ue_sync_get_sfidx(&q->ue_sync) == 0) { + if (ret == 1) { + mib_ret = srslte_ue_mib_decode(&q->ue_mib, bch_payload, nof_tx_ports, sfn_offset); + } else { + DEBUG("Resetting PBCH decoder after %d frames\n", q->ue_mib.frame_cnt); + srslte_ue_mib_reset(&q->ue_mib); + } + nof_frames++; + } + } while (mib_ret == SRSLTE_UE_MIB_NOTFOUND && ret >= 0 && nof_frames < max_frames_timeout); + if (mib_ret < 0) { + ret = mib_ret; + } + } + return mib_ret; +} + + diff --git a/lib/src/phy/ue/ue_sync.c b/lib/src/phy/ue/ue_sync.c new file mode 100644 index 0000000..2b146ae --- /dev/null +++ b/lib/src/phy/ue/ue_sync.c @@ -0,0 +1,823 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include "srslte/srslte.h" + + +#include "srslte/phy/ue/ue_sync.h" + +#include "srslte/phy/io/filesource.h" +#include "srslte/phy/utils/debug.h" +#include "srslte/phy/utils/vector.h" + + +#define MAX_TIME_OFFSET 128 + +#define TRACK_MAX_LOST 10 +#define TRACK_FRAME_SIZE 32 +#define FIND_NOF_AVG_FRAMES 4 + + +cf_t dummy_buffer0[15*2048/2]; +cf_t dummy_buffer1[15*2048/2]; + +// FIXME: this will break for 4 antennas!! +cf_t *dummy_offset_buffer[SRSLTE_MAX_PORTS] = {dummy_buffer0, dummy_buffer1}; + +int srslte_ue_sync_init_file(srslte_ue_sync_t *q, uint32_t nof_prb, char *file_name, int offset_time, float offset_freq) { + return srslte_ue_sync_init_file_multi(q, nof_prb, file_name, offset_time, offset_freq, 1); +} + +void srslte_ue_sync_file_wrap(srslte_ue_sync_t *q, bool enable) { + q->file_wrap_enable = enable; +} + +int srslte_ue_sync_init_file_multi(srslte_ue_sync_t *q, uint32_t nof_prb, char *file_name, int offset_time, + float offset_freq, uint32_t nof_rx_ant) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + file_name != NULL && + srslte_nofprb_isvalid(nof_prb)) + { + ret = SRSLTE_ERROR; + bzero(q, sizeof(srslte_ue_sync_t)); + q->file_mode = true; + q->file_wrap_enable = true; + q->sf_len = SRSLTE_SF_LEN(srslte_symbol_sz(nof_prb)); + q->file_cfo = -offset_freq; + q->fft_size = srslte_symbol_sz(nof_prb); + q->nof_rx_antennas = nof_rx_ant; + + q->cfo_correct_enable_find = false; + q->cfo_correct_enable_track = true; + + if (srslte_cfo_init(&q->file_cfo_correct, 2*q->sf_len)) { + fprintf(stderr, "Error initiating CFO\n"); + goto clean_exit; + } + + if (srslte_filesource_init(&q->file_source, file_name, SRSLTE_COMPLEX_FLOAT_BIN)) { + fprintf(stderr, "Error opening file %s\n", file_name); + goto clean_exit; + } + + INFO("Offseting input file by %d samples and %.1f kHz\n", offset_time, offset_freq/1000); + + if (offset_time) { + cf_t *file_offset_buffer = srslte_vec_malloc(offset_time * nof_rx_ant * sizeof(cf_t)); + if (!file_offset_buffer) { + perror("malloc"); + goto clean_exit; + } + srslte_filesource_read(&q->file_source, file_offset_buffer, offset_time * nof_rx_ant); + free(file_offset_buffer); + } + + srslte_ue_sync_cfo_reset(q); + srslte_ue_sync_reset(q); + + ret = SRSLTE_SUCCESS; + } +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_sync_free(q); + } + return ret; +} + +void srslte_ue_sync_cfo_reset(srslte_ue_sync_t *q) +{ + q->cfo_is_copied = false; + q->cfo_current_value = 0; + srslte_sync_cfo_reset(&q->strack); + srslte_sync_cfo_reset(&q->sfind); +} + +void srslte_ue_sync_reset(srslte_ue_sync_t *q) { + + if (!q->file_mode) { + srslte_sync_reset(&q->sfind); + srslte_sync_reset(&q->strack); + } else { + q->sf_idx = 9; + } + q->pss_stable_timeout = false; + q->state = SF_FIND; + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + q->mean_sample_offset = 0.0; + q->next_rf_sample_offset = 0; + q->frame_find_cnt = 0; +} + +int srslte_ue_sync_start_agc(srslte_ue_sync_t *q, + double (set_gain_callback)(void *, double), + double min_gain, + double max_gain, + double init_gain_value) { + int n = srslte_agc_init_uhd(&q->agc, SRSLTE_AGC_MODE_PEAK_AMPLITUDE, 0, set_gain_callback, q->stream); + q->do_agc = n==0?true:false; + if (q->do_agc) { + srslte_agc_set_gain_range(&q->agc, min_gain, max_gain); + srslte_agc_set_gain(&q->agc, init_gain_value); + srslte_ue_sync_set_agc_period(q, 4); + } + return n; +} + +int recv_callback_multi_to_single(void *h, cf_t *x[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t*t) +{ + srslte_ue_sync_t *q = (srslte_ue_sync_t*) h; + return q->recv_callback_single(q->stream_single, (void*) x[0], nsamples, t); +} + +int srslte_ue_sync_init(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, void*, uint32_t,srslte_timestamp_t*), + void *stream_handler) +{ + int ret = srslte_ue_sync_init_multi(q, max_prb, search_cell, recv_callback_multi_to_single, 1, (void*) q); + q->recv_callback_single = recv_callback; + q->stream_single = stream_handler; + return ret; +} + +int srslte_ue_sync_init_multi(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t,srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler) + +{ + + return srslte_ue_sync_init_multi_decim(q, max_prb,search_cell, recv_callback ,nof_rx_antennas,stream_handler,1); +} + +int srslte_ue_sync_init_multi_decim(srslte_ue_sync_t *q, + uint32_t max_prb, + bool search_cell, + int (recv_callback)(void*, cf_t*[SRSLTE_MAX_PORTS], uint32_t,srslte_timestamp_t*), + uint32_t nof_rx_antennas, + void *stream_handler, + int decimate) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + stream_handler != NULL && + nof_rx_antennas <= SRSLTE_MAX_PORTS && + recv_callback != NULL) + { + ret = SRSLTE_ERROR; + //int decimate = q->decimate; + bzero(q, sizeof(srslte_ue_sync_t)); + q->decimate = decimate; + q->stream = stream_handler; + q->recv_callback = recv_callback; + q->nof_rx_antennas = nof_rx_antennas; + q->fft_size = srslte_symbol_sz(max_prb); + q->sf_len = SRSLTE_SF_LEN(q->fft_size); + q->file_mode = false; + q->agc_period = 0; + q->sample_offset_correct_period = DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD; + q->sfo_ema = DEFAULT_SFO_EMA_COEFF; + + q->max_prb = max_prb; + + q->cfo_ref_max = DEFAULT_CFO_REF_MAX; + q->cfo_ref_min = DEFAULT_CFO_REF_MIN; + q->cfo_pss_min = DEFAULT_CFO_PSS_MIN; + q->cfo_loop_bw_pss = DEFAULT_CFO_BW_PSS; + q->cfo_loop_bw_ref = DEFAULT_CFO_BW_REF; + + q->cfo_correct_enable_find = false; + q->cfo_correct_enable_track = true; + + q->pss_stable_cnt = 0; + q->pss_stable_timeout = DEFAULT_PSS_STABLE_TIMEOUT; + + if (search_cell) { + + /* If the cell is unkown, we search PSS/SSS in 5 ms */ + q->nof_recv_sf = 5; + + } else { + + /* If the cell is known, we work on a 1ms basis */ + q->nof_recv_sf = 1; + + } + + q->frame_len = q->nof_recv_sf*q->sf_len; + + if(q->fft_size < 700 && q->decimate) { + q->decimate = 1; + } + + if(srslte_sync_init_decim(&q->sfind, q->frame_len, q->frame_len, q->fft_size,q->decimate)) { + fprintf(stderr, "Error initiating sync find\n"); + goto clean_exit; + } + if (search_cell) { + if(srslte_sync_init(&q->strack, q->frame_len, TRACK_FRAME_SIZE, q->fft_size)) { + fprintf(stderr, "Error initiating sync track\n"); + goto clean_exit; + } + } else { + if(srslte_sync_init(&q->strack, q->frame_len, SRSLTE_CP_LEN_NORM(1,q->fft_size), q->fft_size)) { + fprintf(stderr, "Error initiating sync track\n"); + goto clean_exit; + } + } + + // Configure FIND and TRACK sync objects behaviour (this configuration is always the same) + srslte_sync_set_cfo_i_enable(&q->sfind, false); + srslte_sync_set_cfo_pss_enable(&q->sfind, true); + srslte_sync_set_pss_filt_enable(&q->sfind, true); + srslte_sync_set_sss_eq_enable(&q->sfind, false); + + // During track, we do CFO correction outside the sync object + srslte_sync_set_cfo_i_enable(&q->strack, false); + srslte_sync_set_cfo_pss_enable(&q->strack, true); + srslte_sync_set_pss_filt_enable(&q->strack, true); + srslte_sync_set_sss_eq_enable(&q->strack, false); + + // FIXME: CP detection not working very well. Not supporting Extended CP right now + srslte_sync_cp_en(&q->strack, false); + srslte_sync_cp_en(&q->sfind, false); + + srslte_sync_sss_en(&q->strack, true); + q->decode_sss_on_track = true; + + ret = SRSLTE_SUCCESS; + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_sync_free(q); + } + return ret; +} + +uint32_t srslte_ue_sync_sf_len(srslte_ue_sync_t *q) { + return q->frame_len; +} + +void srslte_ue_sync_free(srslte_ue_sync_t *q) { + if (q->do_agc) { + srslte_agc_free(&q->agc); + } + if (!q->file_mode) { + srslte_sync_free(&q->sfind); + srslte_sync_free(&q->strack); + } else { + srslte_filesource_free(&q->file_source); + } + bzero(q, sizeof(srslte_ue_sync_t)); +} + + +int srslte_ue_sync_set_cell(srslte_ue_sync_t *q, srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + srslte_nofprb_isvalid(cell.nof_prb)) + { + if (cell.nof_prb > q->max_prb) { + fprintf(stderr, "Error in ue_sync_set_cell(): cell.nof_prb must be lower than initialized\n"); + return SRSLTE_ERROR; + } + + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + q->fft_size = srslte_symbol_sz(q->cell.nof_prb); + q->sf_len = SRSLTE_SF_LEN(q->fft_size); + + if (cell.id == 1000) { + + /* If the cell is unkown, we search PSS/SSS in 5 ms */ + q->nof_recv_sf = 5; + } else { + + /* If the cell is known, we work on a 1ms basis */ + q->nof_recv_sf = 1; + } + + q->frame_len = q->nof_recv_sf*q->sf_len; + + if(q->fft_size < 700 && q->decimate) { + q->decimate = 1; + } + + if(srslte_sync_resize(&q->sfind, q->frame_len, q->frame_len, q->fft_size)) { + fprintf(stderr, "Error setting cell sync find\n"); + return SRSLTE_ERROR; + } + if (cell.id == 1000) { + if(srslte_sync_resize(&q->strack, q->frame_len, TRACK_FRAME_SIZE, q->fft_size)) { + fprintf(stderr, "Error setting cell sync track\n"); + return SRSLTE_ERROR; + } + } else { + if(srslte_sync_resize(&q->strack, q->frame_len, SRSLTE_CP_LEN_NORM(1,q->fft_size), q->fft_size)) { + fprintf(stderr, "Error setting cell sync track\n"); + return SRSLTE_ERROR; + } + } + + if (cell.id == 1000) { + q->nof_avg_find_frames = FIND_NOF_AVG_FRAMES; + + srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.8); + srslte_sync_set_cfo_ema_alpha(&q->strack, 0.1); + + srslte_sync_set_em_alpha(&q->sfind, 1); + + srslte_sync_set_threshold(&q->sfind, 2.0); + srslte_sync_set_threshold(&q->strack, 1.2); + + srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.1); + srslte_sync_set_cfo_ema_alpha(&q->strack, 0.1); + + } else { + q->sfind.cp = cell.cp; + q->strack.cp = cell.cp; + + srslte_sync_set_N_id_2(&q->sfind, cell.id%3); + srslte_sync_set_N_id_2(&q->strack, cell.id%3); + + srslte_sync_set_cfo_ema_alpha(&q->sfind, 0.1); + srslte_sync_set_cfo_ema_alpha(&q->strack, DEFAULT_CFO_EMA_TRACK); + + /* In find phase and if the cell is known, do not average pss correlation + * because we only capture 1 subframe and do not know where the peak is. + */ + q->nof_avg_find_frames = 1; + srslte_sync_set_em_alpha(&q->sfind, 1); + srslte_sync_set_threshold(&q->sfind, 3.0); + + srslte_sync_set_em_alpha(&q->strack, 0.0); + srslte_sync_set_threshold(&q->strack, 1.2); + + } + + // When cell is unknown, do CP CFO correction + srslte_sync_set_cfo_cp_enable(&q->sfind, true, q->frame_len<10000?14:3); + q->cfo_correct_enable_find = false; + + srslte_ue_sync_reset(q); + + ret = SRSLTE_SUCCESS; + } + + return ret; +} + + +void srslte_ue_sync_get_last_timestamp(srslte_ue_sync_t *q, srslte_timestamp_t *timestamp) { + memcpy(timestamp, &q->last_timestamp, sizeof(srslte_timestamp_t)); +} + +void srslte_ue_sync_set_cfo_loop_bw(srslte_ue_sync_t *q, float bw_pss, float bw_ref, + float pss_tol, float ref_tol, float ref_max, + uint32_t pss_stable_conv_time) { + q->cfo_loop_bw_pss = bw_pss; + q->cfo_loop_bw_ref = bw_ref; + q->cfo_pss_min = pss_tol; + q->cfo_ref_min = ref_tol; + q->cfo_ref_max = ref_max; + q->pss_stable_timeout = pss_stable_conv_time; +} + +void srslte_ue_sync_set_cfo_ema(srslte_ue_sync_t *q, float ema) { + srslte_sync_set_cfo_ema_alpha(&q->strack, ema); +} + +void srslte_ue_sync_set_cfo_ref(srslte_ue_sync_t *q, float ref_cfo) +{ + // Accept REF-based CFO adjustments only after PSS CFO is stable + if (q->pss_is_stable) { + if (fabsf(ref_cfo)*15000 > q->cfo_ref_min) { + if (fabsf(ref_cfo)*15000 > q->cfo_ref_max) { + ref_cfo = (ref_cfo>0?q->cfo_ref_max:-q->cfo_ref_max)/15000; + } + q->cfo_current_value += ref_cfo*q->cfo_loop_bw_ref; + } + } +} + +uint32_t srslte_ue_sync_get_sfidx(srslte_ue_sync_t *q) { + return q->sf_idx; +} + +void srslte_ue_sync_set_cfo_i_enable(srslte_ue_sync_t *q, bool enable) { + printf("Warning: Setting integer CFO detection/correction. This is experimental!\n"); + srslte_sync_set_cfo_i_enable(&q->strack, enable); + srslte_sync_set_cfo_i_enable(&q->sfind, enable); +} + +float srslte_ue_sync_get_cfo(srslte_ue_sync_t *q) { + return 15000 * q->cfo_current_value; +} + +void srslte_ue_sync_copy_cfo(srslte_ue_sync_t *q, srslte_ue_sync_t *src_obj) { + // Copy find object internal CFO averages + srslte_sync_copy_cfo(&q->sfind, &src_obj->sfind); + // Current CFO is tracking-phase CFO of previous object + q->cfo_current_value = src_obj->cfo_current_value; + q->cfo_is_copied = true; +} + +void srslte_ue_sync_set_cfo_tol(srslte_ue_sync_t *q, float cfo_tol) { + srslte_sync_set_cfo_tol(&q->strack, cfo_tol); + srslte_sync_set_cfo_tol(&q->sfind, cfo_tol); +} + +float srslte_ue_sync_get_sfo(srslte_ue_sync_t *q) { + return q->mean_sample_offset/5e-3; +} + +int srslte_ue_sync_get_last_sample_offset(srslte_ue_sync_t *q) { + return q->last_sample_offset; +} + +void srslte_ue_sync_set_sfo_correct_period(srslte_ue_sync_t *q, uint32_t nof_subframes) { + q->sample_offset_correct_period = nof_subframes; +} + +void srslte_ue_sync_set_sfo_ema(srslte_ue_sync_t *q, float ema_coefficient) { + q->sfo_ema = ema_coefficient; +} + +void srslte_ue_sync_decode_sss_on_track(srslte_ue_sync_t *q, bool enabled) { + q->decode_sss_on_track = enabled; + srslte_sync_sss_en(&q->strack, q->decode_sss_on_track); +} + +void srslte_ue_sync_set_N_id_2(srslte_ue_sync_t *q, uint32_t N_id_2) { + if (!q->file_mode) { + srslte_ue_sync_reset(q); + srslte_sync_set_N_id_2(&q->strack, N_id_2); + srslte_sync_set_N_id_2(&q->sfind, N_id_2); + } +} + +void srslte_ue_sync_set_agc_period(srslte_ue_sync_t *q, uint32_t period) { + q->agc_period = period; +} + +static int find_peak_ok(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE_MAX_PORTS]) { + + + if (srslte_sync_sss_detected(&q->sfind)) { + /* Get the subframe index (0 or 5) */ + q->sf_idx = srslte_sync_get_sf_idx(&q->sfind) + q->nof_recv_sf; + } else { + DEBUG("Found peak at %d, SSS not detected\n", q->peak_idx); + } + + q->frame_find_cnt++; + DEBUG("Found peak %d at %d, value %.3f, Cell_id: %d CP: %s\n", + q->frame_find_cnt, q->peak_idx, + srslte_sync_get_peak_value(&q->sfind), q->cell.id, srslte_cp_string(q->cell.cp)); + + if (q->frame_find_cnt >= q->nof_avg_find_frames || q->peak_idx < 2*q->fft_size) { + INFO("Realigning frame, reading %d samples\n", q->peak_idx+q->sf_len/2); + /* Receive the rest of the subframe so that we are subframe aligned */ + if (q->recv_callback(q->stream, input_buffer, q->peak_idx+q->sf_len/2, &q->last_timestamp) < 0) { + return SRSLTE_ERROR; + } + + /* Reset variables */ + q->frame_ok_cnt = 0; + q->frame_no_cnt = 0; + q->frame_total_cnt = 0; + q->frame_find_cnt = 0; + q->mean_sample_offset = 0; + + /* Goto Tracking state */ + q->state = SF_TRACK; + + /* Set CFO values. Since we correct before track, the initial track state is CFO=0 Hz */ + if (!q->cfo_is_copied) { + q->cfo_current_value = srslte_sync_get_cfo(&q->sfind); + } + srslte_sync_cfo_reset(&q->strack); + } + + return 0; +} + +static int track_peak_ok(srslte_ue_sync_t *q, uint32_t track_idx) { + + /* Make sure subframe idx is what we expect */ + if ((q->sf_idx != srslte_sync_get_sf_idx(&q->strack)) && + q->decode_sss_on_track && + srslte_sync_sss_detected(&q->strack)) + { + INFO("Warning: Expected SF idx %d but got %d! (%d frames)\n", + q->sf_idx, srslte_sync_get_sf_idx(&q->strack), q->frame_no_cnt); + q->frame_no_cnt++; + if (q->frame_no_cnt >= TRACK_MAX_LOST) { + INFO("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt); + q->state = SF_FIND; + } + } else { + q->frame_no_cnt = 0; + } + + // Get sampling time offset + q->last_sample_offset = ((int) track_idx - (int) q->strack.max_offset/2 - (int) q->strack.fft_size); + + // Adjust sampling time every q->sample_offset_correct_period subframes + uint32_t frame_idx = 0; + if (q->sample_offset_correct_period) { + frame_idx = q->frame_ok_cnt%q->sample_offset_correct_period; + q->mean_sample_offset = SRSLTE_VEC_EMA((float) q->last_sample_offset, q->mean_sample_offset, q->sfo_ema); + } else { + q->mean_sample_offset = q->last_sample_offset; + } + + /* Adjust current CFO estimation with PSS + * Since sync track has enabled only PSS-based correlation, get_cfo() returns that value only, already filtered. + */ + INFO("TRACK: cfo_current=%f, cfo_strack=%f\n", 15000*q->cfo_current_value, 15000*srslte_sync_get_cfo(&q->strack)); + if (15000*fabsf(srslte_sync_get_cfo(&q->strack)) > q->cfo_pss_min) { + q->cfo_current_value += srslte_sync_get_cfo(&q->strack)*q->cfo_loop_bw_pss; + q->pss_stable_cnt = 0; + q->pss_is_stable = false; + } else { + if (!q->pss_is_stable) { + q->pss_stable_cnt++; + if (q->pss_stable_cnt >= q->pss_stable_timeout) { + q->pss_is_stable = true; + } + } + } + + // Compute cumulative moving average time offset */ + if (!frame_idx) { + // Adjust RF sampling time based on the mean sampling offset + q->next_rf_sample_offset = (int) round(q->mean_sample_offset); + + if (q->next_rf_sample_offset) { + INFO("Time offset adjustment: %d samples (%.2f), mean SFO: %.2f Hz, ema=%f, length=%d\n", + q->next_rf_sample_offset, q->mean_sample_offset, + srslte_ue_sync_get_sfo(q), + q->sfo_ema, q->sample_offset_correct_period); + } + q->mean_sample_offset = 0; + } + + /* If the PSS peak is beyond the frame (we sample too slowly), + discard the offseted samples to align next frame */ + if (q->next_rf_sample_offset > 0 && q->next_rf_sample_offset < MAX_TIME_OFFSET) { + DEBUG("Positive time offset %d samples.\n", q->next_rf_sample_offset); + if (q->recv_callback(q->stream, dummy_offset_buffer, (uint32_t) q->next_rf_sample_offset, NULL) < 0) { + fprintf(stderr, "Error receiving from USRP\n"); + return SRSLTE_ERROR; + } + q->next_rf_sample_offset = 0; + } + + q->peak_idx = q->sf_len/2 + q->last_sample_offset; + q->frame_ok_cnt++; + + return 1; +} + +static int track_peak_no(srslte_ue_sync_t *q) { + + /* if we missed too many PSS go back to FIND and consider this frame unsynchronized */ + q->frame_no_cnt++; + if (q->frame_no_cnt >= TRACK_MAX_LOST) { + INFO("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt); + q->state = SF_FIND; + return 0; + } else { + INFO("Tracking peak not found. Peak %.3f, %d lost\n", + srslte_sync_get_peak_value(&q->strack), (int) q->frame_no_cnt); + return 1; + } + +} + +static int receive_samples(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE_MAX_PORTS]) { + + /* A negative time offset means there are samples in our buffer for the next subframe, + because we are sampling too fast. + */ + if (q->next_rf_sample_offset < 0) { + q->next_rf_sample_offset = -q->next_rf_sample_offset; + } + + /* Get N subframes from the USRP getting more samples and keeping the previous samples, if any */ + cf_t *ptr[SRSLTE_MAX_PORTS]; + for (int i=0;inof_rx_antennas;i++) { + ptr[i] = &input_buffer[i][q->next_rf_sample_offset]; + } + if (q->recv_callback(q->stream, ptr, q->frame_len - q->next_rf_sample_offset, &q->last_timestamp) < 0) { + return SRSLTE_ERROR; + } + /* reset time offset */ + q->next_rf_sample_offset = 0; + + return SRSLTE_SUCCESS; +} + +int srslte_ue_sync_zerocopy(srslte_ue_sync_t *q, cf_t *input_buffer) { + cf_t *_input_buffer[SRSLTE_MAX_PORTS]; + _input_buffer[0] = input_buffer; + return srslte_ue_sync_zerocopy_multi(q, _input_buffer); +} + +/* Returns 1 if the subframe is synchronized in time, 0 otherwise */ +int srslte_ue_sync_zerocopy_multi(srslte_ue_sync_t *q, cf_t *input_buffer[SRSLTE_MAX_PORTS]) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + uint32_t track_idx; + + if (q != NULL && + input_buffer != NULL) + { + + if (q->file_mode) { + int n = srslte_filesource_read_multi(&q->file_source, (void **) input_buffer, q->sf_len, q->nof_rx_antennas); + if (n < 0) { + fprintf(stderr, "Error reading input file\n"); + return SRSLTE_ERROR; + } + if (n == 0) { + if (q->file_wrap_enable) { + srslte_filesource_seek(&q->file_source, 0); + q->sf_idx = 9; + n = srslte_filesource_read_multi(&q->file_source, (void **) input_buffer, q->sf_len, q->nof_rx_antennas); + if (n < 0) { + fprintf(stderr, "Error reading input file\n"); + return SRSLTE_ERROR; + } + } else { + return SRSLTE_ERROR; + } + } + if (q->cfo_correct_enable_track) { + for (int i = 0; i < q->nof_rx_antennas; i++) { + srslte_cfo_correct(&q->file_cfo_correct, + input_buffer[i], + input_buffer[i], + q->file_cfo / 15000 / q->fft_size); + } + } + q->sf_idx++; + if (q->sf_idx == 10) { + q->sf_idx = 0; + } + INFO("Reading %d samples. sf_idx = %d\n", q->sf_len, q->sf_idx); + ret = 1; + } else { + + if (receive_samples(q, input_buffer)) { + fprintf(stderr, "Error receiving samples\n"); + return SRSLTE_ERROR; + } + int n; + switch (q->state) { + case SF_FIND: + // Correct CFO before PSS/SSS find using the sync object corrector (initialized for 1 ms) + if (q->cfo_correct_enable_find) { + for (int i=0;inof_rx_antennas;i++) { + srslte_cfo_correct(&q->strack.cfo_corr_frame, + input_buffer[i], + input_buffer[i], + -q->cfo_current_value/q->fft_size); + } + } + n = srslte_sync_find(&q->sfind, input_buffer[0], 0, &q->peak_idx); + switch(n) { + case SRSLTE_SYNC_ERROR: + ret = SRSLTE_ERROR; + fprintf(stderr, "Error finding correlation peak (%d)\n", ret); + return SRSLTE_ERROR; + case SRSLTE_SYNC_FOUND: + ret = find_peak_ok(q, input_buffer); + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + /* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */ + INFO("No space for SSS/CP detection. Realigning frame...\n"); + q->recv_callback(q->stream, dummy_offset_buffer, q->frame_len/2, NULL); + srslte_sync_reset(&q->sfind); + ret = SRSLTE_SUCCESS; + break; + default: + ret = SRSLTE_SUCCESS; + break; + } + if (q->do_agc) { + srslte_agc_process(&q->agc, input_buffer[0], q->sf_len); + } + + INFO("SYNC FIND: sf_idx=%d, ret=%d, next_state=%d\n", q->sf_idx, ret, q->state); + + break; + case SF_TRACK: + + ret = 1; + + // Increase subframe counter + q->sf_idx = (q->sf_idx + q->nof_recv_sf) % 10; + + // Correct CFO before PSS/SSS tracking using the sync object corrector (initialized for 1 ms) + if (q->cfo_correct_enable_track) { + for (int i=0;inof_rx_antennas;i++) { + srslte_cfo_correct(&q->strack.cfo_corr_frame, + input_buffer[i], + input_buffer[i], + -q->cfo_current_value/q->fft_size); + } + } + + /* Every SF idx 0 and 5, find peak around known position q->peak_idx */ + if (q->sf_idx == 0 || q->sf_idx == 5) + { + // Process AGC every period + if (q->do_agc && (q->agc_period == 0 || + (q->agc_period && (q->frame_total_cnt%q->agc_period) == 0))) + { + srslte_agc_process(&q->agc, input_buffer[0], q->sf_len); + } + + /* Track PSS/SSS around the expected PSS position + * In tracking phase, the subframe carrying the PSS is always the last one of the frame + */ + n = srslte_sync_find(&q->strack, input_buffer[0], + q->frame_len - q->sf_len/2 - q->fft_size - q->strack.max_offset/2, + &track_idx); + switch(n) + { + case SRSLTE_SYNC_ERROR: + fprintf(stderr, "Error tracking correlation peak\n"); + return SRSLTE_ERROR; + case SRSLTE_SYNC_FOUND: + ret = track_peak_ok(q, track_idx); + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + // It's very very unlikely that we fall here because this event should happen at FIND phase only + ret = 0; + q->state = SF_FIND; + INFO("Warning: No space for SSS/CP while in tracking phase\n"); + break; + case SRSLTE_SYNC_NOFOUND: + ret = track_peak_no(q); + break; + } + + if (ret == SRSLTE_ERROR) { + fprintf(stderr, "Error processing tracking peak\n"); + q->state = SF_FIND; + return SRSLTE_SUCCESS; + } + + q->frame_total_cnt++; + } + + INFO("SYNC TRACK: sf_idx=%d, ret=%d, next_state=%d\n", q->sf_idx, ret, q->state); + + break; + } + } + } + return ret; +} + + diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c new file mode 100644 index 0000000..dc19ac8 --- /dev/null +++ b/lib/src/phy/ue/ue_ul.c @@ -0,0 +1,675 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/ue/ue_ul.h" + +#define CURRENT_FFTSIZE srslte_symbol_sz(q->cell.nof_prb) +#define CURRENT_SFLEN SRSLTE_SF_LEN(CURRENT_FFTSIZE) + +#define CURRENT_SLOTLEN_RE SRSLTE_SLOT_LEN_RE(q->cell.nof_prb, q->cell.cp) +#define CURRENT_SFLEN_RE SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp) + +#define MAX_SFLEN SRSLTE_SF_LEN(srslte_symbol_sz(max_prb)) + +#define DEFAULT_CFO_TOL 1.0 // Hz + +int srslte_ue_ul_init(srslte_ue_ul_t *q, + cf_t *out_buffer, + uint32_t max_prb) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL) + { + ret = SRSLTE_ERROR; + + bzero(q, sizeof(srslte_ue_ul_t)); + + q->sf_symbols = srslte_vec_malloc(SRSLTE_SF_LEN_PRB(max_prb) * sizeof(cf_t)); + if (!q->sf_symbols) { + perror("malloc"); + goto clean_exit; + } + + if (srslte_ofdm_tx_init(&q->fft, SRSLTE_CP_NORM, q->sf_symbols, out_buffer, max_prb)) { + fprintf(stderr, "Error initiating FFT\n"); + goto clean_exit; + } + srslte_ofdm_set_freq_shift(&q->fft, 0.5); + srslte_ofdm_set_normalize(&q->fft, true); + + q->normalize_en = false; + + if (srslte_cfo_init(&q->cfo, MAX_SFLEN)) { + fprintf(stderr, "Error creating CFO object\n"); + goto clean_exit; + } + + srslte_ue_ul_set_cfo_tol(q, DEFAULT_CFO_TOL); + + if (srslte_pusch_init_ue(&q->pusch, max_prb)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto clean_exit; + } + if (srslte_pucch_init(&q->pucch)) { + fprintf(stderr, "Error creating PUSCH object\n"); + goto clean_exit; + } + if (srslte_softbuffer_tx_init(&q->softbuffer, max_prb)) { + fprintf(stderr, "Error initiating soft buffer\n"); + goto clean_exit; + } + if (srslte_refsignal_ul_init(&q->signals, max_prb)) { + fprintf(stderr, "Error initiating srslte_refsignal_ul\n"); + goto clean_exit; + } + q->refsignal = srslte_vec_malloc(2 * SRSLTE_NRE * max_prb * sizeof(cf_t)); + if (!q->refsignal) { + perror("malloc"); + goto clean_exit; + } + + q->srs_signal = srslte_vec_malloc(SRSLTE_NRE * max_prb * sizeof(cf_t)); + if (!q->srs_signal) { + perror("malloc"); + goto clean_exit; + } + q->signals_pregenerated = false; + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid parameters\n"); + } + +clean_exit: + if (ret == SRSLTE_ERROR) { + srslte_ue_ul_free(q); + } + return ret; +} + +void srslte_ue_ul_free(srslte_ue_ul_t *q) { + if (q) { + srslte_ofdm_tx_free(&q->fft); + srslte_pusch_free(&q->pusch); + srslte_pucch_free(&q->pucch); + srslte_softbuffer_tx_free(&q->softbuffer); + + srslte_cfo_free(&q->cfo); + srslte_refsignal_ul_free(&q->signals); + + if (q->sf_symbols) { + free(q->sf_symbols); + } + if (q->refsignal) { + free(q->refsignal); + } + if (q->srs_signal) { + free(q->srs_signal); + } + if (q->signals_pregenerated) { + srslte_refsignal_dmrs_pusch_pregen_free(&q->signals, &q->pregen_drms); + srslte_refsignal_srs_pregen_free(&q->signals, &q->pregen_srs); + } + bzero(q, sizeof(srslte_ue_ul_t)); + } +} + +int srslte_ue_ul_set_cell(srslte_ue_ul_t *q, + srslte_cell_t cell) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && srslte_cell_isvalid(&cell)) + { + if (q->cell.id != cell.id || q->cell.nof_prb == 0) { + memcpy(&q->cell, &cell, sizeof(srslte_cell_t)); + + if (srslte_ofdm_tx_set_prb(&q->fft, q->cell.cp, q->cell.nof_prb)) { + fprintf(stderr, "Error resizing FFT\n"); + return SRSLTE_ERROR; + } + if (srslte_cfo_resize(&q->cfo, SRSLTE_SF_LEN_PRB(q->cell.nof_prb))) { + fprintf(stderr, "Error resizing CFO object\n"); + return SRSLTE_ERROR; + } + + srslte_ue_ul_set_cfo_tol(q, q->current_cfo_tol); + + if (srslte_pusch_set_cell(&q->pusch, q->cell)) { + fprintf(stderr, "Error resizing PUSCH object\n"); + return SRSLTE_ERROR; + } + if (srslte_pucch_set_cell(&q->pucch, q->cell)) { + fprintf(stderr, "Error resizing PUSCH object\n"); + return SRSLTE_ERROR; + } + if (srslte_refsignal_ul_set_cell(&q->signals, q->cell)) { + fprintf(stderr, "Error resizing srslte_refsignal_ul\n"); + return SRSLTE_ERROR; + } + q->signals_pregenerated = false; + } + ret = SRSLTE_SUCCESS; + } else { + fprintf(stderr, "Invalid cell properties ue_ul: Id=%d, Ports=%d, PRBs=%d\n", + cell.id, cell.nof_ports, cell.nof_prb); + } + return ret; +} + +void srslte_ue_ul_set_cfo_tol(srslte_ue_ul_t *q, float tol) { + q->current_cfo_tol = tol; + srslte_cfo_set_tol(&q->cfo, tol/(15000.0*srslte_symbol_sz(q->cell.nof_prb))); +} + +void srslte_ue_ul_set_cfo(srslte_ue_ul_t *q, float cur_cfo) { + q->current_cfo = cur_cfo; +} + +void srslte_ue_ul_set_cfo_enable(srslte_ue_ul_t *q, bool enabled) +{ + q->cfo_en = enabled; +} + +void srslte_ue_ul_set_normalization(srslte_ue_ul_t *q, bool enabled) +{ + q->normalize_en = enabled; +} + +/* Precalculate the PUSCH scramble sequences for a given RNTI. This function takes a while + * to execute, so shall be called once the final C-RNTI has been allocated for the session. + * For the connection procedure, use srslte_pusch_encode_rnti() or srslte_pusch_decode_rnti() functions + */ +void srslte_ue_ul_set_rnti(srslte_ue_ul_t *q, uint16_t rnti) { + srslte_pusch_set_rnti(&q->pusch, rnti); + srslte_pucch_set_crnti(&q->pucch, rnti); + q->current_rnti = rnti; +} + +void srslte_ue_ul_reset(srslte_ue_ul_t *q) { + srslte_softbuffer_tx_reset(&q->softbuffer); +} + +int srslte_ue_ul_pregen_signals(srslte_ue_ul_t *q) { + if (q->signals_pregenerated) { + srslte_refsignal_dmrs_pusch_pregen_free(&q->signals, &q->pregen_drms); + srslte_refsignal_srs_pregen_free(&q->signals, &q->pregen_srs); + } + if (srslte_refsignal_dmrs_pusch_pregen(&q->signals, &q->pregen_drms)) { + return SRSLTE_ERROR; + } + if (srslte_refsignal_srs_pregen(&q->signals, &q->pregen_srs)) { + return SRSLTE_ERROR; + } + q->signals_pregenerated = true; + return SRSLTE_SUCCESS; +} + + +void srslte_ue_ul_set_cfg(srslte_ue_ul_t *q, + srslte_refsignal_dmrs_pusch_cfg_t *dmrs_cfg, + srslte_refsignal_srs_cfg_t *srs_cfg, + srslte_pucch_cfg_t *pucch_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_uci_cfg_t *uci_cfg, + srslte_pusch_hopping_cfg_t *hopping_cfg, + srslte_ue_ul_powerctrl_t *power_ctrl) +{ + srslte_refsignal_ul_set_cfg(&q->signals, dmrs_cfg, pucch_cfg, srs_cfg); + if (pucch_cfg && dmrs_cfg) { + srslte_pucch_set_cfg(&q->pucch, pucch_cfg, dmrs_cfg->group_hopping_en); + } + if (pucch_sched) { + memcpy(&q->pucch_sched, pucch_sched, sizeof(srslte_pucch_sched_t)); + } + if (srs_cfg) { + memcpy(&q->srs_cfg, srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + } + if (uci_cfg) { + memcpy(&q->uci_cfg, uci_cfg, sizeof(srslte_uci_cfg_t)); + } + if (hopping_cfg) { + memcpy(&q->hopping_cfg, hopping_cfg, sizeof(srslte_pusch_hopping_cfg_t)); + } + if (power_ctrl) { + memcpy(&q->power_ctrl, power_ctrl, sizeof(srslte_ue_ul_powerctrl_t)); + } +} + +int srslte_ue_ul_cfg_grant(srslte_ue_ul_t *q, srslte_ra_ul_grant_t *grant, + uint32_t tti, uint32_t rvidx, uint32_t current_tx_nb) +{ + return srslte_pusch_cfg(&q->pusch, &q->pusch_cfg, grant, &q->uci_cfg, &q->hopping_cfg, &q->srs_cfg, tti, rvidx, current_tx_nb); +} + +// Encode bits from uci_data +void pucch_encode_bits(srslte_uci_data_t *uci_data, srslte_pucch_format_t format, + uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS], + uint8_t pucch2_bits[SRSLTE_PUCCH_MAX_BITS]) +{ + if (format == SRSLTE_PUCCH_FORMAT_1A || format == SRSLTE_PUCCH_FORMAT_1B) { + pucch_bits[0] = uci_data->uci_ack; + pucch_bits[1] = uci_data->uci_ack_2; // this will be ignored in format 1a + } + if (format >= SRSLTE_PUCCH_FORMAT_2) { + /* Put RI (goes alone) */ + if (uci_data->ri_periodic_report) { + uint8_t temp[2] = {uci_data->uci_ri, 0}; + srslte_uci_encode_cqi_pucch(temp, uci_data->uci_ri_len, pucch_bits); + } else { + /* Put CQI Report*/ + srslte_uci_encode_cqi_pucch(uci_data->uci_cqi, uci_data->uci_cqi_len, pucch_bits); + } + if (format > SRSLTE_PUCCH_FORMAT_2) { + pucch2_bits[0] = uci_data->uci_ack; + pucch2_bits[1] = uci_data->uci_ack_2; // this will be ignored in format 2a + } + } +} + +static float limit_norm_factor(srslte_ue_ul_t *q, float norm_factor, cf_t *output_signal) +{ + uint32_t p = srslte_vec_max_abs_fi((float*) output_signal, 2*SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + float amp = fabsf(*((float*) output_signal + p)); + + if (amp*norm_factor > 0.95) { + norm_factor = 0.95/amp; + } + if (amp*norm_factor < 0.1) { + norm_factor = 0.1/amp; + } + q->last_amplitude = norm_factor*amp; + return norm_factor; +} + +float srslte_ue_ul_get_last_amplitude(srslte_ue_ul_t *q) { + return q->last_amplitude; +} + +/* Choose PUCCH format as in Sec 10.1 of 36.213 and generate PUCCH signal + */ +int srslte_ue_ul_pucch_encode(srslte_ue_ul_t *q, srslte_uci_data_t uci_data, + uint32_t pdcch_n_cce, + uint32_t tti, + cf_t *output_signal) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + output_signal != NULL) + { + + uint32_t sf_idx = tti%10; + ret = SRSLTE_ERROR; + + uint8_t pucch_bits[SRSLTE_PUCCH_MAX_BITS]; + uint8_t pucch2_bits[2]; + bzero(pucch_bits, SRSLTE_PUCCH_MAX_BITS*sizeof(uint8_t)); + bzero(pucch2_bits, 2*sizeof(uint8_t)); + + srslte_pucch_format_t format = srslte_pucch_get_format(&uci_data, q->cell.cp); + + // Encode UCI information + pucch_encode_bits(&uci_data, format, pucch_bits, pucch2_bits); + + // Choose n_pucch + uint32_t n_pucch = srslte_pucch_get_npucch(pdcch_n_cce, format, uci_data.scheduling_request, &q->pucch_sched); + + if (srslte_pucch_encode(&q->pucch, format, n_pucch, sf_idx, q->current_rnti, pucch_bits, q->sf_symbols)) { + fprintf(stderr, "Error encoding TB\n"); + return ret; + } + + if (srslte_refsignal_dmrs_pucch_gen(&q->signals, format, n_pucch, sf_idx, pucch2_bits, q->refsignal)) + { + fprintf(stderr, "Error generating PUSCH DRMS signals\n"); + return ret; + } + srslte_refsignal_dmrs_pucch_put(&q->signals, format, n_pucch, q->refsignal, q->sf_symbols); + + if (srslte_ue_ul_srs_tx_enabled(&q->signals.srs_cfg, tti) && q->pucch.shortened) { + if (q->signals_pregenerated) { + srslte_refsignal_srs_pregen_put(&q->signals, &q->pregen_srs, tti, q->sf_symbols); + } else { + srslte_refsignal_srs_gen(&q->signals, tti%10, q->srs_signal); + srslte_refsignal_srs_put(&q->signals, tti, q->srs_signal, q->sf_symbols); + } + } + + q->last_pucch_format = format; + + srslte_ofdm_tx_sf(&q->fft); + + if (q->cfo_en) { + srslte_cfo_correct(&q->cfo, output_signal, output_signal, q->current_cfo / srslte_symbol_sz(q->cell.nof_prb)); + } + + if (q->normalize_en) { + float norm_factor = (float) q->cell.nof_prb/15/40; + + norm_factor = limit_norm_factor(q, norm_factor, output_signal); + + srslte_vec_sc_prod_cfc(output_signal, norm_factor, output_signal, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + } + ret = SRSLTE_SUCCESS; + } + + return ret; +} + +int srslte_ue_ul_pusch_encode(srslte_ue_ul_t *q, uint8_t *data, cf_t *output_signal) +{ + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + return srslte_ue_ul_pusch_uci_encode_rnti(q, data, uci_data, q->current_rnti, output_signal); +} + +int srslte_ue_ul_pusch_encode_rnti(srslte_ue_ul_t *q, uint8_t *data, uint16_t rnti, cf_t *output_signal) +{ + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + return srslte_ue_ul_pusch_uci_encode_rnti(q, data, uci_data, rnti, output_signal); +} + +int srslte_ue_ul_pusch_uci_encode(srslte_ue_ul_t *q, uint8_t *data, srslte_uci_data_t uci_data, cf_t *output_signal) +{ + return srslte_ue_ul_pusch_uci_encode_rnti(q, data, uci_data, q->current_rnti, output_signal); +} + +int srslte_ue_ul_pusch_uci_encode_rnti(srslte_ue_ul_t *q, + uint8_t *data, srslte_uci_data_t uci_data, + uint16_t rnti, + cf_t *output_signal) +{ + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + output_signal != NULL) + { + + if (q->pusch_cfg.grant.L_prb == 0) { + fprintf(stderr, "Invalid UL PRB allocation (L_prb=0)\n"); + return SRSLTE_ERROR; + } + + return srslte_ue_ul_pusch_encode_rnti_softbuffer(q, data, uci_data, &q->softbuffer, rnti, output_signal); + } + return ret; +} + +int srslte_ue_ul_srs_encode(srslte_ue_ul_t *q, uint32_t tti, cf_t *output_signal) { + int ret = SRSLTE_ERROR_INVALID_INPUTS; + if (q && output_signal) { + ret = SRSLTE_ERROR; + + if (srslte_ue_ul_srs_tx_enabled(&q->signals.srs_cfg, tti)) { + if (q->signals_pregenerated) { + srslte_refsignal_srs_pregen_put(&q->signals, &q->pregen_srs, tti, q->sf_symbols); + } else { + srslte_refsignal_srs_gen(&q->signals, tti%10, q->srs_signal); + srslte_refsignal_srs_put(&q->signals, tti, q->srs_signal, q->sf_symbols); + } + } + + srslte_ofdm_tx_sf(&q->fft); + + if (q->cfo_en) { + srslte_cfo_correct(&q->cfo, output_signal, output_signal, q->current_cfo / srslte_symbol_sz(q->cell.nof_prb)); + } + + if (q->normalize_en) { + float norm_factor = (float) q->cell.nof_prb/15/sqrtf(srslte_refsignal_srs_M_sc(&q->signals)); + + norm_factor = limit_norm_factor(q, norm_factor, output_signal); + + srslte_vec_sc_prod_cfc(output_signal, norm_factor, output_signal, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + } + + ret = SRSLTE_SUCCESS; + } + return ret; +} + +int srslte_ue_ul_pusch_encode_rnti_softbuffer(srslte_ue_ul_t *q, + uint8_t *data, srslte_uci_data_t uci_data, + srslte_softbuffer_tx_t *softbuffer, + uint16_t rnti, + cf_t *output_signal) +{ + + int ret = SRSLTE_ERROR_INVALID_INPUTS; + + if (q != NULL && + output_signal != NULL) + { + + bzero(q->sf_symbols, sizeof(cf_t)*SRSLTE_SF_LEN_RE(q->cell.nof_prb, q->cell.cp)); + + if (srslte_pusch_encode(&q->pusch, &q->pusch_cfg, softbuffer, data, uci_data, rnti, q->sf_symbols)) { + fprintf(stderr, "Error encoding TB\n"); + return SRSLTE_ERROR; + } + + if (q->signals_pregenerated) { + srslte_refsignal_dmrs_pusch_pregen_put(&q->signals, &q->pregen_drms, + q->pusch_cfg.grant.L_prb, + q->pusch_cfg.sf_idx, + q->pusch_cfg.grant.ncs_dmrs, + q->pusch_cfg.grant.n_prb_tilde, + q->sf_symbols); + } else { + + if (srslte_refsignal_dmrs_pusch_gen(&q->signals, q->pusch_cfg.grant.L_prb, + q->pusch_cfg.sf_idx, + q->pusch_cfg.grant.ncs_dmrs, + q->refsignal)) + { + fprintf(stderr, "Error generating PUSCH DRMS signals\n"); + return ret; + } + srslte_refsignal_dmrs_pusch_put(&q->signals, q->refsignal, + q->pusch_cfg.grant.L_prb, + q->pusch_cfg.grant.n_prb_tilde, + q->sf_symbols); + } + + if (srslte_ue_ul_srs_tx_enabled(&q->signals.srs_cfg, q->pusch_cfg.tti)) { + if (q->signals_pregenerated) { + srslte_refsignal_srs_pregen_put(&q->signals, &q->pregen_srs, q->pusch_cfg.tti, q->sf_symbols); + } else { + srslte_refsignal_srs_gen(&q->signals, q->pusch_cfg.sf_idx, q->srs_signal); + srslte_refsignal_srs_put(&q->signals, q->pusch_cfg.tti, q->srs_signal, q->sf_symbols); + } + } + + srslte_ofdm_tx_sf(&q->fft); + + if (q->cfo_en) { + srslte_cfo_correct(&q->cfo, output_signal, output_signal, q->current_cfo / srslte_symbol_sz(q->cell.nof_prb)); + } + + if (q->normalize_en) { + float norm_factor = (float) q->cell.nof_prb/15/sqrtf(q->pusch_cfg.grant.L_prb)/2; + + norm_factor = limit_norm_factor(q, norm_factor, output_signal); + + srslte_vec_sc_prod_cfc(output_signal, norm_factor, output_signal, SRSLTE_SF_LEN_PRB(q->cell.nof_prb)); + } + + ret = SRSLTE_SUCCESS; + } + + return ret; +} + +/* Returns the transmission power for PUSCH for this subframe as defined in Section 5.1.1 of 36.213 */ +float srslte_ue_ul_pusch_power(srslte_ue_ul_t *q, float PL, float p0_preamble) +{ + float p0_pusch, alpha; + if (p0_preamble) { + p0_pusch = p0_preamble + q->power_ctrl.delta_preamble_msg3; + alpha = 1; + } else { + alpha = q->power_ctrl.alpha; + p0_pusch = q->power_ctrl.p0_nominal_pusch + q->power_ctrl.p0_ue_pusch; + } + float delta=0; + if (q->power_ctrl.delta_mcs_based) { + float beta_offset_pusch = 1; + float MPR = q->pusch_cfg.cb_segm.K1*q->pusch_cfg.cb_segm.C1+q->pusch_cfg.cb_segm.K2*q->pusch_cfg.cb_segm.C2; + if (q->pusch_cfg.cb_segm.tbs == 0) { + beta_offset_pusch = srslte_sch_beta_cqi(q->pusch_cfg.uci_cfg.I_offset_cqi); + MPR = q->pusch_cfg.last_O_cqi; + } + MPR /= q->pusch_cfg.nbits.nof_re; + delta = 10*log10((pow(2,MPR*1.25)-1)*beta_offset_pusch); + } + //TODO: This implements closed-loop power control + float f=0; + + float pusch_power = 10*log10(q->pusch_cfg.grant.L_prb)+p0_pusch+alpha*PL+delta+f; + DEBUG("PUSCH: P=%f -- 10M=%f, p0=%f,alpha=%f,PL=%f,\n", + pusch_power, 10*log10(q->pusch_cfg.grant.L_prb), p0_pusch, alpha, PL); + return SRSLTE_MIN(SRSLTE_PC_MAX, pusch_power); +} + +/* Returns the transmission power for PUCCH for this subframe as defined in Section 5.1.2 of 36.213 */ +float srslte_ue_ul_pucch_power(srslte_ue_ul_t *q, float PL, srslte_pucch_format_t format, uint32_t n_cqi, uint32_t n_harq) { + float p0_pucch = q->power_ctrl.p0_nominal_pucch + q->power_ctrl.p0_ue_pucch; + + uint8_t format_idx = format==0?0:((uint32_t) format-1); + + float delta_f = q->power_ctrl.delta_f_pucch[format_idx]; + + float h; + + if(format <= SRSLTE_PUCCH_FORMAT_1B) { + h = 0; + } else { + if (SRSLTE_CP_ISNORM(q->cell.cp)) { + if (n_cqi >= 4) { + h = 10*log10(n_cqi/4); + } else { + h = 0; + } + } else { + if (n_cqi + n_harq >= 4) { + h = 10*log10((n_cqi+n_harq)/4); + } else { + h = 0; + } + } + } + + //TODO: This implements closed-loop power control + float g = 0; + + float pucch_power = p0_pucch + PL + h + delta_f + g; + + DEBUG("PUCCH: P=%f -- p0=%f, PL=%f, delta_f=%f, h=%f, g=%f\n", + pucch_power, p0_pucch, PL, delta_f, h, g); + + return pucch_power; +} + +/* Returns the transmission power for SRS for this subframe as defined in Section 5.1.3 of 36.213 */ +float srslte_ue_ul_srs_power(srslte_ue_ul_t *q, float PL) { + float alpha = q->power_ctrl.alpha; + float p0_pusch = q->power_ctrl.p0_nominal_pusch + q->power_ctrl.p0_ue_pusch; + + //TODO: This implements closed-loop power control + float f=0; + + uint32_t M_sc = srslte_refsignal_srs_M_sc(&q->signals); + + float p_srs_offset; + if (q->power_ctrl.delta_mcs_based) { + p_srs_offset = -3 + q->power_ctrl.p_srs_offset; + } else { + p_srs_offset = -10.5 + 1.5*q->power_ctrl.p_srs_offset; + } + + float p_srs = p_srs_offset + 10*log10(M_sc) + p0_pusch + alpha*PL + f; + + DEBUG("SRS: P=%f -- p_offset=%f, 10M=%f, p0_pusch=%f, alpha=%f, PL=%f, f=%f\n", + p_srs, p_srs_offset, 10*log10(M_sc), p0_pusch, alpha, PL, f); + + return p_srs; +} + +/* Returns 1 if a SR needs to be sent at current_tti given I_sr, as defined in Section 10.1 of 36.213 */ +int srslte_ue_ul_sr_send_tti(uint32_t I_sr, uint32_t current_tti) { + uint32_t sr_periodicity; + uint32_t sr_N_offset; + if (I_sr < 5) { + sr_periodicity = 5; + sr_N_offset = I_sr; + } else if (I_sr < 15) { + sr_periodicity = 10; + sr_N_offset = I_sr-5; + } else if (I_sr < 35) { + sr_periodicity = 20; + sr_N_offset = I_sr-15; + } else if (I_sr < 75) { + sr_periodicity = 40; + sr_N_offset = I_sr-35; + } else if (I_sr < 155) { + sr_periodicity = 80; + sr_N_offset = I_sr-75; + } else if (I_sr < 157) { + sr_periodicity = 2; + sr_N_offset = I_sr-155; + } else if (I_sr == 157) { + sr_periodicity = 1; + sr_N_offset = I_sr-157; + } else { + return SRSLTE_ERROR; + } + if (current_tti >= sr_N_offset) { + if ((current_tti - sr_N_offset) % sr_periodicity == 0) { + return 1; + } + } + return SRSLTE_SUCCESS; +} + + +bool srslte_ue_ul_srs_tx_enabled(srslte_refsignal_srs_cfg_t *srs_cfg, uint32_t tti) { + if (srs_cfg->configured) { + if (srslte_refsignal_srs_send_cs(srs_cfg->subframe_config, tti%10) == 1 && + srslte_refsignal_srs_send_ue(srs_cfg->I_srs, tti) == 1) + { + return true; + } + } + return false; +} + + diff --git a/lib/src/phy/utils/CMakeLists.txt b/lib/src/phy/utils/CMakeLists.txt new file mode 100644 index 0000000..f8a886e --- /dev/null +++ b/lib/src/phy/utils/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.c") +add_library(srslte_utils OBJECT ${SOURCES}) + +if(VOLK_FOUND) + set_target_properties(srslte_utils PROPERTIES COMPILE_DEFINITIONS "${VOLK_DEFINITIONS}") +endif(VOLK_FOUND) + +add_subdirectory(test) diff --git a/lib/src/phy/utils/bit.c b/lib/src/phy/utils/bit.c new file mode 100644 index 0000000..674c1da --- /dev/null +++ b/lib/src/phy/utils/bit.c @@ -0,0 +1,793 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#ifdef LV_HAVE_SSE + +#include + +#endif /* LV_HAVE_SSE */ + +#include "srslte/phy/utils/bit.h" +#include "srslte/phy/utils/vector.h" + +void srslte_bit_interleaver_init(srslte_bit_interleaver_t *q, + uint16_t *interleaver, + uint32_t nof_bits) { + static const uint8_t mask[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + + bzero(q, sizeof(srslte_bit_interleaver_t)); + + q->interleaver = srslte_vec_malloc(sizeof(uint16_t)*nof_bits); + q->byte_idx = srslte_vec_malloc(sizeof(uint16_t)*nof_bits); + q->bit_mask = srslte_vec_malloc(sizeof(uint8_t)*nof_bits); + q->nof_bits = nof_bits; + + for (int i = 0; i < nof_bits; i++) { + uint16_t i_px = interleaver[i]; + q->interleaver[i] = i_px; + q->byte_idx[i] = (uint16_t) (interleaver[i] / 8); + q->bit_mask[i] = (uint8_t) (mask[i_px%8]); + } +} + +void srslte_bit_interleaver_free(srslte_bit_interleaver_t *q) { + if (q->interleaver) { + free(q->interleaver); + } + + if (q->byte_idx) { + free(q->byte_idx); + } + + if (q->bit_mask) { + free(q->bit_mask); + } + + bzero(q, sizeof(srslte_bit_interleaver_t)); +} + +void srslte_bit_interleaver_run(srslte_bit_interleaver_t *q, uint8_t *input, uint8_t *output, uint16_t w_offset) { + static const uint8_t mask[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + uint16_t *byte_idx = q->byte_idx; + uint8_t *bit_mask = q->bit_mask; + uint8_t *output_ptr = output; + + uint32_t st=0, w_offset_p=0; + + if (w_offset < 8 && w_offset > 0) { + st=1; + for (uint32_t j=0;j<8-w_offset;j++) { + uint16_t i_p = q->interleaver[j]; + if (input[i_p/8] & mask[i_p%8]) { + output[0] |= mask[j+w_offset]; + } else { + output[0] &= ~(mask[j+w_offset]); + } + } + w_offset_p=8-w_offset; + } + + int i = st * 8; + + byte_idx += i - w_offset_p; + bit_mask += i - w_offset_p; + output_ptr += st; + +#ifdef LV_HAVE_SSE + for(; i < (int) q->nof_bits - 15; i += 16) { + __m128i in128; + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x7); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x6); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x5); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x4); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x3); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x2); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x1); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x0); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xF); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xE); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xD); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xC); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xB); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0xA); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x9); + in128 = _mm_insert_epi8(in128, input[*(byte_idx++)], 0x8); + + __m128i mask128 = _mm_loadu_si128((__m128i *) bit_mask); + mask128 = _mm_shuffle_epi8(mask128, _mm_set_epi8(0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7)); + + __m128i cmp128 = _mm_cmpeq_epi8(_mm_and_si128(in128, mask128), mask128); + *((uint16_t *) (output_ptr)) = (uint16_t) _mm_movemask_epi8(cmp128); + + bit_mask += 16; + output_ptr += 2; + } + +#endif /* LV_HAVE_SSE */ + + for(; i < (int) q->nof_bits - 7; i += 8) { + uint8_t out0 = (input[*(byte_idx++)] & *(bit_mask++))?mask[0]:(uint8_t)0; + uint8_t out1 = (input[*(byte_idx++)] & *(bit_mask++))?mask[1]:(uint8_t)0; + uint8_t out2 = (input[*(byte_idx++)] & *(bit_mask++))?mask[2]:(uint8_t)0; + uint8_t out3 = (input[*(byte_idx++)] & *(bit_mask++))?mask[3]:(uint8_t)0; + uint8_t out4 = (input[*(byte_idx++)] & *(bit_mask++))?mask[4]:(uint8_t)0; + uint8_t out5 = (input[*(byte_idx++)] & *(bit_mask++))?mask[5]:(uint8_t)0; + uint8_t out6 = (input[*(byte_idx++)] & *(bit_mask++))?mask[6]:(uint8_t)0; + uint8_t out7 = (input[*(byte_idx++)] & *(bit_mask++))?mask[7]:(uint8_t)0; + + *output_ptr = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7; + output_ptr++; + } + + for (uint32_t j=0;jnof_bits%8;j++) { + uint16_t i_p = q->interleaver[(q->nof_bits/8)*8+j-w_offset]; + if (input[i_p/8] & mask[i_p%8]) { + output[q->nof_bits/8] |= mask[j]; + } else { + output[q->nof_bits/8] &= ~(mask[j]); + } + } + for (uint32_t j=0;jinterleaver[(q->nof_bits/8)*8+j-w_offset]; + if (input[i_p/8] & (1<<(7-i_p%8))) { + output[q->nof_bits/8] |= mask[j]; + } else { + output[q->nof_bits/8] &= ~(mask[j]); + } + } + +#if 0 + /* THIS PIECE OF CODE IS FOR CHECKING SIMD BEHAVIOUR. DO NOT ENABLE. */ + uint8_t *output2 = malloc(q->nof_bits/8); + for (i=st;inof_bits/8;i++) { + + uint16_t i_p0 = q->interleaver[i*8+0-w_offset_p]; + uint16_t i_p1 = q->interleaver[i*8+1-w_offset_p]; + uint16_t i_p2 = q->interleaver[i*8+2-w_offset_p]; + uint16_t i_p3 = q->interleaver[i*8+3-w_offset_p]; + uint16_t i_p4 = q->interleaver[i*8+4-w_offset_p]; + uint16_t i_p5 = q->interleaver[i*8+5-w_offset_p]; + uint16_t i_p6 = q->interleaver[i*8+6-w_offset_p]; + uint16_t i_p7 = q->interleaver[i*8+7-w_offset_p]; + + uint8_t out0 = (input[i_p0/8] & mask[i_p0%8])?mask[0]:(uint8_t)0; + uint8_t out1 = (input[i_p1/8] & mask[i_p1%8])?mask[1]:(uint8_t)0; + uint8_t out2 = (input[i_p2/8] & mask[i_p2%8])?mask[2]:(uint8_t)0; + uint8_t out3 = (input[i_p3/8] & mask[i_p3%8])?mask[3]:(uint8_t)0; + uint8_t out4 = (input[i_p4/8] & mask[i_p4%8])?mask[4]:(uint8_t)0; + uint8_t out5 = (input[i_p5/8] & mask[i_p5%8])?mask[5]:(uint8_t)0; + uint8_t out6 = (input[i_p6/8] & mask[i_p6%8])?mask[6]:(uint8_t)0; + uint8_t out7 = (input[i_p7/8] & mask[i_p7%8])?mask[7]:(uint8_t)0; + + output2[i] = out0 | out1 | out2 | out3 | out4 | out5 | out6 | out7; + } + + for(i = st; i < q->nof_bits/8; i++) { + if (true || output[i] != output2[i]) { + printf("%05d/%05d %02X %02X\n", i, q->nof_bits/8, output[i], output2[i]); + } + //output[i] = output2[i]; + } + free(output2); +#endif +} + + +void srslte_bit_interleave_i(uint8_t *input, uint8_t *output, uint32_t *interleaver, uint32_t nof_bits) { + srslte_bit_interleave_i_w_offset(input, output, interleaver, nof_bits, 0); +} + +void srslte_bit_interleave_i_w_offset(uint8_t *input, uint8_t *output, uint32_t *interleaver, uint32_t nof_bits, uint32_t w_offset) { + uint32_t st=0, w_offset_p=0; + static const uint8_t mask[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; + + if (w_offset < 8 && w_offset > 0) { + st=1; + for (uint32_t j=0;j<8-w_offset;j++) { + uint32_t i_p = interleaver[j]; + if (input[i_p/8] & mask[i_p%8]) { + output[0] |= mask[j+w_offset]; + } else { + output[0] &= ~(mask[j+w_offset]); + } + } + w_offset_p=8-w_offset; + } + +#ifdef LV_HAVE_SSE + __m64 m64mask = _mm_setr_pi8((uint8_t) 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1); + + union { + uint8_t v[8]; + __m64 m64; + } a, b, c; + + union { + __m128i m128; + uint16_t u32[4]; + uint16_t u16[8]; + uint8_t u8[16]; + struct { + __m64 reg_a; + __m64 reg_b; + } m64; + struct { + uint16_t i0, i1, i2, i3, i4, i5, i6, i7; + } v16; + struct { + uint32_t i0, i1, i2, i3; + } v32; + } ipx1, ipx2, epx1, epx2; + for (uint32_t i = st; i < nof_bits / 8; i++) { + ipx1.m128 = _mm_loadu_si128((__m128i *) (interleaver + (i * 8 + 0) - w_offset_p)); + epx1.m128 = _mm_shuffle_epi8(ipx1.m128, _mm_set_epi8(0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C)); + ipx2.m128 = _mm_loadu_si128((__m128i *) (interleaver + (i * 8 + 4) - w_offset_p)); + epx2.m128 = _mm_shuffle_epi8(ipx2.m128, _mm_set_epi8(0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C, + 0x00, 0x04, 0x08, 0x0C)); + + epx1.m128 = _mm_blendv_epi8(epx2.m128, epx1.m128, _mm_setr_epi8(+1, +1, +1, +1, + -1, -1, -1, -1, + +1, +1, +1, +1, + -1, -1, -1, -1)); + + b.m64 = _mm_and_si64(epx1.m64.reg_a, _mm_set1_pi8(0x7)); + b.m64 = _mm_shuffle_pi8(m64mask, b.m64); + + ipx1.m128 = _mm_srli_epi32(ipx1.m128, 3); + ipx2.m128 = _mm_srli_epi32(ipx2.m128, 3); + + a.m64 = _mm_set_pi8(input[ipx1.v32.i0], + input[ipx1.v32.i1], + input[ipx1.v32.i2], + input[ipx1.v32.i3], + input[ipx2.v32.i0], + input[ipx2.v32.i1], + input[ipx2.v32.i2], + input[ipx2.v32.i3]); + + c.m64 = _mm_cmpeq_pi8(_mm_and_si64(a.m64, b.m64), b.m64); + output[i] = (uint8_t) _mm_movemask_pi8(c.m64); + } + +#if 0 /* Disabled */ + /* THIS PIECE OF CODE IS FOR CHECKING SIMD BEHAVIOUR. DO NOT ENABLE. */ + uint8_t *output2 = malloc(nof_bits/8); + for (uint32_t i=st;i 0) { + st=1; + for (uint32_t j=0;j<8-w_offset;j++) { + uint16_t i_p = interleaver[j]; + if (input[i_p/8] & mask[i_p%8]) { + output[0] |= mask[j+w_offset]; + } else { + output[0] &= ~(mask[j+w_offset]); + } + } + w_offset_p=8-w_offset; + } +#ifdef LV_HAVE_SSE + __m64 m64mask = _mm_setr_pi8((uint8_t) 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1); + __m128i m128mask = _mm_set1_epi64(m64mask); + + union { + uint8_t v[8]; + __m64 m64; + } a, b, c; + + union { + __m128i m128; + uint16_t u16[8]; + uint8_t u8[16]; + struct { + __m64 reg_a; + __m64 reg_b; + } m64; + struct { + uint16_t i0, i1, i2, i3, i4, i5, i6, i7; + } v; + } ipx, epx, ipx2, epx2, b128, a128, c128; + + uint32_t i = st; + for (; i < (nof_bits / 8 - 1); i += 2) { + ipx.m128 = _mm_loadu_si128((__m128i *) (interleaver + (i * 8) - w_offset_p)); + epx.m128 = _mm_shuffle_epi8(ipx.m128, _mm_set_epi8(0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E)); + ipx2.m128 = _mm_loadu_si128((__m128i *) (interleaver + ((i + 1) * 8) - w_offset_p)); + epx2.m128 = _mm_shuffle_epi8(ipx2.m128, _mm_set_epi8(0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E)); + + epx.m128 = _mm_blendv_epi8(epx.m128, epx2.m128, _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, + (uint8_t) 0xFF, (uint8_t) 0xFF, + (uint8_t) 0xFF, (uint8_t) 0xFF, + (uint8_t) 0xFF, (uint8_t) 0xFF, + (uint8_t) 0xFF, (uint8_t) 0xFF)); + + b128.m128 = _mm_and_si128(epx.m128, _mm_set1_epi8(0x7)); + b128.m128 = _mm_shuffle_epi8(m128mask, b128.m128); + + ipx.m128 = _mm_srli_epi16(ipx.m128, 3); + ipx2.m128 = _mm_srli_epi16(ipx2.m128, 3); + + a128.m128 = _mm_set_epi8(input[ipx2.v.i0], + input[ipx2.v.i1], + input[ipx2.v.i2], + input[ipx2.v.i3], + input[ipx2.v.i4], + input[ipx2.v.i5], + input[ipx2.v.i6], + input[ipx2.v.i7], + input[ipx.v.i0], + input[ipx.v.i1], + input[ipx.v.i2], + input[ipx.v.i3], + input[ipx.v.i4], + input[ipx.v.i5], + input[ipx.v.i6], + input[ipx.v.i7]); + + c128.m128 = _mm_cmpeq_epi8(_mm_and_si128(a128.m128, b128.m128), b128.m128); + uint16_t o = (uint16_t) _mm_movemask_epi8(c128.m128); + *((uint16_t *) (output + i)) = o; + } + + for (; i < nof_bits / 8; i++) { + ipx.m128 = _mm_loadu_si128((__m128i *) (interleaver + i * 8 - w_offset_p)); + epx.m128 = _mm_shuffle_epi8(ipx.m128, _mm_set_epi8(0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E)); + b.m64 = _mm_and_si64(epx.m64.reg_a, _mm_set1_pi8(0x7)); + b.m64 = _mm_shuffle_pi8(m64mask, b.m64); + + ipx.m128 = _mm_srli_epi16(ipx.m128, 3); + + a.m64 = _mm_set_pi8(input[ipx.v.i0], + input[ipx.v.i1], + input[ipx.v.i2], + input[ipx.v.i3], + input[ipx.v.i4], + input[ipx.v.i5], + input[ipx.v.i6], + input[ipx.v.i7]); + + c.m64 = _mm_cmpeq_pi8(_mm_and_si64(a.m64, b.m64), b.m64); + output[i] = (uint8_t) _mm_movemask_pi8(c.m64); + } + +#if 0 + /* THIS PIECE OF CODE IS FOR CHECKING SIMD BEHAVIOUR. DO NOT ENABLE. */ + uint8_t *output2 = malloc(nof_bits/8); + for (i=st;i= (CHAR_BIT - dst_offset_modulo)) { \ + *dst &= reverse_mask[dst_offset_modulo]; \ + src_len -= CHAR_BIT - dst_offset_modulo; \ + } else { \ + *dst &= reverse_mask[dst_offset_modulo] \ + | reverse_mask_xor[dst_offset_modulo + src_len]; \ + c &= reverse_mask[dst_offset_modulo + src_len]; \ + src_len = 0; \ + } } while (0) + + +static void +bitarray_copy(const unsigned char *src_org, int src_offset, int src_len, + unsigned char *dst_org, int dst_offset) +{ + static const unsigned char reverse_mask[] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + static const unsigned char reverse_mask_xor[] = + { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00 }; + + if (src_len) { + const unsigned char *src; + unsigned char *dst; + int src_offset_modulo, + dst_offset_modulo; + + src = src_org + (src_offset / CHAR_BIT); + dst = dst_org + (dst_offset / CHAR_BIT); + + src_offset_modulo = src_offset % CHAR_BIT; + dst_offset_modulo = dst_offset % CHAR_BIT; + + if (src_offset_modulo == dst_offset_modulo) { + int byte_len; + int src_len_modulo; + if (src_offset_modulo) { + unsigned char c; + + c = reverse_mask_xor[dst_offset_modulo] & *src++; + + PREPARE_FIRST_COPY(); + *dst++ |= c; + } + + byte_len = src_len / CHAR_BIT; + src_len_modulo = src_len % CHAR_BIT; + + if (byte_len) { + memcpy(dst, src, byte_len); + src += byte_len; + dst += byte_len; + } + if (src_len_modulo) { + *dst &= reverse_mask_xor[src_len_modulo]; + *dst |= reverse_mask[src_len_modulo] & *src; + } + } else { + int bit_diff_ls, + bit_diff_rs; + int byte_len; + int src_len_modulo; + unsigned char c; + /* + * Begin: Line things up on destination. + */ + if (src_offset_modulo > dst_offset_modulo) { + bit_diff_ls = src_offset_modulo - dst_offset_modulo; + bit_diff_rs = CHAR_BIT - bit_diff_ls; + + c = *src++ << bit_diff_ls; + c |= *src >> bit_diff_rs; + c &= reverse_mask_xor[dst_offset_modulo]; + } else { + bit_diff_rs = dst_offset_modulo - src_offset_modulo; + bit_diff_ls = CHAR_BIT - bit_diff_rs; + + c = *src >> bit_diff_rs & + reverse_mask_xor[dst_offset_modulo]; + } + PREPARE_FIRST_COPY(); + *dst++ |= c; + + /* + * Middle: copy with only shifting the source. + */ + byte_len = src_len / CHAR_BIT; + + while (--byte_len >= 0) { + c = *src++ << bit_diff_ls; + c |= *src >> bit_diff_rs; + *dst++ = c; + } + + /* + * End: copy the remaing bits; + */ + src_len_modulo = src_len % CHAR_BIT; + if (src_len_modulo) { + c = *src++ << bit_diff_ls; + c |= *src >> bit_diff_rs; + c &= reverse_mask[src_len_modulo]; + + *dst &= reverse_mask_xor[src_len_modulo]; + *dst |= c; + } + } + } +} + +/** + * Copy bits from src to dst, with offsets and length in bits + * + * @param[out] dst Output array + * @param[in] src Input array + * @param dst_offset Output array write offset in bits + * @param src_offset Input array read offset in bits + * @param nof_bits Number of bits to copy + */ +void srslte_bit_copy(uint8_t *dst, uint32_t dst_offset, uint8_t *src, uint32_t src_offset, uint32_t nof_bits) +{ + static const uint8_t mask_dst[] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + if ((dst_offset%8) == 0 && (src_offset%8) == 0) { + // copy rest of words + memcpy(&dst[dst_offset/8], &src[src_offset/8], nof_bits/8); + // copy last word + if (nof_bits%8) { + dst[dst_offset/8+nof_bits/8] = src[src_offset/8+nof_bits/8] & mask_dst[nof_bits%8]; + } + } else { + bitarray_copy(src, src_offset, nof_bits, dst, dst_offset); + } +} + +void srslte_bit_unpack_vector(uint8_t *packed, uint8_t *unpacked, int nof_bits) +{ + uint32_t i, nbytes; + nbytes = nof_bits/8; + for (i=0;i>(8-nof_bits%8), &unpacked, nof_bits%8); + } +} + +void srslte_bit_unpack_l(uint64_t value, uint8_t **bits, int nof_bits) +{ + int i; + + for(i=0; i> (nof_bits-i-1)) & 0x1; + } + *bits += nof_bits; +} + +/** + * Unpacks nof_bits from LSBs of value in MSB order to *bits. Advances pointer past unpacked bits. + * + * @param[in] value nof_bits lowest order bits will be unpacked in MSB order + * @param[in] nof_bits Number of bits to unpack + * @param[out] bits Points to buffer pointer. The buffer pointer will be advanced by nof_bits + */ +void srslte_bit_unpack(uint32_t value, uint8_t **bits, int nof_bits) +{ + int i; + + for(i=0; i> (nof_bits-i-1)) & 0x1; + } + *bits += nof_bits; +} + +void srslte_bit_pack_vector(uint8_t *unpacked, uint8_t *packed, int nof_bits) +{ + uint32_t i, nbytes; + nbytes = nof_bits/8; + for (i=0;i +#include +#include +#include +#include + +#include "srslte/phy/utils/cexptab.h" + +int srslte_cexptab_init(srslte_cexptab_t *h, uint32_t size) { + uint32_t i; + + h->size = size; + h->tab = malloc(sizeof(cf_t) * (1+size)); + if (h->tab) { + for (i = 0; i < size; i++) { + h->tab[i] = cexpf(_Complex_I * 2 * M_PI * (float) i / size); + } + return SRSLTE_SUCCESS; + } else { + return SRSLTE_ERROR; + } +} + +void srslte_cexptab_free(srslte_cexptab_t *h) { + if (h->tab) { + free(h->tab); + } + bzero(h, sizeof(srslte_cexptab_t)); +} + +void srslte_cexptab_gen(srslte_cexptab_t *h, cf_t *x, float freq, uint32_t len) { + uint32_t i; + uint32_t idx; + float phase_inc = freq * h->size; + float phase=0; + + for (i = 0; i < len; i++) { + while (phase >= (float) h->size) { + phase -= (float) h->size; + } + while (phase < 0) { + phase += (float) h->size; + } + idx = (uint32_t) phase; + x[i] = h->tab[idx]; + phase += phase_inc; + + } +} + +void srslte_cexptab_gen_direct(cf_t *x, float freq, uint32_t len) { + uint32_t i; + for (i = 0; i < len; i++) { + x[i] = cexpf(_Complex_I * 2 * M_PI * freq * i); + } +} + diff --git a/lib/src/phy/utils/convolution.c b/lib/src/phy/utils/convolution.c new file mode 100644 index 0000000..59807ad --- /dev/null +++ b/lib/src/phy/utils/convolution.c @@ -0,0 +1,231 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/phy/dft/dft.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/convolution.h" + + +int srslte_conv_fft_cc_init(srslte_conv_fft_cc_t *q, uint32_t input_len, uint32_t filter_len) { + bzero(q, sizeof(srslte_conv_fft_cc_t)); + + q->input_len = input_len; + q->filter_len = filter_len; + q->output_len = input_len+filter_len; + q->max_filter_len = filter_len; + q->max_input_len = input_len; + q->input_fft = srslte_vec_malloc(sizeof(cf_t)*q->output_len); + q->filter_fft = srslte_vec_malloc(sizeof(cf_t)*q->output_len); + q->output_fft = srslte_vec_malloc(sizeof(cf_t)*q->output_len); + + if (!q->input_fft || !q->filter_fft || !q->output_fft) { + return SRSLTE_ERROR; + } + if (srslte_dft_plan(&q->input_plan,q->output_len,SRSLTE_DFT_FORWARD,SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error initiating input plan\n"); + return SRSLTE_ERROR; + } + if (srslte_dft_plan(&q->filter_plan,q->output_len,SRSLTE_DFT_FORWARD,SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error initiating filter plan\n"); + return SRSLTE_ERROR; + } + if (srslte_dft_plan(&q->output_plan,q->output_len,SRSLTE_DFT_BACKWARD,SRSLTE_DFT_COMPLEX)) { + fprintf(stderr, "Error initiating output plan\n"); + return SRSLTE_ERROR; + } + srslte_dft_plan_set_norm(&q->input_plan, true); + srslte_dft_plan_set_norm(&q->filter_plan, true); + srslte_dft_plan_set_norm(&q->output_plan, false); + + return SRSLTE_SUCCESS; +} + +int srslte_conv_fft_cc_replan(srslte_conv_fft_cc_t *q, uint32_t input_len, uint32_t filter_len) { + if (input_len > q->max_input_len || filter_len > q->max_filter_len) { + fprintf(stderr, "Error in conv_fft_cc_replan(): input_len and filter_len must be lower than initialized\n"); + return -1; + } + + q->input_len = input_len; + q->filter_len = filter_len; + q->output_len = input_len+filter_len; + + if (!q->input_fft || !q->filter_fft || !q->output_fft) { + return SRSLTE_ERROR; + } + if (srslte_dft_replan(&q->input_plan,q->output_len)) { + fprintf(stderr, "Error initiating input plan\n"); + return SRSLTE_ERROR; + } + if (srslte_dft_replan(&q->filter_plan,q->output_len)) { + fprintf(stderr, "Error initiating filter plan\n"); + return SRSLTE_ERROR; + } + if (srslte_dft_replan(&q->output_plan,q->output_len)) { + fprintf(stderr, "Error initiating output plan\n"); + return SRSLTE_ERROR; + } + return SRSLTE_SUCCESS; +} + +void srslte_conv_fft_cc_free(srslte_conv_fft_cc_t *q) { + if (q->input_fft) { + free(q->input_fft); + } + if (q->filter_fft) { + free(q->filter_fft); + } + if (q->output_fft) { + free(q->output_fft); + } + + srslte_dft_plan_free(&q->input_plan); + srslte_dft_plan_free(&q->filter_plan); + srslte_dft_plan_free(&q->output_plan); + + bzero(q, sizeof(srslte_conv_fft_cc_t)); + +} + +uint32_t srslte_conv_fft_cc_run_opt(srslte_conv_fft_cc_t *q, const cf_t *input, const cf_t *filter_freq, cf_t *output) +{ + srslte_dft_run_c(&q->input_plan, input, q->input_fft); + srslte_vec_prod_ccc(q->input_fft, filter_freq, q->output_fft, q->output_len); + srslte_dft_run_c(&q->output_plan, q->output_fft, output); + + return (q->output_len-1); // divide output length by dec factor + +} + +uint32_t srslte_conv_fft_cc_run(srslte_conv_fft_cc_t *q, const cf_t *input, const cf_t *filter, cf_t *output) { + + srslte_dft_run_c(&q->filter_plan, filter, q->filter_fft); + + return srslte_conv_fft_cc_run_opt(q, input, q->filter_fft, output); + +} + +uint32_t srslte_conv_cc(const cf_t *input, const cf_t *filter, cf_t *output, uint32_t input_len, uint32_t filter_len) { + uint32_t i; + uint32_t M = filter_len; + uint32_t N = input_len; + + for (i=0;i=M-1) { + last[i] = (2+i-M/2)*input[N-1]-(1+i-M/2)*input[N-2]; + } else { + last[i] = input[N-M+i+1]; + } + } + + for (i=0;i +#include +#include +#include +#include + +#include "srslte/phy/utils/debug.h" +#include "srslte/version.h" + +int srslte_verbose = 0; +int handler_registered = 0; + +void get_time_interval(struct timeval * tdata) { + + tdata[0].tv_sec = tdata[2].tv_sec - tdata[1].tv_sec; + tdata[0].tv_usec = tdata[2].tv_usec - tdata[1].tv_usec; + if (tdata[0].tv_usec < 0) { + tdata[0].tv_sec--; + tdata[0].tv_usec += 1000000; + } +} + +const static char crash_file_name[] = "./srsLTE.backtrace.crash"; +static int bt_argc; +static char **bt_argv; + +static void crash_handler(int sig) { + void *array[128]; + int size; + + /* Get all stack traces */ + size = backtrace(array, 128); + + FILE *f = fopen(crash_file_name, "a"); + if (!f) { + printf("srsLTE crashed... we could not save backtrace in '%s'...\n", crash_file_name); + } else { + char **symbols = backtrace_symbols(array, size); + + time_t lnTime; + struct tm *stTime; + char strdate[32]; + + time(&lnTime); + stTime = localtime(&lnTime); + + strftime(strdate, 32, "%d/%m/%Y %H:%M:%S", stTime); + + fprintf(f, "--- command='"); + for (int i = 0; i < bt_argc; i++) { + fprintf(f, "%s%s", (i == 0) ? "" : " ", bt_argv[i]); + } + fprintf(f, "' version=%s signal=%d date='%s' ---\n", SRSLTE_VERSION_STRING, sig, strdate); + + for (int i = 0; i < size; i++) { + fprintf(f, "\t%s\n", symbols[i]); + } + fprintf(f, "\n"); + + printf("srsLTE crashed... backtrace saved in '%s'...\n", crash_file_name); + fclose(f); + } + printf("--- exiting ---\n"); + exit(1); +} + +void srslte_debug_handle_crash(int argc, char **argv) { + bt_argc = argc; + bt_argv = argv; + + signal(SIGSEGV, crash_handler); + signal(SIGABRT, crash_handler); + signal(SIGILL, crash_handler); + signal(SIGFPE, crash_handler); +} diff --git a/lib/src/phy/utils/filter.c b/lib/src/phy/utils/filter.c new file mode 100644 index 0000000..7fa9635 --- /dev/null +++ b/lib/src/phy/utils/filter.c @@ -0,0 +1,126 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 "srslte/phy/utils/filter.h" +#define SRSLTE_NUM_FILTERS 8 +#define SRSLTE_MAX_FILTER_SIZE 11 + +float srslte_filt_decim2[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + {0.0167364016736, 0.48326359832636, 0.48326359832636, 0.01673640167364,0,0,0,0,0,0,0}, + {0.000000000000000, 0.203712369200737, 0.592575261598526, 0.203712369200737, 0.000000000000000,0,0,0,0,0,0}, + {-0.007776312719103, 0.064454645578710, 0.443321667140393, 0.443321667140393, 0.064454645578710, -0.007776312719103,0,0,0,0,0}, + {-0.008721828105097, 0.000000000000000, 0.251842786534672, 0.513758083140849, 0.251842786534672, 0.000000000000000, -0.008721828105097,0,0,0,0}, + {-0.005164298061200, -0.022882524920256, 0.096755650536968, 0.431291172444487, 0.431291172444487, 0.096755650536968, -0.022882524920256, -0.005164298061200,0,0,0}, + {-0.000000000000000, -0.022663985459553, 0.000000000000000, 0.273977082565524, 0.497373805788057, 0.273977082565524, 0.000000000000000, -0.022663985459553, -0.000000000000000,0,0}, + { 0.003971846362414, -0.011976365116169, -0.041119498116286, 0.114687063714704, 0.434436953155337, 0.434436953155337, 0.114687063714704, -0.041119498116286, -0.011976365116169, 0.003971846362414,0}, + {0.005060317124845, -0.000000000000000, -0.041942879431345, 0.000000000000000, 0.288484826302638, 0.496795472007725, 0.288484826302638, 0.000000000000000, -0.041942879431345, -0.000000000000000, 0.005060317124845} + }; + +float srslte_filt_decim3[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + {0.032388663967611, 0.467611336032389, 0.467611336032389, 0.032388663967611,0,0,0,0,0,0,0}, + {0.016883339167609, 0.227925078762723, 0.510383164139335, 0.227925078762723, 0.016883339167609,0,0,0,0,0,0}, + {0.006703633822959, 0.111127306155495, 0.382169060021546, 0.382169060021546, 0.111127306155495, 0.006703633822959,0,0,0,0,0}, + {0.000000000000000, 0.050666848023938, 0.251699825667307, 0.395266652617510, 0.251699825667307, 0.050666848023938, 0.000000000000000,0,0,0,0}, + {-0.004018779518049, 0.017806838679915, 0.150587600493065, 0.335624340345069, 0.335624340345069, 0.150587600493065, 0.017806838679915, -0.004018779518049,0,0,0}, + {-0.005814396641997, 0.000000000000000, 0.078494354666956, 0.251550893097387, 0.351538297755307, 0.251550893097387, 0.078494354666956, 0.000000000000000, -0.005814396641997,0,0}, + { -0.005798226803038, -0.008741738083915, 0.030013771222565, 0.167423798937736, 0.317102394726653, 0.317102394726653, 0.167423798937736, 0.030013771222565, -0.008741738083915, -0.005798226803038,0}, + {-0.004444793932295, -0.011657318166992, 0.000000000000000, 0.094750202492597, 0.253394317761931, 0.335915183689516, 0.253394317761931, 0.094750202492597, 0.000000000000000, -0.011657318166992, -0.004444793932295}, + +}; + + +float srslte_filt_decim4[SRSLTE_NUM_FILTERS][SRSLTE_MAX_FILTER_SIZE] = +{ + { 0.038579006748772, 0.461420993251228, 0.461420993251228, 0.038579006748772,0,0,0,0,0,0,0}, + {0.024553834015017, 0.234389464237986, 0.482113403493995, 0.234389464237986, 0.024553834015017,0,0,0,0,0,0}, + {0.015196373491712, 0.125956465856097, 0.358847160652191, 0.358847160652191, 0.125956465856097, 0.015196373491712,0,0,0,0,0}, + {0.008485920061584, 0.069755250084282, 0.245030941778248, 0.353455776151771, 0.245030941778248, 0.069755250084282, 0.008485920061584,0,0,0,0}, + {0.003560172702629, 0.038083722795699, 0.161031852333115, 0.297324252168557, 0.297324252168557, 0.161031852333115, 0.038083722795699, 0.003560172702629,0,0,0}, + {0.000000000000000, 0.019096925170212, 0.101875313412667, 0.230856124287772, 0.296343274258697, 0.230856124287772, 0.101875313412667, 0.019096925170212, 0.000000000000000,0,0}, + {-0.002426023829880, 0.007315224335493, 0.060635381185575, 0.169119131895270, 0.265356286413542, 0.265356286413542, 0.169119131895270, 0.060635381185575 , 0.007315224335493, -0.002426023829880,0}, + {-0.003871323167475, 0.000000000000000, 0.032087799410030, 0.116708621643743, 0.220701186106900, 0.268747432013603, 0.220701186106900, 0.116708621643743 , 0.032087799410030, 0.000000000000000,-0.003871323167475} +}; + + +void srslte_filt_decim_cc_init(srslte_filt_cc_t *q, int factor, int order) +{ + q->factor = factor; + q->num_taps = order + 1; + q->is_decimator = true; + q->taps = malloc(q->num_taps * sizeof(float)); + + switch(q->factor) + { + case 2: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim2[(q->num_taps) - 4][i]; + break; + case 3: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim3[(q->num_taps) - 4][i]; + break; + case 4: + for(int i = 0; i <(q->num_taps); i++) + q->taps[i] = srslte_filt_decim4[(q->num_taps) - 4][i]; + break; + default: + + break; + } + + for(int x = 0; x<(q->num_taps);x++) + { + printf("tap : %f.9\n" ,q->taps[x]); + } +} + +void srslte_filt_decim_cc_free(srslte_filt_cc_t *q) +{ + free(q->taps); +} + +void srslte_filt_decim_cc_execute(srslte_filt_cc_t *q, cf_t *input, cf_t *downsampled_input, cf_t *output, int size) +{ + // we assume that "downsampled_input" made size (input/2 + order) so as to have prepended zeros // + srslte_downsample_cc(input, downsampled_input + (q->num_taps - 1), q->factor, size); + + for(int i = 0;i < size/q->factor;i++) + { + output[i] = srslte_vec_dot_prod_cfc(&(downsampled_input[i]), q->taps, q->num_taps); + } + + +} + +/* Performs integer linear downsamling by a factor of M */ +void srslte_downsample_cc(cf_t *input, cf_t *output, int M, int size) { + int i; + for (i=0;i +#include + +#include +#include "srslte/phy/utils/mat.h" + + +/* Generic implementation for complex reciprocal */ +inline cf_t srslte_mat_cf_recip_gen(cf_t a) { + return conjf(a) / (crealf(a) * crealf(a) + cimagf(a) * cimagf(a)); +} + +/* Generic implementation for 2x2 determinant */ +inline cf_t srslte_mat_2x2_det_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11) { + return a00 * a11 - a01 * a10; +} + +/* 2x2 Matrix inversion, generic implementation */ +inline void srslte_mat_2x2_inv_gen(cf_t a00, cf_t a01, cf_t a10, cf_t a11, + cf_t *r00, cf_t *r01, cf_t *r10, cf_t *r11) { + cf_t div = srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(a00, a01, a10, a11)); + *r00 = a11 * div; + *r01 = -a01 * div; + *r10 = -a10 * div; + *r11 = a00 * div; +} + +/* Generic implementation for Zero Forcing (ZF) solver */ +inline void srslte_mat_2x2_zf_gen(cf_t y0, cf_t y1, cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, float norm) { + cf_t _norm = srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(h00, h01, h10, h11)) * norm; + *x0 = (y0 * h11 - h01 * y1) * _norm; + *x1 = (y1 * h00 - h10 * y0) * _norm; +} + +/* Generic implementation for Minimum Mean Squared Error (MMSE) solver */ +inline void srslte_mat_2x2_mmse_csi_gen(cf_t y0, cf_t y1, cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, float *csi0, float *csi1, float noise_estimate, float norm) { + /* Create conjugated matrix */ + cf_t _h00 = conjf(h00); + cf_t _h01 = conjf(h01); + cf_t _h10 = conjf(h10); + cf_t _h11 = conjf(h11); + + /* 1. A = H' x H + No*/ + cf_t a00 = _h00 * h00 + _h10 * h10 + noise_estimate; + cf_t a01 = _h00 * h01 + _h10 * h11; + cf_t a10 = _h01 * h00 + _h11 * h10; + cf_t a11 = _h01 * h01 + _h11 * h11 + noise_estimate; + cf_t a_det_rcp = srslte_mat_cf_recip_gen(srslte_mat_2x2_det_gen(a00, a01, a10, a11)); + + /* 2. B = inv(H' x H + No) = inv(A) */ + cf_t _norm = norm * a_det_rcp; + cf_t b00 = a11 * _norm; + cf_t b01 = -a01 * _norm; + cf_t b10 = -a10 * _norm; + cf_t b11 = a00 * _norm; + + /* 3. W = inv(H' x H + No) x H' = B x H' */ + cf_t w00 = b00 * _h00 + b01 * _h01; + cf_t w01 = b00 * _h10 + b01 * _h11; + cf_t w10 = b10 * _h00 + b11 * _h01; + cf_t w11 = b10 * _h10 + b11 * _h11; + + /* 4. X = W x Y */ + *x0 = (y0 * w00 + y1 * w01); + *x1 = (y0 * w10 + y1 * w11); + + /* 5. Set CSI */ + *csi0 = 1.0f / crealf(b00); + *csi1 = 1.0f / crealf(b11); +} + +/* Generic implementation for Minimum Mean Squared Error (MMSE) solver */ +void srslte_mat_2x2_mmse_gen(cf_t y0, cf_t y1, cf_t h00, cf_t h01, cf_t h10, cf_t h11, + cf_t *x0, cf_t *x1, float noise_estimate, float norm) { + float csi0, csi1; + srslte_mat_2x2_mmse_csi_gen(y0, y1, h00, h01, h10, h11, x0, x1, &csi0, &csi1, noise_estimate, norm); +} + +inline float srslte_mat_2x2_cn(cf_t h00, cf_t h01, cf_t h10, cf_t h11) { + /* 1. A = H * H' (A = A') */ + float a00 = + crealf(h00) * crealf(h00) + crealf(h01) * crealf(h01) + cimagf(h00) * cimagf(h00) + cimagf(h01) * cimagf(h01); + cf_t a01 = h00 * conjf(h10) + h01 * conjf(h11); + //cf_t a10 = h10*conjf(h00) + h11*conjf(h01) = conjf(a01); + float a11 = + crealf(h10) * crealf(h10) + crealf(h11) * crealf(h11) + cimagf(h10) * cimagf(h10) + cimagf(h11) * cimagf(h11); + + /* 2. |H * H' - {λ0, λ1}| = 0 -> aλ² + bλ + c = 0 */ + float b = a00 + a11; + float c = a00 * a11 - (crealf(a01) * crealf(a01) + cimagf(a01) * cimagf(a01)); + + /* 3. λ = (-b ± sqrt(b² - 4 * c))/2 */ + float sqr = sqrtf(b * b - 4.0f * c); + float xmax = b + sqr; + float xmin = b - sqr; + + /* 4. κ = sqrt(λ_max / λ_min) */ + return 10 * log10f(xmax / xmin); +} + +#ifdef LV_HAVE_SSE +#include + +/* SSE implementation for complex reciprocal */ +inline __m128 srslte_mat_cf_recip_sse(__m128 a) { + __m128 conj = _MM_CONJ_PS(a); + __m128 sqabs = _mm_mul_ps(a, a); + sqabs = _mm_add_ps(_mm_movehdup_ps(sqabs), _mm_moveldup_ps(sqabs)); + + __m128 recp = _mm_rcp_ps(sqabs); + + return _mm_mul_ps(recp, conj); +} + +/* SSE implementation for 2x2 determinant */ +inline __m128 srslte_mat_2x2_det_sse(__m128 a00, __m128 a01, __m128 a10, __m128 a11) { + return _mm_sub_ps(_MM_PROD_PS(a00, a11), _MM_PROD_PS(a01, a10)); +} + +/* SSE implementation for Zero Forcing (ZF) solver */ +inline void srslte_mat_2x2_zf_sse(__m128 y0, __m128 y1, __m128 h00, __m128 h01, __m128 h10, __m128 h11, + __m128 *x0, __m128 *x1, float norm) { + __m128 detmult1 = _MM_PROD_PS(h00, h11); + __m128 detmult2 = _MM_PROD_PS(h01, h10); + + __m128 det = _mm_sub_ps(detmult1, detmult2); + __m128 detrec = _mm_mul_ps(srslte_mat_cf_recip_sse(det), _mm_set1_ps(norm)); + + *x0 = _MM_PROD_PS(_mm_sub_ps(_MM_PROD_PS(h11, y0), _MM_PROD_PS(h01, y1)), detrec); + *x1 = _MM_PROD_PS(_mm_sub_ps(_MM_PROD_PS(h00, y1), _MM_PROD_PS(h10, y0)), detrec); +} + +/* SSE implementation for Minimum Mean Squared Error (MMSE) solver */ +inline void srslte_mat_2x2_mmse_sse(__m128 y0, __m128 y1, __m128 h00, __m128 h01, __m128 h10, __m128 h11, + __m128 *x0, __m128 *x1, float noise_estimate, float norm) { + __m128 _noise_estimate = _mm_set_ps(0.0f, noise_estimate, 0.0f, noise_estimate); + __m128 _norm = _mm_set1_ps(norm); + + /* Create conjugated matrix */ + __m128 _h00 = _MM_CONJ_PS(h00); + __m128 _h01 = _MM_CONJ_PS(h01); + __m128 _h10 = _MM_CONJ_PS(h10); + __m128 _h11 = _MM_CONJ_PS(h11); + + /* 1. A = H' x H + No*/ + __m128 a00 = _mm_add_ps(_mm_add_ps(_MM_SQMOD_PS(h00), _MM_SQMOD_PS(h10)), _noise_estimate); + __m128 a01 = _mm_add_ps(_MM_PROD_PS(_h00, h01), _MM_PROD_PS(_h10, h11)); + __m128 a10 = _mm_add_ps(_MM_PROD_PS(_h01, h00), _MM_PROD_PS(_h11, h10)); + __m128 a11 = _mm_add_ps(_mm_add_ps(_MM_SQMOD_PS(h01), _MM_SQMOD_PS(h11)), _noise_estimate); + + /* 2. B = inv(H' x H + No) = inv(A) */ + __m128 b00 = a11; + __m128 b01 = _mm_xor_ps(a01, _mm_set1_ps(-0.0f)); + __m128 b10 = _mm_xor_ps(a10, _mm_set1_ps(-0.0f)); + __m128 b11 = a00; + _norm = _mm_mul_ps(_norm, srslte_mat_cf_recip_sse(srslte_mat_2x2_det_sse(a00, a01, a10, a11))); + + + /* 3. W = inv(H' x H + No) x H' = B x H' */ + __m128 w00 = _mm_add_ps(_MM_PROD_PS(b00, _h00), _MM_PROD_PS(b01, _h01)); + __m128 w01 = _mm_add_ps(_MM_PROD_PS(b00, _h10), _MM_PROD_PS(b01, _h11)); + __m128 w10 = _mm_add_ps(_MM_PROD_PS(b10, _h00), _MM_PROD_PS(b11, _h01)); + __m128 w11 = _mm_add_ps(_MM_PROD_PS(b10, _h10), _MM_PROD_PS(b11, _h11)); + + /* 4. X = W x Y */ + *x0 = _MM_PROD_PS(_mm_add_ps(_MM_PROD_PS(y0, w00), _MM_PROD_PS(y1, w01)), _norm); + *x1 = _MM_PROD_PS(_mm_add_ps(_MM_PROD_PS(y0, w10), _MM_PROD_PS(y1, w11)), _norm); +} + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX +#include + +/* AVX implementation for complex reciprocal */ +inline __m256 srslte_mat_cf_recip_avx(__m256 a) { + __m256 conj = _MM256_CONJ_PS(a); + __m256 sqabs = _mm256_mul_ps(a, a); + sqabs = _mm256_add_ps(_mm256_movehdup_ps(sqabs), _mm256_moveldup_ps(sqabs)); + + __m256 recp = _mm256_rcp_ps(sqabs); + + return _mm256_mul_ps(recp, conj); +} + +/* AVX implementation for 2x2 determinant */ +inline __m256 srslte_mat_2x2_det_avx(__m256 a00, __m256 a01, __m256 a10, __m256 a11) { +#ifdef LV_HAVE_FMA + return _MM256_PROD_SUB_PS(a00, a11, _MM256_PROD_PS(a01, a10)); +#else + return _mm256_sub_ps(_MM256_PROD_PS(a00, a11), _MM256_PROD_PS(a01, a10)); +#endif /* LV_HAVE_FMA */ +} + +/* AVX implementation for Zero Forcing (ZF) solver */ +inline void srslte_mat_2x2_zf_avx(__m256 y0, __m256 y1, __m256 h00, __m256 h01, __m256 h10, __m256 h11, + __m256 *x0, __m256 *x1, float norm) { + + __m256 det = srslte_mat_2x2_det_avx(h00, h01, h10, h11); + __m256 detrec = _mm256_mul_ps(srslte_mat_cf_recip_avx(det), _mm256_set1_ps(norm)); + +#ifdef LV_HAVE_FMA + *x0 = _MM256_PROD_PS(_MM256_PROD_SUB_PS(h11, y0, _MM256_PROD_PS(h01, y1)), detrec); + *x1 = _MM256_PROD_PS(_MM256_PROD_SUB_PS(h00, y1, _MM256_PROD_PS(h10, y0)), detrec); +#else + *x0 = _MM256_PROD_PS(_mm256_sub_ps(_MM256_PROD_PS(h11, y0), _MM256_PROD_PS(h01, y1)), detrec); + *x1 = _MM256_PROD_PS(_mm256_sub_ps(_MM256_PROD_PS(h00, y1), _MM256_PROD_PS(h10, y0)), detrec); +#endif /* LV_HAVE_FMA */ +} + +/* AVX implementation for Minimum Mean Squared Error (MMSE) solver */ +inline void srslte_mat_2x2_mmse_avx(__m256 y0, __m256 y1, __m256 h00, __m256 h01, __m256 h10, __m256 h11, + __m256 *x0, __m256 *x1, float noise_estimate, float norm) { + __m256 _noise_estimate = _mm256_set_ps(0.0f, noise_estimate, 0.0f, noise_estimate, + 0.0f, noise_estimate, 0.0f, noise_estimate); + __m256 _norm = _mm256_set1_ps(norm); + + /* Create conjugated matrix */ + __m256 _h00 = _MM256_CONJ_PS(h00); + __m256 _h01 = _MM256_CONJ_PS(h01); + __m256 _h10 = _MM256_CONJ_PS(h10); + __m256 _h11 = _MM256_CONJ_PS(h11); + + /* 1. A = H' x H + No*/ +#ifdef LV_HAVE_FMA + __m256 a00 = _MM256_SQMOD_ADD_PS(h00, h10, _noise_estimate); + __m256 a01 = _MM256_PROD_ADD_PS(_h00, h01, _MM256_PROD_PS(_h10, h11)); + __m256 a10 = _MM256_PROD_ADD_PS(_h01, h00, _MM256_PROD_PS(_h11, h10)); + __m256 a11 = _MM256_SQMOD_ADD_PS(h01, h11, _noise_estimate); +#else + __m256 a00 = _mm256_add_ps(_MM256_SQMOD_PS(h00, h10), _noise_estimate); + __m256 a01 = _mm256_add_ps(_MM256_PROD_PS(_h00, h01), _MM256_PROD_PS(_h10, h11)); + __m256 a10 = _mm256_add_ps(_MM256_PROD_PS(_h01, h00), _MM256_PROD_PS(_h11, h10)); + __m256 a11 = _mm256_add_ps(_MM256_SQMOD_PS(h01, h11), _noise_estimate); +#endif /* LV_HAVE_FMA */ + + /* 2. B = inv(H' x H + No) = inv(A) */ + __m256 b00 = a11; + __m256 b01 = _mm256_xor_ps(a01, _mm256_set1_ps(-0.0f)); + __m256 b10 = _mm256_xor_ps(a10, _mm256_set1_ps(-0.0f)); + __m256 b11 = a00; + _norm = _mm256_mul_ps(_norm, srslte_mat_cf_recip_avx(srslte_mat_2x2_det_avx(a00, a01, a10, a11))); + + + /* 3. W = inv(H' x H + No) x H' = B x H' */ +#ifdef LV_HAVE_FMA + __m256 w00 = _MM256_PROD_ADD_PS(b00, _h00, _MM256_PROD_PS(b01, _h01)); + __m256 w01 = _MM256_PROD_ADD_PS(b00, _h10, _MM256_PROD_PS(b01, _h11)); + __m256 w10 = _MM256_PROD_ADD_PS(b10, _h00, _MM256_PROD_PS(b11, _h01)); + __m256 w11 = _MM256_PROD_ADD_PS(b10, _h10, _MM256_PROD_PS(b11, _h11)); +#else + __m256 w00 = _mm256_add_ps(_MM256_PROD_PS(b00, _h00), _MM256_PROD_PS(b01, _h01)); + __m256 w01 = _mm256_add_ps(_MM256_PROD_PS(b00, _h10), _MM256_PROD_PS(b01, _h11)); + __m256 w10 = _mm256_add_ps(_MM256_PROD_PS(b10, _h00), _MM256_PROD_PS(b11, _h01)); + __m256 w11 = _mm256_add_ps(_MM256_PROD_PS(b10, _h10), _MM256_PROD_PS(b11, _h11)); +#endif /* LV_HAVE_FMA */ + + /* 4. X = W x Y */ +#ifdef LV_HAVE_FMA + *x0 = _MM256_PROD_PS(_MM256_PROD_ADD_PS(y0, w00, _MM256_PROD_PS(y1, w01)), _norm); + *x1 = _MM256_PROD_PS(_MM256_PROD_ADD_PS(y0, w10, _MM256_PROD_PS(y1, w11)), _norm); +#else + *x0 = _MM256_PROD_PS(_mm256_add_ps(_MM256_PROD_PS(y0, w00), _MM256_PROD_PS(y1, w01)), _norm); + *x1 = _MM256_PROD_PS(_mm256_add_ps(_MM256_PROD_PS(y0, w10), _MM256_PROD_PS(y1, w11)), _norm); +#endif /* LV_HAVE_FMA */ +} + +#endif /* LV_HAVE_AVX */ diff --git a/lib/src/phy/utils/ringbuffer.c b/lib/src/phy/utils/ringbuffer.c new file mode 100644 index 0000000..02b98a4 --- /dev/null +++ b/lib/src/phy/utils/ringbuffer.c @@ -0,0 +1,117 @@ + +#include +#include + +#include "srslte/phy/utils/ringbuffer.h" +#include "srslte/phy/utils/vector.h" + +int srslte_ringbuffer_init(srslte_ringbuffer_t *q, int capacity) +{ + q->buffer = srslte_vec_malloc(capacity); + if (!q->buffer) { + return -1; + } + q->active = true; + q->capacity = capacity; + pthread_mutex_init(&q->mutex, NULL); + pthread_cond_init(&q->cvar, NULL); + srslte_ringbuffer_reset(q); + + return 0; +} + +void srslte_ringbuffer_free(srslte_ringbuffer_t *q) +{ + if (q) { + srslte_ringbuffer_stop(q); + if (q->buffer) { + free(q->buffer); + q->buffer = NULL; + } + pthread_mutex_destroy(&q->mutex); + pthread_cond_destroy(&q->cvar); + } +} + +void srslte_ringbuffer_reset(srslte_ringbuffer_t *q) +{ + pthread_mutex_lock(&q->mutex); + q->count = 0; + q->wpm = 0; + q->rpm = 0; + pthread_mutex_unlock(&q->mutex); +} + +int srslte_ringbuffer_status(srslte_ringbuffer_t *q) +{ + return q->count; +} + +int srslte_ringbuffer_space(srslte_ringbuffer_t *q) +{ + return q->capacity - q->count; +} + +int srslte_ringbuffer_write(srslte_ringbuffer_t *q, void *p, int nof_bytes) +{ + uint8_t *ptr = (uint8_t*) p; + int w_bytes = nof_bytes; + pthread_mutex_lock(&q->mutex); + if (!q->active) { + pthread_mutex_unlock(&q->mutex); + return 0; + } + if (q->count + w_bytes > q->capacity) { + w_bytes = q->capacity - q->count; + fprintf(stderr, "Buffer overrun: lost %d bytes\n", nof_bytes - w_bytes); + } + if (w_bytes > q->capacity - q->wpm) { + int x = q->capacity - q->wpm; + memcpy(&q->buffer[q->wpm], ptr, x); + memcpy(q->buffer, &ptr[x], w_bytes - x); + } else { + memcpy(&q->buffer[q->wpm], ptr, w_bytes); + } + q->wpm += w_bytes; + if (q->wpm >= q->capacity) { + q->wpm -= q->capacity; + } + q->count += w_bytes; + pthread_cond_broadcast(&q->cvar); + pthread_mutex_unlock(&q->mutex); + return w_bytes; +} + +int srslte_ringbuffer_read(srslte_ringbuffer_t *q, void *p, int nof_bytes) +{ + uint8_t *ptr = (uint8_t*) p; + pthread_mutex_lock(&q->mutex); + while(q->count < nof_bytes && q->active) { + pthread_cond_wait(&q->cvar, &q->mutex); + } + if (!q->active) { + pthread_mutex_unlock(&q->mutex); + return 0; + } + if (nof_bytes + q->rpm > q->capacity) { + int x = q->capacity - q->rpm; + memcpy(ptr, &q->buffer[q->rpm], x); + memcpy(&ptr[x], q->buffer, nof_bytes - x); + } else { + memcpy(ptr, &q->buffer[q->rpm], nof_bytes); + } + q->rpm += nof_bytes; + if (q->rpm >= q->capacity) { + q->rpm -= q->capacity; + } + q->count -= nof_bytes; + pthread_mutex_unlock(&q->mutex); + return nof_bytes; +} + +void srslte_ringbuffer_stop(srslte_ringbuffer_t *q) { + pthread_mutex_lock(&q->mutex); + pthread_cond_broadcast(&q->cvar); + pthread_mutex_unlock(&q->mutex); +} + diff --git a/lib/src/phy/utils/test/CMakeLists.txt b/lib/src/phy/utils/test/CMakeLists.txt new file mode 100644 index 0000000..1f5c668 --- /dev/null +++ b/lib/src/phy/utils/test/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# DFT TEST +######################################################################## + +add_executable(dft_test dft_test.c) +target_link_libraries(dft_test srslte_phy) + +add_test(dft_test dft_test) +add_test(dft_reverse dft_test -b) # Backwards first +add_test(dft_mirror dft_test -m) # Mirror the freq bins +add_test(dft_norm dft_test -n) # Normalize each transform +add_test(dft_dc dft_test -b -d) # Backwards first & handle dc internally +add_test(dft_odd dft_test -N 255) # Odd-length +add_test(dft_odd_dc dft_test -N 255 -b -d) # Odd-length, backwards first, handle dc + +######################################################################## +# Algebra TEST +######################################################################## + +add_executable(algebra_test mat_test.c) +target_link_libraries(algebra_test srslte_phy) + +add_test(algebra_2x2_zf_solver_test algebra_test -z) +add_test(algebra_2x2_mmse_solver_test algebra_test -m) + +add_executable(vector_test vector_test.c) +target_link_libraries(vector_test srslte_phy) +add_test(vector_test vector_test) diff --git a/lib/src/phy/utils/test/dft_test.c b/lib/src/phy/utils/test/dft_test.c new file mode 100644 index 0000000..b880f80 --- /dev/null +++ b/lib/src/phy/utils/test/dft_test.c @@ -0,0 +1,165 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/dft/dft.h" + + + +int N = 256; +bool forward = true; +bool mirror = false; +bool norm = false; +bool dc = false; + +void usage(char *prog) { + printf("Usage: %s\n", prog); + printf("\t-N Transform size [Default 256]\n"); + printf("\t-b Backwards transform first [Default Forwards]\n"); + printf("\t-m Mirror the transform freq bins [Default false]\n"); + printf("\t-n Normalize the transform output [Default false]\n"); + printf("\t-d Handle insertion/removal of null DC carrier internally [Default false]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "Nbmnd")) != -1) { + switch (opt) { + case 'N': + N = atoi(argv[optind]); + break; + case 'b': + forward = false; + break; + case 'm': + mirror = true; + break; + case 'n': + norm = true; + break; + case 'd': + dc = true; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +void print(cf_t* in, int len) +{ + for(int i=0;i 0.01) + res = -1; + } + + srslte_dft_plan_free(&plan); + srslte_dft_plan_free(&plan_rev); + free(out1); + free(out2); + + return res; +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + cf_t* in = malloc(sizeof(cf_t)*N); + bzero(in, sizeof(cf_t)*N); + for(int i=1;i +#include +#include +#include +#include +#include + +#include "srslte/phy/utils/mat.h" +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/vector_simd.h" + + +bool zf_solver = false; +bool mmse_solver = false; +bool verbose = false; + +#define RANDOM_F() ((float)rand())/((float)RAND_MAX) +#define RANDOM_S() ((int16_t)(rand() && 0x800F)) +#define RANDOM_CF() (RANDOM_F() + _Complex_I*RANDOM_F()) + +double elapsed_us(struct timeval *ts_start, struct timeval *ts_end) { + if (ts_end->tv_usec > ts_start->tv_usec) { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec) * 1000000 + + (double) ts_end->tv_usec - (double) ts_start->tv_usec; + } else { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec - 1) * 1000000 + + ((double) ts_end->tv_usec + 1000000) - (double) ts_start->tv_usec; + } +} + +#define BLOCK_SIZE 1000 +#define RUN_TEST(FUNCTION) /*TYPE NAME (void)*/ { \ + int i;\ + struct timeval start, end;\ + gettimeofday(&start, NULL); \ + bool ret = true; \ + for (i = 0; i < BLOCK_SIZE; i++) {ret &= FUNCTION ();}\ + gettimeofday(&end, NULL);\ + if (verbose) printf("%32s: %s ... %6.2f us/call\n", #FUNCTION, (ret)?"Pass":"Fail", \ + elapsed_us(&start, &end)/BLOCK_SIZE);\ + passed &= ret;\ +} + +void usage(char *prog) { + printf("Usage: %s [mzvh]\n", prog); + printf("\t-m Test Minimum Mean Squared Error (MMSE) solver\n"); + printf("\t-z Test Zero Forcing (ZF) solver\n"); + printf("\t-v Verbose\n"); + printf("\t-h Show this message\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "mzvh")) != -1) { + switch (opt) { + case 'm': + mmse_solver = true; + break; + case 'z': + zf_solver = true; + break; + case 'v': + verbose = true; + break; + case 'h': + default: + usage(argv[0]); + exit(-1); + } + } +} + +bool test_zf_solver_gen(void) { + cf_t x0, x1, cf_error0, cf_error1; + float error; + + cf_t x0_gold = RANDOM_CF(); + cf_t x1_gold = RANDOM_CF(); + cf_t h00 = RANDOM_CF(); + cf_t h01 = RANDOM_CF(); + cf_t h10 = RANDOM_CF(); + cf_t h11 = (1 - h01 * h10) / h00; + cf_t y0 = x0_gold * h00 + x1_gold * h01; + cf_t y1 = x0_gold * h10 + x1_gold * h11; + + srslte_mat_2x2_zf_gen(y0, y1, h00, h01, h10, h11, &x0, &x1, 1.0f); + + cf_error0 = x0 - x0_gold; + cf_error1 = x1 - x1_gold; + error = crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-6); +} + +bool test_mmse_solver_gen(void) { + cf_t x0, x1, cf_error0, cf_error1; + float error; + + cf_t x0_gold = RANDOM_CF(); + cf_t x1_gold = RANDOM_CF(); + cf_t h00 = RANDOM_CF(); + cf_t h01 = RANDOM_CF(); + cf_t h10 = RANDOM_CF(); + cf_t h11 = (1 - h01 * h10) / h00; + cf_t y0 = x0_gold * h00 + x1_gold * h01; + cf_t y1 = x0_gold * h10 + x1_gold * h11; + + srslte_mat_2x2_mmse_gen(y0, y1, h00, h01, h10, h11, &x0, &x1, 0.0f, 1.0f); + + cf_error0 = x0 - x0_gold; + cf_error1 = x1 - x1_gold; + error = crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-6); +} + +#ifdef LV_HAVE_SSE + +bool test_zf_solver_sse(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold_1 = RANDOM_CF(); + cf_t x1_gold_1 = RANDOM_CF(); + cf_t h00_1 = RANDOM_CF(); + cf_t h01_1 = RANDOM_CF(); + cf_t h10_1 = RANDOM_CF(); + cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1; + cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1; + cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1; + + cf_t x0_gold_2 = RANDOM_CF(); + cf_t x1_gold_2 = RANDOM_CF(); + cf_t h00_2 = RANDOM_CF(); + cf_t h01_2 = RANDOM_CF(); + cf_t h10_2 = RANDOM_CF(); + cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2; + cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2; + cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2; + + __m128 _y0 = _mm_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2)); + __m128 _y1 = _mm_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2)); + + __m128 _h00 = _mm_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2)); + __m128 _h01 = _mm_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2)); + __m128 _h10 = _mm_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2)); + __m128 _h11 = _mm_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2)); + + __m128 _x0, _x1; + + srslte_mat_2x2_zf_sse(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 1.0f); + + + __attribute__((aligned(128))) cf_t x0[2]; + __attribute__((aligned(128))) cf_t x1[2]; + + _mm_store_ps((float *) x0, _x0); + _mm_store_ps((float *) x1, _x1); + + cf_error0 = x0[1] - x0_gold_1; + cf_error1 = x1[1] - x1_gold_1; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + cf_error0 = x0[0] - x0_gold_2; + cf_error1 = x1[0] - x1_gold_2; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +bool test_mmse_solver_sse(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold_1 = RANDOM_CF(); + cf_t x1_gold_1 = RANDOM_CF(); + cf_t h00_1 = RANDOM_CF(); + cf_t h01_1 = RANDOM_CF(); + cf_t h10_1 = RANDOM_CF(); + cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1; + cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1; + cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1; + + cf_t x0_gold_2 = RANDOM_CF(); + cf_t x1_gold_2 = RANDOM_CF(); + cf_t h00_2 = RANDOM_CF(); + cf_t h01_2 = RANDOM_CF(); + cf_t h10_2 = RANDOM_CF(); + cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2; + cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2; + cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2; + + __m128 _y0 = _mm_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2)); + __m128 _y1 = _mm_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2)); + + __m128 _h00 = _mm_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2)); + __m128 _h01 = _mm_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2)); + __m128 _h10 = _mm_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2)); + __m128 _h11 = _mm_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2)); + + __m128 _x0, _x1; + + srslte_mat_2x2_mmse_sse(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 0.0f, 1.0f); + + + __attribute__((aligned(128))) cf_t x0[2]; + __attribute__((aligned(128))) cf_t x1[2]; + + _mm_store_ps((float *) x0, _x0); + _mm_store_ps((float *) x1, _x1); + + cf_error0 = x0[1] - x0_gold_1; + cf_error1 = x1[1] - x1_gold_1; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + cf_error0 = x0[0] - x0_gold_2; + cf_error1 = x1[0] - x1_gold_2; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + +bool test_zf_solver_avx(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold_1 = RANDOM_CF(); + cf_t x1_gold_1 = RANDOM_CF(); + cf_t h00_1 = RANDOM_CF(); + cf_t h01_1 = RANDOM_CF(); + cf_t h10_1 = RANDOM_CF(); + cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1; + cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1; + cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1; + + cf_t x0_gold_2 = RANDOM_CF(); + cf_t x1_gold_2 = RANDOM_CF(); + cf_t h00_2 = RANDOM_CF(); + cf_t h01_2 = RANDOM_CF(); + cf_t h10_2 = RANDOM_CF(); + cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2; + cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2; + cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2; + + __m256 _y0 = _mm256_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2), + cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2)); + __m256 _y1 = _mm256_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2), + cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2)); + + __m256 _h00 = _mm256_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2), + cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2)); + __m256 _h01 = _mm256_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2), + cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2)); + __m256 _h10 = _mm256_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2), + cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2)); + __m256 _h11 = _mm256_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2), + cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2)); + + __m256 _x0, _x1; + + srslte_mat_2x2_zf_avx(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 1.0f); + + + __attribute__((aligned(256))) cf_t x0[4]; + __attribute__((aligned(256))) cf_t x1[4]; + + _mm256_store_ps((float *) x0, _x0); + _mm256_store_ps((float *) x1, _x1); + + cf_error0 = x0[1] - x0_gold_1; + cf_error1 = x1[1] - x1_gold_1; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + cf_error0 = x0[0] - x0_gold_2; + cf_error1 = x1[0] - x1_gold_2; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +bool test_mmse_solver_avx(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold_1 = RANDOM_CF(); + cf_t x1_gold_1 = RANDOM_CF(); + cf_t h00_1 = RANDOM_CF(); + cf_t h01_1 = RANDOM_CF(); + cf_t h10_1 = RANDOM_CF(); + cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1; + cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1; + cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1; + + cf_t x0_gold_2 = RANDOM_CF(); + cf_t x1_gold_2 = RANDOM_CF(); + cf_t h00_2 = RANDOM_CF(); + cf_t h01_2 = RANDOM_CF(); + cf_t h10_2 = RANDOM_CF(); + cf_t h11_2 = (1 - h01_2 * h10_2) / h00_2; + cf_t y0_2 = x0_gold_2 * h00_2 + x1_gold_2 * h01_2; + cf_t y1_2 = x0_gold_2 * h10_2 + x1_gold_2 * h11_2; + + __m256 _y0 = _mm256_set_ps(cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2), + cimagf(y0_1), crealf(y0_1), cimagf(y0_2), crealf(y0_2)); + __m256 _y1 = _mm256_set_ps(cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2), + cimagf(y1_1), crealf(y1_1), cimagf(y1_2), crealf(y1_2)); + + __m256 _h00 = _mm256_set_ps(cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2), + cimagf(h00_1), crealf(h00_1), cimagf(h00_2), crealf(h00_2)); + __m256 _h01 = _mm256_set_ps(cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2), + cimagf(h01_1), crealf(h01_1), cimagf(h01_2), crealf(h01_2)); + __m256 _h10 = _mm256_set_ps(cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2), + cimagf(h10_1), crealf(h10_1), cimagf(h10_2), crealf(h10_2)); + __m256 _h11 = _mm256_set_ps(cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2), + cimagf(h11_1), crealf(h11_1), cimagf(h11_2), crealf(h11_2)); + + __m256 _x0, _x1; + + srslte_mat_2x2_mmse_avx(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 0.0f, 1.0f); + + + __attribute__((aligned(256))) cf_t x0[4]; + __attribute__((aligned(256))) cf_t x1[4]; + + _mm256_store_ps((float *) x0, _x0); + _mm256_store_ps((float *) x1, _x1); + + cf_error0 = x0[1] - x0_gold_1; + cf_error1 = x1[1] - x1_gold_1; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + cf_error0 = x0[0] - x0_gold_2; + cf_error1 = x1[0] - x1_gold_2; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +#endif /* LV_HAVE_AVX */ + +#if SRSLTE_SIMD_CF_SIZE != 0 + +bool test_zf_solver_simd(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold_1 = RANDOM_CF(); + cf_t x1_gold_1 = RANDOM_CF(); + cf_t h00_1 = RANDOM_CF(); + cf_t h01_1 = RANDOM_CF(); + cf_t h10_1 = RANDOM_CF(); + cf_t h11_1 = (1 - h01_1 * h10_1) / h00_1; + cf_t y0_1 = x0_gold_1 * h00_1 + x1_gold_1 * h01_1; + cf_t y1_1 = x0_gold_1 * h10_1 + x1_gold_1 * h11_1; + + simd_cf_t _y0 = srslte_simd_cf_set1(y0_1); + simd_cf_t _y1 = srslte_simd_cf_set1(y1_1); + + simd_cf_t _h00 = srslte_simd_cf_set1(h00_1); + simd_cf_t _h01 = srslte_simd_cf_set1(h01_1); + simd_cf_t _h10 = srslte_simd_cf_set1(h10_1); + simd_cf_t _h11 = srslte_simd_cf_set1(h11_1); + + simd_cf_t _x0, _x1; + + srslte_mat_2x2_zf_simd(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 1.0f); + + __attribute__((aligned(SRSLTE_SIMD_BIT_ALIGN))) cf_t x0[SRSLTE_SIMD_CF_SIZE]; + __attribute__((aligned(SRSLTE_SIMD_BIT_ALIGN))) cf_t x1[SRSLTE_SIMD_CF_SIZE]; + + srslte_simd_cfi_store(x0, _x0); + srslte_simd_cfi_store(x1, _x1); + + cf_error0 = x0[1] - x0_gold_1; + cf_error1 = x1[1] - x1_gold_1; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +bool test_mmse_solver_simd(void) { + cf_t cf_error0, cf_error1; + float error = 0.0f; + + cf_t x0_gold[SRSLTE_SIMD_CF_SIZE]; + cf_t x1_gold[SRSLTE_SIMD_CF_SIZE]; + cf_t h00[SRSLTE_SIMD_CF_SIZE]; + cf_t h01[SRSLTE_SIMD_CF_SIZE]; + cf_t h10[SRSLTE_SIMD_CF_SIZE]; + cf_t h11[SRSLTE_SIMD_CF_SIZE]; + cf_t y0[SRSLTE_SIMD_CF_SIZE]; + cf_t y1[SRSLTE_SIMD_CF_SIZE]; + for (int i = 0; i < SRSLTE_SIMD_CF_SIZE; i++) { + x0_gold[i] = RANDOM_CF(); + x1_gold[i] = RANDOM_CF(); + h00[i] = RANDOM_CF(); + h01[i] = RANDOM_CF(); + h10[i] = RANDOM_CF(); + h11[i] = (1 - h01[i] * h10[i]) / h00[i]; + y0[i] = x0_gold[i] * h00[i]+ x1_gold[i] * h01[i]; + y1[i] = x0_gold[i] * h10[i] + x1_gold[i] * h11[i]; + } + + simd_cf_t _y0 = srslte_simd_cfi_loadu(y0); + simd_cf_t _y1 = srslte_simd_cfi_loadu(y1); + + simd_cf_t _h00 = srslte_simd_cfi_loadu(h00); + simd_cf_t _h01 = srslte_simd_cfi_loadu(h01); + simd_cf_t _h10 = srslte_simd_cfi_loadu(h10); + simd_cf_t _h11 = srslte_simd_cfi_loadu(h11); + + simd_cf_t _x0, _x1; + + srslte_mat_2x2_mmse_simd(_y0, _y1, _h00, _h01, _h10, _h11, &_x0, &_x1, 0.0f, 1.0f); + + __attribute__((aligned(SRSLTE_SIMD_BIT_ALIGN))) cf_t x0[SRSLTE_SIMD_CF_SIZE]; + __attribute__((aligned(SRSLTE_SIMD_BIT_ALIGN))) cf_t x1[SRSLTE_SIMD_CF_SIZE]; + + srslte_simd_cfi_store(x0, _x0); + srslte_simd_cfi_store(x1, _x1); + + cf_error0 = x0[1] - x0_gold[1]; + cf_error1 = x1[1] - x1_gold[1]; + error += crealf(cf_error0) * crealf(cf_error0) + cimagf(cf_error0) * cimagf(cf_error0) + + crealf(cf_error1) * crealf(cf_error1) + cimagf(cf_error1) * cimagf(cf_error1); + + return (error < 1e-3); +} + +#endif /* SRSLTE_SIMD_CF_SIZE != 0 */ + +bool test_vec_dot_prod_ccc(void) { + __attribute__((aligned(256))) cf_t a[14]; + __attribute__((aligned(256))) cf_t b[14]; + cf_t res = 0, gold = 0; + + for (int i = 0; i < 14; i++) { + a[i] = RANDOM_CF(); + b[i] = RANDOM_CF(); + } + + res = srslte_vec_dot_prod_ccc(a, b, 14); + + for (int i=0;i<14;i++) { + gold += a[i]*b[i]; + } + + return (cabsf(res - gold) < 1e-3); +} + +int main(int argc, char **argv) { + bool passed = true; + int ret = 0; + + parse_args(argc, argv); + + if (zf_solver) { + RUN_TEST(test_zf_solver_gen); + +#ifdef LV_HAVE_SSE + RUN_TEST(test_zf_solver_sse); +#endif /* LV_HAVE_SSE */ + +#ifdef LV_HAVE_AVX + RUN_TEST(test_zf_solver_avx); +#endif /* LV_HAVE_AVX */ + +#if SRSLTE_SIMD_CF_SIZE != 0 + RUN_TEST(test_zf_solver_simd); +#endif /* SRSLTE_SIMD_CF_SIZE != 0*/ + } + + if (mmse_solver) { + RUN_TEST(test_mmse_solver_gen); + +#ifdef LV_HAVE_SSE + RUN_TEST(test_mmse_solver_sse); +#endif /* LV_HAVE_SSE */ + + +#ifdef LV_HAVE_AVX + RUN_TEST(test_mmse_solver_avx); +#endif /* LV_HAVE_AVX */ + +#if SRSLTE_SIMD_CF_SIZE != 0 + RUN_TEST(test_mmse_solver_simd); +#endif /* SRSLTE_SIMD_CF_SIZE != 0*/ + } + + RUN_TEST(test_vec_dot_prod_ccc); + + printf("%s!\n", (passed) ? "Ok" : "Failed"); + + if (!passed) { + exit(ret); + } + + exit(ret); +} diff --git a/lib/src/phy/utils/test/vector_test.c b/lib/src/phy/utils/test/vector_test.c new file mode 100644 index 0000000..85b1c3f --- /dev/null +++ b/lib/src/phy/utils/test/vector_test.c @@ -0,0 +1,993 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/utils/mat.h" +#include "srslte/phy/utils/simd.h" +#include "srslte/phy/utils/vector.h" + + +bool zf_solver = false; +bool mmse_solver = false; +bool verbose = false; + +#define MAX_MSE (1e-3) +#define NOF_REPETITIONS (1024) +#define MAX_FUNCTIONS (64) +#define MAX_BLOCKS (16) + +#define RANDOM_F() (((float) rand()) / ((float) RAND_MAX) * 2.0f - 1.0f) +#define RANDOM_S() ((int16_t)(rand() & 0x800F)) +#define RANDOM_B() ((int8_t)(rand() & 0x8008)) +#define RANDOM_CF() (RANDOM_F() + _Complex_I*RANDOM_F()) + +#define TEST_CALL(TEST_CODE) gettimeofday(&start, NULL);\ + for (int i = 0; i < NOF_REPETITIONS; i++){TEST_CODE;}\ + gettimeofday(&end, NULL); \ + *timing = elapsed_us(&start, &end); + +#define TEST(X, CODE) static bool test_##X (char *func_name, double *timing, uint32_t block_size) {\ + struct timeval start, end;\ + float mse = 0.0f;\ + bool passed;\ + strncpy(func_name, #X, 32);\ + CODE;\ + passed = (mse < MAX_MSE);\ + printf("%32s (%5d) ... %7.1f MSamp/s ... %3s Passed (%.6f)\n", func_name, block_size, \ + (double) block_size*NOF_REPETITIONS/ *timing, passed?"":"Not", mse);\ + return passed;\ +} + +#define MALLOC(TYPE, NAME) TYPE *NAME = srslte_vec_malloc(sizeof(TYPE)*block_size) + + +static double elapsed_us(struct timeval *ts_start, struct timeval *ts_end) { + if (ts_end->tv_usec > ts_start->tv_usec) { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec) * 1000000 + + (double) ts_end->tv_usec - (double) ts_start->tv_usec; + } else { + return ((double) ts_end->tv_sec - (double) ts_start->tv_sec - 1) * 1000000 + + ((double) ts_end->tv_usec + 1000000) - (double) ts_start->tv_usec; + } +} + +float squared_error (cf_t a, cf_t b) { + float diff_re = __real__ a - __real__ b; + float diff_im = __imag__ a - __imag__ b; + return diff_re*diff_re + diff_im*diff_im; +} + + TEST(srslte_vec_xor_bbb, + MALLOC(int8_t, x); + MALLOC(int8_t, y); + MALLOC(int8_t, z); + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_B(); + y[i] = RANDOM_B(); + } + + TEST_CALL(srslte_vec_xor_bbb(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] ^ y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_acc_ff, + MALLOC(float, x); + float z; + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + } + + TEST_CALL(z = srslte_vec_acc_ff(x, block_size)) + + for (int i = 0; i < block_size; i++) { + gold += x[i]; + } + + mse += fabs(gold - z) / gold; + + free(x); +) + +TEST(srslte_vec_dot_prod_sss, + MALLOC(int16_t, x); + MALLOC(int16_t, y); + int16_t z; + + int16_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_S(); + y[i] = RANDOM_S(); + } + + TEST_CALL(z = srslte_vec_dot_prod_sss(x, y, block_size)) + + for (int i = 0; i < block_size; i++) { + gold += x[i] * y[i]; + } + + mse = (gold - z) / abs(gold); + + free(x); + free(y); +) + +TEST(srslte_vec_sum_sss, + MALLOC(int16_t, x); + MALLOC(int16_t, y); + MALLOC(int16_t, z); + + int16_t gold = 0; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_S(); + y[i] = RANDOM_S(); + } + + TEST_CALL(srslte_vec_sum_sss(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] + y[i]; + mse += abs(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_sub_sss, + MALLOC(int16_t, x); + MALLOC(int16_t, y); + MALLOC(int16_t, z); + + int16_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_S(); + y[i] = RANDOM_S(); + } + + TEST_CALL(srslte_vec_sub_sss(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] - y[i]; + mse += abs(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_prod_sss, + MALLOC(int16_t, x); + MALLOC(int16_t, y); + MALLOC(int16_t, z); + + int16_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_S(); + y[i] = RANDOM_S(); + } + + TEST_CALL(srslte_vec_prod_sss(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y[i]; + mse += abs(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_acc_cc, + MALLOC(cf_t, x); + cf_t z; + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + } + + TEST_CALL(z = srslte_vec_acc_cc(x, block_size)) + + for (int i = 0; i < block_size; i++) { + gold += x[i]; + } + + mse += cabsf(gold - z)/cabsf(gold); + + free(x); +) + + +TEST(srslte_vec_sum_fff, + MALLOC(float, x); + MALLOC(float, y); + MALLOC(float, z); + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + y[i] = RANDOM_F(); + } + + TEST_CALL(srslte_vec_sum_fff(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] + y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_sub_fff, + MALLOC(float, x); + MALLOC(float, y); + MALLOC(float, z); + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + y[i] = RANDOM_F(); + } + + TEST_CALL(srslte_vec_sub_fff(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] - y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_dot_prod_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, y); + cf_t z; + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(z = srslte_vec_dot_prod_ccc(x, y, block_size)) + + for (int i = 0; i < block_size; i++) { + gold += x[i] * y[i]; + } + + mse = cabsf(gold - z) / cabsf(gold); + + free(x); + free(y); +) + +TEST(srslte_vec_dot_prod_conj_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, y); + cf_t z; + + cf_t gold = 0.0f; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(z = srslte_vec_dot_prod_conj_ccc(x, y, block_size)) + + for (int i = 0; i < block_size; i++) { + gold += x[i] * conjf(y[i]); + } + + mse = cabsf(gold - z) / cabsf(gold); + + free(x); + free(y); +) + +TEST(srslte_vec_prod_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, y); + MALLOC(cf_t, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_prod_ccc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_prod_ccc_split, + MALLOC(float, x_re); + MALLOC(float, x_im); + MALLOC(float, y_re); + MALLOC(float, y_im); + MALLOC(float, z_re); + MALLOC(float, z_im); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x_re[i] = RANDOM_F(); + x_im[i] = RANDOM_F(); + y_re[i] = RANDOM_F(); + y_im[i] = RANDOM_F(); + } + + TEST_CALL(srslte_vec_prod_ccc_split(x_re, x_im, y_re, y_im, z_re, z_im, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = (x_re[i] + I * x_im[i]) * (y_re[i] + I * y_im[i]); + mse += cabsf(gold - (z_re[i] + I*z_im[i])); + } + + free(x_re); + free(x_im); + free(y_re); + free(y_im); + free(z_re); + free(z_im); +) + +TEST(srslte_vec_prod_conj_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, y); + MALLOC(cf_t, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_prod_conj_ccc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * conjf(y[i]); + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_sc_prod_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, z); + cf_t y = RANDOM_CF(); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_sc_prod_ccc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y; + mse += cabsf(gold - z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_convert_fi, + MALLOC(float, x); + MALLOC(short, z); + float scale = 1000.0f; + + short gold; + for (int i = 0; i < block_size; i++) { + x[i] = (float) RANDOM_F(); + } + + TEST_CALL(srslte_vec_convert_fi(x, scale, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = (short) ((x[i] * scale)); + mse += cabsf((float)gold - (float) z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_convert_if, + MALLOC(int16_t, x); + MALLOC(float, z); + float scale = 1000.0f; + + float gold; + float k = 1.0f/scale; + for (int i = 0; i < block_size; i++) { + x[i] = (int16_t) RANDOM_S(); + } + + TEST_CALL(srslte_vec_convert_if(x, scale, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = ((float)x[i]) * k; + mse += fabsf(gold - z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_prod_fff, + MALLOC(float, x); + MALLOC(float, y); + MALLOC(float, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_prod_fff(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_prod_cfc, + MALLOC(cf_t, x); + MALLOC(float, y); + MALLOC(cf_t, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_F(); + } + + TEST_CALL(srslte_vec_prod_cfc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y[i]; + mse += cabsf(gold - z[i]); + } + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_sc_prod_fff, + MALLOC(float, x); + MALLOC(float, z); + float y = RANDOM_F(); + + float gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_sc_prod_fff(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * y; + mse += cabsf(gold - z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_abs_cf, + MALLOC(cf_t, x); + MALLOC(float, z); + float gold; + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + x[0] = 0.0f; + + TEST_CALL(srslte_vec_abs_cf(x, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = sqrtf(crealf(x[i]) * crealf(x[i]) + cimagf(x[i])*cimagf(x[i])); + mse += cabsf(gold - z[i])/block_size; + } + + free(x); + free(z); +) + +TEST(srslte_vec_abs_square_cf, + MALLOC(cf_t, x); + MALLOC(float, z); + float gold; + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_abs_square_cf(x, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = crealf(x[i]) * crealf(x[i]) + cimagf(x[i])*cimagf(x[i]); + mse += cabsf(gold - z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_sc_prod_cfc, + MALLOC(cf_t, x); + MALLOC(cf_t, z); + cf_t gold; + float h = RANDOM_F(); + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_sc_prod_cfc(x, h, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * h; + mse += cabsf(gold - z[i]); + } + + free(x); + free(z); +) + +TEST(srslte_vec_div_ccc, + MALLOC(cf_t, x); + MALLOC(cf_t, y); + MALLOC(cf_t, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_div_ccc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] / y[i]; + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(y); + free(z); +) + + +TEST(srslte_vec_div_cfc, + MALLOC(cf_t, x); + MALLOC(float, y); + MALLOC(cf_t, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + y[i] = RANDOM_F() + 0.0001f; + } + + TEST_CALL(srslte_vec_div_cfc(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] / y[i]; + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(y); + free(z); +) + + +TEST(srslte_vec_div_fff, + MALLOC(float, x); + MALLOC(float, y); + MALLOC(float, z); + + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + y[i] = RANDOM_F() + 0.0001f; + } + + TEST_CALL(srslte_vec_div_fff(x, y, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] / y[i]; + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(y); + free(z); +) + +TEST(srslte_vec_max_fi, + MALLOC(float, x); + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + } + + uint32_t max_index = 0; + TEST_CALL(max_index = srslte_vec_max_fi(x, block_size);) + + float gold_value = -INFINITY; + uint32_t gold_index = 0; + for (int i = 0; i < block_size; i++) { + if (gold_value < x[i]) { + gold_value = x[i]; + gold_index = i; + } + } + mse = (gold_index != max_index) ? 1:0; + + free(x); +) + +TEST(srslte_vec_max_abs_fi, + MALLOC(float, x); + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + } + + uint32_t max_index = 0; + TEST_CALL(max_index = srslte_vec_max_abs_fi(x, block_size);) + + float gold_value = -INFINITY; + uint32_t gold_index = 0; + for (int i = 0; i < block_size; i++) { + if (gold_value < fabsf(x[i])) { + gold_value = fabsf(x[i]); + gold_index = i; + } + } + mse = (gold_index != max_index) ? 1:0; + + free(x); +) + +TEST(srslte_vec_max_abs_ci, + MALLOC(cf_t, x); + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + uint32_t max_index = 0; + TEST_CALL(max_index = srslte_vec_max_abs_ci(x, block_size);) + + float gold_value = -INFINITY; + uint32_t gold_index = 0; + for (int i = 0; i < block_size; i++) { + cf_t a = x[i]; + float abs2 = __real__ a * __real__ a + __imag__ a * __imag__ a; + if (abs2 > gold_value) { + gold_value = abs2; + gold_index = (uint32_t)i; + } + } + mse = (gold_index != max_index) ? 1:0; + + free(x); +) + +TEST(srslte_vec_apply_cfo, + MALLOC(cf_t, x); + MALLOC(cf_t, z); + + const float cfo = 0.1f; + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + TEST_CALL(srslte_vec_apply_cfo(x, cfo, z, block_size)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * cexpf(_Complex_I * 2.0f * (float) M_PI * i * cfo); + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(z); +) + +TEST(srslte_cfo_correct, + srslte_cfo_t srslte_cfo = {0}; + MALLOC(cf_t, x); + MALLOC(cf_t, z); + + const float cfo = 0.1f; + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + srslte_cfo_init(&srslte_cfo, block_size); + + TEST_CALL(srslte_cfo_correct(&srslte_cfo, x, z, cfo)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * cexpf(_Complex_I * 2.0f * (float) M_PI * i * cfo); + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(z); + srslte_cfo_free(&srslte_cfo); +) + +TEST(srslte_cfo_correct_change, + srslte_cfo_t srslte_cfo = {0}; + MALLOC(cf_t, x); + MALLOC(cf_t, z); + + float cfo = 0.1f; + cf_t gold; + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_CF(); + } + + srslte_cfo_init(&srslte_cfo, block_size); + + TEST_CALL(cfo = (i%2)?0.1:-0.1; srslte_cfo_correct(&srslte_cfo, x, z, cfo)) + + for (int i = 0; i < block_size; i++) { + gold = x[i] * cexpf(_Complex_I * 2.0f * (float) M_PI * i * cfo); + mse += cabsf(gold - z[i]) / cabsf(gold); + } + mse /= block_size; + + free(x); + free(z); + srslte_cfo_free(&srslte_cfo); +) + +int main(int argc, char **argv) { + char func_names[MAX_FUNCTIONS][32]; + double timmings[MAX_FUNCTIONS][MAX_BLOCKS]; + uint32_t sizes[32]; + uint32_t size_count = 0; + uint32_t func_count = 0; + bool passed[MAX_FUNCTIONS][MAX_BLOCKS]; + bool all_passed = true; + + for (uint32_t block_size = 1; block_size <= 1024*32; block_size *= 2) { + func_count = 0; + + + passed[func_count][size_count] = test_srslte_vec_xor_bbb(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + + passed[func_count][size_count] = test_srslte_vec_acc_ff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_dot_prod_sss(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sum_sss(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sub_sss(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_sss(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_acc_cc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sum_fff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sub_fff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_dot_prod_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_dot_prod_conj_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_convert_fi(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_convert_if(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_fff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_cfc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_ccc_split(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_prod_conj_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sc_prod_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sc_prod_fff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_abs_cf(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_abs_square_cf(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_sc_prod_cfc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_div_ccc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_div_cfc(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_div_fff(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_max_fi(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_max_abs_fi(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_max_abs_ci(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_vec_apply_cfo(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_cfo_correct(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + passed[func_count][size_count] = test_srslte_cfo_correct_change(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + + sizes[size_count] = block_size; + size_count++; + } + + char fname[69]; + FILE *f = NULL; + void * p = popen("(date +%g%m%d && hostname) | tr '\\r\\n' '__'", "r"); + if (p) { + fgets(fname, 64, p); + strncpy(fname + strnlen(fname, 64) - 1, ".tsv", 5); + f = fopen(fname, "w"); + if (f) { + printf("Saving benchmark results in '%s'\n", fname); + } + pclose(p); + } + + + printf("\n"); + printf("%32s |", "Subroutine/MSps"); + if (f) fprintf(f, "Subroutine/MSps Vs Vector size\t"); + for (int i = 0; i < size_count; i++) { + printf(" %7d", sizes[i]); + if (f) fprintf(f, "%d\t", sizes[i]); + } + printf(" |\n"); + if (f) fprintf(f, "\n"); + + for (int j = 0; j < 32; j++) { + printf("-"); + } + printf("-+-"); + for (int j = 0; j < size_count; j++) { + printf("--------"); + } + printf("-|\n"); + + for (int i = 0; i < func_count; i++) { + printf("%32s | ", func_names[i]); + if (f) fprintf(f, "%s\t", func_names[i]); + + for (int j = 0; j < size_count; j++) { + printf(" %s%7.1f\x1b[0m", (passed[i][j])?"":"\x1B[31m", (double) NOF_REPETITIONS*(double)sizes[j]/timmings[i][j]); + if (f) fprintf(f, "%.1f\t", (double) NOF_REPETITIONS*(double)sizes[j]/timmings[i][j]); + + all_passed &= passed[i][j]; + } + printf(" |\n"); + if (f) fprintf(f, "\n"); + } + + if (f) fclose(f); + + return (all_passed)?SRSLTE_SUCCESS:SRSLTE_ERROR; +} diff --git a/lib/src/phy/utils/vector.c b/lib/src/phy/utils/vector.c new file mode 100644 index 0000000..9c1b84f --- /dev/null +++ b/lib/src/phy/utils/vector.c @@ -0,0 +1,448 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include +#include +#include + +#include "srslte/phy/utils/vector.h" +#include "srslte/phy/utils/vector_simd.h" +#include "srslte/phy/utils/bit.h" + + + +void srslte_vec_xor_bbb(int8_t *x,int8_t *y,int8_t *z, const uint32_t len) { + srslte_vec_xor_bbb_simd(x, y, z, len); +} + +// Used in PRACH detector, AGC and chest_dl for noise averaging +float srslte_vec_acc_ff(const float *x, const uint32_t len) { + return srslte_vec_acc_ff_simd(x, len); +} + +cf_t srslte_vec_acc_cc(const cf_t *x, const uint32_t len) { + return srslte_vec_acc_cc_simd(x, len); +} + +void srslte_vec_sub_fff(const float *x, const float *y, float *z, const uint32_t len) { + srslte_vec_sub_fff_simd(x, y, z, len); +} + +void srslte_vec_sub_sss(const int16_t *x, const int16_t *y, int16_t *z, const uint32_t len) { + srslte_vec_sub_sss_simd(x, y, z, len); +} + +// Noise estimation in chest_dl, interpolation +void srslte_vec_sub_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len) { + return srslte_vec_sub_fff((const float*) x,(const float*) y,(float*) z, 2*len); +} + +// Used in PSS/SSS and sum_ccc +void srslte_vec_sum_fff(const float *x, const float *y, float *z, const uint32_t len) { + srslte_vec_add_fff_simd(x, y, z, len); +} + +void srslte_vec_sum_sss(const int16_t *x, const int16_t *y, int16_t *z, const uint32_t len) { + srslte_vec_sum_sss_simd(x, y, z, len); +} + +void srslte_vec_sum_ccc(const cf_t *x, const cf_t *y, cf_t *z, const uint32_t len) { + srslte_vec_sum_fff((float*) x,(float*) y,(float*) z,2*len); +} + +// PSS, PBCH, DEMOD, FFTW, etc. +void srslte_vec_sc_prod_fff(const float *x, const float h, float *z, const uint32_t len) { + srslte_vec_sc_prod_fff_simd(x, h, z, len); +} + +// Used throughout +void srslte_vec_sc_prod_cfc(const cf_t *x, const float h, cf_t *z, const uint32_t len) { + srslte_vec_sc_prod_cfc_simd(x,h,z,len); +} + +// Chest UL +void srslte_vec_sc_prod_ccc(const cf_t *x, const cf_t h, cf_t *z, const uint32_t len) { + srslte_vec_sc_prod_ccc_simd(x,h,z,len); +} + +// Used in turbo decoder +void srslte_vec_convert_if(const int16_t *x, const float scale, float *z, const uint32_t len) { + srslte_vec_convert_if_simd(x, z, scale, len); +} + +void srslte_vec_convert_fi(const float *x, const float scale, int16_t *z, const uint32_t len) { + srslte_vec_convert_fi_simd(x, z, scale, len); +} + +void srslte_vec_lut_sss(const short *x, const unsigned short *lut, short *y, const uint32_t len) { + srslte_vec_lut_sss_simd(x, lut, y, len); +} + +void srslte_vec_lut_sis(const short *x, const unsigned int *lut, short *y, const uint32_t len) { + for (int i=0; i < len; i++) { + y[lut[i]] = x[i]; + } +} + +void *srslte_vec_malloc(uint32_t size) { + void *ptr; + if (posix_memalign(&ptr, SRSLTE_SIMD_BIT_ALIGN, size)) { + return NULL; + } else { + return ptr; + } +} + +void *srslte_vec_realloc(void *ptr, uint32_t old_size, uint32_t new_size) { +#ifndef LV_HAVE_SSE + return realloc(ptr, new_size); +#else + void *new_ptr; + if (posix_memalign(&new_ptr, SRSLTE_SIMD_BIT_ALIGN, new_size)) { + return NULL; + } else { + memcpy(new_ptr, ptr, old_size); + free(ptr); + return new_ptr; + } +#endif +} + + +void srslte_vec_fprint_c(FILE *stream, cf_t *x, const uint32_t len) { + int i; + fprintf(stream, "["); + for (i=0;i= max_str_len) { + fprintf(stderr, "Buffer too small for printing hex string (max_str_len=%d, payload_len=%d).\n", max_str_len, len); + return; + } + + int n=0; + n+=sprintf(&str[n], "["); + for (i=0;i clip) + tmp = clip; + out[i] = (uint16_t) tmp; + } +} + +void srslte_vec_quant_fuc(const float *in, uint8_t *out, const float gain, const float offset, const float clip, const uint32_t len) { + int i; + int tmp; + + for (i=0;i clip) + tmp = clip; + out[i] = (uint8_t) tmp; + } +} + +void srslte_vec_quant_suc(const int16_t *in, uint8_t *out, const float gain, const int16_t offset, const int16_t clip, const uint32_t len) { + int i; + int16_t tmp; + + for (i=0;i clip) + tmp = clip; + out[i] = (uint8_t) tmp; + } +} + +void srslte_vec_quant_sus(const int16_t *in, uint16_t *out, const float gain, const int16_t offset, const uint32_t len) { + int i; + int16_t tmp; + + for (i=0;i +#include +#include +#include +#include +#include + +#include +#include "srslte/phy/utils/vector_simd.h" +#include "srslte/phy/utils/simd.h" + + +void srslte_vec_xor_bbb_simd(const int8_t *x, const int8_t *y, int8_t *z, const int len) { + int i = 0; +#if SRSLTE_SIMD_B_SIZE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_B_SIZE + 1; i += SRSLTE_SIMD_B_SIZE) { + simd_b_t a = srslte_simd_b_load(&x[i]); + simd_b_t b = srslte_simd_b_load(&y[i]); + + simd_b_t r = srslte_simd_b_xor(a, b); + + srslte_simd_b_store(&z[i], r); + } + } else { + for (; i < len - SRSLTE_SIMD_B_SIZE + 1; i += SRSLTE_SIMD_B_SIZE) { + simd_b_t a = srslte_simd_b_loadu(&x[i]); + simd_b_t b = srslte_simd_b_loadu(&y[i]); + + simd_s_t r = srslte_simd_b_xor(a, b); + + srslte_simd_b_storeu(&z[i], r); + } + } +#endif /* SRSLTE_SIMD_B_SIZE */ + + for(; i < len; i++){ + z[i] = x[i] ^ y[i]; + } +} + +int srslte_vec_dot_prod_sss_simd(const int16_t *x, const int16_t *y, const int len) { + int i = 0; + int result = 0; +#if SRSLTE_SIMD_S_SIZE + simd_s_t simd_dotProdVal = srslte_simd_s_zero(); + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y)) { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_load(&x[i]); + simd_s_t b = srslte_simd_s_load(&y[i]); + + simd_s_t z = srslte_simd_s_mul(a, b); + + simd_dotProdVal = srslte_simd_s_add(simd_dotProdVal, z); + } + } else { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_loadu(&x[i]); + simd_s_t b = srslte_simd_s_loadu(&y[i]); + + simd_s_t z = srslte_simd_s_mul(a, b); + + simd_dotProdVal = srslte_simd_s_add(simd_dotProdVal, z); + } + } + __attribute__ ((aligned (SRSLTE_SIMD_S_SIZE*2))) short dotProdVector[SRSLTE_SIMD_S_SIZE]; + srslte_simd_s_store(dotProdVector, simd_dotProdVal); + for (int k = 0; k < SRSLTE_SIMD_S_SIZE; k++) { + result += dotProdVector[k]; + } +#endif /* SRSLTE_SIMD_S_SIZE */ + + for(; i < len; i++){ + result += (x[i] * y[i]); + } + + return result; +} + +void srslte_vec_sum_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, const int len) { + int i = 0; +#if SRSLTE_SIMD_S_SIZE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_load(&x[i]); + simd_s_t b = srslte_simd_s_load(&y[i]); + + simd_s_t r = srslte_simd_s_add(a, b); + + srslte_simd_s_store(&z[i], r); + } + } else { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_loadu(&x[i]); + simd_s_t b = srslte_simd_s_loadu(&y[i]); + + simd_s_t r = srslte_simd_s_add(a, b); + + srslte_simd_s_storeu(&z[i], r); + } + } +#endif /* SRSLTE_SIMD_S_SIZE */ + + for(; i < len; i++){ + z[i] = x[i] + y[i]; + } +} + +void srslte_vec_sub_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, const int len) { + int i = 0; +#if SRSLTE_SIMD_S_SIZE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_load(&x[i]); + simd_s_t b = srslte_simd_s_load(&y[i]); + + simd_s_t r = srslte_simd_s_sub(a, b); + + srslte_simd_s_store(&z[i], r); + } + } else { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_loadu(&x[i]); + simd_s_t b = srslte_simd_s_loadu(&y[i]); + + simd_s_t r = srslte_simd_s_sub(a, b); + + srslte_simd_s_storeu(&z[i], r); + } + } +#endif /* SRSLTE_SIMD_S_SIZE */ + + for(; i < len; i++){ + z[i] = x[i] - y[i]; + } +} + +void srslte_vec_prod_sss_simd(const int16_t *x, const int16_t *y, int16_t *z, const int len) { + int i = 0; +#if SRSLTE_SIMD_S_SIZE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_load(&x[i]); + simd_s_t b = srslte_simd_s_load(&y[i]); + + simd_s_t r = srslte_simd_s_mul(a, b); + + srslte_simd_s_store(&z[i], r); + } + } else { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_s_t a = srslte_simd_s_loadu(&x[i]); + simd_s_t b = srslte_simd_s_loadu(&y[i]); + + simd_s_t r = srslte_simd_s_mul(a, b); + + srslte_simd_s_storeu(&z[i], r); + } + } +#endif /* SRSLTE_SIMD_S_SIZE */ + + for(; i < len; i++){ + z[i] = x[i] * y[i]; + } +} + +/* No improvement with AVX */ +void srslte_vec_lut_sss_simd(const short *x, const unsigned short *lut, short *y, const int len) { + int i = 0; +#ifdef LV_HAVE_SSE +#if CMAKE_BUILD_TYPE!=Debug + + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(lut)) { + for (; i < len - 7; i += 8) { + __m128i xVal = _mm_load_si128((__m128i *) &x[i]); + __m128i lutVal = _mm_load_si128((__m128i *) &lut[i]); + + for (int k = 0; k < 8; k++) { + int16_t x = (int16_t) _mm_extract_epi16(xVal, k); + uint16_t l = (uint16_t) _mm_extract_epi16(lutVal, k); + y[l] = (short) x; + } + } + } else { + for (; i < len - 7; i += 8) { + __m128i xVal = _mm_loadu_si128((__m128i *) &x[i]); + __m128i lutVal = _mm_loadu_si128((__m128i *) &lut[i]); + + for (int k = 0; k < 8; k++) { + int16_t x = (int16_t) _mm_extract_epi16(xVal, k); + uint16_t l = (uint16_t) _mm_extract_epi16(lutVal, k); + y[l] = (short) x; + } + } + } +#endif +#endif + + for (; i < len; i++) { + y[lut[i]] = x[i]; + } +} + +void srslte_vec_convert_if_simd(const int16_t *x, float *z, const float scale, const int len) { + int i = 0; + const float gain = 1.0f / scale; + +#ifdef LV_HAVE_SSE + __m128 s = _mm_set1_ps(gain); + if (SRSLTE_IS_ALIGNED(z)) { + for (; i < len - 3; i += 4) { + __m64 *ptr = (__m64 *) &x[i]; + __m128 fl = _mm_cvtpi16_ps(*ptr); + __m128 v = _mm_mul_ps(fl, s); + + _mm_store_ps(&z[i], v); + } + } else { + for (; i < len - 3; i += 4) { + __m64 *ptr = (__m64 *) &x[i]; + __m128 fl = _mm_cvtpi16_ps(*ptr); + __m128 v = _mm_mul_ps(fl, s); + + _mm_storeu_ps(&z[i], v); + } + } +#endif /* LV_HAVE_SSE */ + + for (; i < len; i++) { + z[i] = ((float) x[i]) * gain; + } +} + +void srslte_vec_convert_fi_simd(const float *x, int16_t *z, const float scale, const int len) { + int i = 0; + +#if SRSLTE_SIMD_F_SIZE && SRSLTE_SIMD_S_SIZE + simd_f_t s = srslte_simd_f_set1(scale); + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_f_t a = srslte_simd_f_load(&x[i]); + simd_f_t b = srslte_simd_f_load(&x[i + SRSLTE_SIMD_F_SIZE]); + + simd_f_t sa = srslte_simd_f_mul(a, s); + simd_f_t sb = srslte_simd_f_mul(b, s); + + simd_s_t i16 = srslte_simd_convert_2f_s(sa, sb); + + srslte_simd_s_store(&z[i], i16); + } + } else { + for (; i < len - SRSLTE_SIMD_S_SIZE + 1; i += SRSLTE_SIMD_S_SIZE) { + simd_f_t a = srslte_simd_f_loadu(&x[i]); + simd_f_t b = srslte_simd_f_loadu(&x[i + SRSLTE_SIMD_F_SIZE]); + + simd_f_t sa = srslte_simd_f_mul(a, s); + simd_f_t sb = srslte_simd_f_mul(b, s); + + simd_s_t i16 = srslte_simd_convert_2f_s(sa, sb); + + srslte_simd_s_storeu(&z[i], i16); + } + } +#endif /* SRSLTE_SIMD_F_SIZE && SRSLTE_SIMD_S_SIZE */ + + for(; i < len; i++){ + z[i] = (int16_t) (x[i] * scale); + } +} + +float srslte_vec_acc_ff_simd(const float *x, const int len) { + int i = 0; + float acc_sum = 0.0f; + +#if SRSLTE_SIMD_F_SIZE + simd_f_t simd_sum = srslte_simd_f_zero(); + + if (SRSLTE_IS_ALIGNED(x)) { + for (; i < len - SRSLTE_SIMD_F_SIZE + 1; i += SRSLTE_SIMD_F_SIZE) { + simd_f_t a = srslte_simd_f_load(&x[i]); + + simd_sum = srslte_simd_f_add(simd_sum, a); + } + } else { + for (; i < len - SRSLTE_SIMD_F_SIZE + 1; i += SRSLTE_SIMD_F_SIZE) { + simd_f_t a = srslte_simd_f_loadu(&x[i]); + + simd_sum = srslte_simd_f_add(simd_sum, a); + } + } + + __attribute__((aligned(SRSLTE_SIMD_F_SIZE*4))) float sum[SRSLTE_SIMD_F_SIZE]; + srslte_simd_f_store(sum, simd_sum); + for (int k = 0; k < SRSLTE_SIMD_F_SIZE; k++) { + acc_sum += sum[k]; + } +#endif + + for (; i max_value) { + max_value = values_buffer[k]; + max_index = (uint32_t) indexes_buffer[k]; + } + } +#endif /* SRSLTE_SIMD_I_SIZE */ + + for (; i < len; i++) { + if (x[i] > max_value) { + max_value = x[i]; + max_index = (uint32_t)i; + } + } + + return max_index; +} + +uint32_t srslte_vec_max_abs_fi_simd(const float *x, const int len) { + int i = 0; + + float max_value = -INFINITY; + uint32_t max_index = 0; + +#if SRSLTE_SIMD_I_SIZE + __attribute__ ((aligned (SRSLTE_SIMD_I_SIZE*sizeof(int)))) int indexes_buffer[SRSLTE_SIMD_I_SIZE] = {0}; + __attribute__ ((aligned (SRSLTE_SIMD_I_SIZE*sizeof(float)))) float values_buffer[SRSLTE_SIMD_I_SIZE] = {0}; + + for (int k = 0; k < SRSLTE_SIMD_I_SIZE; k++) indexes_buffer[k] = k; + simd_i_t simd_inc = srslte_simd_i_set1(SRSLTE_SIMD_I_SIZE); + simd_i_t simd_indexes = srslte_simd_i_load(indexes_buffer); + simd_i_t simd_max_indexes = srslte_simd_i_set1(0); + + simd_f_t simd_max_values = srslte_simd_f_set1(-INFINITY); + + if (SRSLTE_IS_ALIGNED(x)) { + for (; i < len - SRSLTE_SIMD_I_SIZE + 1; i += SRSLTE_SIMD_I_SIZE) { + simd_f_t a = srslte_simd_f_abs(srslte_simd_f_load(&x[i])); + simd_sel_t res = srslte_simd_f_max(a, simd_max_values); + simd_max_indexes = srslte_simd_i_select(simd_max_indexes, simd_indexes, res); + simd_max_values = (simd_f_t) srslte_simd_i_select((simd_i_t) simd_max_values, (simd_i_t) a, res); + simd_indexes = srslte_simd_i_add(simd_indexes, simd_inc); + } + } else { + for (; i < len - SRSLTE_SIMD_I_SIZE + 1; i += SRSLTE_SIMD_I_SIZE) { + simd_f_t a = srslte_simd_f_abs(srslte_simd_f_loadu(&x[i])); + simd_sel_t res = srslte_simd_f_max(a, simd_max_values); + simd_max_indexes = srslte_simd_i_select(simd_max_indexes, simd_indexes, res); + simd_max_values = (simd_f_t) srslte_simd_i_select((simd_i_t) simd_max_values, (simd_i_t) a, res); + simd_indexes = srslte_simd_i_add(simd_indexes, simd_inc); + } + } + + srslte_simd_i_store(indexes_buffer, simd_max_indexes); + srslte_simd_f_store(values_buffer, simd_max_values); + + for (int k = 0; k < SRSLTE_SIMD_I_SIZE; k++) { + if (values_buffer[k] > max_value) { + max_value = values_buffer[k]; + max_index = (uint32_t) indexes_buffer[k]; + } + } +#endif /* SRSLTE_SIMD_I_SIZE */ + + for (; i < len; i++) { + float a = fabsf(x[i]); + if (a > max_value) { + max_value = a; + max_index = (uint32_t)i; + } + } + + return max_index; +} + +uint32_t srslte_vec_max_ci_simd(const cf_t *x, const int len) { + int i = 0; + + float max_value = -INFINITY; + uint32_t max_index = 0; + +#if SRSLTE_SIMD_I_SIZE + __attribute__ ((aligned (SRSLTE_SIMD_I_SIZE*sizeof(int)))) int indexes_buffer[SRSLTE_SIMD_I_SIZE] = {0}; + __attribute__ ((aligned (SRSLTE_SIMD_I_SIZE*sizeof(float)))) float values_buffer[SRSLTE_SIMD_I_SIZE] = {0}; + + for (int k = 0; k < SRSLTE_SIMD_I_SIZE; k++) indexes_buffer[k] = k; + simd_i_t simd_inc = srslte_simd_i_set1(SRSLTE_SIMD_I_SIZE); + simd_i_t simd_indexes = srslte_simd_i_load(indexes_buffer); + simd_i_t simd_max_indexes = srslte_simd_i_set1(0); + + simd_f_t simd_max_values = srslte_simd_f_set1(-INFINITY); + + if (SRSLTE_IS_ALIGNED(x)) { + for (; i < len - SRSLTE_SIMD_I_SIZE + 1; i += SRSLTE_SIMD_I_SIZE) { + simd_f_t x1 = srslte_simd_f_load((float *) &x[i]); + simd_f_t x2 = srslte_simd_f_load((float *) &x[i + SRSLTE_SIMD_F_SIZE / 2]); + + simd_f_t mul1 = srslte_simd_f_mul(x1, x1); + simd_f_t mul2 = srslte_simd_f_mul(x2, x2); + + simd_f_t z1 = srslte_simd_f_hadd(mul1, mul2); + + simd_sel_t res = srslte_simd_f_max(z1, simd_max_values); + + simd_max_indexes = srslte_simd_i_select(simd_max_indexes, simd_indexes, res); + simd_max_values = (simd_f_t) srslte_simd_i_select((simd_i_t) simd_max_values, (simd_i_t) z1, res); + simd_indexes = srslte_simd_i_add(simd_indexes, simd_inc); + } + } else { + for (; i < len - SRSLTE_SIMD_I_SIZE + 1; i += SRSLTE_SIMD_I_SIZE) { + simd_f_t x1 = srslte_simd_f_loadu((float *) &x[i]); + simd_f_t x2 = srslte_simd_f_loadu((float *) &x[i + SRSLTE_SIMD_F_SIZE / 2]); + + simd_f_t mul1 = srslte_simd_f_mul(x1, x1); + simd_f_t mul2 = srslte_simd_f_mul(x2, x2); + + simd_f_t z1 = srslte_simd_f_hadd(mul1, mul2); + + simd_sel_t res = srslte_simd_f_max(z1, simd_max_values); + + simd_max_indexes = srslte_simd_i_select(simd_max_indexes, simd_indexes, res); + simd_max_values = (simd_f_t) srslte_simd_i_select((simd_i_t) simd_max_values, (simd_i_t) z1, res); + simd_indexes = srslte_simd_i_add(simd_indexes, simd_inc); + } + } + + srslte_simd_i_store(indexes_buffer, simd_max_indexes); + srslte_simd_f_store(values_buffer, simd_max_values); + + for (int k = 0; k < SRSLTE_SIMD_I_SIZE; k++) { + if (values_buffer[k] > max_value) { + max_value = values_buffer[k]; + max_index = (uint32_t) indexes_buffer[k]; + } + } +#endif /* SRSLTE_SIMD_I_SIZE */ + + for (; i < len; i++) { + cf_t a = x[i]; + float abs2 = __real__ a * __real__ a + __imag__ a * __imag__ a; + if (abs2 > max_value) { + max_value = abs2; + max_index = (uint32_t)i; + } + } + + return max_index; +} + +void srslte_vec_interleave_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len) { + uint32_t i = 0, k = 0; + +#ifdef LV_HAVE_SSE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - 2 + 1; i += 2) { + __m128i a = _mm_load_si128((__m128i *) &x[i]); + __m128i b = _mm_load_si128((__m128i *) &y[i]); + + __m128i r1 = _mm_unpacklo_epi64(a, b); + _mm_store_si128((__m128i *) &z[k], r1); + k += 2; + + __m128i r2 = _mm_unpackhi_epi64(a, b); + _mm_store_si128((__m128i *) &z[k], r2); + k += 2; + } + } else { + for (; i < len - 2 + 1; i += 2) { + __m128i a = _mm_loadu_si128((__m128i *) &x[i]); + __m128i b = _mm_loadu_si128((__m128i *) &y[i]); + + __m128i r1 = _mm_unpacklo_epi64(a, b); + _mm_storeu_si128((__m128i *) &z[k], r1); + k += 2; + + __m128i r2 = _mm_unpackhi_epi64(a, b); + _mm_storeu_si128((__m128i *) &z[k], r2); + k += 2; + } + } +#endif /* LV_HAVE_SSE */ + + for (;i < len; i++) { + z[k++] = x[i]; + z[k++] = y[i]; + } +} + +void srslte_vec_interleave_add_simd(const cf_t *x, const cf_t *y, cf_t *z, const int len) { + uint32_t i = 0, k = 0; + +#ifdef LV_HAVE_SSE + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(y) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - 2 + 1; i += 2) { + __m128i a = _mm_load_si128((__m128i *) &x[i]); + __m128i b = _mm_load_si128((__m128i *) &y[i]); + + __m128 r1 = (__m128) _mm_unpacklo_epi64(a, b); + __m128 z1 = _mm_load_ps((float *) &z[k]); + r1 = _mm_add_ps((__m128) r1, z1); + _mm_store_ps((float *) &z[k], r1); + k += 2; + + __m128 r2 = (__m128) _mm_unpackhi_epi64(a, b); + __m128 z2 = _mm_load_ps((float *) &z[k]); + r2 = _mm_add_ps((__m128) r2, z2); + _mm_store_ps((float *) &z[k], r2); + k += 2; + } + } else { + for (; i < len - 2 + 1; i += 2) { + __m128i a = _mm_loadu_si128((__m128i *) &x[i]); + __m128i b = _mm_loadu_si128((__m128i *) &y[i]); + + __m128 r1 = (__m128) _mm_unpacklo_epi64(a, b); + __m128 z1 = _mm_loadu_ps((float *) &z[k]); + r1 = _mm_add_ps((__m128) r1, z1); + _mm_storeu_ps((float *) &z[k], r1); + k += 2; + + __m128 r2 = (__m128) _mm_unpackhi_epi64(a, b); + __m128 z2 = _mm_loadu_ps((float *) &z[k]); + r2 = _mm_add_ps((__m128) r2, z2); + _mm_storeu_ps((float *) &z[k], r2); + k += 2; + } + } +#endif /* LV_HAVE_SSE */ + + for (;i < len; i++) { + z[k++] += x[i]; + z[k++] += y[i]; + } +} + +void srslte_vec_apply_cfo_simd(const cf_t *x, float cfo, cf_t *z, int len) { + const float TWOPI = 2.0f * (float) M_PI; + int i = 0; + +#if SRSLTE_SIMD_CF_SIZE + __attribute__ ((aligned (SRSLTE_SIMD_BIT_ALIGN/8))) cf_t _osc[SRSLTE_SIMD_CF_SIZE]; + __attribute__ ((aligned (SRSLTE_SIMD_BIT_ALIGN/8))) cf_t _phase[SRSLTE_SIMD_CF_SIZE]; + + if (i < len - SRSLTE_SIMD_CF_SIZE + 1) { + for (int k = 0; k < SRSLTE_SIMD_CF_SIZE; k++) { + _osc[k] = cexpf(_Complex_I * TWOPI * cfo * SRSLTE_SIMD_CF_SIZE); + _phase[k] = cexpf(_Complex_I * TWOPI * cfo * k); + } + } + simd_cf_t _simd_osc = srslte_simd_cfi_load(_osc); + simd_cf_t _simd_phase = srslte_simd_cfi_load(_phase); + + if (SRSLTE_IS_ALIGNED(x) && SRSLTE_IS_ALIGNED(z)) { + for (; i < len - SRSLTE_SIMD_CF_SIZE + 1; i += SRSLTE_SIMD_CF_SIZE) { + simd_cf_t a = srslte_simd_cfi_load(&x[i]); + + simd_cf_t r = srslte_simd_cf_prod(a, _simd_phase); + + srslte_simd_cfi_store(&z[i], r); + + _simd_phase = srslte_simd_cf_prod(_simd_phase, _simd_osc); + + } + } else { + for (; i < len - SRSLTE_SIMD_F_SIZE + 1; i += SRSLTE_SIMD_F_SIZE) { + for (; i < len - SRSLTE_SIMD_CF_SIZE + 1; i += SRSLTE_SIMD_CF_SIZE) { + simd_cf_t a = srslte_simd_cfi_loadu(&x[i]); + + simd_cf_t r = srslte_simd_cf_prod(a, _simd_phase); + _simd_phase = srslte_simd_cf_prod(_simd_phase, _simd_osc); + + srslte_simd_cfi_storeu(&z[i], r); + } + } + } +#endif + cf_t osc = cexpf(_Complex_I * TWOPI * cfo); + cf_t phase = cexpf(_Complex_I * TWOPI * cfo * i); + for (; i < len; i++) { + z[i] = x[i] * phase; + + phase *= osc; + } +} + diff --git a/lib/src/radio/CMakeLists.txt b/lib/src/radio/CMakeLists.txt new file mode 100644 index 0000000..7fa479f --- /dev/null +++ b/lib/src/radio/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2013-2017 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/. +# + +if(RF_FOUND) + add_library(srslte_radio STATIC radio.cc radio_multi.cc) + target_link_libraries(srslte_radio srslte_rf) + install(TARGETS srslte_radio DESTINATION ${LIBRARY_DIR}) +endif(RF_FOUND) + +add_subdirectory(test) \ No newline at end of file diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc new file mode 100644 index 0000000..9eb7073 --- /dev/null +++ b/lib/src/radio/radio.cc @@ -0,0 +1,483 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/srslte.h" +extern "C" { +#include "srslte/phy/rf/rf.h" +} +#include "srslte/radio/radio.h" +#include +#include + +namespace srslte { + +bool radio::init(char *args, char *devname, uint32_t nof_channels) +{ + if (srslte_rf_open_devname(&rf_device, devname, args, nof_channels)) { + fprintf(stderr, "Error opening RF device\n"); + return false; + } + + tx_adv_negative = false; + agc_enabled = false; + burst_preamble_samples = 0; + burst_preamble_time_rounded = 0; + cur_tx_srate = 0; + is_start_of_burst = true; + + // Suppress radio stdout + srslte_rf_suppress_stdout(&rf_device); + + continuous_tx = true; + tx_adv_auto = true; + // Set default preamble length each known device + // We distinguish by device family, maybe we should calibrate per device + if (strstr(srslte_rf_name(&rf_device), "uhd")) { + } else if (strstr(srslte_rf_name(&rf_device), "bladerf")) { + burst_preamble_sec = blade_default_burst_preamble_sec; + } else { + burst_preamble_sec = 0; + printf("\nWarning burst preamble is not calibrated for device %s. Set a value manually\n\n", srslte_rf_name(&rf_device)); + } + + if (args) { + strncpy(saved_args, args, 127); + } + if (devname) { + strncpy(saved_devname, devname, 127); + } + saved_nof_channels = nof_channels; + + is_initialized = true; + return true; +} + +bool radio::is_init() { + return is_initialized; +} + +void radio::stop() +{ + if (zeros) { + free(zeros); + } + if (is_initialized) { + srslte_rf_close(&rf_device); + } +} + +void radio::reset() +{ + printf("Resetting Radio...\n"); + srslte_rf_stop_rx_stream(&rf_device); + radio_is_streaming = false; + usleep(100000); +} + +void radio::set_manual_calibration(rf_cal_t* calibration) +{ + srslte_rf_cal_t tx_cal; + tx_cal.dc_gain = calibration->tx_corr_dc_gain; + tx_cal.dc_phase = calibration->tx_corr_dc_phase; + tx_cal.iq_i = calibration->tx_corr_iq_i; + tx_cal.iq_q = calibration->tx_corr_iq_q; + srslte_rf_set_tx_cal(&rf_device, &tx_cal); +} + +void radio::set_tx_rx_gain_offset(float offset) { + srslte_rf_set_tx_rx_gain_offset(&rf_device, offset); +} + +void radio::set_burst_preamble(double preamble_us) +{ + burst_preamble_sec = (double) preamble_us/1e6; +} + +void radio::set_continuous_tx(bool enable) { + continuous_tx = enable; +} + +bool radio::is_continuous_tx() { + return continuous_tx; +} + +void radio::set_tx_adv(int nsamples) +{ + tx_adv_auto = false; + tx_adv_nsamples = nsamples; + if (!nsamples) { + tx_adv_sec = 0; + } + +} + +void radio::set_tx_adv_neg(bool tx_adv_is_neg) { + tx_adv_negative = tx_adv_is_neg; +} + +bool radio::start_agc(bool tx_gain_same_rx) +{ + if (srslte_rf_start_gain_thread(&rf_device, tx_gain_same_rx)) { + fprintf(stderr, "Error opening RF device\n"); + return false; + } + + agc_enabled = true; + + return true; +} +bool radio::rx_at(void* buffer, uint32_t nof_samples, srslte_timestamp_t rx_time) +{ + fprintf(stderr, "Not implemented\n"); + return false; +} + +bool radio::rx_now(void* buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t* rxd_time) +{ + if (!radio_is_streaming) { + srslte_rf_start_rx_stream(&rf_device, false); + radio_is_streaming = true; + } + if (srslte_rf_recv_with_time_multi(&rf_device, buffer, nof_samples, true, + rxd_time?&rxd_time->full_secs:NULL, rxd_time?&rxd_time->frac_secs:NULL) > 0) { + return true; + } else { + return false; + } +} + +void radio::get_time(srslte_timestamp_t *now) { + srslte_rf_get_time(&rf_device, &now->full_secs, &now->frac_secs); +} + +// TODO: Use Calibrated values for this +float radio::set_tx_power(float power) +{ + if (power > 10) { + power = 10; + } + if (power < -50) { + power = -50; + } + float gain = power + 74; + srslte_rf_set_tx_gain(&rf_device, gain); + return gain; +} + +float radio::get_max_tx_power() +{ + return 40; +} + +float radio::get_rssi() +{ + return srslte_rf_get_rssi(&rf_device); +} + +bool radio::has_rssi() +{ + return srslte_rf_has_rssi(&rf_device); +} + +bool radio::is_first_of_burst() { + return is_start_of_burst; +} + +#define BLOCKING_TX true + +bool radio::tx_single(void *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time) { + void *_buffer[SRSLTE_MAX_PORTS]; + + _buffer[0] = buffer; + for (int p = 1; p < SRSLTE_MAX_PORTS; p++) { + _buffer[p] = zeros; + } + + return this->tx(_buffer, nof_samples, tx_time); +} + +bool radio::tx(void *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time) { + if (!tx_adv_negative) { + srslte_timestamp_sub(&tx_time, 0, tx_adv_sec); + } else { + srslte_timestamp_add(&tx_time, 0, tx_adv_sec); + } + + if (is_start_of_burst) { + if (burst_preamble_samples != 0) { + srslte_timestamp_t tx_time_pad; + srslte_timestamp_copy(&tx_time_pad, &tx_time); + srslte_timestamp_sub(&tx_time_pad, 0, burst_preamble_time_rounded); + save_trace(1, &tx_time_pad); + srslte_rf_send_timed_multi(&rf_device, buffer, burst_preamble_samples, tx_time_pad.full_secs, tx_time_pad.frac_secs, true, true, false); + is_start_of_burst = false; + } + } + + // Save possible end of burst time + srslte_timestamp_copy(&end_of_burst_time, &tx_time); + srslte_timestamp_add(&end_of_burst_time, 0, (double) nof_samples/cur_tx_srate); + + save_trace(0, &tx_time); + int ret = srslte_rf_send_timed_multi(&rf_device, buffer, nof_samples, + tx_time.full_secs, tx_time.frac_secs, + BLOCKING_TX, is_start_of_burst, false); + is_start_of_burst = false; + if (ret > 0) { + return true; + } else { + return false; + } +} + +void radio::tx_end() +{ + if (!is_start_of_burst) { + save_trace(2, &end_of_burst_time); + srslte_rf_send_timed2(&rf_device, zeros, 0, end_of_burst_time.full_secs, end_of_burst_time.frac_secs, false, true); + is_start_of_burst = true; + } +} + +void radio::start_trace() { + trace_enabled = true; +} + +void radio::set_tti(uint32_t tti_) { + tti = tti_; +} + +void radio::write_trace(std::string filename) +{ + tr_local_time.writeToBinary(filename + ".local"); + tr_is_eob.writeToBinary(filename + ".eob"); + tr_usrp_time.writeToBinary(filename + ".usrp"); + tr_tx_time.writeToBinary(filename + ".tx"); +} + +void radio::save_trace(uint32_t is_eob, srslte_timestamp_t *tx_time) { + if (trace_enabled) { + tr_local_time.push_cur_time_us(tti); + srslte_timestamp_t usrp_time; + srslte_rf_get_time(&rf_device, &usrp_time.full_secs, &usrp_time.frac_secs); + tr_usrp_time.push(tti, srslte_timestamp_uint32(&usrp_time)); + tr_tx_time.push(tti, srslte_timestamp_uint32(tx_time)); + tr_is_eob.push(tti, is_eob); + } +} + +void radio::set_freq_offset(double freq) { + freq_offset = freq; +} + +void radio::set_rx_freq(double freq) +{ + rx_freq = srslte_rf_set_rx_freq(&rf_device, freq+freq_offset); +} + +void radio::set_rx_gain(float gain) +{ + srslte_rf_set_rx_gain(&rf_device, gain); +} + +double radio::set_rx_gain_th(float gain) +{ + return srslte_rf_set_rx_gain_th(&rf_device, gain); +} + +void radio::set_master_clock_rate(double rate) +{ + srslte_rf_set_master_clock_rate(&rf_device, rate); +} + +void radio::set_rx_srate(double srate) +{ + srslte_rf_set_rx_srate(&rf_device, srate); +} + +void radio::set_tx_freq(double freq) +{ + tx_freq = srslte_rf_set_tx_freq(&rf_device, freq+freq_offset); +} + +void radio::set_tx_gain(float gain) +{ + srslte_rf_set_tx_gain(&rf_device, gain); +} + +double radio::get_rx_freq() +{ + return rx_freq; +} + +double radio::get_freq_offset() +{ + return freq_offset; +} + +double radio::get_tx_freq() +{ + return tx_freq; +} + +float radio::get_tx_gain() +{ + return srslte_rf_get_tx_gain(&rf_device); +} + +float radio::get_rx_gain() +{ + return srslte_rf_get_rx_gain(&rf_device); +} + +void radio::set_tx_srate(double srate) +{ + cur_tx_srate = srslte_rf_set_tx_srate(&rf_device, srate); + burst_preamble_samples = (uint32_t) (cur_tx_srate * burst_preamble_sec); + if (burst_preamble_samples > burst_preamble_max_samples) { + burst_preamble_samples = burst_preamble_max_samples; + fprintf(stderr, "Error setting TX srate %.1f MHz. Maximum frequency for zero prepadding is 30.72 MHz\n", srate*1e-6); + } + burst_preamble_time_rounded = (double) burst_preamble_samples/cur_tx_srate; + + int nsamples=0; + /* Set time advance for each known device if in auto mode */ + if (tx_adv_auto) { + + /* This values have been calibrated using the prach_test_usrp tool in srsLTE */ + + if (!strcmp(srslte_rf_name(&rf_device), "uhd_b200")) { + + double srate_khz = round(cur_tx_srate/1e3); + if (srate_khz == 1.92e3) { + nsamples = 54; + } else if (srate_khz == 3.84e3) { + nsamples = 69; + } else if (srate_khz == 5.76e3) { + nsamples = 93; + } else if (srate_khz == 11.52e3) { + nsamples = 120; + } else if (srate_khz == 15.36e3) { + nsamples = 131; + } else if (srate_khz == 23.04e3) { + nsamples = 150; + } else { + /* Interpolate from known values */ + printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); + nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec); + } + + }else if(!strcmp(srslte_rf_name(&rf_device), "uhd_usrp2")) { + double srate_khz = round(cur_tx_srate/1e3); + if (srate_khz == 1.92e3) { + nsamples = 14;// estimated + } else if (srate_khz == 3.84e3) { + nsamples = 32; + } else if (srate_khz == 5.76e3) { + nsamples = 43; + } else if (srate_khz == 11.52e3) { + nsamples = 54; + } else if (srate_khz == 15.36e3) { + nsamples = 65;// to calc + } else if (srate_khz == 23.04e3) { + nsamples = 80; // to calc + } else { + /* Interpolate from known values */ + printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); + nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec); + } + + } else if(!strcmp(srslte_rf_name(&rf_device), "lime")) { + double srate_khz = round(cur_tx_srate/1e3); + if (srate_khz == 1.92e3) { + nsamples = 70;// estimated + } else if (srate_khz == 3.84e3) { + nsamples = 76;//estimated + } else if (srate_khz == 5.76e3) { + nsamples = 76; + } else if (srate_khz == 11.52e3) { + nsamples = 76; + } else if (srate_khz == 15.36e3) { + nsamples = 73; + } else if (srate_khz == 23.04e3) { + nsamples = 87; + } else { + /* Interpolate from known values */ + printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); + nsamples = cur_tx_srate*(uhd_default_tx_adv_samples * (1/cur_tx_srate) + uhd_default_tx_adv_offset_sec); + } + + } else if (!strcmp(srslte_rf_name(&rf_device), "uhd_x300")) { + + // In X300 TX/RX offset is independent of sampling rate + nsamples = 45; + } else if (!strcmp(srslte_rf_name(&rf_device), "bladerf")) { + + double srate_khz = round(cur_tx_srate/1e3); + if (srate_khz == 1.92e3) { + nsamples = 16; + } else if (srate_khz == 3.84e3) { + nsamples = 18; + } else if (srate_khz == 5.76e3) { + nsamples = 16; + } else if (srate_khz == 11.52e3) { + nsamples = 21; + } else if (srate_khz == 15.36e3) { + nsamples = 14; + } else if (srate_khz == 23.04e3) { + nsamples = 21; + } else { + /* Interpolate from known values */ + printf("\nWarning TX/RX time offset for sampling rate %.0f KHz not calibrated. Using interpolated value\n\n", cur_tx_srate); + tx_adv_sec = blade_default_tx_adv_samples * (1/cur_tx_srate) + blade_default_tx_adv_offset_sec; + } + } else { + printf("\nWarning TX/RX time offset has not been calibrated for device %s. Set a value manually\n\n", srslte_rf_name(&rf_device)); + } + } else { + nsamples = tx_adv_nsamples; + printf("Setting manual TX/RX offset to %d samples\n", nsamples); + } + + // Calculate TX advance in seconds from samples and sampling rate + tx_adv_sec = nsamples/cur_tx_srate; + if (tx_adv_sec<0) { + tx_adv_sec *= -1; + tx_adv_negative = true; + } +} + +void radio::register_error_handler(srslte_rf_error_handler_t h) +{ + srslte_rf_register_error_handler(&rf_device, h); +} + +srslte_rf_info_t *radio::get_info() { + return srslte_rf_get_info(&rf_device); +} + +} + diff --git a/lib/src/radio/radio_multi.cc b/lib/src/radio/radio_multi.cc new file mode 100644 index 0000000..43160c4 --- /dev/null +++ b/lib/src/radio/radio_multi.cc @@ -0,0 +1,64 @@ +#include "srslte/radio/radio_multi.h" + +namespace srslte { + +bool radio_multi::init_multi(uint32_t nof_rx_antennas, char* args, char* devname) +{ + if (srslte_rf_open_devname(&rf_device, devname, args, nof_rx_antennas)) { + fprintf(stderr, "Error opening RF device\n"); + return false; + } + + tx_adv_negative = false; + agc_enabled = false; + burst_preamble_samples = 0; + burst_preamble_time_rounded = 0; + cur_tx_srate = 0; + is_start_of_burst = true; + + // Suppress radio stdout + srslte_rf_suppress_stdout(&rf_device); + + continuous_tx = true; + tx_adv_auto = true; + // Set default preamble length each known device + // We distinguish by device family, maybe we should calibrate per device + if (strstr(srslte_rf_name(&rf_device), "uhd")) { + burst_preamble_sec = uhd_default_burst_preamble_sec; + } else if (strstr(srslte_rf_name(&rf_device), "bladerf")) { + burst_preamble_sec = blade_default_burst_preamble_sec; + } else { + printf("\nWarning burst preamble is not calibrated for device %s. Set a value manually\n\n", srslte_rf_name(&rf_device)); + } + + if (args) { + strncpy(saved_args, args, 127); + } + if (devname) { + strncpy(saved_devname, devname, 127); + } + + is_initialized = true; + return true; +} + +bool radio_multi::rx_now(cf_t *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t* rxd_time) +{ + void *ptr[SRSLTE_MAX_PORTS]; + for (int i=0;ifull_secs:NULL, rxd_time?&rxd_time->frac_secs:NULL) > 0) { + return true; + } else { + return false; + } +} + + +} \ No newline at end of file diff --git a/lib/src/radio/test/CMakeLists.txt b/lib/src/radio/test/CMakeLists.txt new file mode 100644 index 0000000..c44fd82 --- /dev/null +++ b/lib/src/radio/test/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright 2013-2017 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/. +# + +if(RF_FOUND) + add_executable(benchmark_radio benchmark_radio.cc) + target_link_libraries(benchmark_radio srslte_radio) +endif(RF_FOUND) + + diff --git a/lib/src/radio/test/benchmark_radio.cc b/lib/src/radio/test/benchmark_radio.cc new file mode 100644 index 0000000..d15adaa --- /dev/null +++ b/lib/src/radio/test/benchmark_radio.cc @@ -0,0 +1,230 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include "srslte/srslte.h" +#include "srslte/radio/radio_multi.h" + +using namespace srslte; + +std::string device_args = "auto"; + +double freq = 2630e6; +uint32_t nof_ports = 1; +double srate = 1.92e6; /* Hz */ +double duration = 0.01; /* in seconds, 10 ms by default */ +cf_t *buffers[SRSLTE_MAX_PORTS]; +bool tx_enable = false; + +uint32_t num_lates = 0; +uint32_t num_overflows = 0; +uint32_t num_underflows = 0; +uint32_t num_other_error = 0; + + +void usage(char *prog) { + printf("Usage: %s [rpstvh]\n", prog); + printf("\t-f Carrier frequency in Hz [Default %f]\n", freq); + printf("\t-a Arguments for first radio [Default %s]\n", device_args.c_str()); + printf("\t-p number of ports 1-%d [Default %d]\n", SRSLTE_MAX_PORTS, nof_ports); + printf("\t-s sampling rate [Default %.0f]\n", srate); + printf("\t-t duration in seconds [Default %.3f]\n", duration); + printf("\t-x enable transmit [Default %s]\n", (tx_enable) ? "enabled" : "disabled"); + printf("\t-v Set srslte_verbose to info (v) or debug (vv) [Default none]\n"); + printf("\t-h show this message\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "foabcderpstvhmxw")) != -1) { + switch (opt) { + case 'f': + freq = atof(argv[optind]); + break; + case 'a': + device_args = std::string(argv[optind]); + break; + case 'p': + nof_ports = (uint32_t) atoi(argv[optind]); + break; + case 's': + srate = atof(argv[optind]); + break; + case 't': + duration = atof(argv[optind]); + break; + case 'x': + tx_enable ^= true; + break; + case 'v': + srslte_verbose++; + break; + case 'h': + default: + usage(argv[0]); + exit(-1); + } + } +} + +bool go_exit = false; +void sig_int_handler(int signo) +{ + printf("SIGINT received. Exiting...\n"); + if (signo == SIGINT) { + go_exit = true; + } else if (signo == SIGSEGV) { + exit(1); + } +} + +void rf_msg(srslte_rf_error_t error) +{ + if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + num_overflows++; + } else + if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) { + num_underflows++; + } else + if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) { + num_lates++; + } else { + num_other_error++; + } +} + +void print_rf_summary(void) +{ + printf("#lates=%d\n", num_lates); + printf("#overflows=%d\n", num_overflows); + printf("#underflows=%d\n", num_underflows); + printf("#num_other_error=%d\n", num_other_error); +} + +int main(int argc, char **argv) +{ + int ret = SRSLTE_ERROR; + srslte::radio_multi *radio_h = NULL; + srslte_timestamp_t ts_rx = {}, ts_tx = {}; + + signal(SIGINT, sig_int_handler); + + /* Parse args */ + parse_args(argc, argv); + + uint32_t nof_samples = (uint32_t) (duration * srate); + uint32_t frame_size = (uint32_t) (srate / 1000.0); /* 1 ms at srate */ + uint32_t nof_frames = duration * 1e3; + + radio_h = new radio_multi(); + if (!radio_h) { + fprintf(stderr, "Error: Calling radio_multi constructor\n"); + goto clean_exit; + } + + for (uint32_t p = 0; p < SRSLTE_MAX_PORTS; p++) { + buffers[p] = NULL; + } + + for (uint32_t p = 0; p < nof_ports; p++) { + buffers[p] = (cf_t *) srslte_vec_malloc(sizeof(cf_t) * frame_size); + if (!buffers[p]) { + fprintf(stderr, "Error: Allocating buffer (%d)\n", p); + goto clean_exit; + } + } + + /* Initialise instances */ + printf("Initialising instances...\n"); + if (!radio_h->init((char*)device_args.c_str(), NULL, nof_ports)) { + fprintf(stderr, "Error: Calling radio_multi constructor\n"); + goto clean_exit; + } + + radio_h->register_error_handler(rf_msg); + + radio_h->set_rx_freq(freq); + + /* Set radio */ + printf("Setting radio...\n"); + if (srate < 10e6) { + radio_h->set_master_clock_rate(4 * srate); + } else { + radio_h->set_master_clock_rate(srate); + } + + radio_h->set_rx_srate(srate); + if (tx_enable) { + radio_h->set_tx_srate(srate); + } + + /* Receive */ + printf("Initial receive for aligning radios...\n"); + radio_h->rx_now(buffers, frame_size, &ts_rx); + + printf("Start capturing %d frames of %d samples...\n", nof_frames, frame_size); + + for (uint32_t i = 0; i < nof_frames; i++) { + frame_size = SRSLTE_MIN(frame_size, nof_samples); + radio_h->rx_now(buffers, frame_size, &ts_rx); + + if (tx_enable) { + srslte_timestamp_copy(&ts_tx, &ts_rx); + srslte_timestamp_add(&ts_tx, 0, 0.004); + radio_h->tx_single(buffers[0], frame_size, ts_tx); + } + + nof_samples -= frame_size; + + if (go_exit) break; + } + + printf("Finished streaming ...\n"); + + ret = SRSLTE_SUCCESS; + +clean_exit: + printf("Tearing down...\n"); + + radio_h->stop(); + + for (uint32_t p = 0; p < nof_ports; p++) { + if (buffers[p]) { + free(buffers[p]); + } + } + + if (ret) { + printf("Failed!\n"); + } else { + printf("Ok!\n"); + } + + print_rf_summary(); + + return ret; +} diff --git a/lib/src/upper/CMakeLists.txt b/lib/src/upper/CMakeLists.txt new file mode 100644 index 0000000..725d6de --- /dev/null +++ b/lib/src/upper/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srslte_upper STATIC ${SOURCES}) +target_link_libraries(srslte_upper srslte_common srslte_asn1) +install(TARGETS srslte_upper DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/upper/gtpu.cc b/lib/src/upper/gtpu.cc new file mode 100644 index 0000000..a8046aa --- /dev/null +++ b/lib/src/upper/gtpu.cc @@ -0,0 +1,96 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/gtpu.h" + + +namespace srslte { + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + ***************************************************************************/ + +bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log) +{ + if(header->flags != 0x30) { + gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + if(pdu->get_headroom() < GTPU_HEADER_LEN) { + gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); + return false; + } + + pdu->msg -= GTPU_HEADER_LEN; + pdu->N_bytes += GTPU_HEADER_LEN; + + uint8_t *ptr = pdu->msg; + + *ptr = header->flags; + ptr++; + *ptr = header->message_type; + ptr++; + uint16_to_uint8(header->length, ptr); + ptr += 2; + uint32_to_uint8(header->teid, ptr); + + return true; +} + +bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header, srslte::log *gtpu_log) +{ + uint8_t *ptr = pdu->msg; + + pdu->msg += GTPU_HEADER_LEN; + pdu->N_bytes -= GTPU_HEADER_LEN; + + header->flags = *ptr; + ptr++; + header->message_type = *ptr; + ptr++; + uint8_to_uint16(ptr, &header->length); + ptr += 2; + uint8_to_uint32(ptr, &header->teid); + + if(header->flags != 0x30) { + gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + + return true; +} + +} // namespace srslte diff --git a/lib/src/upper/pdcp.cc b/lib/src/upper/pdcp.cc new file mode 100644 index 0000000..e577a12 --- /dev/null +++ b/lib/src/upper/pdcp.cc @@ -0,0 +1,237 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/pdcp.h" + +namespace srslte { + +pdcp::pdcp() +{ + rlc = NULL; + rrc = NULL; + gw = NULL; + pdcp_log = NULL; + lcid = 0; + direction = 0; +} + +void pdcp::init(srsue::rlc_interface_pdcp *rlc_, srsue::rrc_interface_pdcp *rrc_, srsue::gw_interface_pdcp *gw_, log *pdcp_log_, uint32_t lcid_, uint8_t direction_) +{ + rlc = rlc_; + rrc = rrc_; + gw = gw_; + pdcp_log = pdcp_log_; + lcid = lcid_; + direction = direction_; + + // Default config + srslte_pdcp_config_t cnfg; + cnfg.is_control = false; + cnfg.is_data = false; + cnfg.direction = direction_; + + pdcp_array[0].init(rlc, rrc, gw, pdcp_log, lcid, cnfg); +} + +void pdcp::stop() +{ +} + +void pdcp::reestablish() { + for(uint32_t i=0;i= SRSLTE_N_RADIO_BEARERS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid); + return false; + } + return pdcp_array[lcid].is_active(); +} + +void pdcp::write_sdu(uint32_t lcid, byte_buffer_t *sdu) +{ + if(valid_lcid(lcid)) { + pdcp_array[lcid].write_sdu(sdu); + } else { + pdcp_log->warning("Writing sdu: lcid=%d. Deallocating sdu\n", lcid); + byte_buffer_pool::get_instance()->deallocate(sdu); + } +} + +void pdcp::write_sdu_mch(uint32_t lcid, byte_buffer_t *sdu) +{ + if(valid_mch_lcid(lcid)){ + pdcp_array_mrb[lcid].write_sdu(sdu); + } +} +void pdcp::add_bearer(uint32_t lcid, srslte_pdcp_config_t cfg) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid); + return; + } + if (!pdcp_array[lcid].is_active()) { + pdcp_array[lcid].init(rlc, rrc, gw, pdcp_log, lcid, cfg); + pdcp_log->info("Added bearer %s\n", rrc->get_rb_name(lcid).c_str()); + } else { + pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rrc->get_rb_name(lcid).c_str()); + } +} + + +void pdcp::add_bearer_mrb(uint32_t lcid, srslte_pdcp_config_t cfg) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid); + return; + } + if (!pdcp_array_mrb[lcid].is_active()) { + pdcp_array_mrb[lcid].init(rlc, rrc, gw, pdcp_log, lcid, cfg); + pdcp_log->info("Added bearer %s\n", rrc->get_rb_name(lcid).c_str()); + } else { + pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rrc->get_rb_name(lcid).c_str()); + } +} + +void pdcp::config_security(uint32_t lcid, + uint8_t *k_enc, + uint8_t *k_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if(valid_lcid(lcid)) + pdcp_array[lcid].config_security(k_enc, k_int, cipher_algo, integ_algo); +} + +void pdcp::config_security_all(uint8_t *k_enc, + uint8_t *k_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + for(uint32_t i=0;iwarning("Writing pdu: lcid=%d. Deallocating pdu\n", lcid); + byte_buffer_pool::get_instance()->deallocate(pdu); + } +} + +void pdcp::write_pdu_bcch_bch(byte_buffer_t *sdu) +{ + rrc->write_pdu_bcch_bch(sdu); +} +void pdcp::write_pdu_bcch_dlsch(byte_buffer_t *sdu) +{ + rrc->write_pdu_bcch_dlsch(sdu); +} + +void pdcp::write_pdu_pcch(byte_buffer_t *sdu) +{ + rrc->write_pdu_pcch(sdu); +} + +void pdcp::write_pdu_mch(uint32_t lcid, byte_buffer_t *sdu) +{ + if(0 == lcid) { + rrc->write_pdu_mch(lcid, sdu); + } else { + gw->write_pdu_mch(lcid, sdu); + } +} + +/******************************************************************************* + Helpers +*******************************************************************************/ +bool pdcp::valid_lcid(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d", SRSLTE_N_RADIO_BEARERS, lcid); + return false; + } + if(!pdcp_array[lcid].is_active()) { + pdcp_log->error("PDCP entity for logical channel %d has not been activated\n", lcid); + return false; + } + return true; +} + +bool pdcp::valid_mch_lcid(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + pdcp_log->error("Radio bearer id must be in [0:%d] - %d", SRSLTE_N_RADIO_BEARERS, lcid); + return false; + } + if(!pdcp_array_mrb[lcid].is_active()) { + pdcp_log->error("PDCP entity for logical channel %d has not been activated\n", lcid); + return false; + } + return true; +} + +} // namespace srsue diff --git a/lib/src/upper/pdcp_entity.cc b/lib/src/upper/pdcp_entity.cc new file mode 100644 index 0000000..c2c8295 --- /dev/null +++ b/lib/src/upper/pdcp_entity.cc @@ -0,0 +1,471 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/pdcp_entity.h" +#include "srslte/common/security.h" + +namespace srslte { + +pdcp_entity::pdcp_entity() + :active(false) + ,tx_count(0) +{ + pool = byte_buffer_pool::get_instance(); + log = NULL; + rlc = NULL; + rrc = NULL; + gw = NULL; + lcid = 0; + sn_len_bytes = 0; + do_integrity = false; + do_encryption = false; + rx_count = 0; + cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; + integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; +} + +void pdcp_entity::init(srsue::rlc_interface_pdcp *rlc_, + srsue::rrc_interface_pdcp *rrc_, + srsue::gw_interface_pdcp *gw_, + srslte::log *log_, + uint32_t lcid_, + srslte_pdcp_config_t cfg_) +{ + rlc = rlc_; + rrc = rrc_; + gw = gw_; + log = log_; + lcid = lcid_; + cfg = cfg_; + active = true; + tx_count = 0; + rx_count = 0; + do_integrity = false; + do_encryption = false; + + cfg.sn_len = 0; + sn_len_bytes = 0; + + if(cfg.is_control) { + cfg.sn_len = 5; + sn_len_bytes = 1; + } + if(cfg.is_data) { + cfg.sn_len = 12; + sn_len_bytes = 2; + } + + log->debug("Init %s\n", rrc->get_rb_name(lcid).c_str()); +} + +// Reestablishment procedure: 36.323 5.2 +void pdcp_entity::reestablish() { + // For SRBs + if (cfg.is_control) { + tx_count = 0; + rx_count = 0; + } else { + if (rlc->rb_is_um(lcid)) { + tx_count = 0; + rx_count = 0; + } + } +} + +void pdcp_entity::reset() +{ + active = false; + if(log) + log->debug("Reset %s\n", rrc->get_rb_name(lcid).c_str()); +} + +bool pdcp_entity::is_active() +{ + return active; +} + +// RRC interface +void pdcp_entity::write_sdu(byte_buffer_t *sdu) +{ + log->info_hex(sdu->msg, sdu->N_bytes, + "TX %s SDU, SN: %d, do_integrity = %s, do_encryption = %s", + rrc->get_rb_name(lcid).c_str(), tx_count, + (do_integrity) ? "true" : "false", (do_encryption) ? "true" : "false"); + + if (cfg.is_control) { + pdcp_pack_control_pdu(tx_count, sdu); + if(do_integrity) { + integrity_generate(sdu->msg, + sdu->N_bytes-4, + &sdu->msg[sdu->N_bytes-4]); + } + } + + if (cfg.is_data) { + if(12 == cfg.sn_len) { + pdcp_pack_data_pdu_long_sn(tx_count, sdu); + } else { + pdcp_pack_data_pdu_short_sn(tx_count, sdu); + } + } + + if(do_encryption) { + cipher_encrypt(&sdu->msg[sn_len_bytes], + sdu->N_bytes-sn_len_bytes, + &sdu->msg[sn_len_bytes]); + log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU (encrypted)", rrc->get_rb_name(lcid).c_str()); + } + tx_count++; + + rlc->write_sdu(lcid, sdu); +} + +void pdcp_entity::config_security(uint8_t *k_enc_, + uint8_t *k_int_, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) +{ + for(int i=0; i<32; i++) + { + k_enc[i] = k_enc_[i]; + k_int[i] = k_int_[i]; + } + cipher_algo = cipher_algo_; + integ_algo = integ_algo_; +} + +void pdcp_entity::enable_integrity() +{ + do_integrity = true; +} + +void pdcp_entity::enable_encryption() +{ + do_encryption = true; +} + +// RLC interface +void pdcp_entity::write_pdu(byte_buffer_t *pdu) +{ + log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU, do_integrity = %s, do_encryption = %s", + rrc->get_rb_name(lcid).c_str(), (do_integrity) ? "true" : "false", (do_encryption) ? "true" : "false"); + + // Sanity check + if(pdu->N_bytes <= sn_len_bytes) { + pool->deallocate(pdu); + return; + } + + // Handle DRB messages + if (cfg.is_data) { + uint32_t sn; + if (do_encryption) { + cipher_decrypt(&(pdu->msg[sn_len_bytes]), + rx_count, + pdu->N_bytes - sn_len_bytes, + &(pdu->msg[sn_len_bytes])); + log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU (decrypted)", rrc->get_rb_name(lcid).c_str()); + } + if(12 == cfg.sn_len) + { + pdcp_unpack_data_pdu_long_sn(pdu, &sn); + } else { + pdcp_unpack_data_pdu_short_sn(pdu, &sn); + } + log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU SN: %d", rrc->get_rb_name(lcid).c_str(), sn); + gw->write_pdu(lcid, pdu); + } else { + // Handle SRB messages + if (cfg.is_control) { + uint32_t sn; + if (do_encryption) { + cipher_decrypt(&(pdu->msg[sn_len_bytes]), + rx_count, + pdu->N_bytes - sn_len_bytes, + &(pdu->msg[sn_len_bytes])); + log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU (decrypted)", rrc->get_rb_name(lcid).c_str()); + } + + if (do_integrity) { + integrity_verify(pdu->msg, + rx_count, + pdu->N_bytes - 4, + &(pdu->msg[pdu->N_bytes - 4])); + } + + pdcp_unpack_control_pdu(pdu, &sn); + log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU SN: %d", rrc->get_rb_name(lcid).c_str(), sn); + } + // pass to RRC + rrc->write_pdu(lcid, pdu); + } + rx_count++; +} + +void pdcp_entity::integrity_generate( uint8_t *msg, + uint32_t msg_len, + uint8_t *mac) +{ + uint8_t bearer; + + switch(integ_algo) + { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(&k_int[16], + tx_count, + get_bearer_id(lcid), + cfg.direction, + msg, + msg_len, + mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(&k_int[16], + tx_count, + get_bearer_id(lcid), + cfg.direction, + msg, + msg_len, + mac); + break; + default: + break; + } +} + +bool pdcp_entity::integrity_verify(uint8_t *msg, + uint32_t count, + uint32_t msg_len, + uint8_t *mac) +{ + uint8_t mac_exp[4] = {0x00}; + uint8_t i = 0; + bool isValid = true; + + switch(integ_algo) + { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(&k_int[16], + count, + get_bearer_id(lcid), + (cfg.direction == SECURITY_DIRECTION_DOWNLINK) ? (SECURITY_DIRECTION_UPLINK) : (SECURITY_DIRECTION_DOWNLINK), + msg, + msg_len, + mac_exp); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(&k_int[16], + count, + get_bearer_id(lcid), + (cfg.direction == SECURITY_DIRECTION_DOWNLINK) ? (SECURITY_DIRECTION_UPLINK) : (SECURITY_DIRECTION_DOWNLINK), + msg, + msg_len, + mac_exp); + break; + default: + break; + } + + switch(integ_algo) + { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: // Intentional fall-through + case INTEGRITY_ALGORITHM_ID_128_EIA2: + for(i=0; i<4; i++){ + if(mac[i] != mac_exp[i]){ + log->error_hex(mac_exp, 4, "MAC mismatch (expected)"); + log->error_hex(mac, 4, "MAC mismatch (found)"); + isValid = false; + break; + } + } + if (isValid){ + log->info_hex(mac_exp, 4, "MAC match (expected)"); + log->info_hex(mac, 4, "MAC match (found)"); + } + break; + default: + break; + } + + return isValid; +} + +void pdcp_entity::cipher_encrypt(uint8_t *msg, + uint32_t msg_len, + uint8_t *ct) +{ + byte_buffer_t ct_tmp; + switch(cipher_algo) + { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&(k_enc[16]), + tx_count, + get_bearer_id(lcid), + cfg.direction, + msg, + msg_len, + ct_tmp.msg); + memcpy(ct, ct_tmp.msg, msg_len); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&(k_enc[16]), + tx_count, + get_bearer_id(lcid), + cfg.direction, + msg, + msg_len, + ct_tmp.msg); + memcpy(ct, ct_tmp.msg, msg_len); + break; + default: + break; + } +} + +void pdcp_entity::cipher_decrypt(uint8_t *ct, + uint32_t count, + uint32_t ct_len, + uint8_t *msg) +{ + byte_buffer_t msg_tmp; + switch(cipher_algo) + { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&(k_enc[16]), + count, + get_bearer_id(lcid), + (cfg.direction == SECURITY_DIRECTION_DOWNLINK) ? (SECURITY_DIRECTION_UPLINK) : (SECURITY_DIRECTION_DOWNLINK), + ct, + ct_len, + msg_tmp.msg); + memcpy(msg, msg_tmp.msg, ct_len); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&(k_enc[16]), + count, + get_bearer_id(lcid), + (cfg.direction == SECURITY_DIRECTION_DOWNLINK) ? (SECURITY_DIRECTION_UPLINK) : (SECURITY_DIRECTION_DOWNLINK), + ct, + ct_len, + msg_tmp.msg); + memcpy(msg, msg_tmp.msg, ct_len); + break; + default: + break; + } +} + + +uint8_t pdcp_entity::get_bearer_id(uint8_t lcid) +{ + #define RB_ID_SRB2 2 + if(lcid <= RB_ID_SRB2) { + return lcid - 1; + } else { + return lcid - RB_ID_SRB2 - 1; + } +} + + +/**************************************************************************** + * Pack/Unpack helper functions + * Ref: 3GPP TS 36.323 v10.1.0 + ***************************************************************************/ + +void pdcp_pack_control_pdu(uint32_t sn, byte_buffer_t *sdu) +{ + // Make room and add header + sdu->msg--; + sdu->N_bytes++; + *sdu->msg = sn & 0x1F; + + // Add MAC + sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 24) & 0xFF; + sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 16) & 0xFF; + sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 8) & 0xFF; + sdu->msg[sdu->N_bytes++] = PDCP_CONTROL_MAC_I & 0xFF; + +} + +void pdcp_unpack_control_pdu(byte_buffer_t *pdu, uint32_t *sn) +{ + // Strip header + *sn = *pdu->msg & 0x1F; + pdu->msg++; + pdu->N_bytes--; + + // Strip MAC + pdu->N_bytes -= 4; + + // TODO: integrity check MAC +} + +void pdcp_pack_data_pdu_short_sn(uint32_t sn, byte_buffer_t *sdu) +{ + // Make room and add header + sdu->msg--; + sdu->N_bytes++; + sdu->msg[0] = (PDCP_D_C_DATA_PDU << 7) | (sn & 0x7F); +} + +void pdcp_unpack_data_pdu_short_sn(byte_buffer_t *sdu, uint32_t *sn) +{ + // Strip header + *sn = sdu->msg[0] & 0x7F; + sdu->msg++; + sdu->N_bytes--; +} + +void pdcp_pack_data_pdu_long_sn(uint32_t sn, byte_buffer_t *sdu) +{ + // Make room and add header + sdu->msg -= 2; + sdu->N_bytes += 2; + sdu->msg[0] = (PDCP_D_C_DATA_PDU << 7) | ((sn >> 8) & 0x0F); + sdu->msg[1] = sn & 0xFF; +} + +void pdcp_unpack_data_pdu_long_sn(byte_buffer_t *sdu, uint32_t *sn) +{ + // Strip header + *sn = (sdu->msg[0] & 0x0F) << 8; + *sn |= sdu->msg[1]; + sdu->msg += 2; + sdu->N_bytes -= 2; +} + +} diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc new file mode 100644 index 0000000..c24f007 --- /dev/null +++ b/lib/src/upper/rlc.cc @@ -0,0 +1,387 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 PARTICRXAR 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 "srslte/upper/rlc.h" +#include "srslte/upper/rlc_tm.h" +#include "srslte/upper/rlc_um.h" +#include "srslte/upper/rlc_am.h" + +namespace srslte { + +rlc::rlc() +{ + pool = byte_buffer_pool::get_instance(); + rlc_log = NULL; + pdcp = NULL; + rrc = NULL; + mac_timers = NULL; + ue = NULL; + default_lcid = 0; + bzero(metrics_time, sizeof(metrics_time)); + bzero(ul_tput_bytes, sizeof(ul_tput_bytes)); + bzero(dl_tput_bytes, sizeof(dl_tput_bytes)); +} + +void rlc::init(srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + srsue::ue_interface *ue_, + log *rlc_log_, + mac_interface_timers *mac_timers_, + uint32_t lcid_, + int buffer_size_) +{ + pdcp = pdcp_; + rrc = rrc_; + ue = ue_; + rlc_log = rlc_log_; + mac_timers = mac_timers_; + default_lcid = lcid_; + buffer_size = buffer_size_; + + gettimeofday(&metrics_time[1], NULL); + reset_metrics(); + + rlc_array[0].init(RLC_MODE_TM, rlc_log, default_lcid, pdcp, rrc, mac_timers, buffer_size); // SRB0 +} + +void rlc::reset_metrics() +{ + bzero(dl_tput_bytes, sizeof(long)*SRSLTE_N_RADIO_BEARERS); + bzero(ul_tput_bytes, sizeof(long)*SRSLTE_N_RADIO_BEARERS); +} + +void rlc::stop() +{ + for(uint32_t i=0; iinfo("LCID=%d, RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n", + i, + (dl_tput_bytes[i]*8/(double)1e6)/secs, + (ul_tput_bytes[i]*8/(double)1e6)/secs); + } + } + + // Add multicast metrics + for (int i=0;iinfo("MCH_LCID=%d, RX throughput: %4.6f Mbps.\n", + i, + (dl_tput_bytes_mrb[i]*8/(double)1e6)/secs); + } + } + + memcpy(&metrics_time[1], &metrics_time[2], sizeof(struct timeval)); + reset_metrics(); +} + +// A call to reestablish stops all lcids but does not delete the instances. The mapping lcid to rlc mode can not change +void rlc::reestablish() { + for(uint32_t i=0; iinfo_hex(payload, nof_bytes, "BCCH BCH message received."); + dl_tput_bytes[0] += nof_bytes; + byte_buffer_t *buf = pool_allocate; + if (buf) { + memcpy(buf->msg, payload, nof_bytes); + buf->N_bytes = nof_bytes; + buf->set_timestamp(); + pdcp->write_pdu_bcch_bch(buf); + } else { + rlc_log->error("Fatal error: Out of buffers from the pool in write_pdu_bcch_bch()\n"); + } +} + +void rlc::write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes) +{ + rlc_log->info_hex(payload, nof_bytes, "BCCH TXSCH message received."); + dl_tput_bytes[0] += nof_bytes; + byte_buffer_t *buf = pool_allocate; + if (buf) { + memcpy(buf->msg, payload, nof_bytes); + buf->N_bytes = nof_bytes; + buf->set_timestamp(); + pdcp->write_pdu_bcch_dlsch(buf); + } else { + rlc_log->error("Fatal error: Out of buffers from the pool in write_pdu_bcch_dlsch()\n"); + } +} + +void rlc::write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) +{ + rlc_log->info_hex(payload, nof_bytes, "PCCH message received."); + dl_tput_bytes[0] += nof_bytes; + byte_buffer_t *buf = pool_allocate; + if (buf) { + memcpy(buf->msg, payload, nof_bytes); + buf->N_bytes = nof_bytes; + buf->set_timestamp(); + pdcp->write_pdu_pcch(buf); + } else { + rlc_log->error("Fatal error: Out of buffers from the pool in write_pdu_pcch()\n"); + } +} + +void rlc::write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) +{ + if(valid_lcid_mrb(lcid)) { + dl_tput_bytes_mrb[lcid] += nof_bytes; + rlc_array_mrb[lcid].write_pdu(payload, nof_bytes); + } +} + +/******************************************************************************* + RRC interface +*******************************************************************************/ +void rlc::add_bearer(uint32_t lcid) +{ + // No config provided - use defaults for SRB1 and SRB2 + if(lcid < 3) { + if (!rlc_array[lcid].active()) { + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS45; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_INFINITY; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_INFINITY; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS35; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS0; + add_bearer(lcid, srslte_rlc_config_t(&cnfg)); + } else { + rlc_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rrc->get_rb_name(lcid).c_str()); + } + }else{ + rlc_log->error("Radio bearer %s does not support default RLC configuration.\n", rrc->get_rb_name(lcid).c_str()); + } +} + +void rlc::add_bearer(uint32_t lcid, srslte_rlc_config_t cnfg) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_RADIO_BEARERS, lcid); + return; + } + + if (!rlc_array[lcid].active()) { + rlc_log->warning("Adding radio bearer %s with mode %s\n", + rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg.rlc_mode]); + switch(cnfg.rlc_mode) + { + case LIBLTE_RRC_RLC_MODE_AM: + rlc_array[lcid].init(RLC_MODE_AM, rlc_log, lcid, pdcp, rrc, mac_timers, buffer_size); + break; + case LIBLTE_RRC_RLC_MODE_UM_BI: + rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers, buffer_size); + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_DL: + rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers, buffer_size); + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_UL: + rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers, buffer_size); + break; + default: + rlc_log->error("Cannot add RLC entity - invalid mode\n"); + return; + } + } else { + rlc_log->warning("Bearer %s already created.\n", rrc->get_rb_name(lcid).c_str()); + } + rlc_array[lcid].configure(cnfg); + +} + +void rlc::add_bearer_mrb(uint32_t lcid) +{ + // 36.321 Table 6.2.1-4 + if(lcid >= SRSLTE_N_MCH_LCIDS) { + rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + rlc_array_mrb[lcid].init(rlc_log, lcid, pdcp, rrc, mac_timers); + rlc_array_mrb[lcid].configure(srslte_rlc_config_t::mch_config()); +} + +void rlc::add_bearer_mrb_enb(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + rlc_array_mrb[lcid].init(rlc_log,lcid,pdcp,rrc,mac_timers); + rlc_array_mrb[lcid].configure(srslte_rlc_config_t::mch_config()); +} + +/******************************************************************************* + Helpers +*******************************************************************************/ +bool rlc::valid_lcid(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_RADIO_BEARERS) { + rlc_log->warning("Invalid LCID=%d\n", lcid); + return false; + } else if(!rlc_array[lcid].active()) { + return false; + } + return true; +} + +bool rlc::valid_lcid_mrb(uint32_t lcid) +{ + if(lcid >= SRSLTE_N_MCH_LCIDS) { + return false; + } + if(!rlc_array_mrb[lcid].is_mrb()) { + return false; + } + return true; +} + + +} // namespace srsue diff --git a/lib/src/upper/rlc_am.cc b/lib/src/upper/rlc_am.cc new file mode 100644 index 0000000..768f500 --- /dev/null +++ b/lib/src/upper/rlc_am.cc @@ -0,0 +1,1816 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/rlc_am.h" + +#include +#include + +#define MOD 1024 +#define RX_MOD_BASE(x) ((x-vr_r)%1024) +#define TX_MOD_BASE(x) ((x-vt_a)%1024) + +namespace srslte { + +rlc_am::rlc_am(uint32_t queue_len) : tx_sdu_queue(queue_len) +{ + log = NULL; + pdcp = NULL; + rrc = NULL; + lcid = 0; + bzero(&cfg, sizeof(srslte_rlc_am_config_t)); + + tx_sdu = NULL; + rx_sdu = NULL; + pool = byte_buffer_pool::get_instance(); + + pthread_mutex_init(&mutex, NULL); + + vt_a = 0; + vt_ms = RLC_AM_WINDOW_SIZE; + vt_s = 0; + poll_sn = 0; + + vr_r = 0; + vr_mr = RLC_AM_WINDOW_SIZE; + vr_x = 0; + vr_ms = 0; + vr_h = 0; + + pdu_without_poll = 0; + byte_without_poll = 0; + + poll_received = false; + do_status = false; +} + +// Warning: must call stop() to properly deallocate all buffers +rlc_am::~rlc_am() +{ + pthread_mutex_destroy(&mutex); + pool = NULL; +} + +void rlc_am::init(srslte::log *log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + srslte::mac_interface_timers *mac_timers) +{ + log = log_; + lcid = lcid_; + pdcp = pdcp_; + rrc = rrc_; + tx_enabled = true; +} + +void rlc_am::configure(srslte_rlc_config_t cfg_) +{ + cfg = cfg_.am; + log->warning("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " + "t_reordering=%d, t_status_prohibit=%d\n", + rrc->get_rb_name(lcid).c_str(), cfg.t_poll_retx, cfg.poll_pdu, cfg.poll_byte, cfg.max_retx_thresh, + cfg.t_reordering, cfg.t_status_prohibit); +} + + +void rlc_am::empty_queue() { + // Drop all messages in TX SDU queue + byte_buffer_t *buf; + while(tx_sdu_queue.try_read(&buf)) { + pool->deallocate(buf); + } + tx_sdu_queue.reset(); +} + +void rlc_am::reestablish() { + stop(); + tx_enabled = true; +} + +void rlc_am::stop() +{ + // Empty tx_sdu_queue before locking the mutex + tx_enabled = false; + usleep(100); + empty_queue(); + + pthread_mutex_lock(&mutex); + reordering_timeout.reset(); + if(tx_sdu) { + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + if(rx_sdu) { + pool->deallocate(rx_sdu); + rx_sdu = NULL; + } + + vt_a = 0; + vt_ms = RLC_AM_WINDOW_SIZE; + vt_s = 0; + poll_sn = 0; + + vr_r = 0; + vr_mr = RLC_AM_WINDOW_SIZE; + vr_x = 0; + vr_ms = 0; + vr_h = 0; + + pdu_without_poll = 0; + byte_without_poll = 0; + + poll_received = false; + do_status = false; + + // Drop all messages in RX segments + std::map::iterator rxsegsit; + std::list::iterator segit; + for(rxsegsit = rx_segments.begin(); rxsegsit != rx_segments.end(); rxsegsit++) { + std::list l = rxsegsit->second.segments; + for(segit = l.begin(); segit != l.end(); segit++) { + pool->deallocate(segit->buf); + } + l.clear(); + } + rx_segments.clear(); + + // Drop all messages in RX window + std::map::iterator rxit; + for(rxit = rx_window.begin(); rxit != rx_window.end(); rxit++) { + pool->deallocate(rxit->second.buf); + } + rx_window.clear(); + + // Drop all messages in TX window + std::map::iterator txit; + for(txit = tx_window.begin(); txit != tx_window.end(); txit++) { + pool->deallocate(txit->second.buf); + } + tx_window.clear(); + + // Drop all messages in RETX queue + retx_queue.clear(); + pthread_mutex_unlock(&mutex); +} + +rlc_mode_t rlc_am::get_mode() +{ + return RLC_MODE_AM; +} + +uint32_t rlc_am::get_bearer() +{ + return lcid; +} + +/**************************************************************************** + * PDCP interface + ***************************************************************************/ + +void rlc_am::write_sdu(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + tx_sdu_queue.write(sdu); + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +void rlc_am::write_sdu_nb(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + if (tx_sdu_queue.try_write(sdu)) { + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + } else { + log->debug_hex(sdu->msg, sdu->N_bytes, "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + pool->deallocate(sdu); + } + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +/**************************************************************************** + * MAC interface + ***************************************************************************/ + +uint32_t rlc_am::get_total_buffer_state() +{ + pthread_mutex_lock(&mutex); + uint32_t n_bytes = 0; + uint32_t n_sdus = 0; + + // Bytes needed for status report + check_reordering_timeout(); + if(do_status && !status_prohibited()) { + n_bytes += prepare_status(); + log->debug("Buffer state - status report: %d bytes\n", n_bytes); + } + + // Bytes needed for retx + if(retx_queue.size() > 0) { + rlc_amd_retx_t retx = retx_queue.front(); + log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end); + if(tx_window.end() != tx_window.find(retx.sn)) { + int req_bytes = required_buffer_size(retx); + if (req_bytes < 0) { + log->error("In get_total_buffer_state(): Removing retx.sn=%d from queue\n", retx.sn); + retx_queue.pop_front(); + } else { + n_bytes += req_bytes; + log->debug("Buffer state - retx: %d bytes\n", n_bytes); + } + } + } + + // Bytes needed for tx SDUs + if(tx_window.size() < 1024) { + n_sdus = tx_sdu_queue.size(); + n_bytes += tx_sdu_queue.size_bytes(); + if(tx_sdu) + { + n_sdus++; + n_bytes += tx_sdu->N_bytes; + } + } + + // Room needed for header extensions? (integer rounding) + if(n_sdus > 1) + n_bytes += ((n_sdus-1)*1.5)+0.5; + + // Room needed for fixed header? + if(n_bytes > 0) { + n_bytes += 3; + log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes); + } + + pthread_mutex_unlock(&mutex); + return n_bytes; +} + +uint32_t rlc_am::get_buffer_state() +{ + pthread_mutex_lock(&mutex); + uint32_t n_bytes = 0; + uint32_t n_sdus = 0; + + // Bytes needed for status report + check_reordering_timeout(); + if(do_status && !status_prohibited()) { + n_bytes = prepare_status(); + log->debug("Buffer state - status report: %d bytes\n", n_bytes); + goto unlock_and_return; + } + + // check if pollRetx timer expired (Section 5.2.2.3 in TS 36.322) + if (poll_retx()) { + // if both tx and retx buffer are empty, retransmit next PDU to be ack'ed + log->debug("Poll reTx timer expired (lcid=%d)\n", lcid); + if ((tx_window.size() > 0 && retx_queue.size() == 0 && tx_sdu_queue.size() == 0)) { + std::map::iterator it = tx_window.find(vt_s - 1); + if (it != tx_window.end()) { + log->info("Schedule last PDU (SN=%d) for reTx.\n", vt_s - 1); + rlc_amd_retx_t retx; + retx.is_segment = false; + retx.so_start = 0; + retx.so_end = tx_window[vt_s - 1].buf->N_bytes; + retx.sn = vt_s - 1; + retx_queue.push_back(retx); + } else { + log->error("Found invalid PDU in tx_window.\n"); + } + poll_retx_timeout.start(cfg.t_poll_retx); + } + } + + // Bytes needed for retx + if(retx_queue.size() > 0) { + rlc_amd_retx_t retx = retx_queue.front(); + log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end); + if(tx_window.end() != tx_window.find(retx.sn)) { + int req_bytes = required_buffer_size(retx); + if (req_bytes < 0) { + log->error("In get_buffer_state(): Removing retx.sn=%d from queue\n", retx.sn); + retx_queue.pop_front(); + goto unlock_and_return; + } + n_bytes = (uint32_t) req_bytes; + log->debug("Buffer state - retx: %d bytes\n", n_bytes); + goto unlock_and_return; + } + } + + // Bytes needed for tx SDUs + if(tx_window.size() < 1024) { + n_sdus = tx_sdu_queue.size(); + n_bytes = tx_sdu_queue.size_bytes(); + if(tx_sdu) + { + n_sdus++; + n_bytes += tx_sdu->N_bytes; + } + } + + // Room needed for header extensions? (integer rounding) + if(n_sdus > 1) + n_bytes += ((n_sdus-1)*1.5)+0.5; + + // Room needed for fixed header? + if(n_bytes > 0) { + n_bytes += 3; + log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes); + } + +unlock_and_return: + pthread_mutex_unlock(&mutex); + return n_bytes; +} + +int rlc_am::read_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + pthread_mutex_lock(&mutex); + + log->debug("MAC opportunity - %d bytes\n", nof_bytes); + log->debug("tx_window size - %zu PDUs\n", tx_window.size()); + + // Tx STATUS if requested + if(do_status && !status_prohibited()) { + pthread_mutex_unlock(&mutex); + return build_status_pdu(payload, nof_bytes); + } + + // if tx_window is full and retx_queue empty, retransmit next PDU to be ack'ed + if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.size() == 0) { + if (tx_window[vt_a].buf != NULL) { + log->warning("Full Tx window, ReTx'ing first outstanding PDU\n"); + rlc_amd_retx_t retx; + retx.is_segment = false; + retx.so_start = 0; + retx.so_end = tx_window[vt_a].buf->N_bytes; + retx.sn = vt_a; + retx_queue.push_back(retx); + } else { + log->error("Found invalid PDU in tx_window.\n"); + } + } + + // RETX if required + if(retx_queue.size() > 0) { + int ret = build_retx_pdu(payload, nof_bytes); + if (ret > 0) { + pthread_mutex_unlock(&mutex); + return ret; + } + } + + // Build a PDU from SDUs + int ret = build_data_pdu(payload, nof_bytes); + + pthread_mutex_unlock(&mutex); + return ret; +} + +void rlc_am::write_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(nof_bytes < 1) + return; + pthread_mutex_lock(&mutex); + + if(rlc_am_is_control_pdu(payload)) { + handle_control_pdu(payload, nof_bytes); + } else { + rlc_amd_pdu_header_t header; + rlc_am_read_data_pdu_header(&payload, &nof_bytes, &header); + if(header.rf) { + handle_data_pdu_segment(payload, nof_bytes, header); + }else{ + handle_data_pdu(payload, nof_bytes, header); + } + } + pthread_mutex_unlock(&mutex); +} + +/**************************************************************************** + * Timer checks + ***************************************************************************/ + +bool rlc_am::status_prohibited() +{ + return (status_prohibit_timeout.is_running() && !status_prohibit_timeout.expired()); +} + +bool rlc_am::poll_retx() +{ + return (poll_retx_timeout.is_running() && poll_retx_timeout.expired()); +} + +void rlc_am::check_reordering_timeout() +{ + if(reordering_timeout.is_running() && reordering_timeout.expired()) + { + reordering_timeout.reset(); + log->debug("%s reordering timeout expiry - updating vr_ms\n", rrc->get_rb_name(lcid).c_str()); + + // 36.322 v10 Section 5.1.3.2.4 + vr_ms = vr_x; + std::map::iterator it = rx_window.find(vr_ms); + while(rx_window.end() != it) + { + vr_ms = (vr_ms + 1)%MOD; + it = rx_window.find(vr_ms); + } + if(poll_received) + do_status = true; + + if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_ms)) + { + reordering_timeout.start(cfg.t_reordering); + vr_x = vr_h; + } + + debug_state(); + } +} + +/**************************************************************************** + * Helpers + ***************************************************************************/ + +bool rlc_am::poll_required() +{ + if(cfg.poll_pdu > 0 && pdu_without_poll > (uint32_t)cfg.poll_pdu) + return true; + if(cfg.poll_byte > 0 && byte_without_poll > (uint32_t)cfg.poll_byte) + return true; + if(poll_retx()) + return true; + + if(tx_sdu_queue.size() == 0 && retx_queue.size() == 0) + return true; + + /* According to 5.2.2.1 in 36.322 v13.3.0 a poll should be requested if + * the entire AM window is unacknowledged, i.e. no new PDU can be transmitted. + * However, it seems more appropiate to request more often if polling + * is disabled otherwise, e.g. every N PDUs. + */ + if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % poll_periodicity == 0) + return true; + + return false; +} + +int rlc_am::prepare_status() +{ + status.N_nack = 0; + status.ack_sn = vr_ms; + + // We don't use segment NACKs - just NACK the full PDU + + uint32_t i = vr_r; + while(RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms)) + { + if(rx_window.find(i) == rx_window.end()) + status.nacks[status.N_nack++].nack_sn = i; + i = (i + 1)%MOD; + } + + return rlc_am_packed_length(&status); +} + +int rlc_am::build_status_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + int pdu_len = rlc_am_packed_length(&status); + if(pdu_len > 0 && nof_bytes >= (uint32_t)pdu_len) + { + log->info("%s Tx status PDU - %s\n", + rrc->get_rb_name(lcid).c_str(), rlc_am_to_string(&status).c_str()); + + do_status = false; + poll_received = false; + + if(cfg.t_status_prohibit > 0) + status_prohibit_timeout.start(cfg.t_status_prohibit); + debug_state(); + return rlc_am_write_status_pdu(&status, payload); + }else{ + log->warning("%s Cannot tx status PDU - %d bytes available, %d bytes required\n", + rrc->get_rb_name(lcid).c_str(), nof_bytes, pdu_len); + return 0; + } +} + +int rlc_am::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + // Check there is at least 1 element before calling front() + if (retx_queue.empty()) { + log->error("In build_retx_pdu(): retx_queue is empty\n"); + return -1; + } + + rlc_amd_retx_t retx = retx_queue.front(); + + // Sanity check - drop any retx SNs not present in tx_window + while(tx_window.end() == tx_window.find(retx.sn)) { + retx_queue.pop_front(); + if (!retx_queue.empty()) { + retx = retx_queue.front(); + } else { + log->info("In build_retx_pdu(): retx_queue is empty during sanity check, sn=%d\n", retx.sn); + return 0; + } + } + + // Is resegmentation needed? + int req_size = required_buffer_size(retx); + if (req_size < 0) { + log->error("In build_retx_pdu(): Removing retx.sn=%d from queue\n", retx.sn); + retx_queue.pop_front(); + return -1; + } + if(retx.is_segment || req_size > (int)nof_bytes) { + log->debug("%s build_retx_pdu - resegmentation required\n", rrc->get_rb_name(lcid).c_str()); + return build_segment(payload, nof_bytes, retx); + } + + // Update & write header + rlc_amd_pdu_header_t new_header = tx_window[retx.sn].header; + new_header.p = 0; + + // Set poll bit + pdu_without_poll++; + byte_without_poll += (tx_window[retx.sn].buf->N_bytes + rlc_am_packed_length(&new_header)); + log->info("%s pdu_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), pdu_without_poll); + log->info("%s byte_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), byte_without_poll); + if(poll_required()) + { + new_header.p = 1; + poll_sn = vt_s; + pdu_without_poll = 0; + byte_without_poll = 0; + poll_retx_timeout.start(cfg.t_poll_retx); + } + + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&new_header, &ptr); + memcpy(ptr, tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes); + + retx_queue.pop_front(); + tx_window[retx.sn].retx_count++; + if(tx_window[retx.sn].retx_count >= cfg.max_retx_thresh) + rrc->max_retx_attempted(); + log->info("%s Retx PDU scheduled for tx. SN: %d, retx count: %d\n", + rrc->get_rb_name(lcid).c_str(), retx.sn, tx_window[retx.sn].retx_count); + + debug_state(); + return (ptr-payload) + tx_window[retx.sn].buf->N_bytes; +} + +int rlc_am::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx) +{ + if (!tx_window[retx.sn].buf) { + log->error("In build_segment: retx.sn=%d has null buffer\n", retx.sn); + return 0; + } + if(!retx.is_segment){ + retx.so_start = 0; + retx.so_end = tx_window[retx.sn].buf->N_bytes; + } + + // Construct new header + rlc_amd_pdu_header_t new_header; + rlc_amd_pdu_header_t old_header = tx_window[retx.sn].header; + + pdu_without_poll++; + byte_without_poll += (tx_window[retx.sn].buf->N_bytes + rlc_am_packed_length(&new_header)); + log->info("%s pdu_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), pdu_without_poll); + log->info("%s byte_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), byte_without_poll); + + new_header.dc = RLC_DC_FIELD_DATA_PDU; + new_header.rf = 1; + new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED; + new_header.sn = old_header.sn; + new_header.lsf = 0; + new_header.so = retx.so_start; + new_header.N_li = 0; + new_header.p = 0; + if(poll_required()) + { + log->debug("%s setting poll bit to request status\n", rrc->get_rb_name(lcid).c_str()); + new_header.p = 1; + poll_sn = vt_s; + pdu_without_poll = 0; + byte_without_poll = 0; + poll_retx_timeout.start(cfg.t_poll_retx); + } + + uint32_t head_len = 0; + uint32_t pdu_space = 0; + + head_len = rlc_am_packed_length(&new_header); + if(nof_bytes <= head_len) + { + log->warning("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header\n", + rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len); + return 0; + } + + pdu_space = nof_bytes-head_len; + if(pdu_space < (retx.so_end-retx.so_start)) + retx.so_end = retx.so_start+pdu_space; + + // Need to rebuild the li table & update fi based on so_start and so_end + if(retx.so_start == 0 && rlc_am_start_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned + + uint32_t lower = 0; + uint32_t upper = 0; + uint32_t li = 0; + + for(uint32_t i=0; i= retx.so_end) + break; + + if(pdu_space <= 2) + break; + + upper += old_header.li[i]; + + head_len = rlc_am_packed_length(&new_header); + pdu_space = nof_bytes-head_len; + if(pdu_space < (retx.so_end-retx.so_start)) + retx.so_end = retx.so_start+pdu_space; + + if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed + li = upper - lower; + if(upper > retx.so_end) + li -= upper - retx.so_end; + if(lower < retx.so_start) + li -= retx.so_start - lower; + if(lower > 0 && lower == retx.so_start) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU + if(upper == retx.so_end) { + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU + } + new_header.li[new_header.N_li++] = li; + } + + lower += old_header.li[i]; + } + + // Make sure LI is not deleted in case the SDU boundary is crossed + // FIXME: fix if N_li > 1 + if (new_header.N_li == 1 && retx.so_start + new_header.li[0] < retx.so_end && retx.so_end <= retx.so_start + pdu_space) { + // This segment crosses a SDU boundary + new_header.N_li++; + } + + // Update retx_queue + if(tx_window[retx.sn].buf->N_bytes == retx.so_end) { + retx_queue.pop_front(); + new_header.lsf = 1; + if(rlc_am_end_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment is end aligned + } else if(retx_queue.front().so_end == retx.so_end) { + retx_queue.pop_front(); + } else { + retx_queue.front().is_segment = true; + retx_queue.front().so_start = retx.so_end; + if(new_header.N_li > 0) + new_header.N_li--; + } + + // Write header and pdu + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&new_header, &ptr); + uint8_t* data = &tx_window[retx.sn].buf->msg[retx.so_start]; + uint32_t len = retx.so_end - retx.so_start; + memcpy(ptr, data, len); + + log->info("%s Retx PDU segment scheduled for tx. SN: %d, SO: %d\n", + rrc->get_rb_name(lcid).c_str(), retx.sn, retx.so_start); + + debug_state(); + int pdu_len = (ptr-payload) + len; + if(pdu_len > (int)nof_bytes) { + log->error("%s Retx PDU segment length error. Available: %d, Used: %d\n", + rrc->get_rb_name(lcid).c_str(), nof_bytes, pdu_len); + log->debug("%s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d\n", + rrc->get_rb_name(lcid).c_str(), (ptr-payload), len, new_header.N_li); + } + return pdu_len; + +} + +int rlc_am::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(!tx_sdu && tx_sdu_queue.size() == 0) + { + log->info("No data available to be sent\n"); + return 0; + } + + // do not build any more PDU if window is already full + if (!tx_sdu && tx_window.size() >= RLC_AM_WINDOW_SIZE) { + log->info("Tx window full.\n"); + return 0; + } + + byte_buffer_t *pdu = pool_allocate; + if (!pdu) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Could not allocate PDU in build_data_pdu()\n"); + log->console("tx_window size: %d PDUs\n", tx_window.size()); + log->console("vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d " + "vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d\n", + vt_a, vt_ms, vt_s, poll_sn, + vr_r, vr_mr, vr_x, vr_ms, vr_h); + log->console("retx_queue size: %d PDUs\n", retx_queue.size()); + std::map::iterator txit; + for(txit = tx_window.begin(); txit != tx_window.end(); txit++) { + log->console("tx_window - SN: %d\n", txit->first); + } + exit(-1); +#else + log->error("Fatal Error: Couldn't allocate PDU in build_data_pdu().\n"); + return 0; +#endif + } + rlc_amd_pdu_header_t header; + header.dc = RLC_DC_FIELD_DATA_PDU; + header.rf = 0; + header.p = 0; + header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; + header.sn = vt_s; + header.lsf = 0; + header.so = 0; + header.N_li = 0; + + uint32_t head_len = rlc_am_packed_length(&header); + uint32_t to_move = 0; + uint32_t last_li = 0; + uint32_t pdu_space = nof_bytes; + uint8_t *pdu_ptr = pdu->msg; + + if(pdu_space <= head_len + 1) + { + log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n", + rrc->get_rb_name(lcid).c_str(), nof_bytes, head_len); + pool->deallocate(pdu); + return 0; + } + + log->debug("%s Building PDU - pdu_space: %d, head_len: %d \n", + rrc->get_rb_name(lcid).c_str(), pdu_space, head_len); + + // Check for SDU segment + if(tx_sdu) + { + to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len; + memcpy(pdu_ptr, tx_sdu->msg, to_move); + last_li = to_move; + pdu_ptr += to_move; + pdu->N_bytes += to_move; + tx_sdu->N_bytes -= to_move; + tx_sdu->msg += to_move; + if(tx_sdu->N_bytes == 0) + { + log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", + rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + if(pdu_space > to_move) + pdu_space -= to_move; + else + pdu_space = 0; + header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU + + log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n", + rrc->get_rb_name(lcid).c_str(), to_move, pdu_space, head_len); + } + + // Pull SDUs from queue + while(pdu_space > head_len + 1 && tx_sdu_queue.size() > 0) + { + if(last_li > 0) + header.li[header.N_li++] = last_li; + head_len = rlc_am_packed_length(&header); + if(head_len >= pdu_space) { + header.N_li--; + break; + } + tx_sdu_queue.read(&tx_sdu); + to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len; + memcpy(pdu_ptr, tx_sdu->msg, to_move); + last_li = to_move; + pdu_ptr += to_move; + pdu->N_bytes += to_move; + tx_sdu->N_bytes -= to_move; + tx_sdu->msg += to_move; + if(tx_sdu->N_bytes == 0) + { + log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", + rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + if(pdu_space > to_move) + pdu_space -= to_move; + else + pdu_space = 0; + + log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n", + rrc->get_rb_name(lcid).c_str(), to_move, pdu_space, head_len); + } + + // Make sure, at least one SDU (segment) has been added until this point + if (pdu->N_bytes == 0) { + log->error("Generated empty RLC PDU.\n"); + return 0; + } + + if(tx_sdu) + header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU + + // Set Poll bit + pdu_without_poll++; + byte_without_poll += (pdu->N_bytes + head_len); + log->debug("%s pdu_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), pdu_without_poll); + log->debug("%s byte_without_poll: %d\n", rrc->get_rb_name(lcid).c_str(), byte_without_poll); + if(poll_required()) + { + log->debug("%s setting poll bit to request status\n", rrc->get_rb_name(lcid).c_str()); + header.p = 1; + poll_sn = vt_s; + pdu_without_poll = 0; + byte_without_poll = 0; + poll_retx_timeout.start(cfg.t_poll_retx); + } + + // Set SN + header.sn = vt_s; + vt_s = (vt_s + 1)%MOD; + + // Place PDU in tx_window, write header and TX + tx_window[header.sn].buf = pdu; + tx_window[header.sn].header = header; + tx_window[header.sn].is_acked = false; + tx_window[header.sn].retx_count = 0; + + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&header, &ptr); + memcpy(ptr, pdu->msg, pdu->N_bytes); + log->info_hex(payload, pdu->N_bytes, "%s PDU scheduled for tx. SN: %d (%d B)\n", rrc->get_rb_name(lcid).c_str(), header.sn, pdu->N_bytes); + + debug_state(); + return (ptr-payload) + pdu->N_bytes; +} + +void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t &header) +{ + std::map::iterator it; + + log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d (%d B), %s", + rrc->get_rb_name(lcid).c_str(), header.sn, nof_bytes, rlc_fi_field_text[header.fi]); + + if(!inside_rx_window(header.sn)) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + do_status = true; + } + log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", + rrc->get_rb_name(lcid).c_str(), header.sn, vr_r, vr_mr); + return; + } + + it = rx_window.find(header.sn); + if(rx_window.end() != it) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + do_status = true; + } + log->info("%s Discarding duplicate SN: %d\n", + rrc->get_rb_name(lcid).c_str(), header.sn); + return; + } + + // Write to rx window + rlc_amd_rx_pdu_t pdu; + pdu.buf = pool_allocate; + if (!pdu.buf) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu().\n"); + exit(-1); +#else + log->error("Fatal Error: Couldn't allocate PDU in handle_data_pdu().\n"); + return; +#endif + } + + // check available space for payload + if (nof_bytes > pdu.buf->get_tailroom()) { + log->error("%s Discarding SN: %d of size %d B (available space %d B)\n", + rrc->get_rb_name(lcid).c_str(), header.sn, nof_bytes, pdu.buf->get_tailroom()); + pool->deallocate(pdu.buf); + return; + } + memcpy(pdu.buf->msg, payload, nof_bytes); + pdu.buf->N_bytes = nof_bytes; + memcpy(&pdu.header, &header, sizeof(rlc_amd_pdu_header_t)); + + rx_window[header.sn] = pdu; + + // Update vr_h + if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h)) + vr_h = (header.sn + 1)%MOD; + + // Update vr_ms + it = rx_window.find(vr_ms); + while(rx_window.end() != it) + { + vr_ms = (vr_ms + 1)%MOD; + it = rx_window.find(vr_ms); + } + + // Check poll bit + if(header.p) + { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + poll_received = true; + + // 36.322 v10 Section 5.2.3 + if(RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || + RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr)) + { + do_status = true; + } + // else delay for reordering timer + } + + // Reassemble and deliver SDUs + reassemble_rx_sdus(); + + // Update reordering variables and timers (36.322 v10.0.0 Section 5.1.3.2.3) + if(reordering_timeout.is_running()) + { + if(vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) + { + reordering_timeout.reset(); + } + } + if(!reordering_timeout.is_running()) + { + if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r)) + { + reordering_timeout.start(cfg.t_reordering); + vr_x = vr_h; + } + } + + debug_state(); +} + +void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t &header) +{ + std::map::iterator it; + + log->info_hex(payload, nof_bytes, "%s Rx data PDU segment. SN: %d, SO: %d", + rrc->get_rb_name(lcid).c_str(), header.sn, header.so); + + // Check inside rx window + if(!inside_rx_window(header.sn)) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + do_status = true; + } + log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", + rrc->get_rb_name(lcid).c_str(), header.sn, vr_r, vr_mr); + return; + } + + rlc_amd_rx_pdu_t segment; + segment.buf = pool_allocate; + if (!segment.buf) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Couldn't allocate PDU in handle_data_pdu_segment().\n"); + exit(-1); +#else + log->error("Fatal Error: Couldn't allocate PDU in handle_data_pdu_segment().\n"); + return; +#endif + } + + memcpy(segment.buf->msg, payload, nof_bytes); + segment.buf->N_bytes = nof_bytes; + memcpy(&segment.header, &header, sizeof(rlc_amd_pdu_header_t)); + + // Check if we already have a segment from the same PDU + it = rx_segments.find(header.sn); + if(rx_segments.end() != it) { + + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + do_status = true; + } + + // Add segment to PDU list and check for complete + if(add_segment_and_check(&it->second, &segment)) { + std::list::iterator segit; + std::list seglist = it->second.segments; + for(segit = seglist.begin(); segit != seglist.end(); segit++) { + pool->deallocate(segit->buf); + } + seglist.clear(); + rx_segments.erase(it); + } + + } else { + + // Create new PDU segment list and write to rx_segments + rlc_amd_rx_pdu_segments_t pdu; + pdu.segments.push_back(segment); + rx_segments[header.sn] = pdu; + + + // Update vr_h + if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h)) + vr_h = (header.sn + 1)%MOD; + + // Check poll bit + if(header.p) + { + log->info("%s Status packet requested through polling bit\n", rrc->get_rb_name(lcid).c_str()); + poll_received = true; + + // 36.322 v10 Section 5.2.3 + if(RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || + RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr)) + { + do_status = true; + } + // else delay for reordering timer + } + } +#ifdef RLC_AM_BUFFER_DEBUG + print_rx_segments(); +#endif + debug_state(); +} + +void rlc_am::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + log->info_hex(payload, nof_bytes, "%s Rx control PDU", rrc->get_rb_name(lcid).c_str()); + + rlc_status_pdu_t status; + rlc_am_read_status_pdu(payload, nof_bytes, &status); + + log->info("%s Rx Status PDU: %s\n", rrc->get_rb_name(lcid).c_str(), rlc_am_to_string(&status).c_str()); + + poll_retx_timeout.reset(); + + // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again + if (status.N_nack > 0) { + retx_queue.clear(); + } + + // Handle ACKs and NACKs + std::map::iterator it; + bool update_vt_a = true; + uint32_t i = vt_a; + + while(TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && + TX_MOD_BASE(i) < TX_MOD_BASE(vt_s)) + { + bool nack = false; + for(uint32_t j=0;jsecond.buf->N_bytes; + + if(status.nacks[j].has_so) { + // sanity check + if (status.nacks[j].so_start >= it->second.buf->N_bytes) { + // print error but try to send original PDU again + log->info("SO_start is larger than original PDU (%d >= %d)\n", + status.nacks[j].so_start, + it->second.buf->N_bytes); + status.nacks[j].so_start = 0; + } + + // check for special SO_end value + if(status.nacks[j].so_end == 0x7FFF) { + status.nacks[j].so_end = it->second.buf->N_bytes; + }else{ + retx.so_end = status.nacks[j].so_end + 1; + } + + if(status.nacks[j].so_start < it->second.buf->N_bytes && + status.nacks[j].so_end <= it->second.buf->N_bytes) { + retx.is_segment = true; + retx.so_start = status.nacks[j].so_start; + } else { + log->warning("%s invalid segment NACK received for SN %d. so_start: %d, so_end: %d, N_bytes: %d\n", + rrc->get_rb_name(lcid).c_str(), i, status.nacks[j].so_start, status.nacks[j].so_end, it->second.buf->N_bytes); + } + } + + retx.sn = i; + retx_queue.push_back(retx); + } + } + } + } + + if(!nack) { + //ACKed SNs get marked and removed from tx_window if possible + if(tx_window.count(i) > 0) { + it = tx_window.find(i); + if (it != tx_window.end()) { + if(update_vt_a) { + if(it->second.buf) { + pool->deallocate(it->second.buf); + it->second.buf = 0; + } + tx_window.erase(it); + vt_a = (vt_a + 1)%MOD; + vt_ms = (vt_ms + 1)%MOD; + } + } + } + } + i = (i+1)%MOD; + } + + debug_state(); +} + +void rlc_am::reassemble_rx_sdus() +{ + uint32_t len = 0; + if(!rx_sdu) { + rx_sdu = pool_allocate; + if (!rx_sdu) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n"); + exit(-1); +#else + log->error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n"); + return; +#endif + } + } + + // Iterate through rx_window, assembling and delivering SDUs + while(rx_window.end() != rx_window.find(vr_r)) + { + // Handle any SDU segments + for(uint32_t i=0; iget_tailroom() >= len) { + if ((rx_window[vr_r].buf->msg - rx_window[vr_r].buf->buffer) + len < SRSLTE_MAX_BUFFER_SIZE_BYTES) { + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len); + rx_sdu->N_bytes += len; + rx_window[vr_r].buf->msg += len; + rx_window[vr_r].buf->N_bytes -= len; + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", rrc->get_rb_name(lcid).c_str(), rx_sdu->N_bytes); + rx_sdu->set_timestamp(); + pdcp->write_pdu(lcid, rx_sdu); + + rx_sdu = pool_allocate; + if (!rx_sdu) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n"); + exit(-1); +#else + log->error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n"); + return; +#endif + } + } else { + log->error("Cannot read %d bytes from rx_window. vr_r=%d, msg-buffer=%ld bytes\n", len, vr_r, (rx_window[vr_r].buf->msg - rx_window[vr_r].buf->buffer)); + pool->deallocate(rx_sdu); + goto exit; + } + } else { + log->error("Cannot fit RLC PDU in SDU buffer, dropping both.\n"); + pool->deallocate(rx_sdu); + goto exit; + } + } + + // Handle last segment + len = rx_window[vr_r].buf->N_bytes; + if (rx_sdu->get_tailroom() >= len) { + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len); + rx_sdu->N_bytes += rx_window[vr_r].buf->N_bytes; + } else { + log->error("Cannot fit RLC PDU in SDU buffer, dropping both.\n"); + pool->deallocate(rx_sdu); + pool->deallocate(rx_window[vr_r].buf); + rx_window.erase(vr_r); + } + + if(rlc_am_end_aligned(rx_window[vr_r].header.fi)) { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", rrc->get_rb_name(lcid).c_str(), rx_sdu->N_bytes); + rx_sdu->set_timestamp(); + pdcp->write_pdu(lcid, rx_sdu); + rx_sdu = pool_allocate; + if (!rx_sdu) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n"); + exit(-1); +#else + log->error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n"); + return; +#endif + } + } + +exit: + // Move the rx_window + pool->deallocate(rx_window[vr_r].buf); + rx_window.erase(vr_r); + vr_r = (vr_r + 1)%MOD; + vr_mr = (vr_mr + 1)%MOD; + } +} + +bool rlc_am::inside_tx_window(uint16_t sn) +{ + if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vt_a) && + RX_MOD_BASE(sn) < RX_MOD_BASE(vt_ms)) + { + return true; + }else{ + return false; + } +} + +bool rlc_am::inside_rx_window(uint16_t sn) +{ + if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_r) && + RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) + { + return true; + }else{ + return false; + } +} + +void rlc_am::debug_state() +{ + log->debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d " + "vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d\n", + rrc->get_rb_name(lcid).c_str(), vt_a, vt_ms, vt_s, poll_sn, + vr_r, vr_mr, vr_x, vr_ms, vr_h); +} + +void rlc_am::print_rx_segments() +{ + std::map::iterator it; + std::stringstream ss; + ss << "rx_segments:" << std::endl; + for(it=rx_segments.begin();it!=rx_segments.end();it++) { + std::list::iterator segit; + for(segit = it->second.segments.begin(); segit != it->second.segments.end(); segit++) { + ss << " SN:" << segit->header.sn << " SO:" << segit->header.so << " N:" << segit->buf->N_bytes << " N_li: " << segit->header.N_li << std::endl; + } + } + log->debug("%s\n", ss.str().c_str()); +} + +bool rlc_am::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pdu_t *segment) +{ + // Check for first segment + if(0 == segment->header.so) { + std::list::iterator it; + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + pool->deallocate(it->buf); + } + pdu->segments.clear(); + pdu->segments.push_back(*segment); + return false; + } + + // Check segment offset + uint32_t n = 0; + if(!pdu->segments.empty()) { + rlc_amd_rx_pdu_t &back = pdu->segments.back(); + n = back.header.so + back.buf->N_bytes; + } + if(segment->header.so != n) { + pool->deallocate(segment->buf); + return false; + } else { + pdu->segments.push_back(*segment); + } + + // Check for complete + uint32_t so = 0; + std::list::iterator it, tmpit; + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + if(so != it->header.so) + return false; + so += it->buf->N_bytes; + } + if(!pdu->segments.back().header.lsf) + return false; + + // We have all segments of the PDU - reconstruct and handle + rlc_amd_pdu_header_t header; + header.dc = RLC_DC_FIELD_DATA_PDU; + header.rf = 0; + header.p = 0; + header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; + header.sn = pdu->segments.front().header.sn; + header.lsf = 0; + header.so = 0; + header.N_li = 0; + + // Reconstruct fi field + header.fi |= (pdu->segments.front().header.fi & RLC_FI_FIELD_NOT_START_ALIGNED); + header.fi |= (pdu->segments.back().header.fi & RLC_FI_FIELD_NOT_END_ALIGNED); + + // Reconstruct li fields + uint16_t count = 0; + uint16_t carryover = 0; + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + if(it->header.N_li > 0) { + header.li[header.N_li++] = it->header.li[0] + carryover; + count += it->header.li[0]; + for(uint32_t i=1; iheader.N_li; i++) { + header.li[header.N_li++] = it->header.li[i]; + count += it->header.li[i]; + } + } + + // accumulate segment sizes until end aligned PDU is received + if (rlc_am_not_start_aligned(it->header.fi)) { + carryover += it->buf->N_bytes - count; + } else { + carryover = it->buf->N_bytes - count; + } + tmpit = it; + if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) { + header.li[header.N_li++] = carryover; + carryover = 0; + } + count = 0; + } + + // Copy data + byte_buffer_t *full_pdu = pool_allocate; + if (!full_pdu) { +#ifdef RLC_AM_BUFFER_DEBUG + log->console("Fatal Error: Could not allocate PDU in add_segment_and_check()\n"); + exit(-1); +#else + log->error("Fatal Error: Could not allocate PDU in add_segment_and_check()\n"); + return false; +#endif + } + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + memcpy(&full_pdu->msg[full_pdu->N_bytes], it->buf->msg, it->buf->N_bytes); + full_pdu->N_bytes += it->buf->N_bytes; + } + + handle_data_pdu(full_pdu->msg, full_pdu->N_bytes, header); + pool->deallocate(full_pdu); + return true; +} + +int rlc_am::required_buffer_size(rlc_amd_retx_t retx) +{ + if(!retx.is_segment){ + if (tx_window.count(retx.sn)) { + if (tx_window[retx.sn].buf) { + return rlc_am_packed_length(&tx_window[retx.sn].header) + tx_window[retx.sn].buf->N_bytes; + } else { + log->warning("retx.sn=%d has null ptr in required_buffer_size()\n", retx.sn); + return -1; + } + } else { + log->warning("retx.sn=%d does not exist in required_buffer_size()\n", retx.sn); + return -1; + } + } + + // Construct new header + rlc_amd_pdu_header_t new_header; + rlc_amd_pdu_header_t old_header = tx_window[retx.sn].header; + + new_header.dc = RLC_DC_FIELD_DATA_PDU; + new_header.rf = 1; + new_header.p = 0; + new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED; + new_header.sn = old_header.sn; + new_header.lsf = 0; + new_header.so = retx.so_start; + new_header.N_li = 0; + + uint32_t head_len = 0; + + // Need to rebuild the li table & update fi based on so_start and so_end + if(retx.so_start != 0 && rlc_am_start_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned + + uint32_t lower = 0; + uint32_t upper = 0; + uint32_t li = 0; + + for(uint32_t i=0; i= retx.so_end) + break; + + upper += old_header.li[i]; + + head_len = rlc_am_packed_length(&new_header); + + if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed + li = upper - lower; + if(upper > retx.so_end) + li -= upper - retx.so_end; + if(lower < retx.so_start) + li -= retx.so_start - lower; + if(lower > 0 && lower == retx.so_start) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU + if(upper == retx.so_end) { + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU + } + new_header.li[new_header.N_li++] = li; + } + + lower += old_header.li[i]; + } + +// if(tx_window[retx.sn].buf->N_bytes != retx.so_end) { +// if(new_header.N_li > 0) +// new_header.N_li--; // No li for last segment +// } + + return rlc_am_packed_length(&new_header) + (retx.so_end-retx.so_start); +} + +bool rlc_am::retx_queue_has_sn(uint32_t sn) +{ + std::deque::iterator q_it; + for(q_it = retx_queue.begin(); q_it != retx_queue.end(); q_it++) { + if(q_it->sn == sn) + return true; + } + return false; +} + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ + +// Read header from pdu struct, don't strip header +void rlc_am_read_data_pdu_header(byte_buffer_t *pdu, rlc_amd_pdu_header_t *header) +{ + uint8_t *ptr = pdu->msg; + uint32_t n = 0; + rlc_am_read_data_pdu_header(&ptr, &n, header); +} + +// Read header from raw pointer, strip header +void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd_pdu_header_t *header) +{ + uint8_t ext; + uint8_t *ptr = *payload; + + header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); + + if(RLC_DC_FIELD_DATA_PDU == header->dc) + { + // Fixed part + header->rf = ((*ptr >> 6) & 0x01); + header->p = ((*ptr >> 5) & 0x01); + header->fi = (rlc_fi_field_t)((*ptr >> 3) & 0x03); + ext = ((*ptr >> 2) & 0x01); + header->sn = (*ptr & 0x03) << 8; // 2 bits SN + ptr++; + header->sn |= (*ptr & 0xFF); // 8 bits SN + ptr++; + + if(header->rf) + { + header->lsf = ((*ptr >> 7) & 0x01); + header->so = (*ptr & 0x7F) << 8; // 7 bits of SO + ptr++; + header->so |= (*ptr & 0xFF); // 8 bits of SO + ptr++; + } + + // Extension part + header->N_li = 0; + while(ext) + { + if(header->N_li%2 == 0) + { + ext = ((*ptr >> 7) & 0x01); + header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI + header->N_li++; + } + else + { + ext = (*ptr >> 3) & 0x01; + header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI + header->N_li++; + ptr++; + } + } + + // Account for padding if N_li is odd + if(header->N_li%2 == 1) + ptr++; + + *nof_bytes -= ptr-*payload; + *payload = ptr; + } +} + +// Write header to pdu struct +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, byte_buffer_t *pdu) +{ + uint8_t *ptr = pdu->msg; + rlc_am_write_data_pdu_header(header, &ptr); + pdu->N_bytes += ptr - pdu->msg; +} + +// Write header to pointer & move pointer +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payload) +{ + uint32_t i; + uint8_t ext = (header->N_li > 0) ? 1 : 0; + + uint8_t *ptr = *payload; + + // Fixed part + *ptr = (header->dc & 0x01) << 7; + *ptr |= (header->rf & 0x01) << 6; + *ptr |= (header->p & 0x01) << 5; + *ptr |= (header->fi & 0x03) << 3; + *ptr |= (ext & 0x01) << 2; + + *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN + ptr++; + *ptr = (header->sn & 0xFF); // 8 bits SN + ptr++; + + // Segment part + if(header->rf) + { + *ptr = (header->lsf & 0x01) << 7; + *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO + ptr++; + *ptr = (header->so & 0x00FF); // 8 bits of SO + ptr++; + } + + // Extension part + i = 0; + while(i < header->N_li) + { + ext = ((i+1) == header->N_li) ? 0 : 1; + *ptr = (ext & 0x01) << 7; // 1 bit header + *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI + ptr++; + *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI + i++; + if(i < header->N_li) + { + ext = ((i+1) == header->N_li) ? 0 : 1; + *ptr |= (ext & 0x01) << 3; // 1 bit header + *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI + ptr++; + *ptr = (header->li[i] & 0x0FF); // 8 bits of LI + ptr++; + i++; + } + } + // Pad if N_li is odd + if(header->N_li%2 == 1) + ptr++; + + *payload = ptr; +} + +void rlc_am_read_status_pdu(byte_buffer_t *pdu, rlc_status_pdu_t *status) +{ + rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); +} + +void rlc_am_read_status_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_status_pdu_t *status) +{ + uint32_t i; + uint8_t ext1, ext2; + bit_buffer_t tmp; + uint8_t *ptr = tmp.msg; + + srslte_bit_unpack_vector(payload, tmp.msg, nof_bytes*8); + tmp.N_bits = nof_bytes*8; + + rlc_dc_field_t dc = (rlc_dc_field_t)srslte_bit_pack(&ptr, 1); + + if(RLC_DC_FIELD_CONTROL_PDU == dc) + { + uint8_t cpt = srslte_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) + if(0 == cpt) + { + status->ack_sn = srslte_bit_pack(&ptr, 10); // 10 bits ACK_SN + ext1 = srslte_bit_pack(&ptr, 1); // 1 bits E1 + status->N_nack = 0; + while(ext1) + { + status->nacks[status->N_nack].nack_sn = srslte_bit_pack(&ptr, 10); + ext1 = srslte_bit_pack(&ptr, 1); // 1 bits E1 + ext2 = srslte_bit_pack(&ptr, 1); // 1 bits E2 + if(ext2) + { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = srslte_bit_pack(&ptr, 15); + status->nacks[status->N_nack].so_end = srslte_bit_pack(&ptr, 15); + } + status->N_nack++; + } + } + } +} + +void rlc_am_write_status_pdu(rlc_status_pdu_t *status, byte_buffer_t *pdu ) +{ + pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); +} + +int rlc_am_write_status_pdu(rlc_status_pdu_t *status, uint8_t *payload) +{ + uint32_t i; + uint8_t ext1; + bit_buffer_t tmp; + uint8_t *ptr = tmp.msg; + + srslte_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C + srslte_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) + srslte_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN + ext1 = (status->N_nack == 0) ? 0 : 1; + srslte_bit_unpack(ext1, &ptr, 1); // E1 + for(i=0;iN_nack;i++) + { + srslte_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN + ext1 = ((status->N_nack-1) == i) ? 0 : 1; + srslte_bit_unpack(ext1, &ptr, 1); // E1 + if(status->nacks[i].has_so) { + srslte_bit_unpack(1 , &ptr, 1); // E2 + srslte_bit_unpack(status->nacks[i].so_start , &ptr, 15); + srslte_bit_unpack(status->nacks[i].so_end , &ptr, 15); + }else{ + srslte_bit_unpack(0 , &ptr, 1); // E2 + } + } + + // Pad + tmp.N_bits = ptr - tmp.msg; + uint8_t n_pad = 8 - (tmp.N_bits%8); + srslte_bit_unpack(0, &ptr, n_pad); + tmp.N_bits = ptr - tmp.msg; + + // Pack bits + srslte_bit_pack_vector(tmp.msg, payload, tmp.N_bits); + return tmp.N_bits/8; +} + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header) +{ + uint32_t len = 2; // Fixed part is 2 bytes + if(header->rf) len += 2; // Segment header is 2 bytes + len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up + return len; +} + +uint32_t rlc_am_packed_length(rlc_status_pdu_t *status) +{ + uint32_t i; + uint32_t len_bits = 15; // Fixed part is 15 bits + for(i=0;iN_nack;i++) + { + if(status->nacks[i].has_so) { + len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end + }else{ + len_bits += 12; // 10 bits SN, 2 bits ext + } + } + + return (len_bits+7)/8; // Convert to bytes - integer rounding up +} + +bool rlc_am_is_control_pdu(byte_buffer_t *pdu) +{ + return rlc_am_is_control_pdu(pdu->msg); +} + +bool rlc_am_is_control_pdu(uint8_t *payload) +{ + return ((*(payload) >> 7) & 0x01) == RLC_DC_FIELD_CONTROL_PDU; +} + +bool rlc_am_is_pdu_segment(uint8_t *payload) +{ + return ((*(payload) >> 6) & 0x01) == 1; +} + +std::string rlc_am_to_string(rlc_status_pdu_t *status) +{ + std::stringstream ss; + ss << "ACK_SN = " << status->ack_sn; + ss << ", N_nack = " << status->N_nack; + if(status->N_nack > 0) + { + ss << ", NACK_SN = "; + for(uint32_t i=0; iN_nack; i++) + { + if(status->nacks[i].has_so) { + ss << "[" << status->nacks[i].nack_sn << " " << status->nacks[i].so_start \ + << ":" << status->nacks[i].so_end << "]"; + }else{ + ss << "[" << status->nacks[i].nack_sn << "]"; + } + } + } + return ss.str(); +} + +bool rlc_am_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); +} + +bool rlc_am_end_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); +} + +bool rlc_am_is_unaligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +bool rlc_am_not_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +} // namespace srsue diff --git a/lib/src/upper/rlc_entity.cc b/lib/src/upper/rlc_entity.cc new file mode 100644 index 0000000..783a149 --- /dev/null +++ b/lib/src/upper/rlc_entity.cc @@ -0,0 +1,170 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/rlc_entity.h" + +namespace srslte { + +rlc_entity::rlc_entity() + :rlc(NULL) +{ +} + +void rlc_entity::init(rlc_mode_t mode_, + log *rlc_entity_log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers_, + int buffer_size) +{ + + if (buffer_size <= 0) { + buffer_size = rlc_common::RLC_BUFFER_NOF_PDU; + } + // Create the RLC instance the first time init() is called. + // If called to reestablished, the entity is stopped but not destroyed + // Next call to init() must use same mode + if (rlc == NULL) { + switch(mode_) + { + case RLC_MODE_TM: + rlc = new rlc_tm((uint32_t) buffer_size); + break; + case RLC_MODE_UM: + rlc = new rlc_um((uint32_t) buffer_size); + break; + case RLC_MODE_AM: + rlc = new rlc_am((uint32_t) buffer_size); + break; + default: + rlc_entity_log_->error("Invalid RLC mode - defaulting to TM\n"); + rlc = new rlc_tm((uint32_t) buffer_size); + break; + } + lcid = lcid_; + mode = mode_; + } else { + if (lcid != lcid_) { + rlc_entity_log_->warning("Reestablishing RLC instance. LCID changed from %d to %d\n", lcid, lcid_); + lcid = lcid_; + } + if (mode != mode_) { + rlc_entity_log_->console("Error reestablishing RLC instance. Mode changed from %d to %d. \n", mode, mode_); + } + } + rlc->init(rlc_entity_log_, lcid_, pdcp_, rrc_, mac_timers_); +} + +void rlc_entity::configure(srslte_rlc_config_t cnfg) +{ + if(rlc) + rlc->configure(cnfg); +} + +// Reestablishment stops the entity but does not destroy it. Mode will not change +void rlc_entity::reestablish() { + rlc->reestablish(); +} + +// A call to stop() stops the entity and clears deletes the instance. Next time this entity can be used for other mode. +void rlc_entity::stop() +{ + rlc->stop(); + delete rlc; + rlc = NULL; +} + +void rlc_entity::empty_queue() +{ + rlc->empty_queue(); +} + +bool rlc_entity::active() +{ + return (rlc != NULL); +} + +rlc_mode_t rlc_entity::get_mode() +{ + if(rlc) + return rlc->get_mode(); + else + return RLC_MODE_TM; +} + +uint32_t rlc_entity::get_bearer() +{ + if(rlc) + return rlc->get_bearer(); + else + return 0; +} + +// PDCP interface +void rlc_entity::write_sdu(byte_buffer_t *sdu) +{ + if(rlc) + rlc->write_sdu(sdu); +} + +void rlc_entity::write_sdu_nb(byte_buffer_t *sdu) +{ + if(rlc) + rlc->write_sdu_nb(sdu); +} + +// MAC interface +uint32_t rlc_entity::get_buffer_state() +{ + if(rlc) + return rlc->get_buffer_state(); + else + return 0; +} + +uint32_t rlc_entity::get_total_buffer_state() +{ + if(rlc) + return rlc->get_total_buffer_state(); + else + return 0; +} + +int rlc_entity::read_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(rlc) + return rlc->read_pdu(payload, nof_bytes); + else + return 0; +} +void rlc_entity::write_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(rlc) + rlc->write_pdu(payload, nof_bytes); +} + +} // namespace srsue diff --git a/lib/src/upper/rlc_tm.cc b/lib/src/upper/rlc_tm.cc new file mode 100644 index 0000000..988acf2 --- /dev/null +++ b/lib/src/upper/rlc_tm.cc @@ -0,0 +1,183 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/rlc_tm.h" + +namespace srslte { + +rlc_tm::rlc_tm(uint32_t queue_len) : ul_queue(queue_len) +{ + log = NULL; + pdcp = NULL; + rrc = NULL; + lcid = 0; + pool = byte_buffer_pool::get_instance(); +} + +// Warning: must call stop() to properly deallocate all buffers +rlc_tm::~rlc_tm() { + pool = NULL; +} + +void rlc_tm::init(srslte::log *log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + mac_interface_timers *mac_timers) +{ + log = log_; + lcid = lcid_; + pdcp = pdcp_; + rrc = rrc_; + tx_enabled = true; +} + +void rlc_tm::configure(srslte_rlc_config_t cnfg) +{ + log->error("Attempted to configure TM RLC entity\n"); +} + +void rlc_tm::empty_queue() +{ + // Drop all messages in TX queue + byte_buffer_t *buf; + while(ul_queue.try_read(&buf)) { + pool->deallocate(buf); + } + ul_queue.reset(); +} + +void rlc_tm::reestablish() { + stop(); + tx_enabled = true; +} + +void rlc_tm::stop() +{ + tx_enabled = false; + empty_queue(); +} + +rlc_mode_t rlc_tm::get_mode() +{ + return RLC_MODE_TM; +} + +uint32_t rlc_tm::get_bearer() +{ + return lcid; +} + +// PDCP interface +void rlc_tm::write_sdu(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + ul_queue.write(sdu); + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU, queue size=%d, bytes=%d", + rrc->get_rb_name(lcid).c_str(), ul_queue.size(), ul_queue.size_bytes()); + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +void rlc_tm::write_sdu_nb(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + if (ul_queue.try_write(sdu)) { + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU, queue size=%d, bytes=%d", + rrc->get_rb_name(lcid).c_str(), ul_queue.size(), ul_queue.size_bytes()); + } else { + log->debug_hex(sdu->msg, sdu->N_bytes, "[Dropped SDU] %s Tx SDU, queue size=%d, bytes=%d", + rrc->get_rb_name(lcid).c_str(), ul_queue.size(), ul_queue.size_bytes()); + pool->deallocate(sdu); + } + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +// MAC interface +uint32_t rlc_tm::get_buffer_state() +{ + return ul_queue.size_bytes(); +} + +uint32_t rlc_tm::get_total_buffer_state() +{ + return get_buffer_state(); +} + +int rlc_tm::read_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + uint32_t pdu_size = ul_queue.size_tail_bytes(); + if(pdu_size > nof_bytes) + { + log->error("TX %s PDU size larger than MAC opportunity\n", rrc->get_rb_name(lcid).c_str()); + return -1; + } + byte_buffer_t *buf; + if (ul_queue.try_read(&buf)) { + pdu_size = buf->N_bytes; + memcpy(payload, buf->msg, buf->N_bytes); + log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", + rrc->get_rb_name(lcid).c_str(), buf->get_latency_us()); + pool->deallocate(buf); + log->info_hex(payload, pdu_size, "TX %s, %s PDU, queue size=%d, bytes=%d", + rrc->get_rb_name(lcid).c_str(), rlc_mode_text[RLC_MODE_TM], ul_queue.size(), ul_queue.size_bytes()); + return pdu_size; + } else { + log->warning("Queue empty while trying to read\n"); + if (ul_queue.size_bytes() > 0) { + log->warning("Corrupted queue: empty but size_bytes > 0. Resetting queue\n"); + ul_queue.reset(); + } + return 0; + } +} + +void rlc_tm::write_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + byte_buffer_t *buf = pool_allocate; + if (buf) { + memcpy(buf->msg, payload, nof_bytes); + buf->N_bytes = nof_bytes; + buf->set_timestamp(); + pdcp->write_pdu(lcid, buf); + } else { + log->error("Fatal Error: Couldn't allocate buffer in rlc_tm::write_pdu().\n"); + } +} + +} // namespace srsue diff --git a/lib/src/upper/rlc_um.cc b/lib/src/upper/rlc_um.cc new file mode 100644 index 0000000..77c40aa --- /dev/null +++ b/lib/src/upper/rlc_um.cc @@ -0,0 +1,863 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/rlc_um.h" +#include +#include + +#define RX_MOD_BASE(x) (x-vr_uh-cfg.rx_window_size)%cfg.rx_mod + +namespace srslte { + +rlc_um::rlc_um(uint32_t queue_len) : tx_sdu_queue(queue_len) +{ + log = NULL; + pdcp = NULL; + rrc = NULL; + reordering_timer = NULL; + lcid = 0; + reordering_timer_id = 0; + bzero(&cfg, sizeof(srslte_rlc_um_config_t)); + + tx_sdu = NULL; + + rx_sdu = NULL; + pool = byte_buffer_pool::get_instance(); + + pthread_mutex_init(&mutex, NULL); + + vt_us = 0; + vr_ur = 0; + vr_ux = 0; + vr_uh = 0; + + vr_ur_in_rx_sdu = 0; + + mac_timers = NULL; + + pdu_lost = false; +} + +// Warning: must call stop() to properly deallocate all buffers +rlc_um::~rlc_um() +{ + pthread_mutex_destroy(&mutex); + pool = NULL; + if (mac_timers && reordering_timer) { + mac_timers->timer_release_id(reordering_timer_id); + reordering_timer = NULL; + } +} + +void rlc_um::init(srslte::log *log_, + uint32_t lcid_, + srsue::pdcp_interface_rlc *pdcp_, + srsue::rrc_interface_rlc *rrc_, + srslte::mac_interface_timers *mac_timers_) +{ + log = log_; + lcid = lcid_; + pdcp = pdcp_; + rrc = rrc_; + mac_timers = mac_timers_; + reordering_timer_id = mac_timers->timer_get_unique_id(); + reordering_timer = mac_timers->timer_get(reordering_timer_id); + tx_enabled = true; +} + +void rlc_um::configure(srslte_rlc_config_t cnfg_) +{ + cfg = cnfg_.um; + if(cnfg_.um.is_mrb){ + tx_sdu_queue.resize(512); + } + switch(cnfg_.rlc_mode) + { + case LIBLTE_RRC_RLC_MODE_UM_BI: + log->warning("%s configured in %s mode: " + "t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n", + rb_name().c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length], rlc_umd_sn_size_num[cfg.rx_sn_field_length]); + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_UL: + log->warning("%s configured in %s mode: tx_sn_field_length=%u bits\n", + rrc->get_rb_name(lcid).c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + + rlc_umd_sn_size_num[cfg.rx_sn_field_length]); + break; + case LIBLTE_RRC_RLC_MODE_UM_UNI_DL: + log->warning("%s configured in %s mode: " + "t_reordering=%d ms, rx_sn_field_length=%u bits\n", + rb_name().c_str(), liblte_rrc_rlc_mode_text[cnfg_.rlc_mode], + cfg.t_reordering, rlc_umd_sn_size_num[cfg.rx_sn_field_length]); + break; + default: + log->error("RLC configuration mode not recognized\n"); + } +} + +void rlc_um::empty_queue() { + // Drop all messages in TX SDU queue + byte_buffer_t *buf; + while(tx_sdu_queue.try_read(&buf)) { + pool->deallocate(buf); + } + tx_sdu_queue.reset(); +} + +bool rlc_um::is_mrb() +{ + return cfg.is_mrb; +} + +void rlc_um::reestablish() { + stop(); + tx_enabled = true; +} + +void rlc_um::stop() +{ + // Empty tx_sdu_queue before locking the mutex + tx_enabled = false; + empty_queue(); + + pthread_mutex_lock(&mutex); + vt_us = 0; + vr_ur = 0; + vr_ux = 0; + vr_uh = 0; + pdu_lost = false; + if(rx_sdu) { + pool->deallocate(rx_sdu); + rx_sdu = NULL; + } + + if(tx_sdu) { + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + + if(reordering_timer) { + reordering_timer->stop(); + } + + // Drop all messages in RX window + std::map::iterator it; + for(it = rx_window.begin(); it != rx_window.end(); it++) { + pool->deallocate(it->second.buf); + } + rx_window.clear(); + pthread_mutex_unlock(&mutex); + +} + +rlc_mode_t rlc_um::get_mode() +{ + return RLC_MODE_UM; +} + +uint32_t rlc_um::get_bearer() +{ + return lcid; +} + +/**************************************************************************** + * PDCP interface + ***************************************************************************/ +void rlc_um::write_sdu(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + tx_sdu_queue.write(sdu); + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B ,tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +void rlc_um::write_sdu_nb(byte_buffer_t *sdu) +{ + if (!tx_enabled) { + byte_buffer_pool::get_instance()->deallocate(sdu); + return; + } + if (sdu) { + if (tx_sdu_queue.try_write(sdu)) { + log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU (%d B,tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + } else { + log->debug_hex(sdu->msg, sdu->N_bytes, "[Dropped SDU] %s Tx SDU (%d B,tx_sdu_queue_len=%d)", rrc->get_rb_name(lcid).c_str(), sdu->N_bytes, tx_sdu_queue.size()); + pool->deallocate(sdu); + } + } else { + log->warning("NULL SDU pointer in write_sdu()\n"); + } +} + +/**************************************************************************** + * MAC interface + ***************************************************************************/ + +uint32_t rlc_um::get_buffer_state() +{ + // Bytes needed for tx SDUs + uint32_t n_sdus = tx_sdu_queue.size(); + + uint32_t n_bytes = tx_sdu_queue.size_bytes(); + if(tx_sdu) + { + n_sdus++; + n_bytes += tx_sdu->N_bytes; + } + + // Room needed for header extensions? (integer rounding) + if(n_sdus > 1) + n_bytes += ((n_sdus-1)*1.5)+0.5; + + // Room needed for fixed header? + if(n_bytes > 0) + n_bytes += (cfg.is_mrb)?2:3; + + return n_bytes; +} + +uint32_t rlc_um::get_total_buffer_state() +{ + return get_buffer_state(); +} + +int rlc_um::read_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + int r; + log->debug("MAC opportunity - %d bytes\n", nof_bytes); + pthread_mutex_lock(&mutex); + r = build_data_pdu(payload, nof_bytes); + pthread_mutex_unlock(&mutex); + return r; +} + +void rlc_um::write_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + pthread_mutex_lock(&mutex); + handle_data_pdu(payload, nof_bytes); + pthread_mutex_unlock(&mutex); +} + +/**************************************************************************** + * Timeout callback interface + ***************************************************************************/ + +void rlc_um::timer_expired(uint32_t timeout_id) +{ + if(reordering_timer_id == timeout_id) + { + pthread_mutex_lock(&mutex); + + // 36.322 v10 Section 5.1.2.2.4 + log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n", + rb_name().c_str()); + + log->warning("Lost PDU SN: %d\n", vr_ur); + pdu_lost = true; + rx_sdu->reset(); + while(RX_MOD_BASE(vr_ur) < RX_MOD_BASE(vr_ux)) + { + vr_ur = (vr_ur + 1)%cfg.rx_mod; + log->debug("Entering Reassemble from timeout id=%d\n", timeout_id); + reassemble_rx_sdus(); + log->debug("Finished reassemble from timeout id=%d\n", timeout_id); + } + reordering_timer->stop(); + if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur)) + { + reordering_timer->set(this, cfg.t_reordering); + reordering_timer->run(); + vr_ux = vr_uh; + } + + debug_state(); + pthread_mutex_unlock(&mutex); + } +} + +bool rlc_um::reordering_timeout_running() +{ + return reordering_timer->is_running(); +} + +/**************************************************************************** + * Helpers + ***************************************************************************/ + +int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(!tx_sdu && tx_sdu_queue.size() == 0) + { + log->info("No data available to be sent\n"); + return 0; + } + + byte_buffer_t *pdu = pool_allocate; + if(!pdu || pdu->N_bytes != 0) + { + log->error("Failed to allocate PDU buffer\n"); + return -1; + } + rlc_umd_pdu_header_t header; + header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; + header.sn = vt_us; + header.N_li = 0; + header.sn_size = cfg.tx_sn_field_length; + + uint32_t to_move = 0; + uint32_t last_li = 0; + uint8_t *pdu_ptr = pdu->msg; + + int head_len = rlc_um_packed_length(&header); + int pdu_space = nof_bytes; + + if(pdu_space <= head_len + 1) + { + pool->deallocate(pdu); + log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n", + rb_name().c_str(), nof_bytes, head_len); + return 0; + } + + // Check for SDU segment + if(tx_sdu) + { + uint32_t space = pdu_space-head_len; + to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; + log->debug("%s adding remainder of SDU segment - %d bytes of %d remaining\n", + rb_name().c_str(), to_move, tx_sdu->N_bytes); + memcpy(pdu_ptr, tx_sdu->msg, to_move); + last_li = to_move; + pdu_ptr += to_move; + pdu->N_bytes += to_move; + tx_sdu->N_bytes -= to_move; + tx_sdu->msg += to_move; + if(tx_sdu->N_bytes == 0) + { + log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", + rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + pdu_space -= to_move; + header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU + } + + // Pull SDUs from queue + while(pdu_space > head_len + 1 && tx_sdu_queue.size() > 0) + { + log->debug("pdu_space=%d, head_len=%d\n", pdu_space, head_len); + if(last_li > 0) + header.li[header.N_li++] = last_li; + head_len = rlc_um_packed_length(&header); + tx_sdu_queue.read(&tx_sdu); + uint32_t space = pdu_space-head_len; + to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; + log->debug("%s adding new SDU segment - %d bytes of %d remaining\n", + rb_name().c_str(), to_move, tx_sdu->N_bytes); + memcpy(pdu_ptr, tx_sdu->msg, to_move); + last_li = to_move; + pdu_ptr += to_move; + pdu->N_bytes += to_move; + tx_sdu->N_bytes -= to_move; + tx_sdu->msg += to_move; + if(tx_sdu->N_bytes == 0) + { + log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", + rrc->get_rb_name(lcid).c_str(), tx_sdu->get_latency_us()); + + pool->deallocate(tx_sdu); + tx_sdu = NULL; + } + pdu_space -= to_move; + } + + if(tx_sdu) + header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU + + // Set SN + + header.sn = vt_us; + vt_us = (vt_us + 1)%cfg.tx_mod; + + // Add header and TX + log->debug("%s packing PDU with length %d\n", rb_name().c_str(), pdu->N_bytes); + rlc_um_write_data_pdu_header(&header, pdu); + memcpy(payload, pdu->msg, pdu->N_bytes); + uint32_t ret = pdu->N_bytes; + + log->debug("%s returning length %d\n", rrc->get_rb_name(lcid).c_str(), pdu->N_bytes); + pool->deallocate(pdu); + + debug_state(); + return ret; +} + +void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + std::map::iterator it; + rlc_umd_pdu_header_t header; + rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); + + log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d", + rb_name().c_str(), header.sn); + + if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) && + RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur)) + { + log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", + rb_name().c_str(), header.sn, vr_ur, vr_uh); + return; + } + it = rx_window.find(header.sn); + if(rx_window.end() != it) + { + log->info("%s Discarding duplicate SN: %d\n", + rb_name().c_str(), header.sn); + return; + } + + // Write to rx window + rlc_umd_pdu_t pdu; + pdu.buf = pool_allocate; + if (!pdu.buf) { + log->error("Discarting packet: no space in buffer pool\n"); + return; + } + memcpy(pdu.buf->msg, payload, nof_bytes); + pdu.buf->N_bytes = nof_bytes; + //Strip header from PDU + int header_len = rlc_um_packed_length(&header); + pdu.buf->msg += header_len; + pdu.buf->N_bytes -= header_len; + pdu.header = header; + rx_window[header.sn] = pdu; + + // Update vr_uh + if(!inside_reordering_window(header.sn)) + vr_uh = (header.sn + 1)%cfg.rx_mod; + + // Reassemble and deliver SDUs, while updating vr_ur + log->debug("Entering Reassemble from received PDU\n"); + reassemble_rx_sdus(); + log->debug("Finished reassemble from received PDU\n"); + + // Update reordering variables and timers + if(reordering_timer->is_running()) + { + if(RX_MOD_BASE(vr_ux) <= RX_MOD_BASE(vr_ur) || + (!inside_reordering_window(vr_ux) && vr_ux != vr_uh)) + { + reordering_timer->stop(); + } + } + if(!reordering_timer->is_running()) + { + if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur)) + { + reordering_timer->set(this, cfg.t_reordering); + reordering_timer->run(); + vr_ux = vr_uh; + } + } + + debug_state(); +} + +void rlc_um::reassemble_rx_sdus() +{ + if(!rx_sdu) { + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } + } + + // First catch up with lower edge of reordering window + while(!inside_reordering_window(vr_ur)) + { + if(rx_window.end() == rx_window.find(vr_ur)) + { + rx_sdu->reset(); + }else{ + // Handle any SDU segments + for(uint32_t i=0; iN_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + // Advance data pointers and continue with next segment + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + rx_sdu->reset(); + break; + } + + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); + rx_sdu->N_bytes += len; + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) { + log->warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu); + rx_sdu->reset(); + } else { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rb_name().c_str(), vr_ur, i); + rx_sdu->set_timestamp(); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } + } + pdu_lost = false; + } + + // Handle last segment + + if (rx_sdu->N_bytes > 0 || rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->debug("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n", + vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); + + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); + rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; + vr_ur_in_rx_sdu = vr_ur; + if(rlc_um_end_aligned(rx_window[vr_ur].header.fi)) + { + if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping remainder of lost PDU (lower edge last segments)\n"); + rx_sdu->reset(); + } else { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur); + rx_sdu->set_timestamp(); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } + } + pdu_lost = false; + } + } + + // Clean up rx_window + pool->deallocate(rx_window[vr_ur].buf); + rx_window.erase(vr_ur); + } + + vr_ur = (vr_ur + 1)%cfg.rx_mod; + } + + + // Now update vr_ur until we reach an SN we haven't yet received + while(rx_window.end() != rx_window.find(vr_ur)) + { + // Handle any SDU segments + for(uint32_t i=0; iN_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + // Advance data pointers and continue with next segment + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + rx_sdu->reset(); + break; + } + + // Check available space in SDU + if ((uint32_t)len > rx_sdu->get_tailroom()) { + log->error("Dropping PDU %d due to buffer mis-alignment (current segment len %d B, received %d B)\n", vr_ur, rx_sdu->N_bytes, len); + rx_sdu->reset(); + goto clean_up_rx_window; + } + + log->debug("Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n", + len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod); + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); + rx_sdu->N_bytes += len; + rx_window[vr_ur].buf->msg += len; + rx_window[vr_ur].buf->N_bytes -= len; + if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) { + log->warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu); + rx_sdu->reset(); + } else { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rb_name().c_str(), vr_ur, i); + rx_sdu->set_timestamp(); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } + } + pdu_lost = false; + } + + // Handle last segment + if (rx_sdu->N_bytes == 0 && rx_window[vr_ur].header.N_li == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); + rx_sdu->reset(); + goto clean_up_rx_window; + } + + if (rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES && + rx_window[vr_ur].buf->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES && + rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES) + { + + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); + rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; + log->debug("Writing last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n", + vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); + } else { + log->error("Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d\n", + rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur); + } + vr_ur_in_rx_sdu = vr_ur; + if(rlc_um_end_aligned(rx_window[vr_ur].header.fi)) + { + if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { + log->warning("Dropping remainder of lost PDU (update vr_ur last segments)\n"); + rx_sdu->reset(); + } else { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rb_name().c_str(), vr_ur); + rx_sdu->set_timestamp(); + if(cfg.is_mrb){ + pdcp->write_pdu_mch(lcid, rx_sdu); + } else { + pdcp->write_pdu(lcid, rx_sdu); + } + rx_sdu = pool_allocate; + if (!rx_sdu) { + log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); + return; + } + } + pdu_lost = false; + } + +clean_up_rx_window: + + // Clean up rx_window + pool->deallocate(rx_window[vr_ur].buf); + rx_window.erase(vr_ur); + + vr_ur = (vr_ur + 1)%cfg.rx_mod; + } +} + +bool rlc_um::inside_reordering_window(uint16_t sn) +{ + if(cfg.rx_window_size == 0) { + return true; + } + if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) && + RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh)) + { + return true; + }else{ + return false; + } +} + +void rlc_um::debug_state() +{ + log->debug("%s vt_us = %d, vr_ur = %d, vr_ux = %d, vr_uh = %d \n", + rb_name().c_str(), vt_us, vr_ur, vr_ux, vr_uh); + +} + +std::string rlc_um::rb_name() { + if(cfg.is_mrb) { + std::stringstream ss; + ss << "MRB" << lcid; + return ss.str(); + } else { + return rrc->get_rb_name(lcid); + } +} + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ + +void rlc_um_read_data_pdu_header(byte_buffer_t *pdu, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header) +{ + rlc_um_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header); +} + +void rlc_um_read_data_pdu_header(uint8_t *payload, uint32_t nof_bytes, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header) +{ + uint8_t ext; + uint8_t *ptr = payload; + // Fixed part + if(RLC_UMD_SN_SIZE_5_BITS == sn_size) + { + header->fi = (rlc_fi_field_t)((*ptr >> 6) & 0x03); // 2 bits FI + ext = ((*ptr >> 5) & 0x01); // 1 bit EXT + header->sn = *ptr & 0x1F; // 5 bits SN + ptr++; + }else{ + header->fi = (rlc_fi_field_t)((*ptr >> 3) & 0x03); // 2 bits FI + ext = ((*ptr >> 2) & 0x01); // 1 bit EXT + header->sn = (*ptr & 0x03) << 8; // 2 bits SN + ptr++; + header->sn |= (*ptr & 0xFF); // 8 bits SN + ptr++; + } + + header->sn_size = sn_size; + + // Extension part + header->N_li = 0; + while(ext) + { + if(header->N_li%2 == 0) + { + ext = ((*ptr >> 7) & 0x01); + header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI + header->N_li++; + } + else + { + ext = (*ptr >> 3) & 0x01; + header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI + header->N_li++; + ptr++; + } + } +} + +void rlc_um_write_data_pdu_header(rlc_umd_pdu_header_t *header, byte_buffer_t *pdu) +{ + uint32_t i; + uint8_t ext = (header->N_li > 0) ? 1 : 0; + // Make room for the header + uint32_t len = rlc_um_packed_length(header); + pdu->msg -= len; + uint8_t *ptr = pdu->msg; + + // Fixed part + if(RLC_UMD_SN_SIZE_5_BITS == header->sn_size) + { + *ptr = (header->fi & 0x03) << 6; // 2 bits FI + *ptr |= (ext & 0x01) << 5; // 1 bit EXT + *ptr |= header->sn & 0x1F; // 5 bits SN + ptr++; + }else{ + *ptr = (header->fi & 0x03) << 3; // 3 Reserved bits | 2 bits FI + *ptr |= (ext & 0x01) << 2; // 1 bit EXT + *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN + ptr++; + *ptr = (header->sn & 0xFF); // 8 bits SN + ptr++; + } + + // Extension part + i = 0; + while(i < header->N_li) + { + ext = ((i+1) == header->N_li) ? 0 : 1; + *ptr = (ext & 0x01) << 7; // 1 bit header + *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI + ptr++; + *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI + i++; + if(i < header->N_li) + { + ext = ((i+1) == header->N_li) ? 0 : 1; + *ptr |= (ext & 0x01) << 3; // 1 bit header + *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI + ptr++; + *ptr = (header->li[i] & 0x0FF); // 8 bits of LI + ptr++; + i++; + } + } + // Pad if N_li is odd + if(header->N_li%2 == 1) + ptr++; + + pdu->N_bytes += ptr-pdu->msg; +} + +uint32_t rlc_um_packed_length(rlc_umd_pdu_header_t *header) +{ + uint32_t len = 0; + if(RLC_UMD_SN_SIZE_5_BITS == header->sn_size) + { + len += 1; // Fixed part is 1 byte + }else{ + len += 2; // Fixed part is 2 bytes + } + len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up + return len; +} + +bool rlc_um_start_aligned(uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); +} + +bool rlc_um_end_aligned(uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); +} + +} // namespace srsue diff --git a/lib/test/CMakeLists.txt b/lib/test/CMakeLists.txt new file mode 100644 index 0000000..90ed8e9 --- /dev/null +++ b/lib/test/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(asn1) +add_subdirectory(common) +add_subdirectory(phy) +add_subdirectory(upper) diff --git a/lib/test/asn1/CMakeLists.txt b/lib/test/asn1/CMakeLists.txt new file mode 100644 index 0000000..0f59d04 --- /dev/null +++ b/lib/test/asn1/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2013-2017 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/. +# + +add_executable(srslte_asn1_rrc_mcch_test srslte_asn1_rrc_mcch_test.cc) +target_link_libraries(srslte_asn1_rrc_mcch_test srslte_asn1 srslte_common) +add_test(srslte_asn1_rrc_mcch_test srslte_asn1_rrc_mcch_test) + +add_executable(srslte_asn1_rrc_meas_test srslte_asn1_rrc_meas_test.cc) +target_link_libraries(srslte_asn1_rrc_meas_test srslte_common srslte_phy srslte_asn1) +add_test(srslte_asn1_rrc_meas_test srslte_asn1_rrc_meas_test) diff --git a/lib/test/asn1/rrc_meas_test.cc b/lib/test/asn1/rrc_meas_test.cc new file mode 100644 index 0000000..63a4d61 --- /dev/null +++ b/lib/test/asn1/rrc_meas_test.cc @@ -0,0 +1,91 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include "srslte/common/log_filter.h" +#include "srslte/asn1/liblte_rrc.h" + + +void basic_test() { + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(128); + + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_BIT_MSG_STRUCT bit_buf2; + LIBLTE_BYTE_MSG_STRUCT byte_buf; + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + uint32_t rrc_message_len = 18; + uint8_t rrc_message[] = {0x08, 0x10, 0x49, 0x3C, 0x0D, 0x97, 0x89, 0x83, + 0xC0, 0x84, 0x20, 0x82, 0x08, 0x21, 0x00, 0x01, + 0xBC, 0x48}; + + srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8); + bit_buf.N_bits = rrc_message_len*8; + liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &ul_dcch_msg); + + assert(ul_dcch_msg.msg_type == LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT); + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *rep = &ul_dcch_msg.msg.measurement_report; + assert(rep->meas_id == 1); + assert(rep->pcell_rsrp_result == 73); + assert(rep->pcell_rsrq_result == 15); + assert(rep->have_meas_result_neigh_cells); + assert(rep->meas_result_neigh_cells_choice == LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA); + LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT *eutra = &rep->meas_result_neigh_cells.eutra; + assert(eutra->n_result == 1); + assert(eutra->result_eutra_list[0].phys_cell_id == 357); + assert(eutra->result_eutra_list[0].have_cgi_info); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mcc == 0xF898); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mnc == 0xFF78); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.cell_id == 0x1084104); + assert(eutra->result_eutra_list[0].cgi_info.tracking_area_code == 0x1042); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.n_plmn_identity_list == 1); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mcc == 0xFFFF); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mnc == 0xFF00); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrp_result == 60); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrq_result == 18); + + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf2); + srslte_bit_pack_vector(bit_buf2.msg, byte_buf.msg, bit_buf2.N_bits); + byte_buf.N_bytes = (bit_buf2.N_bits+7)/8; + log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "UL_DCCH Packed message\n"); + + for(uint32_t i=0; i +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/common/log_filter.h" +#include "srslte/phy/utils/bit.h" + +void pack_test() +{ + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(1024); + + uint32_t known_reference_len = 30; + uint8_t known_reference[256] = {0x0d, 0x8f, 0xdf, 0xff, 0xff, 0xff, 0xe2, 0x2f, + 0xfc, 0x38, 0x5e, 0x61, 0xec, 0xa8, 0x00, 0x00, + 0x02, 0x02, 0x10, 0x00, 0x20, 0x05, 0xe6, 0x1e, + 0xca, 0x80, 0x00, 0x00, 0x40, 0x42}; + + LIBLTE_BYTE_MSG_STRUCT byte_buf; + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch_msg; + + mcch_msg.commonsf_allocpatternlist_r9_size = 2; + mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset = 4; + mcch_msg.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N32; + mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE; + mcch_msg.commonsf_allocpatternlist_r9[0].subfr_alloc = 0x3F; + mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_offset = 7; + mcch_msg.commonsf_allocpatternlist_r9[1].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N8; + mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR; + mcch_msg.commonsf_allocpatternlist_r9[1].subfr_alloc = 0xFFFFFF; + + mcch_msg.commonsf_allocperiod_r9 = LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF256; + + mcch_msg.pmch_infolist_r9_size = 2; + + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654; + mcch_msg.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 1; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 = 16; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF1024; + mcch_msg.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 = 1535; + + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9_size = 1; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].sessionid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0xF987; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 0xF654; + mcch_msg.pmch_infolist_r9[1].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 2; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.datamcs_r9 = 8; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF8; + mcch_msg.pmch_infolist_r9[1].pmch_config_r9.sf_alloc_end_r9 = 0; + + liblte_rrc_pack_mcch_msg(&mcch_msg, &bit_buf); + liblte_pack(bit_buf.msg, bit_buf.N_bits, byte_buf.msg); + byte_buf.N_bytes = (bit_buf.N_bits+7)/8; + + //log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "MCCH packed message:"); + + assert(byte_buf.N_bytes == known_reference_len); + for(uint32 i=0; i +#include +#include +#include "srslte/common/log_filter.h" +#include "srslte/asn1/liblte_rrc.h" + + +void basic_test() { + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(128); + + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_BIT_MSG_STRUCT bit_buf2; + LIBLTE_BYTE_MSG_STRUCT byte_buf; + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + uint32_t rrc_message_len = 18; + uint8_t rrc_message[] = {0x08, 0x10, 0x49, 0x3C, 0x0D, 0x97, 0x89, 0x83, + 0xC0, 0x84, 0x20, 0x82, 0x08, 0x21, 0x00, 0x01, + 0xBC, 0x48}; + + srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8); + bit_buf.N_bits = rrc_message_len*8; + liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &ul_dcch_msg); + + assert(ul_dcch_msg.msg_type == LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT); + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *rep = &ul_dcch_msg.msg.measurement_report; + assert(rep->meas_id == 1); + assert(rep->pcell_rsrp_result == 73); + assert(rep->pcell_rsrq_result == 15); + assert(rep->have_meas_result_neigh_cells); + assert(rep->meas_result_neigh_cells_choice == LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA); + LIBLTE_RRC_MEAS_RESULT_LIST_EUTRA_STRUCT *eutra = &rep->meas_result_neigh_cells.eutra; + assert(eutra->n_result == 1); + assert(eutra->result_eutra_list[0].phys_cell_id == 357); + assert(eutra->result_eutra_list[0].have_cgi_info); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mcc == 0xF898); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.plmn_id.mnc == 0xFF78); + assert(eutra->result_eutra_list[0].cgi_info.cell_global_id.cell_id == 0x1084104); + assert(eutra->result_eutra_list[0].cgi_info.tracking_area_code == 0x1042); + assert(eutra->result_eutra_list[0].cgi_info.have_plmn_identity_list); + assert(eutra->result_eutra_list[0].cgi_info.n_plmn_identity_list == 1); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mcc == 0xFFFF); + assert(eutra->result_eutra_list[0].cgi_info.plmn_identity_list[0].mnc == 0xFF00); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrp_result == 60); + assert(eutra->result_eutra_list[0].meas_result.have_rsrp); + assert(eutra->result_eutra_list[0].meas_result.rsrq_result == 18); + + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf2); + srslte_bit_pack_vector(bit_buf2.msg, byte_buf.msg, bit_buf2.N_bits); + byte_buf.N_bytes = (bit_buf2.N_bits+7)/8; + log1.info_hex(byte_buf.msg, byte_buf.N_bytes, "UL_DCCH Packed message\n"); + + for(uint32_t i=0; i +#include "srslte/common/bcd_helpers.h" + +using namespace srslte; + +int main(int argc, char **argv) +{ + std::string mcc_str = "001"; + std::string mnc_str = "001"; + uint16_t mcc; + uint16_t mnc; + + // String to code + + assert(string_to_mcc(mcc_str, &mcc)); + assert(mcc == 0xF001); + + assert(string_to_mnc(mnc_str, &mnc)); + assert(mnc == 0xF001); + + mnc_str = "01"; + assert(string_to_mnc(mnc_str, &mnc)); + assert(mnc == 0xFF01); + + // Code to string + + mcc_str = ""; + mnc_str = ""; + mcc = 0xF001; + mnc = 0xF001; + + assert(mcc_to_string(mcc, &mcc_str)); + assert(mcc_str.compare("001") == 0); + + assert(mnc_to_string(mnc, &mnc_str)); + assert(mnc_str.compare("001") == 0); + + mnc = 0xFF01; + assert(mnc_to_string(mnc, &mnc_str)); + assert(mnc_str.compare("01") == 0); +} diff --git a/lib/test/common/log_filter_test.cc b/lib/test/common/log_filter_test.cc new file mode 100644 index 0000000..350e428 --- /dev/null +++ b/lib/test/common/log_filter_test.cc @@ -0,0 +1,135 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define NTHREADS 100 +#define NMSGS 100 + +#include +#include "srslte/common/log_filter.h" +#include "srslte/common/logger_file.h" + +using namespace srslte; + +typedef struct { + logger_file *l; + int thread_id; +}args_t; + +void* thread_loop(void *a) { + args_t *args = (args_t*)a; + char buf[100]; + + sprintf(buf, "LAYER%d", args->thread_id); + log_filter filter(buf, args->l); + filter.set_level(LOG_LEVEL_INFO); + + for(int i=0;ithread_id, i); + filter.warning("Thread %d: %d", args->thread_id, i); + filter.info("Thread %d: %d", args->thread_id, i); + filter.debug("Thread %d: %d", args->thread_id, i); + } + return NULL; +} + +void* thread_loop_hex(void *a) { + args_t *args = (args_t*)a; + char buf[100]; + uint8_t hex[100]; + + for(int i=0;i<100;i++) + hex[i] = i & 0xFF; + sprintf(buf, "LAYER%d", args->thread_id); + log_filter filter(buf, args->l); + filter.set_level(LOG_LEVEL_DEBUG); + filter.set_hex_limit(32); + + for(int i=0;ithread_id, i); + filter.warning_hex(hex, 100, "Thread %d: %d", args->thread_id, i); + filter.info_hex(hex, 100, "Thread %d: %d", args->thread_id, i); + filter.debug_hex(hex, 100, "Thread %d: %d", args->thread_id, i); + } + return NULL; +} + +void write(std::string filename) { + logger_file l; + l.init(filename); + pthread_t threads[NTHREADS]; + args_t args[NTHREADS]; + for(int i=0;i +#include +#include "srslte/common/logger_file.h" + +using namespace srslte; + +typedef struct { + logger_file *l; + int thread_id; +}args_t; + +void* thread_loop(void *a) { + args_t *args = (args_t*)a; + char buf[100]; + for(int i=0;ithread_id, i); + args->l->log(new std::string(buf)); + } + return NULL; +} + +void write(std::string filename) { + logger_file l; + l.init(filename); + pthread_t threads[NTHREADS]; + args_t args[NTHREADS]; + for(int i=0;i +#include "srslte/upper/rlc_tx_queue.h" + +using namespace srslte; + +typedef struct { + rlc_tx_queue *q; +}args_t; + +void* write_thread(void *a) { + args_t *args = (args_t*)a; + for(uint32_t i=0;imsg, &i, 4); + b->N_bytes = 4; + args->q->write(b); + } + return NULL; +} + +int main(int argc, char **argv) { + bool result; + rlc_tx_queue q; + byte_buffer_t *b; + pthread_t thread; + args_t args; + u_int32_t r; + + result = true; + args.q = &q; + + pthread_create(&thread, NULL, &write_thread, &args); + + for(uint32_t i=0;imsg, 4); + delete b; + if(r != i) + result = false; + } + + pthread_join(thread, NULL); + + if (q.size() != 0 || q.size_bytes() != 0) { + result = false; + } + + if(result) { + printf("Passed\n"); + exit(0); + }else{ + printf("Failed\n;"); + exit(1); + } +} diff --git a/lib/test/common/test_eea1.cc b/lib/test/common/test_eea1.cc new file mode 100644 index 0000000..2b61ddc --- /dev/null +++ b/lib/test/common/test_eea1.cc @@ -0,0 +1,581 @@ +/* + * Includes + */ + +#include +#include +#include +#include + +#include "srslte/srslte.h" +#include "srslte/common/liblte_security.h" + +/* + * Prototypes + */ + +int32 arrcmp(uint8_t const * const a, uint8_t const * const b, uint32 len) { + uint32 i = 0; + + for (i = 0; i < len; i++) { + if (a[i] != b[i]) { + return a[i] - b[i]; + } + } + return 0; +} + +/* + * Tests + * + * Document Reference: 33.401 V13.1.0 Annex C.3 + * Specification of the 3GPP Confidentiality and + * Integrity Algorithms UEA2 & UIA2 D4 v1.0 + */ + +void test_set_1() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 253, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0x5d, 0x5b, 0xfe, 0x75, 0xeb, 0x04, 0xf6, + 0x8c, 0xe0, 0xa1, 0x23, 0x77, 0xea, 0x00, 0xb3, 0x7d, + 0x47, 0xc6, 0xa0, 0xba, 0x06, 0x30, 0x91, 0x55, 0x08, + 0x6a, 0x85, 0x9c, 0x43, 0x41, 0xb3, 0x78 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_2() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x2b, 0xd6, 0x45, 0x9f, 0x82, 0xc4, 0x40, + 0xe0, 0x95, 0x2c, 0x49, 0x10, 0x48, 0x05, 0xff, 0x48 }; + uint32_t count = 0xc675a64b; + uint8_t bearer = 0x0c; + uint8_t direction = 1; + uint32_t len_bits = 798, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x7e, 0xc6, 0x12, 0x72, 0x74, 0x3b, 0xf1, + 0x61, 0x47, 0x26, 0x44, 0x6a, 0x6c, 0x38, 0xce, 0xd1, + 0x66, 0xf6, 0xca, 0x76, 0xeb, 0x54, 0x30, 0x04, 0x42, + 0x86, 0x34, 0x6c, 0xef, 0x13, 0x0f, 0x92, 0x92, 0x2b, + 0x03, 0x45, 0x0d, 0x3a, 0x99, 0x75, 0xe5, 0xbd, 0x2e, + 0xa0, 0xeb, 0x55, 0xad, 0x8e, 0x1b, 0x19, 0x9e, 0x3e, + 0xc4, 0x31, 0x60, 0x20, 0xe9, 0xa1, 0xb2, 0x85, 0xe7, + 0x62, 0x79, 0x53, 0x59, 0xb7, 0xbd, 0xfd, 0x39, 0xbe, + 0xf4, 0xb2, 0x48, 0x45, 0x83, 0xd5, 0xaf, 0xe0, 0x82, + 0xae, 0xe6, 0x38, 0xbf, 0x5f, 0xd5, 0xa6, 0x06, 0x19, + 0x39, 0x01, 0xa0, 0x8f, 0x4a, 0xb4, 0x1a, 0xab, 0x9b, + 0x13, 0x48, 0x80 }; + uint8_t ct[] = { 0x3f, 0x67, 0x85, 0x07, 0x14, 0xb8, 0xda, + 0x69, 0xef, 0xb7, 0x27, 0xed, 0x7a, 0x6c, 0x0c, 0x50, + 0x71, 0x4a, 0xd7, 0x36, 0xc4, 0xf5, 0x60, 0x00, 0x06, + 0xe3, 0x52, 0x5b, 0xe8, 0x07, 0xc4, 0x67, 0xc6, 0x77, + 0xff, 0x86, 0x4a, 0xf4, 0x5f, 0xba, 0x09, 0xc2, 0x7c, + 0xde, 0x38, 0xf8, 0x7a, 0x1f, 0x84, 0xd5, 0x9a, 0xb2, + 0x55, 0x40, 0x8f, 0x2c, 0x7b, 0x82, 0xf9, 0xea, 0xd4, + 0x1a, 0x1f, 0xe6, 0x5e, 0xab, 0xeb, 0xfb, 0xc1, 0xf3, + 0xa4, 0xc5, 0x6c, 0x9a, 0x26, 0xfc, 0xf7, 0xb3, 0xd6, + 0x6d, 0x02, 0x20, 0xee, 0x47, 0x75, 0xbc, 0x58, 0x17, + 0x0a, 0x2b, 0x12, 0xf3, 0x43, 0x1d, 0x11, 0xb3, 0x44, + 0xd6, 0xe3, 0x6c }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_3() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, 0x8b, + 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, 0x77, 0xfb }; + uint32_t count = 0x544d49cd; + uint8_t bearer = 0x04; + uint8_t direction = 0; + uint32_t len_bits = 310, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0xfd, 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, + 0x65, 0x74, 0x50, 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, + 0x36, 0xd2, 0x34, 0x9e, 0x23, 0xf6, 0x44, 0x39, 0x2c, + 0x8e, 0xa9, 0xc4, 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, + 0xf2, 0x64, 0xd0, 0xf2, 0x48, 0x00 }; + uint8_t ct[] = { 0x48, 0x14, 0x8e, 0x54, 0x52, 0xa2, 0x10, + 0xc0, 0x5f, 0x46, 0xbc, 0x80, 0xdc, 0x6f, 0x73, 0x49, + 0x5b, 0x02, 0x04, 0x8c, 0x1b, 0x95, 0x8b, 0x02, 0x61, + 0x02, 0xca, 0x97, 0x28, 0x02, 0x79, 0xa4, 0xc1, 0x8d, + 0x2e, 0xe3, 0x08, 0x92, 0x1c }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_4() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xaa, 0x1f, 0x95, 0xae, 0xa5, 0x33, 0xbc, + 0xb3, 0x2e, 0xb6, 0x3b, 0xf5, 0x2d, 0x8f, 0x83, 0x1a }; + uint32_t count = 0x72d8c671; + uint8_t bearer = 0x10; + uint8_t direction = 1; + uint32_t len_bits = 1022, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0xfb, 0x1b, 0x96, 0xc5, 0xc8, 0xba, 0xdf, + 0xb2, 0xe8, 0xe8, 0xed, 0xfd, 0xe7, 0x8e, 0x57, 0xf2, + 0xad, 0x81, 0xe7, 0x41, 0x03, 0xfc, 0x43, 0x0a, 0x53, + 0x4d, 0xcc, 0x37, 0xaf, 0xce, 0xc7, 0x0e, 0x15, 0x17, + 0xbb, 0x06, 0xf2, 0x72, 0x19, 0xda, 0xe4, 0x90, 0x22, + 0xdd, 0xc4, 0x7a, 0x06, 0x8d, 0xe4, 0xc9, 0x49, 0x6a, + 0x95, 0x1a, 0x6b, 0x09, 0xed, 0xbd, 0xc8, 0x64, 0xc7, + 0xad, 0xbd, 0x74, 0x0a, 0xc5, 0x0c, 0x02, 0x2f, 0x30, + 0x82, 0xba, 0xfd, 0x22, 0xd7, 0x81, 0x97, 0xc5, 0xd5, + 0x08, 0xb9, 0x77, 0xbc, 0xa1, 0x3f, 0x32, 0xe6, 0x52, + 0xe7, 0x4b, 0xa7, 0x28, 0x57, 0x60, 0x77, 0xce, 0x62, + 0x8c, 0x53, 0x5e, 0x87, 0xdc, 0x60, 0x77, 0xba, 0x07, + 0xd2, 0x90, 0x68, 0x59, 0x0c, 0x8c, 0xb5, 0xf1, 0x08, + 0x8e, 0x08, 0x2c, 0xfa, 0x0e, 0xc9, 0x61, 0x30, 0x2d, + 0x69, 0xcf, 0x3d, 0x44 }; + uint8_t ct[] = { 0xff, 0xcf, 0xc2, 0xfe, 0xad, 0x6c, 0x09, + 0x4e, 0x96, 0xc5, 0x89, 0xd0, 0xf6, 0x77, 0x9b, 0x67, + 0x84, 0x24, 0x6c, 0x3c, 0x4d, 0x1c, 0xea, 0x20, 0x3d, + 0xb3, 0x90, 0x1f, 0x40, 0xad, 0x4f, 0xd7, 0x13, 0x8b, + 0xc6, 0xd7, 0x7e, 0x83, 0x20, 0xcb, 0x10, 0x2f, 0x49, + 0x7f, 0xdd, 0x44, 0xa2, 0x69, 0xa9, 0x6e, 0xcb, 0x28, + 0x61, 0x77, 0x00, 0xe3, 0x32, 0xeb, 0x2f, 0x73, 0x6b, + 0x34, 0xf4, 0xf2, 0x69, 0x30, 0x94, 0xe2, 0x2f, 0xf9, + 0x4f, 0x9b, 0xe4, 0x72, 0x3d, 0xa4, 0x0c, 0x40, 0xdf, + 0xd3, 0x93, 0x1c, 0xc1, 0xac, 0x97, 0x23, 0xf6, 0xb4, + 0xa9, 0x91, 0x3e, 0x96, 0xb6, 0xdb, 0x7a, 0xbc, 0xac, + 0xe4, 0x15, 0x17, 0x7c, 0x1d, 0x01, 0x15, 0xc5, 0xf0, + 0x9b, 0x5f, 0xde, 0xa0, 0xb3, 0xad, 0xb8, 0xf9, 0xda, + 0x6e, 0x9f, 0x9a, 0x04, 0xc5, 0x43, 0x39, 0x7b, 0x9d, + 0x43, 0xf8, 0x73, 0x30 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_5() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x96, 0x18, 0xae, 0x46, 0x89, 0x1f, 0x86, + 0x57, 0x8e, 0xeb, 0xe9, 0x0e, 0xf7, 0xa1, 0x20, 0x2e }; + uint32_t count = 0xc675a64b; + uint8_t bearer = 0x0c; + uint8_t direction = 1; + uint32_t len_bits = 1245, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x8d, 0xaa, 0x17, 0xb1, 0xae, 0x05, 0x05, + 0x29, 0xc6, 0x82, 0x7f, 0x28, 0xc0, 0xef, 0x6a, 0x12, + 0x42, 0xe9, 0x3f, 0x8b, 0x31, 0x4f, 0xb1, 0x8a, 0x77, + 0xf7, 0x90, 0xae, 0x04, 0x9f, 0xed, 0xd6, 0x12, 0x26, + 0x7f, 0xec, 0xae, 0xfc, 0x45, 0x01, 0x74, 0xd7, 0x6d, + 0x9f, 0x9a, 0xa7, 0x75, 0x5a, 0x30, 0xcd, 0x90, 0xa9, + 0xa5, 0x87, 0x4b, 0xf4, 0x8e, 0xaf, 0x70, 0xee, 0xa3, + 0xa6, 0x2a, 0x25, 0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, + 0x8b, 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, 0x77, + 0xfb, 0x54, 0x4d, 0x49, 0xcd, 0x49, 0x72, 0x0e, 0x21, + 0x9d, 0xbf, 0x8b, 0xbe, 0xd3, 0x39, 0x04, 0xe1, 0xfd, + 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, 0x65, 0x74, 0x50, + 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, 0x36, 0xd2, 0x34, + 0x9e, 0x23, 0xf6, 0x44, 0x39, 0x2c, 0x8e, 0xa9, 0xc4, + 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, 0xf2, 0x64, 0xd0, + 0xf2, 0x48, 0x41, 0xd6, 0x46, 0x5f, 0x09, 0x96, 0xff, + 0x84, 0xe6, 0x5f, 0xc5, 0x17, 0xc5, 0x3e, 0xfc, 0x33, + 0x63, 0xc3, 0x84, 0x92, 0xa8 }; + uint8_t ct[] = { 0x6c, 0xdb, 0x18, 0xa7, 0xca, 0x82, 0x18, + 0xe8, 0x6e, 0x4b, 0x4b, 0x71, 0x6a, 0x4d, 0x04, 0x37, + 0x1f, 0xbe, 0xc2, 0x62, 0xfc, 0x5a, 0xd0, 0xb3, 0x81, + 0x9b, 0x18, 0x7b, 0x97, 0xe5, 0x5b, 0x1a, 0x4d, 0x7c, + 0x19, 0xee, 0x24, 0xc8, 0xb4, 0xd7, 0x72, 0x3c, 0xfe, + 0xdf, 0x04, 0x5b, 0x8a, 0xca, 0xe4, 0x86, 0x95, 0x17, + 0xd8, 0x0e, 0x50, 0x61, 0x5d, 0x90, 0x35, 0xd5, 0xd9, + 0xc5, 0xa4, 0x0a, 0xf6, 0x02, 0x28, 0x0b, 0x54, 0x25, + 0x97, 0xb0, 0xcb, 0x18, 0x61, 0x9e, 0xeb, 0x35, 0x92, + 0x57, 0x59, 0xd1, 0x95, 0xe1, 0x00, 0xe8, 0xe4, 0xaa, + 0x0c, 0x38, 0xa3, 0xc2, 0xab, 0xe0, 0xf3, 0xd8, 0xff, + 0x04, 0xf3, 0xc3, 0x3c, 0x29, 0x50, 0x69, 0xc2, 0x36, + 0x94, 0xb5, 0xbb, 0xea, 0xcd, 0xd5, 0x42, 0xe2, 0x8e, + 0x8a, 0x94, 0xed, 0xb9, 0x11, 0x9f, 0x41, 0x2d, 0x05, + 0x4b, 0xe1, 0xfa, 0x72, 0x72, 0xb5, 0xff, 0xb2, 0xb2, + 0x57, 0x0f, 0x4f, 0x7c, 0xea, 0xf3, 0x83, 0xa8, 0xa9, + 0xd9, 0x35, 0x72, 0xf0, 0x4d, 0x6e, 0x3a, 0x6e, 0x29, + 0x37, 0x26, 0xec, 0x62, 0xc8 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_6() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x54, 0xf4, 0xe2, 0xe0, 0x4c, 0x83, 0x78, + 0x6e, 0xec, 0x8f, 0xb5, 0xab, 0xe8, 0xe3, 0x65, 0x66 }; + uint32_t count = 0xaca4f50f; + uint8_t bearer = 0x0b; + uint8_t direction = 0; + uint32_t len_bits = 3861, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x40, 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, + 0xfb, 0x42, 0x86, 0xb2, 0x99, 0x78, 0x3d, 0xaf, 0x44, + 0x2c, 0x09, 0x9f, 0x7a, 0xb0, 0xf5, 0x8d, 0x5c, 0x8e, + 0x46, 0xb1, 0x04, 0xf0, 0x8f, 0x01, 0xb4, 0x1a, 0xb4, + 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x36, 0xbd, 0x1a, + 0x3d, 0x90, 0xdc, 0x3a, 0x41, 0xb4, 0x6d, 0x51, 0x67, + 0x2a, 0xc4, 0xc9, 0x66, 0x3a, 0x2b, 0xe0, 0x63, 0xda, + 0x4b, 0xc8, 0xd2, 0x80, 0x8c, 0xe3, 0x3e, 0x2c, 0xcc, + 0xbf, 0xc6, 0x34, 0xe1, 0xb2, 0x59, 0x06, 0x08, 0x76, + 0xa0, 0xfb, 0xb5, 0xa4, 0x37, 0xeb, 0xcc, 0x8d, 0x31, + 0xc1, 0x9e, 0x44, 0x54, 0x31, 0x87, 0x45, 0xe3, 0xfa, + 0x16, 0xbb, 0x11, 0xad, 0xae, 0x24, 0x88, 0x79, 0xfe, + 0x52, 0xdb, 0x25, 0x43, 0xe5, 0x3c, 0xf4, 0x45, 0xd3, + 0xd8, 0x28, 0xce, 0x0b, 0xf5, 0xc5, 0x60, 0x59, 0x3d, + 0x97, 0x27, 0x8a, 0x59, 0x76, 0x2d, 0xd0, 0xc2, 0xc9, + 0xcd, 0x68, 0xd4, 0x49, 0x6a, 0x79, 0x25, 0x08, 0x61, + 0x40, 0x14, 0xb1, 0x3b, 0x6a, 0xa5, 0x11, 0x28, 0xc1, + 0x8c, 0xd6, 0xa9, 0x0b, 0x87, 0x97, 0x8c, 0x2f, 0xf1, + 0xca, 0xbe, 0x7d, 0x9f, 0x89, 0x8a, 0x41, 0x1b, 0xfd, + 0xb8, 0x4f, 0x68, 0xf6, 0x72, 0x7b, 0x14, 0x99, 0xcd, + 0xd3, 0x0d, 0xf0, 0x44, 0x3a, 0xb4, 0xa6, 0x66, 0x53, + 0x33, 0x0b, 0xcb, 0xa1, 0x10, 0x5e, 0x4c, 0xec, 0x03, + 0x4c, 0x73, 0xe6, 0x05, 0xb4, 0x31, 0x0e, 0xaa, 0xad, + 0xcf, 0xd5, 0xb0, 0xca, 0x27, 0xff, 0xd8, 0x9d, 0x14, + 0x4d, 0xf4, 0x79, 0x27, 0x59, 0x42, 0x7c, 0x9c, 0xc1, + 0xf8, 0xcd, 0x8c, 0x87, 0x20, 0x23, 0x64, 0xb8, 0xa6, + 0x87, 0x95, 0x4c, 0xb0, 0x5a, 0x8d, 0x4e, 0x2d, 0x99, + 0xe7, 0x3d, 0xb1, 0x60, 0xde, 0xb1, 0x80, 0xad, 0x08, + 0x41, 0xe9, 0x67, 0x41, 0xa5, 0xd5, 0x9f, 0xe4, 0x18, + 0x9f, 0x15, 0x42, 0x00, 0x26, 0xfe, 0x4c, 0xd1, 0x21, + 0x04, 0x93, 0x2f, 0xb3, 0x8f, 0x73, 0x53, 0x40, 0x43, + 0x8a, 0xaf, 0x7e, 0xca, 0x6f, 0xd5, 0xcf, 0xd3, 0xa1, + 0x95, 0xce, 0x5a, 0xbe, 0x65, 0x27, 0x2a, 0xf6, 0x07, + 0xad, 0xa1, 0xbe, 0x65, 0xa6, 0xb4, 0xc9, 0xc0, 0x69, + 0x32, 0x34, 0x09, 0x2c, 0x4d, 0x01, 0x8f, 0x17, 0x56, + 0xc6, 0xdb, 0x9d, 0xc8, 0xa6, 0xd8, 0x0b, 0x88, 0x81, + 0x38, 0x61, 0x6b, 0x68, 0x12, 0x62, 0xf9, 0x54, 0xd0, + 0xe7, 0x71, 0x17, 0x48, 0x78, 0x0d, 0x92, 0x29, 0x1d, + 0x86, 0x29, 0x99, 0x72, 0xdb, 0x74, 0x1c, 0xfa, 0x4f, + 0x37, 0xb8, 0xb5, 0x6c, 0xdb, 0x18, 0xa7, 0xca, 0x82, + 0x18, 0xe8, 0x6e, 0x4b, 0x4b, 0x71, 0x6a, 0x4d, 0x04, + 0x37, 0x1f, 0xbe, 0xc2, 0x62, 0xfc, 0x5a, 0xd0, 0xb3, + 0x81, 0x9b, 0x18, 0x7b, 0x97, 0xe5, 0x5b, 0x1a, 0x4d, + 0x7c, 0x19, 0xee, 0x24, 0xc8, 0xb4, 0xd7, 0x72, 0x3c, + 0xfe, 0xdf, 0x04, 0x5b, 0x8a, 0xca, 0xe4, 0x86, 0x95, + 0x17, 0xd8, 0x0e, 0x50, 0x61, 0x5d, 0x90, 0x35, 0xd5, + 0xd9, 0xc5, 0xa4, 0x0a, 0xf6, 0x02, 0x28, 0x0b, 0x54, + 0x25, 0x97, 0xb0, 0xcb, 0x18, 0x61, 0x9e, 0xeb, 0x35, + 0x92, 0x57, 0x59, 0xd1, 0x95, 0xe1, 0x00, 0xe8, 0xe4, + 0xaa, 0x0c, 0x38, 0xa3, 0xc2, 0xab, 0xe0, 0xf3, 0xd8, + 0xff, 0x04, 0xf3, 0xc3, 0x3c, 0x29, 0x50, 0x69, 0xc2, + 0x36, 0x94, 0xb5, 0xbb, 0xea, 0xcd, 0xd5, 0x42, 0xe2, + 0x8e, 0x8a, 0x94, 0xed, 0xb9, 0x11, 0x9f, 0x41, 0x2d, + 0x05, 0x4b, 0xe1, 0xfa, 0x72, 0xb0, 0x95, 0x50 }; + uint8_t ct[] = { 0x35, 0x1e, 0x30, 0xd4, 0xd9, 0x10, 0xc5, + 0xdd, 0x5a, 0xd7, 0x83, 0x4c, 0x42, 0x6e, 0x6c, 0x0c, + 0xab, 0x64, 0x86, 0xda, 0x7b, 0x0f, 0xda, 0x4c, 0xd8, + 0x3a, 0xf1, 0xb9, 0x64, 0x71, 0x37, 0xf1, 0xac, 0x43, + 0xb4, 0x34, 0x22, 0x3b, 0x19, 0xbe, 0x07, 0xbd, 0x89, + 0xd1, 0xcc, 0x30, 0x69, 0x44, 0xd3, 0x36, 0x1e, 0xa1, + 0xa2, 0xf8, 0xcd, 0xbd, 0x32, 0x16, 0x55, 0x97, 0x63, + 0x50, 0xd0, 0x0b, 0x80, 0xdd, 0x83, 0x81, 0x20, 0xa7, + 0x75, 0x5c, 0x6d, 0xea, 0x2a, 0xb2, 0xb0, 0xc9, 0x9a, + 0x91, 0x3f, 0x47, 0xda, 0xe2, 0xb8, 0xde, 0xb9, 0xa8, + 0x29, 0xe5, 0x46, 0x9f, 0xf2, 0xe1, 0x87, 0x77, 0x6f, + 0x6f, 0xd0, 0x81, 0xe3, 0x87, 0x1d, 0x11, 0x9a, 0x76, + 0xe2, 0x4c, 0x91, 0x7e, 0xa6, 0x26, 0x48, 0xe0, 0x2e, + 0x90, 0x36, 0x75, 0x64, 0xde, 0x72, 0xae, 0x7e, 0x4f, + 0x0a, 0x42, 0x49, 0xa9, 0xa5, 0xb0, 0xe4, 0x65, 0xa2, + 0xd6, 0xd9, 0xdc, 0x87, 0x84, 0x3b, 0x1b, 0x87, 0x5c, + 0xc9, 0xa3, 0xbe, 0x93, 0xd8, 0xda, 0x8f, 0x56, 0xec, + 0xaf, 0x59, 0x81, 0xfe, 0x93, 0xc2, 0x84, 0x31, 0x8b, + 0x0d, 0xec, 0x7a, 0x3b, 0xa1, 0x08, 0xe2, 0xcb, 0x1a, + 0x61, 0xe9, 0x66, 0xfa, 0x7a, 0xfa, 0x7a, 0xc7, 0xf6, + 0x7f, 0x65, 0xbc, 0x4a, 0x2d, 0xf0, 0x70, 0xd4, 0xe4, + 0x34, 0x84, 0x5f, 0x10, 0x9a, 0xb2, 0xb6, 0x8a, 0xde, + 0x3d, 0xc3, 0x16, 0xca, 0x63, 0x32, 0xa6, 0x28, 0x93, + 0xe0, 0xa7, 0xec, 0x0b, 0x4f, 0xc2, 0x51, 0x91, 0xbf, + 0x2f, 0xf1, 0xb9, 0xf9, 0x81, 0x5e, 0x4b, 0xa8, 0xa9, + 0x9c, 0x64, 0x3b, 0x52, 0x18, 0x04, 0xf7, 0xd5, 0x85, + 0x0d, 0xde, 0x39, 0x52, 0x20, 0x6e, 0xc6, 0xcc, 0xf3, + 0x40, 0xf9, 0xb3, 0x22, 0x0b, 0x30, 0x23, 0xbd, 0xd0, + 0x63, 0x95, 0x6e, 0xa8, 0x33, 0x39, 0x20, 0xfd, 0xe9, + 0x9e, 0x06, 0x75, 0x41, 0x0e, 0x49, 0xef, 0x3b, 0x4d, + 0x3f, 0xb3, 0xdf, 0x51, 0x92, 0xf9, 0x9c, 0xa8, 0x3d, + 0x3b, 0x00, 0x32, 0xde, 0x08, 0xc2, 0x20, 0x77, 0x6a, + 0x58, 0x65, 0xb0, 0xe4, 0xb3, 0xb0, 0xc7, 0x5d, 0xef, + 0xe7, 0x76, 0x2d, 0xff, 0x01, 0x8e, 0xa7, 0xf5, 0xbe, + 0x2b, 0x2f, 0x97, 0x2b, 0x2a, 0x8b, 0xa5, 0x97, 0x0e, + 0x43, 0xbd, 0x6f, 0xdd, 0x63, 0xda, 0xe6, 0x29, 0x78, + 0x4e, 0xc4, 0x8d, 0x61, 0x00, 0x54, 0xee, 0x4e, 0x4b, + 0x5d, 0xbb, 0xf1, 0xfc, 0x2f, 0xa0, 0xb8, 0x30, 0xe9, + 0x4d, 0xcb, 0xb7, 0x01, 0x4e, 0x8a, 0xb4, 0x29, 0xab, + 0x10, 0x0f, 0xc4, 0x8f, 0x83, 0x17, 0x1d, 0x99, 0xfc, + 0x25, 0x8b, 0x7c, 0x2b, 0xa7, 0xc1, 0x76, 0xea, 0xea, + 0xad, 0x37, 0xf8, 0x60, 0xd5, 0x97, 0xa3, 0x1c, 0xe7, + 0x9b, 0x59, 0x47, 0x33, 0xc7, 0x14, 0x1d, 0xf7, 0x91, + 0x51, 0xfc, 0xa9, 0x0c, 0x08, 0x47, 0x8a, 0x5c, 0x6c, + 0x2c, 0xc4, 0x81, 0xd5, 0x1f, 0xfe, 0xce, 0x3c, 0xd7, + 0xd2, 0x58, 0x13, 0x48, 0x82, 0x7a, 0x71, 0xf0, 0x91, + 0x42, 0x8e, 0xbe, 0x38, 0xc9, 0x5a, 0x3f, 0x5c, 0x63, + 0xe0, 0x56, 0xdf, 0xb7, 0xcc, 0x45, 0xa9, 0xb7, 0xc0, + 0x7d, 0x83, 0x4e, 0x7b, 0x20, 0xb9, 0x9e, 0xd2, 0x02, + 0x42, 0x9c, 0x14, 0xbb, 0x85, 0xff, 0xa4, 0x3b, 0x7c, + 0xb6, 0x84, 0x95, 0xcd, 0x75, 0xab, 0x66, 0xd9, 0x64, + 0xd4, 0xca, 0xfe, 0x64, 0xdd, 0x94, 0x04, 0xda, 0xe2, + 0xdc, 0x51, 0x10, 0x61, 0x7f, 0x19, 0x4f, 0xc3, 0xc1, + 0x84, 0xf5, 0x83, 0xcd, 0x0d, 0xef, 0x6d, 0x00 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + struct timeval t[3]; + + // encryption + gettimeofday(&t[1], NULL); + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("encryption: %u bits, t=%d us, rate=%.1f Mbps/s\n", len_bits, (int) t[0].tv_usec, (float) len_bits/t[0].tv_usec); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + gettimeofday(&t[1], NULL); + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("decryption: %u bits, t=%d us, rate=%.1f Mbps/s\n", len_bits, (int) t[0].tv_usec, (float) len_bits/t[0].tv_usec); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +// set len_bitsgth to multiple of 8 respectively 128 +void test_set_1_block_size() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 256, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0x5d, 0x5b, 0xfe, 0x75, 0xeb, 0x04, 0xf6, + 0x8c, 0xe0, 0xa1, 0x23, 0x77, 0xea, 0x00, 0xb3, 0x7d, + 0x47, 0xc6, 0xa0, 0xba, 0x06, 0x30, 0x91, 0x55, 0x08, + 0x6a, 0x85, 0x9c, 0x43, 0x41, 0xb3, 0x7c }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +// inserted bit flip in msg[0] +void test_set_1_invalid() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 253, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x99, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0x5d, 0x5b, 0xfe, 0x75, 0xeb, 0x04, 0xf6, + 0x8c, 0xe0, 0xa1, 0x23, 0x77, 0xea, 0x00, 0xb3, 0x7d, + 0x47, 0xc6, 0xa0, 0xba, 0x06, 0x30, 0x91, 0x55, 0x08, + 0x6a, 0x85, 0x9c, 0x43, 0x41, 0xb3, 0x78 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea1(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp != 0); + + // decryption + err_lte = liblte_security_decryption_eea1(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp != 0); + + free(out); +} + +/* + * Functions + */ + +int main(int argc, char * argv[]) { + test_set_1(); + test_set_2(); + test_set_3(); + test_set_4(); + test_set_5(); + test_set_6(); + test_set_1_block_size(); + test_set_1_invalid(); +} diff --git a/lib/test/common/test_eea2.cc b/lib/test/common/test_eea2.cc new file mode 100644 index 0000000..d2e8c95 --- /dev/null +++ b/lib/test/common/test_eea2.cc @@ -0,0 +1,583 @@ +/* + * Includes + */ + +#include +#include +#include + +#include "srslte/common/liblte_security.h" +#include "srslte/srslte.h" +#include "srslte/common/liblte_security.h" + +/* + * Prototypes + */ + +int32 arrcmp(uint8_t const * const a, uint8_t const * const b, uint32 len) { + uint32 i = 0; + + for (i = 0; i < len; i++) { + if (a[i] != b[i]) { + return a[i] - b[i]; + } + } + return 0; +} + +/* + * Tests + * + * Document Reference: 33.401 V13.1.0 Annex C.1 + */ + +void test_set_1() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 253, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0xe9, 0xfe, 0xd8, 0xa6, 0x3d, 0x15, 0x53, + 0x04, 0xd7, 0x1d, 0xf2, 0x0b, 0xf3, 0xe8, 0x22, 0x14, + 0xb2, 0x0e, 0xd7, 0xda, 0xd2, 0xf2, 0x33, 0xdc, 0x3c, + 0x22, 0xd7, 0xbd, 0xee, 0xed, 0x8e, 0x78 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_2() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x2b, 0xd6, 0x45, 0x9f, 0x82, 0xc4, 0x40, + 0xe0, 0x95, 0x2c, 0x49, 0x10, 0x48, 0x05, 0xff, 0x48 }; + uint32_t count = 0xc675a64b; + uint8_t bearer = 0x0c; + uint8_t direction = 1; + uint32_t len_bits = 798, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x7e, 0xc6, 0x12, 0x72, 0x74, 0x3b, 0xf1, + 0x61, 0x47, 0x26, 0x44, 0x6a, 0x6c, 0x38, 0xce, 0xd1, + 0x66, 0xf6, 0xca, 0x76, 0xeb, 0x54, 0x30, 0x04, 0x42, + 0x86, 0x34, 0x6c, 0xef, 0x13, 0x0f, 0x92, 0x92, 0x2b, + 0x03, 0x45, 0x0d, 0x3a, 0x99, 0x75, 0xe5, 0xbd, 0x2e, + 0xa0, 0xeb, 0x55, 0xad, 0x8e, 0x1b, 0x19, 0x9e, 0x3e, + 0xc4, 0x31, 0x60, 0x20, 0xe9, 0xa1, 0xb2, 0x85, 0xe7, + 0x62, 0x79, 0x53, 0x59, 0xb7, 0xbd, 0xfd, 0x39, 0xbe, + 0xf4, 0xb2, 0x48, 0x45, 0x83, 0xd5, 0xaf, 0xe0, 0x82, + 0xae, 0xe6, 0x38, 0xbf, 0x5f, 0xd5, 0xa6, 0x06, 0x19, + 0x39, 0x01, 0xa0, 0x8f, 0x4a, 0xb4, 0x1a, 0xab, 0x9b, + 0x13, 0x48, 0x80 }; + uint8_t ct[] = { 0x59, 0x61, 0x60, 0x53, 0x53, 0xc6, 0x4b, + 0xdc, 0xa1, 0x5b, 0x19, 0x5e, 0x28, 0x85, 0x53, 0xa9, + 0x10, 0x63, 0x25, 0x06, 0xd6, 0x20, 0x0a, 0xa7, 0x90, + 0xc4, 0xc8, 0x06, 0xc9, 0x99, 0x04, 0xcf, 0x24, 0x45, + 0xcc, 0x50, 0xbb, 0x1c, 0xf1, 0x68, 0xa4, 0x96, 0x73, + 0x73, 0x4e, 0x08, 0x1b, 0x57, 0xe3, 0x24, 0xce, 0x52, + 0x59, 0xc0, 0xe7, 0x8d, 0x4c, 0xd9, 0x7b, 0x87, 0x09, + 0x76, 0x50, 0x3c, 0x09, 0x43, 0xf2, 0xcb, 0x5a, 0xe8, + 0xf0, 0x52, 0xc7, 0xb7, 0xd3, 0x92, 0x23, 0x95, 0x87, + 0xb8, 0x95, 0x60, 0x86, 0xbc, 0xab, 0x18, 0x83, 0x60, + 0x42, 0xe2, 0xe6, 0xce, 0x42, 0x43, 0x2a, 0x17, 0x10, + 0x5c, 0x53, 0xd0 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_3() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, 0x8b, + 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, 0x77, 0xfb }; + uint32_t count = 0x544d49cd; + uint8_t bearer = 0x04; + uint8_t direction = 0; + uint32_t len_bits = 310, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0xfd, 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, + 0x65, 0x74, 0x50, 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, + 0x36, 0xd2, 0x34, 0x9e, 0x23, 0xf6, 0x44, 0x39, 0x2c, + 0x8e, 0xa9, 0xc4, 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, + 0xf2, 0x64, 0xd0, 0xf2, 0x48, 0x00 }; + uint8_t ct[] = { 0x75, 0x75, 0x0d, 0x37, 0xb4, 0xbb, 0xa2, + 0xa4, 0xde, 0xdb, 0x34, 0x23, 0x5b, 0xd6, 0x8c, 0x66, + 0x45, 0xac, 0xda, 0xac, 0xa4, 0x81, 0x38, 0xa3, 0xb0, + 0xc4, 0x71, 0xe2, 0xa7, 0x04, 0x1a, 0x57, 0x64, 0x23, + 0xd2, 0x92, 0x72, 0x87, 0xf0, 0x00 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_4() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xaa, 0x1f, 0x95, 0xae, 0xa5, 0x33, 0xbc, + 0xb3, 0x2e, 0xb6, 0x3b, 0xf5, 0x2d, 0x8f, 0x83, 0x1a }; + uint32_t count = 0x72d8c671; + uint8_t bearer = 0x10; + uint8_t direction = 1; + uint32_t len_bits = 1022, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0xfb, 0x1b, 0x96, 0xc5, 0xc8, 0xba, 0xdf, + 0xb2, 0xe8, 0xe8, 0xed, 0xfd, 0xe7, 0x8e, 0x57, 0xf2, + 0xad, 0x81, 0xe7, 0x41, 0x03, 0xfc, 0x43, 0x0a, 0x53, + 0x4d, 0xcc, 0x37, 0xaf, 0xce, 0xc7, 0x0e, 0x15, 0x17, + 0xbb, 0x06, 0xf2, 0x72, 0x19, 0xda, 0xe4, 0x90, 0x22, + 0xdd, 0xc4, 0x7a, 0x06, 0x8d, 0xe4, 0xc9, 0x49, 0x6a, + 0x95, 0x1a, 0x6b, 0x09, 0xed, 0xbd, 0xc8, 0x64, 0xc7, + 0xad, 0xbd, 0x74, 0x0a, 0xc5, 0x0c, 0x02, 0x2f, 0x30, + 0x82, 0xba, 0xfd, 0x22, 0xd7, 0x81, 0x97, 0xc5, 0xd5, + 0x08, 0xb9, 0x77, 0xbc, 0xa1, 0x3f, 0x32, 0xe6, 0x52, + 0xe7, 0x4b, 0xa7, 0x28, 0x57, 0x60, 0x77, 0xce, 0x62, + 0x8c, 0x53, 0x5e, 0x87, 0xdc, 0x60, 0x77, 0xba, 0x07, + 0xd2, 0x90, 0x68, 0x59, 0x0c, 0x8c, 0xb5, 0xf1, 0x08, + 0x8e, 0x08, 0x2c, 0xfa, 0x0e, 0xc9, 0x61, 0x30, 0x2d, + 0x69, 0xcf, 0x3d, 0x44 }; + uint8_t ct[] = { 0xdf, 0xb4, 0x40, 0xac, 0xb3, 0x77, 0x35, + 0x49, 0xef, 0xc0, 0x46, 0x28, 0xae, 0xb8, 0xd8, 0x15, + 0x62, 0x75, 0x23, 0x0b, 0xdc, 0x69, 0x0d, 0x94, 0xb0, + 0x0d, 0x8d, 0x95, 0xf2, 0x8c, 0x4b, 0x56, 0x30, 0x7f, + 0x60, 0xf4, 0xca, 0x55, 0xeb, 0xa6, 0x61, 0xeb, 0xba, + 0x72, 0xac, 0x80, 0x8f, 0xa8, 0xc4, 0x9e, 0x26, 0x78, + 0x8e, 0xd0, 0x4a, 0x5d, 0x60, 0x6c, 0xb4, 0x18, 0xde, + 0x74, 0x87, 0x8b, 0x9a, 0x22, 0xf8, 0xef, 0x29, 0x59, + 0x0b, 0xc4, 0xeb, 0x57, 0xc9, 0xfa, 0xf7, 0xc4, 0x15, + 0x24, 0xa8, 0x85, 0xb8, 0x97, 0x9c, 0x42, 0x3f, 0x2f, + 0x8f, 0x8e, 0x05, 0x92, 0xa9, 0x87, 0x92, 0x01, 0xbe, + 0x7f, 0xf9, 0x77, 0x7a, 0x16, 0x2a, 0xb8, 0x10, 0xfe, + 0xb3, 0x24, 0xba, 0x74, 0xc4, 0xc1, 0x56, 0xe0, 0x4d, + 0x39, 0x09, 0x72, 0x09, 0x65, 0x3a, 0xc3, 0x3e, 0x5a, + 0x5f, 0x2d, 0x88, 0x64 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_5() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x96, 0x18, 0xae, 0x46, 0x89, 0x1f, 0x86, + 0x57, 0x8e, 0xeb, 0xe9, 0x0e, 0xf7, 0xa1, 0x20, 0x2e }; + uint32_t count = 0xc675a64b; + uint8_t bearer = 0x0c; + uint8_t direction = 1; + uint32_t len_bits = 1245, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x8d, 0xaa, 0x17, 0xb1, 0xae, 0x05, 0x05, + 0x29, 0xc6, 0x82, 0x7f, 0x28, 0xc0, 0xef, 0x6a, 0x12, + 0x42, 0xe9, 0x3f, 0x8b, 0x31, 0x4f, 0xb1, 0x8a, 0x77, + 0xf7, 0x90, 0xae, 0x04, 0x9f, 0xed, 0xd6, 0x12, 0x26, + 0x7f, 0xec, 0xae, 0xfc, 0x45, 0x01, 0x74, 0xd7, 0x6d, + 0x9f, 0x9a, 0xa7, 0x75, 0x5a, 0x30, 0xcd, 0x90, 0xa9, + 0xa5, 0x87, 0x4b, 0xf4, 0x8e, 0xaf, 0x70, 0xee, 0xa3, + 0xa6, 0x2a, 0x25, 0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, + 0x8b, 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, 0x77, + 0xfb, 0x54, 0x4d, 0x49, 0xcd, 0x49, 0x72, 0x0e, 0x21, + 0x9d, 0xbf, 0x8b, 0xbe, 0xd3, 0x39, 0x04, 0xe1, 0xfd, + 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, 0x65, 0x74, 0x50, + 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, 0x36, 0xd2, 0x34, + 0x9e, 0x23, 0xf6, 0x44, 0x39, 0x2c, 0x8e, 0xa9, 0xc4, + 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, 0xf2, 0x64, 0xd0, + 0xf2, 0x48, 0x41, 0xd6, 0x46, 0x5f, 0x09, 0x96, 0xff, + 0x84, 0xe6, 0x5f, 0xc5, 0x17, 0xc5, 0x3e, 0xfc, 0x33, + 0x63, 0xc3, 0x84, 0x92, 0xa8 }; + uint8_t ct[] = { 0x91, 0x9c, 0x8c, 0x33, 0xd6, 0x67, 0x89, + 0x70, 0x3d, 0x05, 0xa0, 0xd7, 0xce, 0x82, 0xa2, 0xae, + 0xac, 0x4e, 0xe7, 0x6c, 0x0f, 0x4d, 0xa0, 0x50, 0x33, + 0x5e, 0x8a, 0x84, 0xe7, 0x89, 0x7b, 0xa5, 0xdf, 0x2f, + 0x36, 0xbd, 0x51, 0x3e, 0x3d, 0x0c, 0x85, 0x78, 0xc7, + 0xa0, 0xfc, 0xf0, 0x43, 0xe0, 0x3a, 0xa3, 0xa3, 0x9f, + 0xba, 0xad, 0x7d, 0x15, 0xbe, 0x07, 0x4f, 0xaa, 0x5d, + 0x90, 0x29, 0xf7, 0x1f, 0xb4, 0x57, 0xb6, 0x47, 0x83, + 0x47, 0x14, 0xb0, 0xe1, 0x8f, 0x11, 0x7f, 0xca, 0x10, + 0x67, 0x79, 0x45, 0x09, 0x6c, 0x8c, 0x5f, 0x32, 0x6b, + 0xa8, 0xd6, 0x09, 0x5e, 0xb2, 0x9c, 0x3e, 0x36, 0xcf, + 0x24, 0x5d, 0x16, 0x22, 0xaa, 0xfe, 0x92, 0x1f, 0x75, + 0x66, 0xc4, 0xf5, 0xd6, 0x44, 0xf2, 0xf1, 0xfc, 0x0e, + 0xc6, 0x84, 0xdd, 0xb2, 0x13, 0x49, 0x74, 0x76, 0x22, + 0xe2, 0x09, 0x29, 0x5d, 0x27, 0xff, 0x3f, 0x95, 0x62, + 0x33, 0x71, 0xd4, 0x9b, 0x14, 0x7c, 0x0a, 0xf4, 0x86, + 0x17, 0x1f, 0x22, 0xcd, 0x04, 0xb1, 0xcb, 0xeb, 0x26, + 0x58, 0x22, 0x3e, 0x69, 0x38 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +void test_set_6() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0x54, 0xf4, 0xe2, 0xe0, 0x4c, 0x83, 0x78, + 0x6e, 0xec, 0x8f, 0xb5, 0xab, 0xe8, 0xe3, 0x65, 0x66 }; + uint32_t count = 0xaca4f50f; + uint8_t bearer = 0x0b; + uint8_t direction = 0; + uint32_t len_bits = 3861, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x40, 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, + 0xfb, 0x42, 0x86, 0xb2, 0x99, 0x78, 0x3d, 0xaf, 0x44, + 0x2c, 0x09, 0x9f, 0x7a, 0xb0, 0xf5, 0x8d, 0x5c, 0x8e, + 0x46, 0xb1, 0x04, 0xf0, 0x8f, 0x01, 0xb4, 0x1a, 0xb4, + 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x36, 0xbd, 0x1a, + 0x3d, 0x90, 0xdc, 0x3a, 0x41, 0xb4, 0x6d, 0x51, 0x67, + 0x2a, 0xc4, 0xc9, 0x66, 0x3a, 0x2b, 0xe0, 0x63, 0xda, + 0x4b, 0xc8, 0xd2, 0x80, 0x8c, 0xe3, 0x3e, 0x2c, 0xcc, + 0xbf, 0xc6, 0x34, 0xe1, 0xb2, 0x59, 0x06, 0x08, 0x76, + 0xa0, 0xfb, 0xb5, 0xa4, 0x37, 0xeb, 0xcc, 0x8d, 0x31, + 0xc1, 0x9e, 0x44, 0x54, 0x31, 0x87, 0x45, 0xe3, 0xfa, + 0x16, 0xbb, 0x11, 0xad, 0xae, 0x24, 0x88, 0x79, 0xfe, + 0x52, 0xdb, 0x25, 0x43, 0xe5, 0x3c, 0xf4, 0x45, 0xd3, + 0xd8, 0x28, 0xce, 0x0b, 0xf5, 0xc5, 0x60, 0x59, 0x3d, + 0x97, 0x27, 0x8a, 0x59, 0x76, 0x2d, 0xd0, 0xc2, 0xc9, + 0xcd, 0x68, 0xd4, 0x49, 0x6a, 0x79, 0x25, 0x08, 0x61, + 0x40, 0x14, 0xb1, 0x3b, 0x6a, 0xa5, 0x11, 0x28, 0xc1, + 0x8c, 0xd6, 0xa9, 0x0b, 0x87, 0x97, 0x8c, 0x2f, 0xf1, + 0xca, 0xbe, 0x7d, 0x9f, 0x89, 0x8a, 0x41, 0x1b, 0xfd, + 0xb8, 0x4f, 0x68, 0xf6, 0x72, 0x7b, 0x14, 0x99, 0xcd, + 0xd3, 0x0d, 0xf0, 0x44, 0x3a, 0xb4, 0xa6, 0x66, 0x53, + 0x33, 0x0b, 0xcb, 0xa1, 0x10, 0x5e, 0x4c, 0xec, 0x03, + 0x4c, 0x73, 0xe6, 0x05, 0xb4, 0x31, 0x0e, 0xaa, 0xad, + 0xcf, 0xd5, 0xb0, 0xca, 0x27, 0xff, 0xd8, 0x9d, 0x14, + 0x4d, 0xf4, 0x79, 0x27, 0x59, 0x42, 0x7c, 0x9c, 0xc1, + 0xf8, 0xcd, 0x8c, 0x87, 0x20, 0x23, 0x64, 0xb8, 0xa6, + 0x87, 0x95, 0x4c, 0xb0, 0x5a, 0x8d, 0x4e, 0x2d, 0x99, + 0xe7, 0x3d, 0xb1, 0x60, 0xde, 0xb1, 0x80, 0xad, 0x08, + 0x41, 0xe9, 0x67, 0x41, 0xa5, 0xd5, 0x9f, 0xe4, 0x18, + 0x9f, 0x15, 0x42, 0x00, 0x26, 0xfe, 0x4c, 0xd1, 0x21, + 0x04, 0x93, 0x2f, 0xb3, 0x8f, 0x73, 0x53, 0x40, 0x43, + 0x8a, 0xaf, 0x7e, 0xca, 0x6f, 0xd5, 0xcf, 0xd3, 0xa1, + 0x95, 0xce, 0x5a, 0xbe, 0x65, 0x27, 0x2a, 0xf6, 0x07, + 0xad, 0xa1, 0xbe, 0x65, 0xa6, 0xb4, 0xc9, 0xc0, 0x69, + 0x32, 0x34, 0x09, 0x2c, 0x4d, 0x01, 0x8f, 0x17, 0x56, + 0xc6, 0xdb, 0x9d, 0xc8, 0xa6, 0xd8, 0x0b, 0x88, 0x81, + 0x38, 0x61, 0x6b, 0x68, 0x12, 0x62, 0xf9, 0x54, 0xd0, + 0xe7, 0x71, 0x17, 0x48, 0x78, 0x0d, 0x92, 0x29, 0x1d, + 0x86, 0x29, 0x99, 0x72, 0xdb, 0x74, 0x1c, 0xfa, 0x4f, + 0x37, 0xb8, 0xb5, 0x6c, 0xdb, 0x18, 0xa7, 0xca, 0x82, + 0x18, 0xe8, 0x6e, 0x4b, 0x4b, 0x71, 0x6a, 0x4d, 0x04, + 0x37, 0x1f, 0xbe, 0xc2, 0x62, 0xfc, 0x5a, 0xd0, 0xb3, + 0x81, 0x9b, 0x18, 0x7b, 0x97, 0xe5, 0x5b, 0x1a, 0x4d, + 0x7c, 0x19, 0xee, 0x24, 0xc8, 0xb4, 0xd7, 0x72, 0x3c, + 0xfe, 0xdf, 0x04, 0x5b, 0x8a, 0xca, 0xe4, 0x86, 0x95, + 0x17, 0xd8, 0x0e, 0x50, 0x61, 0x5d, 0x90, 0x35, 0xd5, + 0xd9, 0xc5, 0xa4, 0x0a, 0xf6, 0x02, 0x28, 0x0b, 0x54, + 0x25, 0x97, 0xb0, 0xcb, 0x18, 0x61, 0x9e, 0xeb, 0x35, + 0x92, 0x57, 0x59, 0xd1, 0x95, 0xe1, 0x00, 0xe8, 0xe4, + 0xaa, 0x0c, 0x38, 0xa3, 0xc2, 0xab, 0xe0, 0xf3, 0xd8, + 0xff, 0x04, 0xf3, 0xc3, 0x3c, 0x29, 0x50, 0x69, 0xc2, + 0x36, 0x94, 0xb5, 0xbb, 0xea, 0xcd, 0xd5, 0x42, 0xe2, + 0x8e, 0x8a, 0x94, 0xed, 0xb9, 0x11, 0x9f, 0x41, 0x2d, + 0x05, 0x4b, 0xe1, 0xfa, 0x72, 0x00, 0xb0, 0x90, 0x00 }; + uint8_t ct[] = { 0x5c, 0xb7, 0x2c, 0x6e, 0xdc, 0x87, 0x8f, + 0x15, 0x66, 0xe1, 0x02, 0x53, 0xaf, 0xc3, 0x64, 0xc9, + 0xfa, 0x54, 0x0d, 0x91, 0x4d, 0xb9, 0x4c, 0xbe, 0xe2, + 0x75, 0xd0, 0x91, 0x7c, 0xa6, 0xaf, 0x0d, 0x77, 0xac, + 0xb4, 0xef, 0x3b, 0xbe, 0x1a, 0x72, 0x2b, 0x2e, 0xf5, + 0xbd, 0x1d, 0x4b, 0x8e, 0x2a, 0xa5, 0x02, 0x4e, 0xc1, + 0x38, 0x8a, 0x20, 0x1e, 0x7b, 0xce, 0x79, 0x20, 0xae, + 0xc6, 0x15, 0x89, 0x5f, 0x76, 0x3a, 0x55, 0x64, 0xdc, + 0xc4, 0xc4, 0x82, 0xa2, 0xee, 0x1d, 0x8b, 0xfe, 0xcc, + 0x44, 0x98, 0xec, 0xa8, 0x3f, 0xbb, 0x75, 0xf9, 0xab, + 0x53, 0x0e, 0x0d, 0xaf, 0xbe, 0xde, 0x2f, 0xa5, 0x89, + 0x5b, 0x82, 0x99, 0x1b, 0x62, 0x77, 0xc5, 0x29, 0xe0, + 0xf2, 0x52, 0x9d, 0x7f, 0x79, 0x60, 0x6b, 0xe9, 0x67, + 0x06, 0x29, 0x6d, 0xed, 0xfa, 0x9d, 0x74, 0x12, 0xb6, + 0x16, 0x95, 0x8c, 0xb5, 0x63, 0xc6, 0x78, 0xc0, 0x28, + 0x25, 0xc3, 0x0d, 0x0a, 0xee, 0x77, 0xc4, 0xc1, 0x46, + 0xd2, 0x76, 0x54, 0x12, 0x42, 0x1a, 0x80, 0x8d, 0x13, + 0xce, 0xc8, 0x19, 0x69, 0x4c, 0x75, 0xad, 0x57, 0x2e, + 0x9b, 0x97, 0x3d, 0x94, 0x8b, 0x81, 0xa9, 0x33, 0x7c, + 0x3b, 0x2a, 0x17, 0x19, 0x2e, 0x22, 0xc2, 0x06, 0x9f, + 0x7e, 0xd1, 0x16, 0x2a, 0xf4, 0x4c, 0xde, 0xa8, 0x17, + 0x60, 0x36, 0x65, 0xe8, 0x07, 0xce, 0x40, 0xc8, 0xe0, + 0xdd, 0x9d, 0x63, 0x94, 0xdc, 0x6e, 0x31, 0x15, 0x3f, + 0xe1, 0x95, 0x5c, 0x47, 0xaf, 0xb5, 0x1f, 0x26, 0x17, + 0xee, 0x0c, 0x5e, 0x3b, 0x8e, 0xf1, 0xad, 0x75, 0x74, + 0xed, 0x34, 0x3e, 0xdc, 0x27, 0x43, 0xcc, 0x94, 0xc9, + 0x90, 0xe1, 0xf1, 0xfd, 0x26, 0x42, 0x53, 0xc1, 0x78, + 0xde, 0xa7, 0x39, 0xc0, 0xbe, 0xfe, 0xeb, 0xcd, 0x9f, + 0x9b, 0x76, 0xd4, 0x9c, 0x10, 0x15, 0xc9, 0xfe, 0xcf, + 0x50, 0xe5, 0x3b, 0x8b, 0x52, 0x04, 0xdb, 0xcd, 0x3e, + 0xed, 0x86, 0x38, 0x55, 0xda, 0xbc, 0xdc, 0xc9, 0x4b, + 0x31, 0xe3, 0x18, 0x02, 0x15, 0x68, 0x85, 0x5c, 0x8b, + 0x9e, 0x52, 0xa9, 0x81, 0x95, 0x7a, 0x11, 0x28, 0x27, + 0xf9, 0x78, 0xba, 0x96, 0x0f, 0x14, 0x47, 0x91, 0x1b, + 0x31, 0x7b, 0x55, 0x11, 0xfb, 0xcc, 0x7f, 0xb1, 0x3a, + 0xc1, 0x53, 0xdb, 0x74, 0x25, 0x11, 0x17, 0xe4, 0x86, + 0x1e, 0xb9, 0xe8, 0x3b, 0xff, 0xff, 0xc4, 0xeb, 0x77, + 0x55, 0x57, 0x90, 0x38, 0xe5, 0x79, 0x24, 0xb1, 0xf7, + 0x8b, 0x3e, 0x1a, 0xd9, 0x0b, 0xab, 0x2a, 0x07, 0x87, + 0x1b, 0x72, 0xdb, 0x5e, 0xef, 0x96, 0xc3, 0x34, 0x04, + 0x49, 0x66, 0xdb, 0x0c, 0x37, 0xca, 0xfd, 0x1a, 0x89, + 0xe5, 0x64, 0x6a, 0x35, 0x80, 0xeb, 0x64, 0x65, 0xf1, + 0x21, 0xdc, 0xe9, 0xcb, 0x88, 0xd8, 0x5b, 0x96, 0xcf, + 0x23, 0xcc, 0xcc, 0xd4, 0x28, 0x07, 0x67, 0xbe, 0xe8, + 0xee, 0xb2, 0x3d, 0x86, 0x52, 0x46, 0x1d, 0xb6, 0x49, + 0x31, 0x03, 0x00, 0x3b, 0xaf, 0x89, 0xf5, 0xe1, 0x82, + 0x61, 0xea, 0x43, 0xc8, 0x4a, 0x92, 0xeb, 0xff, 0xff, + 0xe4, 0x90, 0x9d, 0xc4, 0x6c, 0x51, 0x92, 0xf8, 0x25, + 0xf7, 0x70, 0x60, 0x0b, 0x96, 0x02, 0xc5, 0x57, 0xb5, + 0xf8, 0xb4, 0x31, 0xa7, 0x9d, 0x45, 0x97, 0x7d, 0xd9, + 0xc4, 0x1b, 0x86, 0x3d, 0xa9, 0xe1, 0x42, 0xe9, 0x00, + 0x20, 0xcf, 0xd0, 0x74, 0xd6, 0x92, 0x7b, 0x7a, 0xb3, + 0xb6, 0x72, 0x5d, 0x1a, 0x6f, 0x3f, 0x98, 0xb9, 0xc9, + 0xda, 0xa8, 0x98, 0x2a, 0xff, 0x06, 0x78, 0x28, 0x00 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + struct timeval t[3]; + + // encryption + gettimeofday(&t[1], NULL); + for (int i=0;i<100;i++) { + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + } + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("encryption: %u bits, t=%d us, rate=%.1f Mbps/s\n", len_bits, (int) t[0].tv_usec/100, (float) 100*len_bits/t[0].tv_usec); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + gettimeofday(&t[1], NULL); + for (int i=0;i<100;i++) { + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + } + gettimeofday(&t[2], NULL); + get_time_interval(t); + printf("decryption: %u bits, t=%d us, rate=%.1f Mbps/s\n", len_bits, (int) t[0].tv_usec/100, (float) 100*len_bits/t[0].tv_usec); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +// set len_bitsgth to multiple of 8 respectively 128 +void test_set_1_block_size() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 256, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0xe9, 0xfe, 0xd8, 0xa6, 0x3d, 0x15, 0x53, + 0x04, 0xd7, 0x1d, 0xf2, 0x0b, 0xf3, 0xe8, 0x22, 0x14, + 0xb2, 0x0e, 0xd7, 0xda, 0xd2, 0xf2, 0x33, 0xdc, 0x3c, + 0x22, 0xd7, 0xbd, 0xee, 0xed, 0x8e, 0x78 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp == 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp == 0); + + free(out); +} + +// inserted bit flip in msg[0] +void test_set_1_invalid() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t key[] = { 0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, + 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1 }; + uint32_t count = 0x398a59b4; + uint8_t bearer = 0x15; + uint8_t direction = 1; + uint32_t len_bits = 253, len_bytes = (len_bits + 7) / 8; + uint8_t msg[] = { 0x99, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, + 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, + 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0 }; + uint8_t ct[] = { 0xe9, 0xfe, 0xd8, 0xa6, 0x3d, 0x15, 0x53, + 0x04, 0xd7, 0x1d, 0xf2, 0x0b, 0xf3, 0xe8, 0x22, 0x14, + 0xb2, 0x0e, 0xd7, 0xda, 0xd2, 0xf2, 0x33, 0xdc, 0x3c, + 0x22, 0xd7, 0xbd, 0xee, 0xed, 0x8e, 0x78 }; + + uint8_t * out = (uint8_t *) calloc(len_bytes, + sizeof(uint8_t)); + + // encryption + err_lte = liblte_security_encryption_eea2(key, count, bearer, + direction, msg, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(ct, out, len_bytes); + assert(err_cmp != 0); + + // decryption + err_lte = liblte_security_decryption_eea2(key, count, bearer, + direction, ct, len_bits, out); + assert(err_lte == LIBLTE_SUCCESS); + + // compare cipher text + err_cmp = arrcmp(msg, out, len_bytes); + assert(err_cmp != 0); + + free(out); +} + +/* + * Functions + */ + +int main(int argc, char * argv[]) { + test_set_1(); + test_set_2(); + test_set_3(); + test_set_4(); + test_set_5(); + test_set_6(); + test_set_1_block_size(); + test_set_1_invalid(); +} diff --git a/lib/test/common/test_f12345.cc b/lib/test/common/test_f12345.cc new file mode 100644 index 0000000..e75d80b --- /dev/null +++ b/lib/test/common/test_f12345.cc @@ -0,0 +1,181 @@ +/* + * Includes + */ + +#include +#include +#include + +#include "srslte/common/liblte_security.h" + + +/* + * Prototypes + */ + +int32 arrcmp(uint8_t const * const a, uint8_t const * const b, uint32 len) { + uint32 i = 0; + + for (i = 0; i < len; i++) { + if (a[i] != b[i]) { + return a[i] - b[i]; + } + } + return 0; +} + +void arrprint(uint8_t const * const a, uint32 len) { + uint32 i = 0; + + for (i = 0; i < len; i++) { + printf("0x%02x ", a[i]); + if ((i%16==0) && i) + printf("\n"); + } + printf("\n"); + return; +} + + + +/* + * Tests + * + * Document Reference: 35.208 e00 + */ + + + +/* + * Functions + */ + + +void test_set_2() +{ + LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; + int32 err_cmp = 0; + + uint8_t k[] = {0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc}; + uint8_t rand[] = {0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35}; + uint8_t sqn[] = {0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07}; + uint8_t amf[] = {0xb9, 0xb9}; + uint8_t op[] = {0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18}; + // f1 + + uint8_t opc_o[16]; + err_lte = liblte_compute_opc(k,op,opc_o); + assert(err_lte == LIBLTE_SUCCESS); + + arrprint(opc_o, sizeof(opc_o)); + + uint8_t opc_a[] = {0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf}; + err_cmp = arrcmp(opc_o,opc_a,sizeof(opc_o)); + assert(err_cmp == 0); + + uint8_t mac_o[8]; + err_lte = liblte_security_milenage_f1(k, + opc_o, + rand, + sqn, + amf, + mac_o); + assert(err_lte == LIBLTE_SUCCESS); + + arrprint(mac_o, sizeof(mac_o)); + + uint8_t mac_a[] = {0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3}; + + // compare mac a + err_cmp = arrcmp(mac_o, mac_a, sizeof(mac_a)); + assert(err_cmp == 0); + + // f1 star + + uint8_t mac_so[8]; + err_lte = liblte_security_milenage_f1_star(k, + opc_o, + rand, + sqn, + amf, + mac_so); + + assert(err_lte == LIBLTE_SUCCESS); + + uint8_t mac_s[] = {0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9}; + + arrprint(mac_so, sizeof(mac_so)); + + err_cmp = arrcmp(mac_so, mac_s, sizeof(mac_s)); + assert(err_cmp == 0); + + // f2345 + uint8_t res_o[8]; + uint8_t ck_o[16]; + uint8_t ik_o[16]; + uint8_t ak_o[6]; + + err_lte = liblte_security_milenage_f2345(k, + opc_o, + rand, + res_o, + ck_o, + ik_o, + ak_o); + + assert(err_lte == LIBLTE_SUCCESS); + + uint8_t res[] = {0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf}; + uint8_t ck[] = {0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb}; + uint8_t ik[] = {0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41}; + uint8_t ak[] = {0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70}; + + // RESPONSE + arrprint(res_o, sizeof(res_o)); + + err_cmp = arrcmp(res_o, res, sizeof(res)); + assert(err_cmp == 0); + + // CK + arrprint(ck_o, sizeof(ck_o)); + + err_cmp = arrcmp(ck_o, ck, sizeof(ck)); + assert(err_cmp == 0); + + // IK + arrprint(ik_o, sizeof(ik_o)); + err_cmp = arrcmp(ik_o, ik, sizeof(ik)); + assert(err_cmp == 0); + + // AK + arrprint(ak_o, sizeof(ak_o)); + err_cmp = arrcmp(ak_o, ak, sizeof(ak)); + assert(err_cmp == 0); + + // f star + uint8_t ak_star_o[6]; + + err_lte = liblte_security_milenage_f5_star(k, opc_o, rand, ak_star_o); + assert(err_lte == LIBLTE_SUCCESS); + + arrprint(ak_star_o, sizeof(ak_star_o)); + uint8_t ak_star[] = {0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b}; + err_cmp = arrcmp(ak_star_o, ak_star, sizeof(ak_star)); + assert(err_cmp == 0); + return; +} + +/* + Own test sets +*/ + +int main(int argc, char * argv[]) { + + test_set_2(); + /* + test_set_3(); + test_set_4(); + test_set_5(); + test_set_6(); + */ +} diff --git a/lib/test/common/timeout_test.cc b/lib/test/common/timeout_test.cc new file mode 100644 index 0000000..4bde95f --- /dev/null +++ b/lib/test/common/timeout_test.cc @@ -0,0 +1,144 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/common/timeout.h" + +using namespace srslte; + +class callback + : public timeout_callback +{ +public: + callback() { + finished = false; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + bzero(&start_time, sizeof(start_time)); + } + + void timeout_expired(uint32_t timeout_id) + { + pthread_mutex_lock(&mutex); + finished = true; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + } + void wait() + { + pthread_mutex_lock(&mutex); + while(!finished) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + } + struct timeval start_time[3]; +private: + bool finished; + pthread_cond_t cvar; + pthread_mutex_t mutex; +}; + + +int timer_thread_test() +{ + bool result; + uint32_t id = 0; + uint32_t duration_msec = 5; + uint32_t result_tolerance = 1; + + callback c; + timeout t; + + gettimeofday(&c.start_time[1], NULL); + t.start(duration_msec); + + while (t.is_running() && !t.expired()) { + printf("time to expire=%dms\n", t.get_msec_to_expire()); + usleep(1000); + } + + gettimeofday(&c.start_time[2], NULL); + get_time_interval(c.start_time); + uint32_t diff_ms = c.start_time[0].tv_usec*1e-3; + printf("Target duration: %dms, started: %ld:%ld, ended: %ld:%ld, actual duration %dms\n", + duration_msec, c.start_time[1].tv_sec, c.start_time[1].tv_usec, c.start_time[2].tv_sec, c.start_time[2].tv_usec, diff_ms); + + result = ((duration_msec - result_tolerance) < diff_ms || diff_ms < (duration_msec + result_tolerance)); + + if(result) { + printf("Timer thread test passed\n"); + return 0; + }else{ + return -1; + } +} + +int single_thread_test() +{ + bool result; + uint32_t id = 0; + uint32_t duration_msec = 5; + + callback c; + timeout t; + + gettimeofday(&c.start_time[1], NULL); + t.start(duration_msec, 0, &c); + c.wait(); + gettimeofday(&c.start_time[2], NULL); + get_time_interval(c.start_time); + uint32_t diff_ms = c.start_time[0].tv_usec*1e-3; + printf("Target duration: %dms, started: %ld:%ld, ended: %ld:%ld, actual duration %dms\n", + duration_msec, c.start_time[1].tv_sec, c.start_time[1].tv_usec, c.start_time[2].tv_sec, c.start_time[2].tv_usec, diff_ms); + + result = (diff_ms == duration_msec); + + if(result) { + printf("Single thread test passed\n"); + return 0; + }else{ + return -1; + } +} + + +int main(int argc, char **argv) +{ + if (single_thread_test()) { + printf("Single thread test failed.\n"); + return -1; + } + + if (timer_thread_test()) { + printf("Timer thread test failed.\n"); + return -1; + } + + return 0; +} diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt new file mode 100644 index 0000000..209ba1b --- /dev/null +++ b/lib/test/phy/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2013-2017 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/. +# + +####################################################################### +# PHY TESTS +####################################################################### +add_executable(phy_dl_test phy_dl_test.c) +target_link_libraries(phy_dl_test srslte_phy srslte_common srslte_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +add_test(phy_dl_test phy_dl_test) + diff --git a/lib/test/phy/phy_dl_test.c b/lib/test/phy/phy_dl_test.c new file mode 100644 index 0000000..8c400c3 --- /dev/null +++ b/lib/test/phy/phy_dl_test.c @@ -0,0 +1,377 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * 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 +#include +#include + +#include "srslte/srslte.h" + +srslte_cell_t cell = { + .nof_prb = 100, + .nof_ports = 1, + .id = 1, + .cp = SRSLTE_CP_NORM, + .phich_resources = SRSLTE_PHICH_R_1, + .phich_length = SRSLTE_PHICH_NORM +}; + +uint32_t tm = 0; +srslte_mimo_type_t mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; +uint32_t sf_idx = 5; +uint32_t cfi = 3; +uint32_t nof_rx_ant = 1; +uint32_t nof_subframes = 10; +uint16_t rnti = 0x1234; +bool print_dci_table; + +void usage(char *prog) { + printf("Usage: %s [cfpndvs]\n", prog); + printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-f cfi [Default %d]\n", cfi); + printf("\t-p cell.nof_prb [Default %d]\n", cell.nof_prb); + printf("\t-s number of subframes to simulate [Default %d]\n", nof_subframes); + printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no"); + printf("\t-x MIMO Type: single, diversity, cdd, multiplex [Default %s]\n", srslte_mimotype2str(mimo_type)); + printf("\t-v [set srslte_verbose to debug, default none]\n"); +} + +void parse_args(int argc, char **argv) { + int opt; + while ((opt = getopt(argc, argv, "cfpndvsx")) != -1) { + switch (opt) { + case 'x': + if (srslte_str2mimotype(argv[optind], &mimo_type)) { + fprintf(stderr, "'%s' is not a valid MIMO type\n", argv[optind]); + usage(argv[0]); + exit(SRSLTE_ERROR); + } + if (mimo_type == SRSLTE_MIMO_TYPE_SINGLE_ANTENNA) { + cell.nof_ports = 1; + nof_rx_ant = 1; + tm = 0; + } else { + cell.nof_ports = 2; + nof_rx_ant = 2; + if (mimo_type == SRSLTE_MIMO_TYPE_TX_DIVERSITY) { + tm = 2; + } + } + break; + case 'f': + cfi = (uint32_t) atoi(argv[optind]); + break; + case 'p': + cell.nof_prb = (uint32_t) atoi(argv[optind]); + break; + case 'c': + cell.id = (uint32_t) atoi(argv[optind]); + break; + case 's': + nof_subframes = (uint32_t) atoi(argv[optind]); + break; + case 'd': + print_dci_table = true; + break; + case 'v': + srslte_verbose++; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int prbset_num = 1, last_prbset_num = 1; +int prbset_orig = 0; + +unsigned int +reverse(register unsigned int x) { + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + return ((x >> 16) | (x << 16)); + +} + +uint32_t prbset_to_bitmask() { + uint32_t mask = 0; + int nb = (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb)); + for (int i = 0; i < nb; i++) { + if (i >= prbset_orig && i < prbset_orig + prbset_num) { + mask = mask | (0x1 << i); + } + } + return reverse(mask) >> (32 - nb); +} + +int main(int argc, char **argv) { + srslte_enb_dl_t enb_dl = {}; + srslte_ue_dl_t ue_dl = {}; + srslte_softbuffer_tx_t *softbuffer_tx[SRSLTE_MAX_TB] = {}; + srslte_softbuffer_rx_t *softbuffer_rx[SRSLTE_MAX_TB] = {}; + uint8_t *data_tx[SRSLTE_MAX_TB] = {}; + uint8_t *data_rx[SRSLTE_MAX_TB] = {}; + uint32_t count_failures = 0; + + int ret = -1; + + parse_args(argc, argv); + + cf_t *signal_buffer[SRSLTE_MAX_PORTS] = {NULL}; + + /* + * Allocate Memory + */ + for (int i = 0; i < cell.nof_ports; i++) { + signal_buffer[i] = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN_PRB(cell.nof_prb)); + if (!signal_buffer[i]) { + fprintf(stderr, "Error allocating buffer\n"); + goto quit; + } + } + + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + softbuffer_tx[i] = (srslte_softbuffer_tx_t *) calloc(sizeof(srslte_softbuffer_tx_t), 1); + if (!softbuffer_tx[i]) { + fprintf(stderr, "Error allocating softbuffer_tx\n"); + goto quit; + } + + if (srslte_softbuffer_tx_init(softbuffer_tx[i], cell.nof_prb)) { + fprintf(stderr, "Error initiating softbuffer_tx\n"); + goto quit; + } + + softbuffer_rx[i] = (srslte_softbuffer_rx_t *) calloc(sizeof(srslte_softbuffer_rx_t), 1); + if (!softbuffer_rx[i]) { + fprintf(stderr, "Error allocating softbuffer_rx\n"); + goto quit; + } + + if (srslte_softbuffer_rx_init(softbuffer_rx[i], cell.nof_prb)) { + fprintf(stderr, "Error initiating softbuffer_rx\n"); + goto quit; + } + + data_tx[i] = srslte_vec_malloc(sizeof(uint8_t) * 150000); + if (!data_tx[i]) { + fprintf(stderr, "Error allocating data tx\n"); + goto quit; + } + + data_rx[i] = srslte_vec_malloc(sizeof(uint8_t) * 150000); + if (!data_rx[i]) { + fprintf(stderr, "Error allocating data tx\n"); + goto quit; + } + } + + /* + * Initialise eNb + */ + if (srslte_enb_dl_init(&enb_dl, signal_buffer, cell.nof_prb)) { + fprintf(stderr, "Error initiating eNb downlink\n"); + goto quit; + } + + if (srslte_enb_dl_set_cell(&enb_dl, cell)) { + fprintf(stderr, "Error setting eNb DL cell\n"); + goto quit; + } + + srslte_enb_dl_set_cfi(&enb_dl, cfi); + srslte_enb_dl_set_power_allocation(&enb_dl, 0.0f, 0.0f); /* Default: none */ + + /* + * Initialise UE + */ + if (srslte_ue_dl_init(&ue_dl, signal_buffer, cell.nof_prb, nof_rx_ant)) { + fprintf(stderr, "Error initiating UE downlink\n"); + goto quit; + } + + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + fprintf(stderr, "Error setting UE downlink cell\n"); + goto quit; + } + + srslte_ue_dl_set_rnti(&ue_dl, rnti); + + /* + * Loop + */ + for (uint32_t sf_idx = 0; sf_idx < nof_subframes; sf_idx++) { + bool acks[SRSLTE_MAX_TB] = {}; + + /* Run eNodeB */ + srslte_enb_dl_clear_sf(&enb_dl); + + srslte_enb_dl_put_base(&enb_dl, sf_idx); + + srslte_ra_dl_dci_t dci = {}; + srslte_dci_format_t dci_format = SRSLTE_DCI_FORMAT1A; + srslte_ra_dl_grant_t grant = {}; + + /* Pupulate TB Common */ + dci.harq_process = 0; + + /* Pupulate TB0 */ + dci.mcs_idx = 0; + dci.ndi = 0; + dci.rv_idx = 0; + dci.tb_en[0] = true; + + if (mimo_type == SRSLTE_MIMO_TYPE_CDD || mimo_type == SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX) { + dci_format = SRSLTE_DCI_FORMAT2B; + + /* Pupulate TB1 */ + dci.mcs_idx_1 = 0; + dci.ndi_1 = 0; + dci.rv_idx_1 = 0; + dci.tb_en[1] = true; + + /* Pupulate Allocation */ + dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci.alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci.type0_alloc.rbg_bitmask = prbset_to_bitmask(); + } else { + dci_format = SRSLTE_DCI_FORMAT1A; + dci.alloc_type = SRSLTE_RA_ALLOC_TYPE2; + dci.type2_alloc.riv = 0; + dci.type2_alloc.L_crb = 4; + dci.type2_alloc.RB_start = 0; + dci.type2_alloc.n_prb1a = 1; + dci.type2_alloc.n_gap = 0; + dci.type2_alloc.mode = 0; + } + + dci.dci_is_1a = (dci_format == SRSLTE_DCI_FORMAT1A); + dci.dci_is_1c = (dci_format == SRSLTE_DCI_FORMAT1C); + + srslte_ra_dl_dci_to_grant(&dci, cell.nof_prb, rnti, &grant); + + srslte_dci_location_t location = { + .ncce = 0, + .L = 2 + }; + + for (int t = 0; t < SRSLTE_RA_DL_GRANT_NOF_TB(&grant); t++) { + for (int i = 0; i < grant.mcs->tbs; i++) { + data_tx[t][i] = (uint8_t) (rand() & 0xff); + } + } + + if (srslte_enb_dl_put_pdcch_dl(&enb_dl, + &dci, + dci_format, + location, + rnti, + sf_idx % 10) < 0) { + fprintf(stderr, "Error putting PDCCH\n"); + goto quit; + } + + if (srslte_enb_dl_put_pdsch(&enb_dl, + &grant, + softbuffer_tx, + rnti, + (int[SRSLTE_MAX_CODEWORDS]) {dci.rv_idx, dci.rv_idx_1}, + sf_idx % 10, + data_tx, + mimo_type) < 0) { + fprintf(stderr, "Error putting PDSCH\n"); + goto quit; + } + + srslte_enb_dl_gen_signal(&enb_dl); + + /* Run UE */ + int n = srslte_ue_dl_decode(&ue_dl, data_rx, tm, sf_idx, acks); + if (n < 0) { + fprintf(stderr, "Error decoding PDSCH\n"); + goto quit; + } + + for (int i = 0; i < SRSLTE_RA_DL_GRANT_NOF_TB(&grant); i++) { + if (!acks[i]) { + INFO("UE Failed decoding subframe %d\n", sf_idx); + count_failures++; + } + } + } + + printf("Finished! The UE failed decoding %d of %d.\n", count_failures, nof_subframes); + if (!count_failures) { + ret = SRSLTE_SUCCESS; + } + + quit: + srslte_enb_dl_free(&enb_dl); + srslte_ue_dl_free(&ue_dl); + + for ( + int i = 0; + i < cell. + nof_ports; + i++) { + if (signal_buffer[i]) { + free(signal_buffer[i]); + } + } + + for ( + int i = 0; + i < SRSLTE_MAX_TB; i++) { + if (softbuffer_tx[i]) { + srslte_softbuffer_tx_free(softbuffer_tx[i]); + free(softbuffer_tx[i]); + } + + if (softbuffer_rx[i]) { + srslte_softbuffer_rx_free(softbuffer_rx[i]); + free(softbuffer_rx[i]); + } + + if (data_tx[i]) { + free(data_tx[i]); + } + + if (data_rx[i]) { + free(data_rx[i]); + } + } + + if (ret) { + printf("Error\n"); + } else { + printf("Ok\n"); + } + srslte_dft_exit(); + exit(ret); +} diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt new file mode 100644 index 0000000..e3fcd4f --- /dev/null +++ b/lib/test/upper/CMakeLists.txt @@ -0,0 +1,56 @@ +# +# Copyright 2013-2017 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/. +# + +add_executable(rlc_am_data_test rlc_am_data_test.cc) +target_link_libraries(rlc_am_data_test srslte_upper srslte_phy srslte_common) +add_test(rlc_am_data_test rlc_am_data_test) + +add_executable(rlc_am_control_test rlc_am_control_test.cc) +target_link_libraries(rlc_am_control_test srslte_upper srslte_phy) +add_test(rlc_am_control_test rlc_am_control_test) + +add_executable(rlc_am_test rlc_am_test.cc) +target_link_libraries(rlc_am_test srslte_upper srslte_phy srslte_common) +add_test(rlc_am_test rlc_am_test) + +add_executable(rlc_stress_test rlc_stress_test.cc) +target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES}) +add_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_delay 250) +add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1) +add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --opp_sdu_ratio=1.0) + +add_executable(rlc_um_data_test rlc_um_data_test.cc) +target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common) +add_test(rlc_um_data_test rlc_um_data_test) + +add_executable(rlc_um_test rlc_um_test.cc) +target_link_libraries(rlc_um_test srslte_upper srslte_phy) +add_test(rlc_um_test rlc_um_test) + + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "Added custom post-build command: ${BUILD_CMD}") + add_custom_command(TARGET ip_test POST_BUILD COMMAND ${BUILD_CMD}) +else(NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "No post-build command defined") +endif (NOT ${BUILD_CMD} STREQUAL "") diff --git a/lib/test/upper/rlc_am_control_test.cc b/lib/test/upper/rlc_am_control_test.cc new file mode 100644 index 0000000..874b256 --- /dev/null +++ b/lib/test/upper/rlc_am_control_test.cc @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/upper/rlc_am.h" + +// Simple status PDU +uint8_t pdu1[] = {0x00, 0x78}; +uint32_t PDU1_LEN = 2; + +// Status PDU with 4 NACKs +uint8_t pdu2[] = {0x00 ,0x22 ,0x00 ,0x40 ,0x0C ,0x01 ,0xC0 ,0x20}; +uint32_t PDU2_LEN = 8; + +int main(int argc, char **argv) { + srslte::rlc_status_pdu_t s; + srslte::byte_buffer_t b1,b2; + + memcpy(b1.msg, &pdu1[0], PDU1_LEN); + b1.N_bytes = PDU1_LEN; + rlc_am_read_status_pdu(&b1, &s); + assert(s.ack_sn == 30); + assert(s.N_nack == 0); + rlc_am_write_status_pdu(&s, &b2); + assert(b2.N_bytes == PDU1_LEN); + for(uint32_t i=0;i +#include +#include "srslte/upper/rlc_am.h" + +// Fixed header only +uint8_t pdu1[] = {0x88, 0x06}; +uint32_t PDU1_LEN = 2; + +// Fixed + 2 LI fields (each 1500) +uint8_t pdu2[] = {0x8C, 0x00, 0xDD, 0xC5, 0xDC}; +uint32_t PDU2_LEN = 5; + +// Fixed + 3 LI fields (each 1500) +uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0}; +uint32_t PDU3_LEN = 7; + +// D/C = 1 = Data PDU +// RF = 0 = AMD PDU +// P = 0 = Status PDU is not requested +// FI = 11 = First byte of the Data field does not corresponds to the first byte of a RLC SDU, +// Last byte of the Data field does not corresponds to the last byte of a RLC SDU +// E = 1 = A set of E field and LI field follows from the octet following the fixed part of the header +// SN = 0000000010 -> SN 2 +// E = 1 +// LI1 = 1010011 1110 (1342 Dec) +// E = 0 +// LI2 = 10111011100 (1500 Dec) +uint8_t pdu4[] = {0x9C, 0x02, 0xD3, 0xE5, 0xDC }; +uint32_t PDU4_LEN = 5; + +using namespace srslte; + +int main(int argc, char **argv) { + srslte::rlc_amd_pdu_header_t h; + srslte::byte_buffer_t b1,b2; + + memcpy(b1.msg, &pdu1[0], PDU1_LEN); + b1.N_bytes = PDU1_LEN; + rlc_am_read_data_pdu_header(&b1, &h); + assert(RLC_DC_FIELD_DATA_PDU == h.dc); + assert(0x01 == h.fi); + assert(0 == h.N_li); + assert(0 == h.lsf); + assert(0 == h.p); + assert(0 == h.rf); + assert(0 == h.so); + assert(6 == h.sn); + rlc_am_write_data_pdu_header(&h, &b2); + assert(b2.N_bytes == PDU1_LEN); + for(uint32_t i=0;i +#include "srslte/common/log_filter.h" +#include "srslte/common/logger_stdout.h" +#include "srslte/upper/rlc_am.h" +#include "srslte/common/rlc_pcap.h" +#include +#define NBUFS 5 +#define HAVE_PCAP 0 + +using namespace srsue; +using namespace srslte; + +class mac_dummy_timers + :public srslte::mac_interface_timers +{ +public: + srslte::timers::timer* timer_get(uint32_t timer_id) + { + return &t; + } + uint32_t timer_get_unique_id(){return 0;} + void timer_release_id(uint32_t id){} + +private: + srslte::timers::timer t; +}; + +class rlc_am_tester + :public pdcp_interface_rlc + ,public rrc_interface_rlc +{ +public: + rlc_am_tester(rlc_pcap *pcap_ = NULL) + { + bzero(sdus, sizeof(sdus)); + n_sdus = 0; + pcap = pcap_; + } + + ~rlc_am_tester(){ + for (uint32_t i = 0; i < 10; i++) { + if (sdus[i] != NULL) { + byte_buffer_pool::get_instance()->deallocate(sdus[i]); + } + } + } + + // PDCP interface + void write_pdu(uint32_t lcid, byte_buffer_t *sdu) + { + assert(lcid == 1); + sdus[n_sdus++] = sdu; + } + void write_pdu_bcch_bch(byte_buffer_t *sdu) {} + void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {} + void write_pdu_pcch(byte_buffer_t *sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu){} + + // RRC interface + void max_retx_attempted(){} + std::string get_rb_name(uint32_t lcid) { return std::string(""); } + + byte_buffer_t *sdus[10]; + int n_sdus; + rlc_pcap *pcap; +}; + +void basic_test() +{ + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void concat_test() +{ + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void segment_test() +{ + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;i 0){ + len = rlc1.read_pdu(pdu_bufs[n_pdus].msg, 10); // 2 header + payload + pdu_bufs[n_pdus++].N_bytes = len; + } + + assert(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void retx_test() +{ + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void resegment_test_1() +{ + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 10 | 10 | 10 | 10 | 10 | + // Retx PDU segments: | 5 | 5| + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_2() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 10 | 20 | 10 | 5 | + // Retx PDU segments: | 10 | 10 | + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_3() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 5| 20 | 10 | 10 | + // Retx PDU segments: | 10 | 10 | + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_4() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 5| 30 | 5 | 5| + // Retx PDU segments: | 15 | 15 | + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_5() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: |2|3| 40 |3|2| + // Retx PDU segments: | 20 | 20 | + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_6() +{ + // SDUs: |10|10|10| 54 | 54 | 54 | 54 | 54 | 54 | + // PDUs: |10|10|10| 270 | 54 | + // Retx PDU segments: | 120 | 150 | + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push SDUs into RLC1 + byte_buffer_t sdu_bufs[9]; + for(int i=0;i<3;i++) + { + for(int j=0;j<10;j++) + sdu_bufs[i].msg[j] = j; + sdu_bufs[i].N_bytes = 10; + rlc1.write_sdu(&sdu_bufs[i]); + } + for(int i=3;i<9;i++) + { + for(int j=0;j<54;j++) + sdu_bufs[i].msg[j] = j; + sdu_bufs[i].N_bytes = 54; + rlc1.write_sdu(&sdu_bufs[i]); + } + + assert(369 == rlc1.get_buffer_state()); + + // Read PDUs from RLC1 (10, 10, 10, 270, 54) + byte_buffer_t pdu_bufs[5]; + for(int i=0;i<3;i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 12); + pdu_bufs[i].N_bytes = len; + } + len = rlc1.read_pdu(pdu_bufs[3].msg, 278); + pdu_bufs[3].N_bytes = len; + len = rlc1.read_pdu(pdu_bufs[4].msg, 56); + pdu_bufs[4].N_bytes = len; + + assert(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 (skip SN 3) + for(int i=0;i<5;i++) + { + if(i != 3) + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + // Sleep to let reordering timeout expire + usleep(10000); + + assert(4 == rlc2.get_buffer_state()); + + // Read status PDU from RLC2 + byte_buffer_t status_buf; + len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + status_buf.N_bytes = len; + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + assert(278 == rlc1.get_buffer_state()); + + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + len = rlc1.read_pdu(retx1.msg, 129); + retx1.N_bytes = len; + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx1.msg, retx1.N_bytes); + + assert(155 == rlc1.get_buffer_state()); + + // Read the remaining segment + byte_buffer_t retx2; + len = rlc1.read_pdu(retx2.msg, 157); + retx2.N_bytes = len; + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx2.msg, retx2.N_bytes); + + assert(tester.n_sdus == 9); + for(int i=0;i<3;i++) + { + assert(tester.sdus[i]->N_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } + for(int i=3;i<9;i++) + { + assert(tester.sdus[i]->N_bytes == 54); + for(int j=0;j<54;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +// Retransmission of PDU segments of the same size +void resegment_test_7() +{ + // SDUs: | 30 | 30 | + // PDUs: | 13 | 13 | 11 | 13 | 10 | + // Rxed PDUs | 13 | 13 | | 13 | 10 | + // Retx PDU segments: | 4 | 7 | + // Retx PDU segments: |3|3]3|2| + const uint32_t N_SDU_BUFS = 2; + const uint32_t N_PDU_BUFS = 5; + const uint32_t sdu_size = 30; + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(100); + log2.set_hex_limit(100); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_test7.pcap", 0); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 2 SDUs into RLC1 + byte_buffer_t sdu_bufs[N_SDU_BUFS]; + for(uint32_t i=0;i 1) { + rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + } + + usleep(10000); + + // Read status PDU from RLC2 + assert(rlc2.get_buffer_state()); + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + assert(15 == rlc1.get_buffer_state()); + + + // second round of retx, forcing resegmentation + byte_buffer_t retx2[4]; + for (uint32_t i = 0; i < 4; i++) { + assert(rlc1.get_buffer_state() != 0); + retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7); + assert(retx2[i].N_bytes != 0); + + rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + + // check buffer states + assert(0 == rlc1.get_buffer_state()); + assert(0 == rlc2.get_buffer_state()); + + // Check number of SDUs and their content + assert(tester.n_sdus == N_SDU_BUFS); + for(int i=0; iN_bytes == sdu_size); + for(uint32_t j=0;jmsg[j] == i); + } + } + +#if HAVE_PCAP + pcap.close(); +#endif +} + + +// Retransmission of PDU segments with different size +void resegment_test_8() +{ + // SDUs: | 30 | 30 | + // PDUs: | 15 | 15 | 15 | 15 | 15 | + // Rxed PDUs | 15 | | 15 | 15 | + // Retx PDU segments: | 7 | 7 | 7 | 7 | + // Retx PDU segments: | 6 | 6 ] 6 | 6 | 6 | 6 | 6 | 6 | + const uint32_t N_SDU_BUFS = 2; + const uint32_t N_PDU_BUFS = 5; + const uint32_t sdu_size = 30; + + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(100); + log2.set_hex_limit(100); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_test8.pcap", 0); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 2 SDUs into RLC1 + byte_buffer_t sdu_bufs[N_SDU_BUFS]; + for(uint32_t i=0;i 2) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif + } + } + + // Sleep to let reordering timeout expire + usleep(10000); + + assert(12 == rlc1.get_buffer_state()); + + // first round of retx, forcing resegmentation + byte_buffer_t retx[4]; + for (uint32_t i = 0; i < 3; i++) { + assert(rlc1.get_buffer_state()); + retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 8); + assert(retx[i].N_bytes); + + // Write the last two segments to RLC2 + if (i > 1) { + rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + } + + usleep(20000); + + // Read status PDU from RLC2 + assert(rlc2.get_buffer_state()); + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + assert(15 == rlc1.get_buffer_state()); + + // second round of retx, reduce grant size to force different segment sizes + byte_buffer_t retx2[20]; + for (uint32_t i = 0; i < 9; i++) { + assert(rlc1.get_buffer_state() != 0); + retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7); + assert(retx2[i].N_bytes != 0); + rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); +#endif + } + + // check buffer states + assert(0 == rlc1.get_buffer_state()); + assert(0 == rlc2.get_buffer_state()); + + // Check number of SDUs and their content + assert(tester.n_sdus == N_SDU_BUFS); + for(int i=0; iN_bytes == sdu_size); + for(uint32_t j=0;jmsg[j] == i); + } + } + +#if HAVE_PCAP + pcap.close(); +#endif +} + + +void reset_test() +{ + srslte::log_filter log1("RLC_AM_1"); + srslte::log_filter log2("RLC_AM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5; + + rlc1.configure(&cnfg); + + // Push 1 SDU of size 10 into RLC1 + byte_buffer_t sdu_buf; + *sdu_buf.msg = 1; // Write the index into the buffer + sdu_buf.N_bytes = 100; + rlc1.write_sdu(&sdu_buf); + + // read 1 PDU from RLC1 and force segmentation + byte_buffer_t pdu_bufs; + len = rlc1.read_pdu(pdu_bufs.msg, 4); + pdu_bufs.N_bytes = len; + + // reset RLC1 + rlc1.stop(); + + // read another PDU segment from RLC1 + len = rlc1.read_pdu(pdu_bufs.msg, 4); + pdu_bufs.N_bytes = len; + + // now empty RLC buffer + len = rlc1.read_pdu(pdu_bufs.msg, 100); + pdu_bufs.N_bytes = len; + + assert(0 == rlc1.get_buffer_state()); +} + +int main(int argc, char **argv) { + basic_test(); + byte_buffer_pool::get_instance()->cleanup(); + + concat_test(); + byte_buffer_pool::get_instance()->cleanup(); + + segment_test(); + byte_buffer_pool::get_instance()->cleanup(); + + retx_test(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_1(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_2(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_3(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_4(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_5(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_6(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_7(); + byte_buffer_pool::get_instance()->cleanup(); + + resegment_test_8(); + byte_buffer_pool::get_instance()->cleanup(); + + reset_test(); + byte_buffer_pool::get_instance()->cleanup(); +} diff --git a/lib/test/upper/rlc_stress_test.cc b/lib/test/upper/rlc_stress_test.cc new file mode 100644 index 0000000..3a6f40a --- /dev/null +++ b/lib/test/upper/rlc_stress_test.cc @@ -0,0 +1,411 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include "srslte/common/log_filter.h" +#include "srslte/common/logger_stdout.h" +#include "srslte/common/threads.h" +#include "srslte/common/rlc_pcap.h" +#include "srslte/upper/rlc.h" +#include +#include +#include +#include + +#define SDU_SIZE 1500 + +using namespace std; +using namespace srsue; +using namespace srslte; +namespace bpo = boost::program_options; + +typedef struct { + std::string mode; + uint32_t test_duration_sec; + float error_rate; + uint32_t sdu_gen_delay_usec; + uint32_t pdu_tx_delay_usec; + bool reestablish; + uint32_t log_level; + bool single_tx; + bool write_pcap; + float opp_sdu_ratio; +} stress_test_args_t; + +void parse_args(stress_test_args_t *args, int argc, char *argv[]) { + + // Command line only options + bpo::options_description general("General options"); + + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit"); + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)") + ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") + ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") + ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") + ("error_rate", bpo::value(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") + ("opp_sdu_ratio", bpo::value(&args->opp_sdu_ratio)->default_value(0.0), "Ratio between MAC opportunity and SDU size (0==random)") + ("reestablish", bpo::value(&args->reestablish)->default_value(false), "Mimic RLC reestablish during execution") + ("loglevel", bpo::value(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)") + ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") + ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file"); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + if (args->log_level > 4) { + args->log_level = 4; + printf("Set log level to %d (%s)\n", args->log_level, srslte::log_level_text[args->log_level]); + } +} + +class mac_reader + :public thread +{ +public: + mac_reader(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, uint32_t pdu_tx_delay_usec_, rlc_pcap *pcap_, uint32_t lcid_, bool is_dl_ = true) + { + rlc1 = rlc1_; + rlc2 = rlc2_; + fail_rate = fail_rate_; + opp_sdu_ratio = opp_sdu_ratio_; + run_enable = true; + running = false; + pdu_tx_delay_usec = pdu_tx_delay_usec_; + pcap = pcap_; + is_dl = is_dl_; + lcid = lcid_; + } + + void stop() + { + run_enable = false; + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; + } + if(running) { + thread_cancel(); + } + wait_thread_finish(); + } + +private: + void run_thread() + { + running = true; + byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("mac_reader::run_thread"); + if (!pdu) { + printf("Fatal Error: Could not allocate PDU in mac_reader::run_thread\n"); + exit(-1); + } + + while(run_enable) { + // generate MAC opportunities of random size or with fixed ratio + float r = opp_sdu_ratio ? opp_sdu_ratio : (float)rand()/RAND_MAX; + int opp_size = r*SDU_SIZE; + uint32_t buf_state = rlc1->get_buffer_state(lcid); + if (buf_state) { + int read = rlc1->read_pdu(lcid, pdu->msg, opp_size); + if (pdu_tx_delay_usec) usleep(pdu_tx_delay_usec); + if(((float)rand()/RAND_MAX > fail_rate) && read>0) { + pdu->N_bytes = read; + rlc2->write_pdu(lcid, pdu->msg, pdu->N_bytes); + if (is_dl) { + pcap->write_dl_am_ccch(pdu->msg, pdu->N_bytes); + } else { + pcap->write_ul_am_ccch(pdu->msg, pdu->N_bytes); + } + } + } + } + running = false; + byte_buffer_pool::get_instance()->deallocate(pdu); + } + + rlc_interface_mac *rlc1; + rlc_interface_mac *rlc2; + float fail_rate; + float opp_sdu_ratio; + uint32_t pdu_tx_delay_usec; + rlc_pcap *pcap; + uint32_t lcid; + bool is_dl; + + bool run_enable; + bool running; +}; + +class mac_dummy + :public srslte::mac_interface_timers +{ +public: + mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, int32_t pdu_tx_delay, uint32_t lcid, rlc_pcap* pcap = NULL) + :r1(rlc1_, rlc2_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, true) + ,r2(rlc2_, rlc1_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, false) + { + } + + void start() + { + r1.start(7); + r2.start(7); + } + + void stop() + { + r1.stop(); + r2.stop(); + } + + srslte::timers::timer* timer_get(uint32_t timer_id) + { + return &t; + } + uint32_t timer_get_unique_id(){return 0;} + void timer_release_id(uint32_t id){} + +private: + srslte::timers::timer t; + + mac_reader r1; + mac_reader r2; +}; + + + +class rlc_tester + :public pdcp_interface_rlc + ,public rrc_interface_rlc + ,public thread +{ +public: + rlc_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_, uint32_t lcid_){ + rlc = rlc_; + run_enable = true; + running = false; + rx_pdus = 0; + name = name_; + sdu_gen_delay_usec = sdu_gen_delay_usec_; + lcid = lcid_; + } + + void stop() + { + run_enable = false; + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; + } + if(running) { + thread_cancel(); + } + wait_thread_finish(); + } + + // PDCP interface + void write_pdu(uint32_t rx_lcid, byte_buffer_t *sdu) + { + assert(rx_lcid == lcid); + if (sdu->N_bytes != SDU_SIZE) { + printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, SDU_SIZE); + exit(-1); + } + + byte_buffer_pool::get_instance()->deallocate(sdu); + rx_pdus++; + } + void write_pdu_bcch_bch(byte_buffer_t *sdu) {} + void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {} + void write_pdu_pcch(byte_buffer_t *sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} + + // RRC interface + void max_retx_attempted(){} + std::string get_rb_name(uint32_t rx_lcid) { return std::string(""); } + + int get_nof_rx_pdus() { return rx_pdus; } + +private: + void run_thread() + { + uint8_t sn = 0; + running = true; + while(run_enable) { + byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_tester::run_thread"); + if (!pdu) { + printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); + // backoff for a bit + usleep(1000); + continue; + } + for (uint32_t i = 0; i < SDU_SIZE; i++) { + pdu->msg[i] = sn; + } + sn++; + pdu->N_bytes = SDU_SIZE; + rlc->write_sdu(lcid, pdu); + if (sdu_gen_delay_usec) usleep(sdu_gen_delay_usec); + } + running = false; + } + + bool run_enable; + bool running; + long rx_pdus; + uint32_t lcid; + + std::string name; + + uint32_t sdu_gen_delay_usec; + + rlc_interface_pdcp *rlc; +}; + +void stress_test(stress_test_args_t args) +{ + srslte::log_filter log1("RLC_1"); + srslte::log_filter log2("RLC_2"); + log1.set_level((LOG_LEVEL_ENUM)args.log_level); + log2.set_level((LOG_LEVEL_ENUM)args.log_level); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_pcap pcap; + uint32_t lcid = 1; + + if (args.write_pcap) { + pcap.open("rlc_stress_test.pcap", 0); + } + + srslte_rlc_config_t cnfg_; + if (args.mode == "AM") { + // config RLC AM bearer + cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg_.am.max_retx_thresh = 4; + cnfg_.am.poll_byte = 25*1000; + cnfg_.am.poll_pdu = 4; + cnfg_.am.t_poll_retx = 5; + cnfg_.am.t_reordering = 5; + cnfg_.am.t_status_prohibit = 5; + } else if (args.mode == "UM") { + // config UM bearer + cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg_.um.t_reordering = 5; + cnfg_.um.rx_mod = 32; + cnfg_.um.rx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cnfg_.um.rx_window_size = 16; + cnfg_.um.tx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS; + cnfg_.um.tx_mod = 32; + } else if (args.mode == "TM") { + // use default LCID in TM + lcid = 0; + } else { + cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; + exit(-1); + } + + rlc rlc1; + rlc rlc2; + + rlc_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec, lcid); + rlc_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec, lcid); + mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.opp_sdu_ratio, args.pdu_tx_delay_usec, lcid, &pcap); + ue_interface ue; + + rlc1.init(&tester1, &tester1, &ue, &log1, &mac, 0); + rlc2.init(&tester2, &tester2, &ue, &log2, &mac, 0); + + // only add AM and UM bearers + if (args.mode != "TM") { + rlc1.add_bearer(lcid, cnfg_); + rlc2.add_bearer(lcid, cnfg_); + } + + tester1.start(7); + if (!args.single_tx) { + tester2.start(7); + } + mac.start(); + + for (uint32_t i = 0; i < args.test_duration_sec; i++) { + // if enabled, mimic reestablishment every second + if (args.reestablish) { + rlc1.reestablish(); + rlc2.reestablish(); + } + usleep(1e6); + } + + tester1.stop(); + tester2.stop(); + mac.stop(); + if (args.write_pcap) { + pcap.close(); + } + + printf("RLC1 received %d SDUs in %ds (%.2f PDU/s)\n", + tester1.get_nof_rx_pdus(), + args.test_duration_sec, + (float)tester1.get_nof_rx_pdus()/args.test_duration_sec); + + printf("RLC2 received %d SDUs in %ds (%.2f PDU/s)\n", + tester2.get_nof_rx_pdus(), + args.test_duration_sec, + (float)tester2.get_nof_rx_pdus()/args.test_duration_sec); +} + + +int main(int argc, char **argv) { + stress_test_args_t args; + parse_args(&args, argc, argv); + + stress_test(args); + byte_buffer_pool::get_instance()->cleanup(); + + exit(0); +} diff --git a/lib/test/upper/rlc_um_data_test.cc b/lib/test/upper/rlc_um_data_test.cc new file mode 100644 index 0000000..bde751b --- /dev/null +++ b/lib/test/upper/rlc_um_data_test.cc @@ -0,0 +1,71 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/upper/rlc_um.h" +#include + +// Fixed header only +uint8_t pdu1[] = {0x18 ,0xE2}; +uint32_t PDU1_LEN = 2; + +// Fixed + 1 LI field (value 104) +uint8_t pdu2[] = {0x1C ,0xE1 ,0x06 ,0x80}; +uint32_t PDU2_LEN = 4; + +using namespace srsue; + +int main(int argc, char **argv) { + srslte::rlc_umd_pdu_header_t h; + srslte::byte_buffer_t b1,b2; + + memcpy(b1.msg, &pdu1[0], PDU1_LEN); + b1.N_bytes = PDU1_LEN; + rlc_um_read_data_pdu_header(&b1, srslte::RLC_UMD_SN_SIZE_10_BITS, &h); + assert(0x03 == h.fi); + assert(0 == h.N_li); + assert(226 == h.sn); + rlc_um_write_data_pdu_header(&h, &b2); + assert(b2.N_bytes == PDU1_LEN); + for(uint32_t i=0;i +#include "srslte/common/log_filter.h" +#include "srslte/upper/rlc_um.h" +#include + +#define MAX_NBUFS 100 +#define NBUFS 5 + +using namespace srslte; +using namespace srsue; + +class mac_dummy_timers + :public srslte::mac_interface_timers +{ +public: + srslte::timers::timer* timer_get(uint32_t timer_id) + { + return &t; + } + uint32_t timer_get_unique_id(){return 0;} + void step() + { + t.step(); + } + void timer_release_id(uint32_t timer_id) {} +private: + srslte::timers::timer t; +}; + +class rlc_um_tester + :public pdcp_interface_rlc + ,public rrc_interface_rlc +{ +public: + rlc_um_tester(){ + bzero(sdus, sizeof(sdus)); + n_sdus = 0; + expected_sdu_len = 0; + } + + ~rlc_um_tester(){ + for (uint32_t i = 0; i < NBUFS; i++) { + if (sdus[i] != NULL) { + byte_buffer_pool::get_instance()->deallocate(sdus[i]); + } + } + } + + // PDCP interface + void write_pdu(uint32_t lcid, byte_buffer_t *sdu) + { + assert(lcid == 3); + if (sdu->N_bytes != expected_sdu_len) { + printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len); + exit(-1); + } + sdus[n_sdus++] = sdu; + } + void write_pdu_bcch_bch(byte_buffer_t *sdu) {} + void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {} + void write_pdu_pcch(byte_buffer_t *sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) + { + assert(lcid == 3); + sdus[n_sdus++] = sdu; + } + + // RRC interface + void max_retx_attempted(){} + std::string get_rb_name(uint32_t lcid) { return std::string(""); } + void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } + + + byte_buffer_t *sdus[MAX_NBUFS]; + int n_sdus; + uint32_t expected_sdu_len; +}; + +void basic_test() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + tester.set_expected_sdu_len(1); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void loss_test() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + tester.set_expected_sdu_len(1); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iis_expired()) + timers.timer_get(1)->step(); + + assert(NBUFS-1 == tester.n_sdus); +} + + +void basic_mbsfn_test() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + rlc1.configure(srslte_rlc_config_t::mch_config()); + rlc2.configure(srslte_rlc_config_t::mch_config()); + + tester.set_expected_sdu_len(1); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS*2]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + + + // This test checks the reassembly routines when a PDU +// is lost that contains the beginning of SDU segment. +// The PDU that contains the end of this SDU _also_ contains +// a segment of another SDU. +// On reassembly of the SDUs, the missing start segment +// should be detected and the complete SDU be discarded +// Therefore, one SDU less should be received than was tx'ed. +// This test sends PDU in two batches so it's not the reordering +// timeout that detects the missing PDU but the fact more +// PDUs than rx_mod are received. +void reassmble_test() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push SDUs into RLC1 + const int n_sdus = 25; + const int sdu_len = 100; + + tester.set_expected_sdu_len(sdu_len); + + byte_buffer_t sdu_bufs[n_sdus]; + + const int n_sdu_first_batch = 17; + + for(int i=0;iN_bytes == sdu_len); + } +} + +// This reassmble test checks the reassembly routines when a PDU +// is lost that _only_ contains the beginning of SDU segment, +// while the next PDU contains the middle part of this SDU (and +// yet another PDU the end part). +// On reassembly of the SDUs, the missing start segment +// should be detected and the complete SDU be discarded +// Therefore, one SDU less should be received than was tx'ed. +void reassmble_test2() +{ + srslte::log_filter log1("RLC_UM_1"); + srslte::log_filter log2("RLC_UM_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_um_tester tester; + mac_dummy_timers timers; + + rlc_um rlc1; + rlc_um rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 3, &tester, &tester, &timers); + rlc2.init(&log2, 3, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push SDUs into RLC1 + const int n_sdus = 25; + const int sdu_len = 100; + + tester.set_expected_sdu_len(sdu_len); + + byte_buffer_t sdu_bufs[n_sdus]; + const int n_sdu_first_batch = 17; + + for(int i=0;iN_bytes == sdu_len); + } +} + +int main(int argc, char **argv) { + basic_test(); + byte_buffer_pool::get_instance()->cleanup(); + + loss_test(); + byte_buffer_pool::get_instance()->cleanup(); + basic_mbsfn_test(); + byte_buffer_pool::get_instance()->cleanup(); + + reassmble_test(); + byte_buffer_pool::get_instance()->cleanup(); + + reassmble_test2(); + byte_buffer_pool::get_instance()->cleanup(); +} + diff --git a/srsenb/CMakeLists.txt b/srsenb/CMakeLists.txt new file mode 100644 index 0000000..8a9f085 --- /dev/null +++ b/srsenb/CMakeLists.txt @@ -0,0 +1,58 @@ +# +# Copyright 2013-2017 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/. +# + +find_package(LibConfig REQUIRED) +find_package(SCTP REQUIRED) + +if(BUILD_STATIC) + set(LIBCONFIGPP_LIBRARIES "${LIBCONFIGPP_STATIC_LIBRARY_PATH}") +endif(BUILD_STATIC) + +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsENB") +endif() + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR} +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +add_subdirectory(test) + +######################################################################## +# Default configuration files +######################################################################## +install(FILES enb.conf.example DESTINATION ${DATA_DIR}) +install(FILES drb.conf.example DESTINATION ${DATA_DIR}) +install(FILES rr.conf.example DESTINATION ${DATA_DIR}) +install(FILES sib.conf.example DESTINATION ${DATA_DIR}) \ No newline at end of file diff --git a/srsenb/drb.conf.example b/srsenb/drb.conf.example new file mode 100644 index 0000000..32b6e93 --- /dev/null +++ b/srsenb/drb.conf.example @@ -0,0 +1,54 @@ + +// All times are in ms. Use -1 for infinity, where available + +qci_config = ( + +{ + qci=7; + pdcp_config = { + discard_timer = 100; + pdcp_sn_size = 12; + } + rlc_config = { + ul_um = { + sn_field_length = 10; + }; + dl_um = { + sn_field_length = 10; + t_reordering = 45; + }; + }; + logical_channel_config = { + priority = 13; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 2; + }; +}, +{ + qci=9; + pdcp_config = { + discard_timer = -1; + status_report_required = true; + } + rlc_config = { + ul_am = { + t_poll_retx = 120; + poll_pdu = 64; + poll_byte = 750; + max_retx_thresh = 16; + }; + dl_am = { + t_reordering = 50; + t_status_prohibit = 50; + }; + }; + logical_channel_config = { + priority = 11; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 3; + }; +} + +); diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example new file mode 100644 index 0000000..0eb65bd --- /dev/null +++ b/srsenb/enb.conf.example @@ -0,0 +1,180 @@ +##################################################################### +# srsENB configuration file +##################################################################### + +##################################################################### +# eNB configuration +# +# enb_id: 20-bit eNB identifier. +# cell_id: 8-bit cell identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# mme_addr: IP address of MME for S1 connnection +# gtp_bind_addr: Local IP address to bind for GTP connection +# s1c_bind_addr: Local IP address to bind for S1AP connection +# n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100) +# tm: Transmission mode 1-4 (TM1 default) +# nof_ports: Number of Tx ports (1 port default, set to 2 for TM2/3/4) +# +##################################################################### +[enb] +enb_id = 0x19B +cell_id = 0x01 +phy_cell_id = 1 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_addr = 127.0.1.100 +gtp_bind_addr = 127.0.1.1 +s1c_bind_addr = 127.0.1.1 +n_prb = 50 +#tm = 4 +#nof_ports = 2 + +##################################################################### +# eNB configuration files +# +# sib_config: SIB1, SIB2 and SIB3 configuration file +# note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 +# rr_config: Radio Resources configuration file +# drb_config: DRB configuration file +##################################################################### +[enb_files] +sib_config = sib.conf +rr_config = rr.conf +drb_config = drb.conf + +##################################################################### +# RF configuration +# +# dl_earfcn: EARFCN code for DL +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn (must be set if dl_freq is set) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +##################################################################### +[rf] +dl_earfcn = 3400 +tx_gain = 80 +rx_gain = 40 + +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/enb.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gtpu, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +all_hex_limit = 32 +filename = /tmp/enb.log +file_max_size = -1 + +[gui] +enable = false + +##################################################################### +# Scheduler configuration options +# +# pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) +# pdsch_max_mcs: Optional PDSCH MCS limit +# pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) +# pusch_max_mcs: Optional PUSCH MCS limit +# #nof_ctrl_symbols: Number of control symbols +# +##################################################################### +[scheduler] +#pdsch_mcs = -1 +#pdsch_max_mcs = -1 +#pusch_mcs = -1 +pusch_max_mcs = 16 +nof_ctrl_symbols = 3 + +##################################################################### +# Expert configuration options +# +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) +# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. +# a link failure is when SNR<0 and CRC=KO +# max_prach_offset_us: Maximum allowed RACH offset (in us) +# +##################################################################### +[expert] +#pdsch_max_its = 4 +#nof_phy_threads = 2 +#pregenerate_signals = false +#tx_amplitude = 0.6 +#link_failure_nof_err = 50 +#rrc_inactivity_timer = 60000 +#max_prach_offset_us = 30 +#enable_mbsfn = false + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97 diff --git a/srsenb/hdr/CMakeLists.txt b/srsenb/hdr/CMakeLists.txt new file mode 100644 index 0000000..83b1673 --- /dev/null +++ b/srsenb/hdr/CMakeLists.txt @@ -0,0 +1,5 @@ + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in + ${PROJECT_BINARY_DIR}/version.h +) diff --git a/srsenb/hdr/cfg_parser.h b/srsenb/hdr/cfg_parser.h new file mode 100644 index 0000000..d399dda --- /dev/null +++ b/srsenb/hdr/cfg_parser.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_CFG_PARSER_H +#define SRSENB_CFG_PARSER_H + +#include "enb.h" + +namespace srsenb { +class cfg_parser +{ +public: + void parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common); +}; + +} + +#endif // CFG_PARSER_H diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h new file mode 100644 index 0000000..08f75bc --- /dev/null +++ b/srsenb/hdr/enb.h @@ -0,0 +1,231 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: enb.h + * Description: Top-level eNodeB class. Creates and links all + * layers and helpers. + *****************************************************************************/ + +#ifndef SRSENB_ENB_H +#define SRSENB_ENB_H + +#include +#include +#include + +#include "phy/phy.h" +#include "mac/mac.h" +#include "upper/rrc.h" +#include "upper/gtpu.h" +#include "upper/s1ap.h" +#include "upper/rlc.h" +#include "upper/pdcp.h" + +#include "srslte/radio/radio.h" + +#include "srslte/common/bcd_helpers.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +/******************************************************************************* + eNodeB Parameters +*******************************************************************************/ + +typedef struct { + s1ap_args_t s1ap; + uint32_t n_prb; + uint32_t pci; + uint32_t nof_ports; + uint32_t transmission_mode; + float p_a; +}enb_args_t; + +typedef struct { + std::string sib_config; + std::string rr_config; + std::string drb_config; +} enb_files_t; + +typedef struct { + uint32_t dl_earfcn; + uint32_t ul_earfcn; + float dl_freq; + float ul_freq; + float rx_gain; + float tx_gain; + std::string device_name; + std::string device_args; + std::string time_adv_nsamples; + std::string burst_preamble; +}rf_args_t; + +typedef struct { + bool enable; + std::string filename; +}pcap_args_t; + +typedef struct { + std::string phy_level; + std::string phy_lib_level; + std::string mac_level; + std::string rlc_level; + std::string pdcp_level; + std::string rrc_level; + std::string gtpu_level; + std::string s1ap_level; + std::string all_level; + int phy_hex_limit; + int mac_hex_limit; + int rlc_hex_limit; + int pdcp_hex_limit; + int rrc_hex_limit; + int gtpu_hex_limit; + int s1ap_hex_limit; + int all_hex_limit; + int file_max_size; + std::string filename; +}log_args_t; + +typedef struct { + bool enable; +}gui_args_t; + +typedef struct { + phy_args_t phy; + mac_args_t mac; + uint32_t rrc_inactivity_timer; + float metrics_period_secs; + bool enable_mbsfn; + bool print_buffer_state; +}expert_args_t; + +typedef struct { + enb_args_t enb; + enb_files_t enb_files; + rf_args_t rf; + rf_cal_t rf_cal; + pcap_args_t pcap; + log_args_t log; + gui_args_t gui; + expert_args_t expert; +}all_args_t; + +/******************************************************************************* + Main UE class +*******************************************************************************/ + +class enb + :public enb_metrics_interface { +public: + static enb *get_instance(void); + + static void cleanup(void); + + bool init(all_args_t *args_); + + void stop(); + + void start_plot(); + + void print_pool(); + + static void rf_msg(srslte_rf_error_t error); + + void handle_rf_msg(srslte_rf_error_t error); + + // eNodeB metrics interface + bool get_metrics(enb_metrics_t &m); + + void pregenerate_signals(bool enable); + + +private: + static enb *instance; + + const static int ENB_POOL_SIZE = 1024*10; + + enb(); + + virtual ~enb(); + + srslte::radio radio; + srsenb::phy phy; + srsenb::mac mac; + srslte::mac_pcap mac_pcap; + srsenb::rlc rlc; + srsenb::pdcp pdcp; + srsenb::rrc rrc; + srsenb::gtpu gtpu; + srsenb::s1ap s1ap; + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + srslte::log_filter rf_log; + std::vector phy_log; + srslte::log_filter mac_log; + srslte::log_filter rlc_log; + srslte::log_filter pdcp_log; + srslte::log_filter rrc_log; + srslte::log_filter gtpu_log; + srslte::log_filter s1ap_log; + srslte::log_filter pool_log; + + srslte::byte_buffer_pool *pool; + + all_args_t *args; + bool started; + rf_metrics_t rf_metrics; + + srslte::LOG_LEVEL_ENUM level(std::string l); + + bool check_srslte_version(); + int parse_sib1(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data); + int parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *data); + int parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data); + int parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data); + int parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *data); + int parse_sib13(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *data); + int parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common); + int parse_rr(all_args_t *args, rrc_cfg_t *rrc_cfg); + int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg); + bool sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num); + int parse_cell_cfg(all_args_t *args, srslte_cell_t *cell); +}; + +} // namespace srsenb + +#endif // SRSENB_ENB_H + diff --git a/srsenb/hdr/mac/mac.h b/srsenb/hdr/mac/mac.h new file mode 100644 index 0000000..ed77268 --- /dev/null +++ b/srsenb/hdr/mac/mac.h @@ -0,0 +1,233 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_MAC_H +#define SRSENB_MAC_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/timers.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/common/threads.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/common/mac_pcap.h" +#include "scheduler.h" +#include "scheduler_metric.h" +#include "srslte/interfaces/enb_metrics_interface.h" +#include "ue.h" + +namespace srsenb { + +class pdu_process_handler +{ +public: + virtual bool process_pdus() = 0; +}; + +typedef struct { + sched_interface::sched_args_t sched; + int link_failure_nof_err; +} mac_args_t; + +class mac + :public mac_interface_phy, + public mac_interface_rlc, + public mac_interface_rrc, + public srslte::mac_interface_timers, + public pdu_process_handler +{ +public: + mac(); + bool init(mac_args_t *args, srslte_cell_t *cell, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h); + void stop(); + + void start_pcap(srslte::mac_pcap* pcap_); + + /******** Interface from PHY (PHY -> MAC) ****************/ + int sr_detected(uint32_t tti, uint16_t rnti); + int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv); + + int set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info); + + int ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); + int pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value); + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); + int snr_info(uint32_t tti, uint16_t rnti, float snr); + int ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack); + int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res); + + int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res); + int get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res); + int get_mch_sched(bool is_mcch, dl_sched_t *dl_sched_res); + void build_mch_sched(uint32_t tbs); + void rl_failure(uint16_t rnti); + void rl_ok(uint16_t rnti); + void tti_clock(); + + /******** Interface from RRC (RRC -> MAC) ****************/ + /* Provides cell configuration including SIB periodicity, etc. */ + int cell_cfg(sched_interface::cell_cfg_t *cell_cfg); + void reset(); + + /* Manages UE scheduling context */ + int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *cfg); + int ue_rem(uint16_t rnti); + + // Indicates that the PHY config dedicated has been enabled or not + void phy_config_enabled(uint16_t rnti, bool enabled); + + /* Manages UE bearers and associated configuration */ + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg); + int bearer_ue_rem(uint16_t rnti, uint32_t lc_id); + int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue); + + bool process_pdus(); + + // Interface for upper-layer timers + srslte::timers::timer* timer_get(uint32_t timer_id); + void timer_release_id(uint32_t timer_id); + u_int32_t timer_get_unique_id(); + + uint32_t get_current_tti(); + void get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]); + void write_mcch(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT *mcch); +private: + + static const int MAX_LOCATIONS = 20; + static const uint32_t cfi = 3; + srslte_dci_location_t locations[MAX_LOCATIONS]; + + static const int MAC_PDU_THREAD_PRIO = 60; + + // We use a rwlock in MAC to allow multiple workers to access MAC simultaneously. No conflicts will happen since access for different TTIs + pthread_rwlock_t rwlock; + + // Interaction with PHY + phy_interface_mac *phy_h; + rlc_interface_mac *rlc_h; + rrc_interface_mac *rrc_h; + srslte::log *log_h; + + srslte_cell_t cell; + mac_args_t args; + + uint32_t tti; + bool started; + + /* Scheduler unit */ + sched scheduler; + dl_metric_rr sched_metric_dl_rr; + ul_metric_rr sched_metric_ul_rr; + sched_interface::cell_cfg_t cell_config; + + + sched_interface::dl_pdu_mch_t mch; + + + /* Map of active UEs */ + std::map ue_db; + uint16_t last_rnti; + + uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t *grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len); + uint8_t* assemble_si(uint32_t index); + + const static int rar_payload_len = 128; + std::vector rar_pdu_msg; + uint8_t rar_payload[sched_interface::MAX_RAR_LIST][rar_payload_len]; + + typedef struct { + uint32_t preamble_idx; + uint32_t ta_cmd; + uint16_t temp_crnti; + } pending_rar_t; + + const static int MAX_PENDING_RARS = 64; + pending_rar_t pending_rars[MAX_PENDING_RARS]; + + const static int NOF_BCCH_DLSCH_MSG=sched_interface::MAX_SIBS; + uint8_t bcch_dlsch_payload[sched_interface::MAX_SIB_PAYLOAD_LEN]; + + const static int pcch_payload_buffer_len = 1024; + uint8_t pcch_payload_buffer[pcch_payload_buffer_len]; + srslte_softbuffer_tx_t bcch_softbuffer_tx[NOF_BCCH_DLSCH_MSG]; + srslte_softbuffer_tx_t pcch_softbuffer_tx; + srslte_softbuffer_tx_t rar_softbuffer_tx; + + const static int mcch_payload_len = 3000; //TODO FIND OUT MAX LENGTH + int current_mcch_length; + uint8_t mcch_payload_buffer[mcch_payload_len]; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; + + const static int mtch_payload_len = 10000; + uint8_t mtch_payload_buffer[mtch_payload_len]; + + /* Functions for MAC Timers */ + srslte::timers timers_db; + void setup_timers(); + + // pointer to MAC PCAP object + srslte::mac_pcap* pcap; + + + /* Class to run upper-layer timers with normal priority */ + class timer_thread : public thread { + public: + timer_thread(srslte::timers *t) : ttisync(10240),timers(t),running(false) {start();} + void tti_clock(); + void stop(); + private: + void run_thread(); + srslte::tti_sync_cv ttisync; + srslte::timers *timers; + bool running; + }; + timer_thread timers_thread; + + /* Class to process MAC PDUs from DEMUX unit */ + class pdu_process : public thread { + public: + pdu_process(pdu_process_handler *h); + void notify(); + void stop(); + private: + void run_thread(); + bool running; + bool have_data; + pthread_mutex_t mutex; + pthread_cond_t cvar; + pdu_process_handler *handler; + }; + pdu_process pdu_process_thread; + +}; + +} // namespace srsenb + +#endif // SRSENB_MAC_H diff --git a/srsenb/hdr/mac/mac_metrics.h b/srsenb/hdr/mac/mac_metrics.h new file mode 100644 index 0000000..d63734d --- /dev/null +++ b/srsenb/hdr/mac/mac_metrics.h @@ -0,0 +1,54 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_MAC_METRICS_H +#define SRSENB_MAC_METRICS_H + + +namespace srsenb { + +// MAC metrics per user + +struct mac_metrics_t +{ + uint16_t rnti; + int tx_pkts; + int tx_errors; + int tx_brate; + int rx_pkts; + int rx_errors; + int rx_brate; + int ul_buffer; + int dl_buffer; + float dl_cqi; + float dl_ri; + float dl_pmi; + float phr; +}; + +} // namespace srsenb + +#endif // SRSENB_MAC_METRICS_H diff --git a/srsenb/hdr/mac/scheduler.h b/srsenb/hdr/mac/scheduler.h new file mode 100644 index 0000000..5193430 --- /dev/null +++ b/srsenb/hdr/mac/scheduler.h @@ -0,0 +1,237 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_SCHEDULER_H +#define SRSENB_SCHEDULER_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "scheduler_ue.h" +#include "scheduler_harq.h" +#include + +namespace srsenb { + + +/* Caution: User addition (ue_cfg) and removal (ue_rem) are not thread-safe + * Rest of operations are thread-safe + * + * The subclass sched_ue is thread-safe so that access to shared variables like buffer states + * from scheduler thread and other threads is protected for each individual user. + */ + +class sched : public sched_interface +{ + + +public: + + + /************************************************************* + * + * Scheduling metric interface definition + * + ************************************************************/ + + class metric_dl + { + public: + + /* Virtual methods for user metric calculation */ + virtual void new_tti(std::map &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols, uint32_t tti) = 0; + virtual dl_harq_proc* get_user_allocation(sched_ue *user) = 0; + }; + + + class metric_ul + { + public: + + /* Virtual methods for user metric calculation */ + virtual void reset_allocation(uint32_t nof_rb_) = 0; + virtual void new_tti(std::map &ue_db, uint32_t nof_rb, uint32_t tti) = 0; + virtual ul_harq_proc* get_user_allocation(sched_ue *user) = 0; + virtual bool update_allocation(ul_harq_proc::ul_alloc_t alloc) = 0; + }; + + + + /************************************************************* + * + * FAPI-like Interface + * + ************************************************************/ + + sched(); + ~sched(); + + void init(rrc_interface_mac *rrc, srslte::log *log); + void set_metric(metric_dl *dl_metric, metric_ul *ul_metric); + int cell_cfg(cell_cfg_t *cell_cfg); + void set_sched_cfg(sched_args_t *sched_cfg); + int reset(); + + int ue_cfg(uint16_t rnti, ue_cfg_t *ue_cfg); + int ue_rem(uint16_t rnti); + bool ue_exists(uint16_t rnti); + + void phy_config_enabled(uint16_t rnti, bool enabled); + + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, ue_bearer_cfg_t *cfg); + int bearer_ue_rem(uint16_t rnti, uint32_t lc_id); + + uint32_t get_ul_buffer(uint16_t rnti); + uint32_t get_dl_buffer(uint16_t rnti); + + int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue); + int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code); + + int dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dedicated); + int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack); + int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size); + int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value); + int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value); + int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); + + int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc); + int ul_sr_info(uint32_t tti, uint16_t rnti); + int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr, bool set_value = true); + int ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len); + int ul_phr(uint16_t rnti, int phr); + int ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code); + + int dl_sched(uint32_t tti, dl_sched_res_t *sched_result); + int ul_sched(uint32_t tti, ul_sched_res_t *sched_result); + + /* Custom TPC functions + */ + void tpc_inc(uint16_t rnti); + void tpc_dec(uint16_t rnti); + + + + static uint32_t get_rvidx(uint32_t retx_idx) { + const static int rv_idx[4] = {0, 2, 3, 1}; + return rv_idx[retx_idx%4]; + } + + + + static void generate_cce_location(srslte_regs_t *regs, sched_ue::sched_dci_cce_t *location, + uint32_t cfi, uint32_t sf_idx = 0, uint16_t rnti = 0); + +private: + + metric_dl *dl_metric; + metric_ul *ul_metric; + srslte::log *log_h; + rrc_interface_mac *rrc; + + pthread_rwlock_t rwlock; + + cell_cfg_t cfg; + sched_args_t sched_cfg; + + const static int MAX_PRB = 100; + const static int MAX_RBG = 25; + const static int MAX_CCE = 128; + + // This is for computing DCI locations + srslte_regs_t regs; + bool used_cce[MAX_CCE]; + + typedef struct { + int buf_rar; + uint16_t rnti; + uint32_t ra_id; + uint32_t rar_tti; + } sched_rar_t; + + typedef struct { + bool is_in_window; + uint32_t window_start; + uint32_t n_tx; + } sched_sib_t; + + + int dl_sched_bc(dl_sched_bc_t bc[MAX_BC_LIST]); + int dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST]); + int dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]); + + + int generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs, uint32_t rv, srslte_ra_dl_dci_t *dci); + bool generate_dci(srslte_dci_location_t *sched_location, sched_ue::sched_dci_cce_t *locations, uint32_t aggr_level, sched_ue *user = NULL); + + + std::map ue_db; + + sched_sib_t pending_sibs[MAX_SIBS]; + + + typedef struct { + bool enabled; + uint16_t rnti; + uint32_t L; + uint32_t n_prb; + uint32_t mcs; + } pending_msg3_t; + + const static int SCHED_MAX_PENDING_RAR = 8; + sched_rar_t pending_rar[SCHED_MAX_PENDING_RAR]; + pending_msg3_t pending_msg3[10]; + + // Allowed DCI locations for SIB and RAR per CFI + sched_ue::sched_dci_cce_t common_locations[3]; + sched_ue::sched_dci_cce_t rar_locations[3][10]; + + uint32_t bc_aggr_level; + uint32_t rar_aggr_level; + + uint32_t pdsch_re[10]; + uint32_t avail_rbg; + uint32_t P; + uint32_t start_rbg; + uint32_t si_n_rbg; + uint32_t rar_n_rbg; + uint32_t nof_rbg; + uint32_t sf_idx; + uint32_t sfn; + uint32_t current_tti; + uint32_t current_cfi; + + bool configured; + +}; + + + + + +} + +#endif // SRSENB_SCHEDULER_H diff --git a/srsenb/hdr/mac/scheduler_harq.h b/srsenb/hdr/mac/scheduler_harq.h new file mode 100644 index 0000000..5ac3845 --- /dev/null +++ b/srsenb/hdr/mac/scheduler_harq.h @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_SCHEDULER_HARQ_H +#define SRSENB_SCHEDULER_HARQ_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/sched_interface.h" + +namespace srsenb { + +class harq_proc +{ +public: + void config(uint32_t id, uint32_t max_retx, srslte::log* log_h); + void set_max_retx(uint32_t max_retx); + void reset(uint32_t tb_idx); + uint32_t get_id(); + bool is_empty(uint32_t tb_idx); + + void new_retx(uint32_t tb_idx, uint32_t tti, int *mcs, int *tbs); + + bool get_ack(uint32_t tb_idx); + void set_ack(uint32_t tb_idx, bool ack); + + uint32_t nof_tx(uint32_t tb_idx); + uint32_t nof_retx(uint32_t tb_idx); + uint32_t get_tti(); + bool get_ndi(uint32_t tb_idx); + +protected: + + void new_tx_common(uint32_t tb_idx, uint32_t tti, int mcs, int tbs); + bool has_pending_retx_common(uint32_t tb_idx); + + bool ack[SRSLTE_MAX_TB]; + bool active[SRSLTE_MAX_TB]; + bool ndi[SRSLTE_MAX_TB]; + uint32_t id; + uint32_t max_retx; + uint32_t n_rtx[SRSLTE_MAX_TB]; + uint32_t tx_cnt[SRSLTE_MAX_TB]; + int tti; + int last_mcs[SRSLTE_MAX_TB]; + int last_tbs[SRSLTE_MAX_TB]; + + srslte::log* log_h; + + private: + bool ack_received[SRSLTE_MAX_TB]; +}; + +class dl_harq_proc : public harq_proc +{ +public: + void new_tx(uint32_t tb_idx, uint32_t tti, int mcs, int tbs, uint32_t n_cce); + uint32_t get_rbgmask(); + void set_rbgmask(uint32_t new_mask); + bool has_pending_retx(uint32_t tb_idx, uint32_t tti); + int get_tbs(uint32_t tb_idx); + uint32_t get_n_cce(); +private: + uint32_t rbgmask; + uint32_t nof_rbg; + uint32_t n_cce; +}; + +class ul_harq_proc : public harq_proc +{ +public: + + struct ul_alloc_t { + uint32_t RB_start; + uint32_t L; + inline void set(uint32_t start, uint32_t len) {RB_start = start; L = len;} + }; + + void new_tx(uint32_t tti, int mcs, int tbs); + + ul_alloc_t get_alloc(); + void set_alloc(ul_alloc_t alloc); + void re_alloc(ul_alloc_t alloc); + bool is_adaptive_retx(); + + void reset_pending_data(); + bool has_pending_ack(); + uint32_t get_pending_data(); + + void set_rar_mcs(uint32_t mcs); + bool get_rar_mcs(int* mcs); + +private: + ul_alloc_t allocation; + bool need_ack; + int pending_data; + uint32_t rar_mcs; + bool has_rar_mcs; + bool is_adaptive; + bool is_msg3; +}; + +} + + +#endif // SRSENB_SCHEDULER_HARQ_H diff --git a/srsenb/hdr/mac/scheduler_metric.h b/srsenb/hdr/mac/scheduler_metric.h new file mode 100644 index 0000000..f4335fd --- /dev/null +++ b/srsenb/hdr/mac/scheduler_metric.h @@ -0,0 +1,89 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_SCHEDULER_METRIC_H +#define SRSENB_SCHEDULER_METRIC_H + +#include "scheduler.h" + +namespace srsenb { + +class dl_metric_rr : public sched::metric_dl +{ +public: + void new_tti(std::map &ue_db, uint32_t start_rbg, uint32_t nof_rbg, uint32_t nof_ctrl_symbols, uint32_t tti); + dl_harq_proc* get_user_allocation(sched_ue *user); +private: + + const static int MAX_RBG = 25; + + bool new_allocation(uint32_t nof_rbg, uint32_t* rbgmask); + void update_allocation(uint32_t new_mask); + bool allocation_is_valid(uint32_t mask); + dl_harq_proc* apply_user_allocation(sched_ue *user); + + uint32_t get_required_rbg(sched_ue *user, uint32_t tti); + uint32_t count_rbg(uint32_t mask); + uint32_t calc_rbg_mask(bool mask[25]); + + bool used_rbg[MAX_RBG]; + + + uint32_t current_tti; + uint32_t total_rbg; + uint32_t used_rbg_mask; + uint32_t nof_ctrl_symbols; + uint32_t available_rbg; +}; + +class ul_metric_rr : public sched::metric_ul +{ +public: + void new_tti(std::map &ue_db, uint32_t nof_rb, uint32_t tti); + ul_harq_proc* get_user_allocation(sched_ue *user); + bool update_allocation(ul_harq_proc::ul_alloc_t alloc); + void reset_allocation(uint32_t nof_rb_); +private: + + const static int MAX_PRB = 100; + + bool new_allocation(uint32_t L, ul_harq_proc::ul_alloc_t *alloc); + bool allocation_is_valid(ul_harq_proc::ul_alloc_t alloc); + ul_harq_proc* apply_user_allocation(sched_ue *user, bool retx_only); + ul_harq_proc* allocate_user_newtx_prbs(sched_ue* user); + ul_harq_proc* allocate_user_retx_prbs(sched_ue *user); + + + bool used_rb[MAX_PRB]; + uint32_t current_tti; + uint32_t nof_rb; +}; + + +} + +#endif // SRSENB_SCHEDULER_METRIC_H + diff --git a/srsenb/hdr/mac/scheduler_ue.h b/srsenb/hdr/mac/scheduler_ue.h new file mode 100644 index 0000000..202ef8b --- /dev/null +++ b/srsenb/hdr/mac/scheduler_ue.h @@ -0,0 +1,219 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_SCHEDULER_UE_H +#define SRSENB_SCHEDULER_UE_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/sched_interface.h" + +#include "scheduler_harq.h" + +namespace srsenb { + + +/** This class is designed to be thread-safe because it is called from workers through scheduler thread and from + * higher layers and mac threads. + * + * 1 mutex is created for every user and only access to same user variables are mutexed + */ +class sched_ue { + +public: + + // used by sched_metric + dl_harq_proc* dl_next_alloc; + ul_harq_proc* ul_next_alloc; + + bool has_pucch; + + typedef struct { + uint32_t cce_start[4][6]; + uint32_t nof_loc[4]; + } sched_dci_cce_t; + + /************************************************************* + * + * FAPI-like Interface + * + ************************************************************/ + sched_ue(); + ~sched_ue(); + void reset(); + void phy_config_enabled(uint32_t tti, bool enabled); + void set_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg, sched_interface::cell_cfg_t *cell_cfg, + srslte_regs_t *regs, srslte::log *log_h); + + void set_bearer_cfg(uint32_t lc_id, srsenb::sched_interface::ue_bearer_cfg_t* cfg); + void rem_bearer(uint32_t lc_id); + + void dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue); + void ul_buffer_state(uint8_t lc_id, uint32_t bsr, bool set_value = true); + void ul_phr(int phr); + void mac_buffer_state(uint32_t ce_code); + void ul_recv_len(uint32_t lcid, uint32_t len); + void set_dl_ant_info(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dedicated); + void set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code); + void set_dl_ri(uint32_t tti, uint32_t ri); + void set_dl_pmi(uint32_t tti, uint32_t ri); + void set_dl_cqi(uint32_t tti, uint32_t cqi); + int set_ack_info(uint32_t tti, uint32_t tb_idx, bool ack); + void set_ul_crc(uint32_t tti, bool crc_res); + +/******************************************************* + * Custom functions + *******************************************************/ + + void tpc_inc(); + void tpc_dec(); + + void set_max_mcs(int mcs_ul, int mcs_dl); + void set_fixed_mcs(int mcs_ul, int mcs_dl); + + + +/******************************************************* + * Functions used by scheduler metric objects + *******************************************************/ + + uint32_t get_required_prb_dl(uint32_t req_bytes, uint32_t nof_ctrl_symbols); + uint32_t get_required_prb_ul(uint32_t req_bytes); + uint32_t prb_to_rbg(uint32_t nof_prb); + uint32_t rgb_to_prb(uint32_t nof_rbg); + + + uint32_t get_pending_dl_new_data(uint32_t tti); + uint32_t get_pending_ul_new_data(uint32_t tti); + uint32_t get_pending_ul_old_data(); + uint32_t get_pending_dl_new_data_total(uint32_t tti); + + void reset_timeout_dl_harq(uint32_t tti); + dl_harq_proc *get_pending_dl_harq(uint32_t tti); + dl_harq_proc *get_empty_dl_harq(); + ul_harq_proc *get_ul_harq(uint32_t tti); + +/******************************************************* + * Functions used by the scheduler object + *******************************************************/ + + void set_sr(); + void unset_sr(); + + int generate_format1(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format2a(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format2(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request); + + srslte_dci_format_t get_dci_format(); + uint32_t get_aggr_level(uint32_t nof_bits); + sched_dci_cce_t *get_locations(uint32_t current_cfi, uint32_t sf_idx); + + bool needs_cqi(uint32_t tti, bool will_send = false); + uint32_t get_max_retx(); + + bool get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2]); + bool pucch_sr_collision(uint32_t current_tti, uint32_t n_cce); + +private: + + typedef struct { + sched_interface::ue_bearer_cfg_t cfg; + int buf_tx; + int buf_retx; + int bsr; + } ue_bearer_t; + + bool is_sr_triggered(); + int alloc_pdu(int tbs, sched_interface::dl_sched_pdu_t* pdu); + + static uint32_t format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb); + static int cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t max_Qm, uint32_t *mcs); + int alloc_tbs_dl(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, int *mcs); + int alloc_tbs_ul(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, int *mcs); + int alloc_tbs(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, bool is_ul, int *mcs); + + static bool bearer_is_ul(ue_bearer_t *lch); + static bool bearer_is_dl(ue_bearer_t *lch); + + uint32_t get_pending_dl_new_data_unlocked(uint32_t tti); + uint32_t get_pending_ul_old_data_unlocked(); + uint32_t get_pending_ul_new_data_unlocked(uint32_t tti); + + bool needs_cqi_unlocked(uint32_t tti, bool will_send = false); + + int generate_format2a_unlocked(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + + bool is_first_dl_tx(); + + sched_interface::ue_cfg_t cfg; + srslte_cell_t cell; + srslte::log* log_h; + + pthread_mutex_t mutex; + + /* Buffer states */ + bool sr; + int buf_mac; + int buf_ul; + ue_bearer_t lch[sched_interface::MAX_LC]; + + int power_headroom; + uint32_t dl_ri; + uint32_t dl_ri_tti; + uint32_t dl_pmi; + uint32_t dl_pmi_tti; + uint32_t dl_cqi; + uint32_t dl_cqi_tti; + uint32_t cqi_request_tti; + uint32_t ul_cqi; + uint32_t ul_cqi_tti; + uint16_t rnti; + uint32_t max_mcs_dl; + uint32_t max_mcs_ul; + int fixed_mcs_ul; + int fixed_mcs_dl; + uint32_t P; + + int next_tpc_pusch; + int next_tpc_pucch; + + // Allowed DCI locations per CFI and per subframe + sched_dci_cce_t dci_locations[3][10]; + + const static int SCHED_MAX_HARQ_PROC = 2*HARQ_DELAY_MS; + dl_harq_proc dl_harq[SCHED_MAX_HARQ_PROC]; + ul_harq_proc ul_harq[SCHED_MAX_HARQ_PROC]; + + bool phy_config_dedicated_enabled; + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT dl_ant_info; + +}; + +} + + +#endif // SRSENB_SCHEDULER_UE_H diff --git a/srsenb/hdr/mac/ue.h b/srsenb/hdr/mac/ue.h new file mode 100644 index 0000000..e74c224 --- /dev/null +++ b/srsenb/hdr/mac/ue.h @@ -0,0 +1,173 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_UE_H +#define SRSENB_UE_H + +#include "srslte/common/log.h" +#include "srslte/common/pdu.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/common/pdu_queue.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include +#include "mac_metrics.h" + +namespace srsenb { + +class ue : public srslte::read_pdu_interface, + public srslte::pdu_queue::process_callback +{ +public: + + ue() : mac_msg_dl(20), mch_mac_msg_dl(10), mac_msg_ul(20), conres_id_available(false), + dl_ri_counter(0), + dl_pmi_counter(0), + conres_id(0), + last_tti(0), + pdus(128) { + rrc = NULL; + sched = NULL; + rlc = NULL; + log_h = NULL; + rnti = 0; + pcap = NULL; + nof_failures = 0; + phr_counter = 0; + dl_cqi_counter = 0; + is_phy_added = false; + for (int i=0;i lc_groups[4]; + + uint32_t phr_counter; + uint32_t dl_cqi_counter; + uint32_t dl_ri_counter; + uint32_t dl_pmi_counter; + mac_metrics_t metrics; + + srslte::mac_pcap* pcap; + + uint64_t conres_id; + + uint16_t rnti; + + uint32_t last_tti; + + uint32_t nof_failures; + + const static int NOF_HARQ_PROCESSES = 2 * HARQ_DELAY_MS * SRSLTE_MAX_TB; + srslte_softbuffer_tx_t softbuffer_tx[NOF_HARQ_PROCESSES]; + srslte_softbuffer_rx_t softbuffer_rx[NOF_HARQ_PROCESSES]; + + uint8_t *pending_buffers[NOF_HARQ_PROCESSES]; + + // For DL there are two buffers, one for each Transport block + const static int payload_buffer_len = 128*1024; + uint8_t tx_payload_buffer[SRSLTE_MAX_TB][payload_buffer_len]; + + // For UL there are multiple buffers per PID and are managed by pdu_queue + srslte::pdu_queue pdus; + srslte::sch_pdu mac_msg_dl, mac_msg_ul; + srslte::mch_pdu mch_mac_msg_dl; + + rlc_interface_mac *rlc; + rrc_interface_mac* rrc; + srslte::log *log_h; + sched_interface* sched; + + bool conres_id_available; + + // Mutexes + pthread_mutex_t mutex; + +}; + +} + +#endif // SRSENB_UE_H + diff --git a/srsenb/hdr/metrics_stdout.h b/srsenb/hdr/metrics_stdout.h new file mode 100644 index 0000000..f5af14e --- /dev/null +++ b/srsenb/hdr/metrics_stdout.h @@ -0,0 +1,74 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +/****************************************************************************** + * File: metrics_stdout.h + * Description: Metrics class printing to stdout. + *****************************************************************************/ + +#ifndef SRSENB_METRICS_STDOUT_H +#define SRSENB_METRICS_STDOUT_H + +#include +#include +#include + +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +class metrics_stdout +{ +public: + metrics_stdout(); + + bool init(enb_metrics_interface *u, float report_period_secs=1.0); + void stop(); + void toggle_print(bool b); + static void* metrics_thread_start(void *m); + void metrics_thread_run(); + +private: + void print_metrics(); + void print_disconnect(); + std::string float_to_string(float f, int digits); + std::string float_to_eng_string(float f, int digits); + std::string int_to_eng_string(int f, int digits); + + enb_metrics_interface *enb_; + + bool started; + bool do_print; + pthread_t metrics_thread; + enb_metrics_t metrics; + float metrics_report_period; // seconds + uint8_t n_reports; +}; + +} // namespace srsenb + +#endif // SRSENB_METRICS_STDOUT_H diff --git a/srsenb/hdr/parser.h b/srsenb/hdr/parser.h new file mode 100644 index 0000000..9042520 --- /dev/null +++ b/srsenb/hdr/parser.h @@ -0,0 +1,310 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PARSER_H +#define SRSENB_PARSER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace srsenb { + +using namespace libconfig; + +class parser +{ +public: + + class field_itf + { + public: + virtual ~field_itf(){} + virtual int parse(Setting &root) = 0; + virtual const char* get_name() = 0; + }; + + template + class field_enum_str : public field_itf + { + public: + field_enum_str(const char* name_, T *store_ptr_, const char (*value_str_)[20], uint32_t nof_items_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + value_str = value_str_; + nof_items = nof_items_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + std::string val; + if (root.exists(name)) { + + if (enabled_value) { + *enabled_value = true; + } + + if (root.lookupValue(name, val)) { + bool found = false; + // find value + for (uint32_t i=0;i + class field_enum_num : public field_itf + { + public: + field_enum_num(const char* name_, T *store_ptr_, const S *value_str_, uint32_t nof_items_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + value_str = value_str_; + nof_items = nof_items_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + S val; + if (root.exists(name)) { + + if (enabled_value) { + *enabled_value = true; + } + if (parser::lookupValue(root, name, &val)) { + bool found = false; + // find value + for (uint32_t i=0;i + class field : public field_itf + { + public: + field(const char* name_, T *store_ptr_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + if (root.exists(name)) { + if (enabled_value) { + *enabled_value = true; + } + if (!parser::lookupValue(root, name, store_ptr)) { + return -1; + } else { + return 0; + } + } else { + if (enabled_value) { + *enabled_value = false; + return 0; + } else { + return -1; + } + } + } + private: + const char* name; + T *store_ptr; + bool *enabled_value; + }; + + class section + { + public: + section(std::string name); + ~section(); + void set_optional(bool *enabled_value); + void add_subsection(section *s); + void add_field(field_itf *f); + int parse(Setting &root); + private: + std::string name; + bool *enabled_value; + std::list sub_sections; + std::list fields; + }; + + + parser(std::string filename); + int parse(); + void add_section(section *s); + + static int parse_section(std::string filename, section *s); + + static bool lookupValue(Setting &root, const char *name, std::string *val) { + return root.lookupValue(name, *val); + } + static bool lookupValue(Setting &root, const char *name, uint8_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = (uint8_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, uint16_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = (uint16_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, uint32_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int8_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = (int8_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int16_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = (int16_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int32_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, double *val) { + double t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, bool *val) { + bool t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + + +private: + std::list sections; + std::string filename; +}; +} +#endif // PARSER_H diff --git a/srsenb/hdr/phy/phch_common.h b/srsenb/hdr/phy/phch_common.h new file mode 100644 index 0000000..03cff5e --- /dev/null +++ b/srsenb/hdr/phy/phch_common.h @@ -0,0 +1,176 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PHCH_COMMON_H +#define SRSENB_PHCH_COMMON_H + +#include +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/enb_metrics_interface.h" +#include "srslte/common/gen_mch_tables.h" +#include "srslte/common/log.h" +#include "srslte/common/threads.h" +#include "srslte/common/thread_pool.h" +#include "srslte/radio/radio.h" +#include +namespace srsenb { + +typedef struct { + float max_prach_offset_us; + int pusch_max_its; + float tx_amplitude; + int nof_phy_threads; + std::string equalizer_mode; + float estimator_fil_w; + bool pregenerate_signals; +} phy_args_t; + +typedef enum{ + SUBFRAME_TYPE_REGULAR = 0, + SUBFRAME_TYPE_MBSFN, + SUBFRAME_TYPE_N_ITEMS, +} subframe_type_t; +static const char subframe_type_text[SUBFRAME_TYPE_N_ITEMS][20] = {"Regular", "MBSFN"}; + +/* Subframe config */ + +typedef struct { + subframe_type_t sf_type; + uint8_t mbsfn_area_id; + uint8_t non_mbsfn_region_length; + uint8_t mbsfn_mcs; + bool mbsfn_encode; + bool is_mcch; +} subframe_cfg_t; + + + +class phch_common +{ +public: + + + phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) { + nof_mutex = 0; + max_mutex = max_mutex_; + params.max_prach_offset_us = 20; + radio = NULL; + mac = NULL; + is_first_tx = false; + is_first_of_burst = false; + pdsch_p_b = 0; + nof_workers = 0; + bzero(&pusch_cfg, sizeof(pusch_cfg)); + bzero(&hopping_cfg, sizeof(hopping_cfg)); + bzero(&pucch_cfg, sizeof(pucch_cfg)); + bzero(&ul_grants, sizeof(ul_grants)); + } + + bool init(srslte_cell_t *cell, srslte::radio *radio_handler, mac_interface_phy *mac); + void reset(); + void stop(); + + void set_nof_mutex(uint32_t nof_mutex); + + void worker_end(uint32_t tx_mutex_cnt, cf_t *buffer[SRSLTE_MAX_PORTS], uint32_t nof_samples, srslte_timestamp_t tx_time); + + // Common objects + srslte_cell_t cell; + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + srslte_pusch_hopping_cfg_t hopping_cfg; + srslte_pucch_cfg_t pucch_cfg; + uint8_t pdsch_p_b; + phy_args_t params; + + srslte::radio *radio; + mac_interface_phy *mac; + + // Common objects for schedulign grants + mac_interface_phy::ul_sched_t ul_grants[TTIMOD_SZ]; + mac_interface_phy::dl_sched_t dl_grants[TTIMOD_SZ]; + + // Map of pending ACKs for each user + typedef struct { + bool is_pending[TTIMOD_SZ][SRSLTE_MAX_TB]; + uint16_t n_pdcch[TTIMOD_SZ]; + } pending_ack_t; + + class common_ue { + public: + pending_ack_t pending_ack; + uint8_t ri; + int last_ul_tbs[2*HARQ_DELAY_MS]; + srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; + }; + + std::map common_ue_db; + + void ue_db_add_rnti(uint16_t rnti); + void ue_db_rem_rnti(uint16_t rnti); + void ue_db_clear(uint32_t sf_idx); + void ue_db_set_ack_pending(uint32_t sf_idx, uint16_t rnti, uint32_t tb_idx, uint32_t n_pdcch); + bool ue_db_is_ack_pending(uint32_t sf_idx, uint16_t rnti, uint32_t tb_idx, uint32_t *last_n_pdcch = NULL); + void ue_db_set_ri(uint16_t rnti, uint8_t ri); + uint8_t ue_db_get_ri(uint16_t rnti); + void ue_db_set_last_ul_mod(uint16_t rnti, uint32_t tti, srslte_mod_t mcs); + srslte_mod_t ue_db_get_last_ul_mod(uint16_t rnti, uint32_t tti); + void ue_db_set_last_ul_tbs(uint16_t rnti, uint32_t tti, int tbs); + int ue_db_get_last_ul_tbs(uint16_t rnti, uint32_t tti); + + void configure_mbsfn(phy_interface_rrc::phy_cfg_mbsfn_t *cfg); + void build_mch_table(); + void build_mcch_table(); + void get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti); + + +private: + std::vector tx_mutex; + bool is_first_tx; + bool is_first_of_burst; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + + pthread_mutex_t user_mutex; + + phy_interface_rrc::phy_cfg_mbsfn_t mbsfn; + bool sib13_configured; + bool mcch_configured; + uint8_t mch_table[40]; + uint8_t mcch_table[10]; + + uint8_t mch_sf_idx_lut[10]; + bool is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + bool is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + + void add_rnti(uint16_t rnti); + +}; + +} // namespace srsenb + +#endif // SRSENB_PHCH_COMMON_H diff --git a/srsenb/hdr/phy/phch_worker.h b/srsenb/hdr/phy/phch_worker.h new file mode 100644 index 0000000..776ba0a --- /dev/null +++ b/srsenb/hdr/phy/phch_worker.h @@ -0,0 +1,143 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PHCH_WORKER_H +#define SRSENB_PHCH_WORKER_H + +#include + +#include "srslte/srslte.h" +#include "phch_common.h" + +#define LOG_EXECTIME + +namespace srsenb { + +class phch_worker : public srslte::thread_pool::worker +{ +public: + + phch_worker(); + void init(phch_common *phy, srslte::log *log_h); + void stop(); + void reset(); + + cf_t *get_buffer_rx(uint32_t antenna_idx); + void set_time(uint32_t tti, uint32_t tx_mutex_cnt, srslte_timestamp_t tx_time); + + int add_rnti(uint16_t rnti); + void rem_rnti(uint16_t rnti); + uint32_t get_nof_rnti(); + + /* These are used by the GUI plotting tools */ + int read_ce_abs(float *ce_abs); + int read_ce_arg(float *ce_abs); + int read_pusch_d(cf_t *pusch_d); + int read_pucch_d(cf_t *pusch_d); + void start_plot(); + + void set_conf_dedicated_ack(uint16_t rnti, + bool rrc_completed); + + void set_config_dedicated(uint16_t rnti, + srslte_refsignal_srs_cfg_t *srs_cfg, + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated); + + uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); + +private: + + const static float PUSCH_RL_SNR_DB_TH = 1.0; + const static float PUCCH_RL_CORR_TH = 0.15; + + void work_imp(); + + int encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants); + int encode_pmch(srslte_enb_dl_pdsch_t *grant, srslte_ra_dl_grant_t *phy_grant); + int decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch); + int encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks); + int encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants); + int encode_pdcch_ul(srslte_enb_ul_pusch_t *grants, uint32_t nof_grants); + int decode_pucch(); + + + /* Common objects */ + srslte::log *log_h; + phch_common *phy; + bool initiated; + bool running; + + cf_t *signal_buffer_rx[SRSLTE_MAX_PORTS]; + cf_t *signal_buffer_tx[SRSLTE_MAX_PORTS]; + uint32_t tti_rx, tti_tx_dl, tti_tx_ul; + uint32_t sf_rx, sf_tx, tx_mutex_cnt; + uint32_t t_rx, t_tx_dl, t_tx_ul; + srslte_enb_dl_t enb_dl; + srslte_enb_ul_t enb_ul; + srslte_softbuffer_tx_t temp_mbsfn_softbuffer; + srslte_timestamp_t tx_time; + + // Class to store user information + class ue { + public: + ue() : I_sr(0), I_sr_en(false), cqi_en(false), pucch_cqi_ack(false), pmi_idx(0), has_grant_tti(0), + dedicated_ack(false), ri_idx(0), ri_en(false), rnti(0) { + bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + bzero(&phich_info, sizeof(srslte_enb_ul_phich_info_t)); + bzero(&metrics, sizeof(phy_metrics_t)); + } + uint32_t I_sr; + uint32_t pmi_idx; + uint32_t ri_idx; + bool I_sr_en; + bool cqi_en; + bool ri_en; + bool pucch_cqi_ack; + int has_grant_tti; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bool dedicated_ack; + uint32_t rnti; + srslte_enb_ul_phich_info_t phich_info; + void metrics_read(phy_metrics_t *metrics); + void metrics_dl(uint32_t mcs); + void metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters); + + int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; + + private: + phy_metrics_t metrics; + }; + std::map ue_db; + + // mutex to protect worker_imp() from configuration interface + pthread_mutex_t mutex; + bool is_worker_running; +}; + +} // namespace srsenb + +#endif // SRSENB_PHCH_WORKER_H + diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h new file mode 100644 index 0000000..d200791 --- /dev/null +++ b/srsenb/hdr/phy/phy.h @@ -0,0 +1,106 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PHY_H +#define SRSENB_PHY_H + +#include "srslte/common/log.h" +#include "srslte/common/log_filter.h" +#include "txrx.h" +#include "phch_worker.h" +#include "phch_common.h" +#include "srslte/radio/radio.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/task_dispatcher.h" +#include "srslte/common/trace.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +typedef struct { + srslte_cell_t cell; + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT prach_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg; + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; +} phy_cfg_t; + + +class phy : public phy_interface_mac, + public phy_interface_rrc +{ +public: + + phy(); + bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log_filter* log_h); + bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, std::vector log_vec); + void stop(); + + /* MAC->PHY interface */ + int add_rnti(uint16_t rnti); + void rem_rnti(uint16_t rnti); + + /*RRC-PHY interface*/ + void configure_mbsfn(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT mcch); + + static uint32_t tti_to_SFN(uint32_t tti); + static uint32_t tti_to_subf(uint32_t tti); + + void start_plot(); + void set_conf_dedicated_ack(uint16_t rnti, bool dedicated_ack); + void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated); + + void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); + +private: + phy_rrc_cfg_t phy_rrc_config; + uint32_t nof_workers; + + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 2; + + const static int PRACH_WORKER_THREAD_PRIO = 80; + const static int SF_RECV_THREAD_PRIO = 1; + const static int WORKERS_THREAD_PRIO = 0; + + srslte::radio *radio_handler; + srslte::log *log_h; + srslte::thread_pool workers_pool; + std::vector workers; + phch_common workers_common; + prach_worker prach; + txrx tx_rx; + + srslte_prach_cfg_t prach_cfg; + + void parse_config(phy_cfg_t* cfg); + +}; + +} // namespace srsenb + +#endif // SRSENB_PHY_H diff --git a/srsenb/hdr/phy/phy_metrics.h b/srsenb/hdr/phy/phy_metrics.h new file mode 100644 index 0000000..5a45baf --- /dev/null +++ b/srsenb/hdr/phy/phy_metrics.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PHY_METRICS_H +#define SRSENB_PHY_METRICS_H + + +namespace srsenb { + +// PHY metrics per user + +struct ul_metrics_t +{ + float n; + float sinr; + float rssi; + float turbo_iters; + float mcs; + int n_samples; +}; + +struct dl_metrics_t +{ + float mcs; + int n_samples; +}; + +struct phy_metrics_t +{ + dl_metrics_t dl; + ul_metrics_t ul; +}; + +} // namespace srsenb + +#endif // SRSENB_PHY_METRICS_H diff --git a/srsenb/hdr/phy/prach_worker.h b/srsenb/hdr/phy/prach_worker.h new file mode 100644 index 0000000..8ca70aa --- /dev/null +++ b/srsenb/hdr/phy/prach_worker.h @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_PRACH_WORKER_H +#define SRSENB_PRACH_WORKER_H + +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/log.h" +#include "srslte/common/threads.h" + +namespace srsenb { + +class prach_worker : thread +{ +public: + prach_worker() : initiated(false), prach_nof_det(0), max_prach_offset_us(0), pending_tti(0), processed_tti(0), + running(false), nof_sf(0), sf_cnt(0) { + log_h = NULL; + mac = NULL; + signal_buffer_rx = NULL; + bzero(&prach, sizeof(srslte_prach_t)); + bzero(&prach_indices, sizeof(prach_indices)); + bzero(&prach_offsets, sizeof(prach_offsets)); + bzero(&prach_p2avg, sizeof(prach_p2avg)); + bzero(&cell, sizeof(cell)); + bzero(&prach_cfg, sizeof(prach_cfg)); + bzero(&mutex, sizeof(mutex)); + bzero(&cvar, sizeof(cvar)); + } + + int init(srslte_cell_t *cell, srslte_prach_cfg_t *prach_cfg, mac_interface_phy *mac, srslte::log *log_h, int priority); + int new_tti(uint32_t tti, cf_t *buffer); + void set_max_prach_offset_us(float delay_us); + void stop(); + +private: + void run_thread(); + int run_tti(uint32_t tti); + + uint32_t prach_nof_det; + uint32_t prach_indices[165]; + float prach_offsets[165]; + float prach_p2avg[165]; + + srslte_cell_t cell; + srslte_prach_cfg_t prach_cfg; + srslte_prach_t prach; + + pthread_mutex_t mutex; + pthread_cond_t cvar; + + cf_t *signal_buffer_rx; + + srslte::log* log_h; + mac_interface_phy *mac; + float max_prach_offset_us; + bool initiated; + uint32_t pending_tti; + int processed_tti; + bool running; + uint32_t nof_sf; + uint32_t sf_cnt; +}; +} +#endif // SRSENB_PRACH_WORKER_H diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h new file mode 100644 index 0000000..69a1199 --- /dev/null +++ b/srsenb/hdr/phy/txrx.h @@ -0,0 +1,76 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_TXRX_H +#define SRSENB_TXRX_H + +#include "srslte/common/log.h" +#include "srslte/common/threads.h" +#include "srslte/common/thread_pool.h" +#include "srslte/radio/radio.h" +#include "phch_common.h" +#include "prach_worker.h" + +namespace srsenb { + +typedef _Complex float cf_t; + +class txrx : public thread +{ +public: + txrx(); + bool init(srslte::radio *radio_handler, + srslte::thread_pool *_workers_pool, + phch_common *worker_com, + prach_worker *prach, + srslte::log *log_h, + uint32_t prio); + void stop(); + + const static int MUTEX_X_WORKER = 4; + +private: + + void run_thread(); + + srslte::radio *radio_h; + srslte::log *log_h; + srslte::thread_pool *workers_pool; + prach_worker *prach; + phch_common *worker_com; + + uint32_t tx_mutex_cnt; + uint32_t nof_tx_mutex; + + // Main system TTI counter + uint32_t tti; + + bool running; +}; + +} // namespace srsenb + +#endif // SRSENB_TXRX_H diff --git a/srsenb/hdr/upper/common_enb.h b/srsenb/hdr/upper/common_enb.h new file mode 100644 index 0000000..2e0b289 --- /dev/null +++ b/srsenb/hdr/upper/common_enb.h @@ -0,0 +1,184 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_COMMON_ENB_H +#define SRSENB_COMMON_ENB_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include + +namespace srsenb { + +#define ENB_METRICS_MAX_USERS 64 + +#define SRSENB_RRC_MAX_N_PLMN_IDENTITIES 6 + +#define SRSENB_N_SRB 3 +#define SRSENB_N_DRB 8 +#define SRSENB_N_RADIO_BEARERS 11 + +typedef enum{ + RB_ID_SRB0 = 0, + RB_ID_SRB1, + RB_ID_SRB2, + RB_ID_DRB1, + RB_ID_DRB2, + RB_ID_DRB3, + RB_ID_DRB4, + RB_ID_DRB5, + RB_ID_DRB6, + RB_ID_DRB7, + RB_ID_DRB8, + RB_ID_N_ITEMS, +}rb_id_t; +static const char rb_id_text[RB_ID_N_ITEMS][20] = { "SRB0", + "SRB1", + "SRB2", + "DRB1", + "DRB2", + "DRB3", + "DRB4", + "DRB5", + "DRB6", + "DRB7", + "DRB8"}; + +// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI +// 3GPP 36.306 Table 4.1.1 +#define SRSENB_MAX_BUFFER_SIZE_BITS 102048 +#define SRSENB_MAX_BUFFER_SIZE_BYTES 12756 +#define SRSENB_BUFFER_HEADER_OFFSET 1024 + +/****************************************************************************** + * Convert PLMN to BCD-coded MCC and MNC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_plmn_to_mccmnc(uint32_t plmn, uint16_t *mcc, uint16_t *mnc) +{ + uint8_t nibbles[6]; + nibbles[0] = (plmn & 0xF00000) >> 20; + nibbles[1] = (plmn & 0x0F0000) >> 16; + nibbles[2] = (plmn & 0x00F000) >> 12; + nibbles[3] = (plmn & 0x000F00) >> 8; + nibbles[4] = (plmn & 0x0000F0) >> 4; + nibbles[5] = (plmn & 0x00000F); + + *mcc = 0xF000; + *mnc = 0xF000; + *mcc |= nibbles[1] << 8; // MCC digit 1 + *mcc |= nibbles[0] << 4; // MCC digit 2 + *mcc |= nibbles[3]; // MCC digit 3 + + if(nibbles[2] == 0xF) { + // 2-digit MNC + *mnc |= 0x0F00; // MNC digit 1 + *mnc |= nibbles[5] << 4; // MNC digit 2 + *mnc |= nibbles[4]; // MNC digit 3 + } else { + // 3-digit MNC + *mnc |= nibbles[5] << 8; // MNC digit 1 + *mnc |= nibbles[4] << 4; // MNC digit 2 + *mnc |= nibbles[2] ; // MNC digit 3 + } +} + +/****************************************************************************** + * Convert BCD-coded MCC and MNC to PLMN. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn) +{ + uint8_t nibbles[6]; + nibbles[1] = (mcc & 0x0F00) >> 8; // MCC digit 1 + nibbles[0] = (mcc & 0x00F0) >> 4; // MCC digit 2 + nibbles[3] = (mcc & 0x000F); // MCC digit 3 + + if((mnc & 0xFF00) == 0xFF00) { + // 2-digit MNC + nibbles[2] = 0x0F; // MNC digit 1 + nibbles[5] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[4] = (mnc & 0x000F); // MNC digit 3 + } else { + // 3-digit MNC + nibbles[5] = (mnc & 0x0F00) >> 8; // MNC digit 1 + nibbles[4] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[2] = (mnc & 0x000F); // MNC digit 3 + } + + *plmn = 0x000000; + *plmn |= nibbles[0] << 20; + *plmn |= nibbles[1] << 16; + *plmn |= nibbles[2] << 12; + *plmn |= nibbles[3] << 8; + *plmn |= nibbles[4] << 4; + *plmn |= nibbles[5]; +} + +/****************************************************************************** + * Safe conversions between byte buffers and integer types. + * Note: these don't perform endian conversion - use e.g. htonl/ntohl if required + *****************************************************************************/ + +inline void uint8_to_uint32(uint8_t *buf, uint32_t *i) +{ + *i = (uint32_t)buf[0] << 24 | + (uint32_t)buf[1] << 16 | + (uint32_t)buf[2] << 8 | + (uint32_t)buf[3]; +} + +inline void uint32_to_uint8(uint32_t i, uint8_t *buf) +{ + buf[0] = (i >> 24) & 0xFF; + buf[1] = (i >> 16) & 0xFF; + buf[2] = (i >> 8) & 0xFF; + buf[3] = i & 0xFF; +} + +inline void uint8_to_uint16(uint8_t *buf, uint16_t *i) +{ + *i = (uint32_t)buf[0] << 8 | + (uint32_t)buf[1]; +} + +inline void uint16_to_uint8(uint16_t i, uint8_t *buf) +{ + buf[0] = (i >> 8) & 0xFF; + buf[1] = i & 0xFF; +} + +} // namespace srsenb + +#endif // SRSENB_COMMON_ENB_H diff --git a/srsenb/hdr/upper/gtpu.h b/srsenb/hdr/upper/gtpu.h new file mode 100644 index 0000000..4dc0558 --- /dev/null +++ b/srsenb/hdr/upper/gtpu.h @@ -0,0 +1,146 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "common_enb.h" +#include "srslte/common/threads.h" +#include "srslte/srslte.h" +#include "srslte/interfaces/enb_interfaces.h" + +#ifndef SRSENB_GTPU_H +#define SRSENB_GTPU_H + + +namespace srsenb { + +/**************************************************************************** + * GTPU Header + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version |PT | * | E | S |PN | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * 5 | TEID (1st Octet) | + * 6 | TEID (2nd Octet) | + * 7 | TEID (3rd Octet) | + * 8 | TEID (4th Octet) | + ***************************************************************************/ + +#define GTPU_HEADER_LEN 8 + +class gtpu + :public gtpu_interface_rrc + ,public gtpu_interface_pdcp + ,public thread +{ +public: + + gtpu(); + + bool init(std::string gtp_bind_addr_, std::string mme_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_, bool enable_mbsfn = false); + void stop(); + + // gtpu_interface_rrc + void add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, uint32_t *teid_in); + void rem_bearer(uint16_t rnti, uint32_t lcid); + void rem_user(uint16_t rnti); + + // gtpu_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + +private: + static const int THREAD_PRIO = 65; + static const int GTPU_PORT = 2152; + srslte::byte_buffer_pool *pool; + bool running; + bool run_enable; + + bool enable_mbsfn; + std::string gtp_bind_addr; + std::string mme_addr; + srsenb::pdcp_interface_gtpu *pdcp; + srslte::log *gtpu_log; + + // Class to create + class mch_thread : public thread { + public: + mch_thread() : initiated(false),running(false),run_enable(false),pool(NULL) {} + bool init(pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_); + void stop(); + private: + void run_thread(); + + bool initiated; + bool running; + bool run_enable; + + static const int MCH_THREAD_PRIO = 65; + + pdcp_interface_gtpu *pdcp; + srslte::log *gtpu_log; + int m1u_sd; + int lcid_counter; + + srslte::byte_buffer_pool *pool; + }; + + // MCH thread insteance + mch_thread mchthread; + + typedef struct{ + uint32_t teids_in[SRSENB_N_RADIO_BEARERS]; + uint32_t teids_out[SRSENB_N_RADIO_BEARERS]; + uint32_t spgw_addrs[SRSENB_N_RADIO_BEARERS]; + }bearer_map; + std::map rnti_bearers; + + // Socket file descriptors + int snk_fd; + int src_fd; + + //Threading + void run_thread(); + + pthread_mutex_t mutex; + + /**************************************************************************** + * TEID to RNIT/LCID helper functions + ***************************************************************************/ + void teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid); + void rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin); +}; + + +} // namespace srsenb + +#endif // SRSENB_GTPU_H diff --git a/srsenb/hdr/upper/pdcp.h b/srsenb/hdr/upper/pdcp.h new file mode 100644 index 0000000..9624958 --- /dev/null +++ b/srsenb/hdr/upper/pdcp.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/upper/pdcp.h" + +#ifndef SRSENB_PDCP_H +#define SRSENB_PDCP_H + +namespace srsenb { + +class pdcp : public pdcp_interface_rlc, + public pdcp_interface_gtpu, + public pdcp_interface_rrc +{ +public: + + void init(rlc_interface_pdcp *rlc_, rrc_interface_pdcp *rrc_, gtpu_interface_pdcp *gtpu_, srslte::log *pdcp_log_); + void stop(); + + // pdcp_interface_rlc + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} + + // pdcp_interface_rrc + void reset(uint16_t rnti); + void add_user(uint16_t rnti); + void rem_user(uint16_t rnti); + void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cnfg); + void config_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc_, + uint8_t *k_rrc_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_); + +private: + + class user_interface_rlc : public srsue::rlc_interface_pdcp + { + public: + uint16_t rnti; + srsenb::rlc_interface_pdcp *rlc; + // rlc_interface_pdcp + void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu); + bool rb_is_um(uint32_t lcid); + }; + + class user_interface_gtpu : public srsue::gw_interface_pdcp + { + public: + uint16_t rnti; + srsenb::gtpu_interface_pdcp *gtpu; + // gw_interface_pdcp + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} + }; + + class user_interface_rrc : public srsue::rrc_interface_pdcp + { + public: + uint16_t rnti; + srsenb::rrc_interface_pdcp *rrc; + // rrc_interface_pdcp + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu); + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu); + void write_pdu_pcch(srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu){} + std::string get_rb_name(uint32_t lcid); + }; + + class user_interface + { + public: + user_interface_rlc rlc_itf; + user_interface_gtpu gtpu_itf; + user_interface_rrc rrc_itf; + srslte::pdcp *pdcp; + }; + + void clear_user(user_interface *ue); + + std::map users; + + pthread_rwlock_t rwlock; + + rlc_interface_pdcp *rlc; + rrc_interface_pdcp *rrc; + gtpu_interface_pdcp *gtpu; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; +}; + +} + +#endif // SRSENB_PDCP_H diff --git a/srsenb/hdr/upper/rlc.h b/srsenb/hdr/upper/rlc.h new file mode 100644 index 0000000..05d4681 --- /dev/null +++ b/srsenb/hdr/upper/rlc.h @@ -0,0 +1,114 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/upper/rlc.h" + +#ifndef SRSENB_RLC_H +#define SRSENB_RLC_H + +typedef struct { + uint32_t lcid; + uint32_t plmn; + uint16_t mtch_stop; + uint8_t *payload; +}mch_service_t; + +namespace srsenb { + +class rlc : public rlc_interface_mac, + public rlc_interface_rrc, + public rlc_interface_pdcp +{ +public: + + void init(pdcp_interface_rlc *pdcp_, rrc_interface_rlc *rrc_, mac_interface_rlc *mac_, + srslte::mac_interface_timers *mac_timers_, srslte::log *log_h); + void stop(); + + // rlc_interface_rrc + void clear_buffer(uint16_t rnti); + void add_user(uint16_t rnti); + void rem_user(uint16_t rnti); + void add_bearer(uint16_t rnti, uint32_t lcid); + void add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg); + void add_bearer_mrb(uint16_t rnti, uint32_t lcid); + + // rlc_interface_pdcp + void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + bool rb_is_um(uint16_t rnti, uint32_t lcid); + std::string get_rb_name(uint32_t lcid); + + // rlc_interface_mac + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload); + void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes); + void read_pdu_pcch(uint8_t *payload, uint32_t buffer_size); + +private: + + class user_interface : public srsue::pdcp_interface_rlc, + public srsue::rrc_interface_rlc, + public srsue::ue_interface + { + public: + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu); + void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu); + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu); + void write_pdu_pcch(srslte::byte_buffer_t *sdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu){} + void max_retx_attempted(); + std::string get_rb_name(uint32_t lcid); + uint16_t rnti; + + srsenb::pdcp_interface_rlc *pdcp; + srsenb::rrc_interface_rlc *rrc; + srslte::rlc *rlc; + srsenb::rlc *parent; + }; + + void clear_user(user_interface *ue); + + const static int RLC_TX_QUEUE_LEN = 512; + + pthread_rwlock_t rwlock; + + std::map users; + std::vector mch_services; + + mac_interface_rlc *mac; + pdcp_interface_rlc *pdcp; + rrc_interface_rlc *rrc; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srslte::mac_interface_timers *mac_timers; +}; + +} + +#endif // SRSENB_RLC_H diff --git a/srsenb/hdr/upper/rrc.h b/srsenb/hdr/upper/rrc.h new file mode 100644 index 0000000..26a60e9 --- /dev/null +++ b/srsenb/hdr/upper/rrc.h @@ -0,0 +1,383 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_RRC_H +#define SRSENB_RRC_H + +#include +#include +#include "srslte/common/buffer_pool.h" +#include "srslte/common/common.h" +#include "srslte/common/block_queue.h" +#include "srslte/common/threads.h" +#include "srslte/common/timeout.h" +#include "srslte/common/log.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "common_enb.h" +#include "rrc_metrics.h" + +namespace srsenb { + +typedef struct { + uint32_t period; + LIBLTE_RRC_DSR_TRANS_MAX_ENUM dsr_max; + uint32_t nof_prb; + uint32_t sf_mapping[80]; + uint32_t nof_subframes; +} rrc_cfg_sr_t; + +typedef enum { + RRC_CFG_CQI_MODE_PERIODIC = 0, + RRC_CFG_CQI_MODE_APERIODIC, + RRC_CFG_CQI_MODE_N_ITEMS +} rrc_cfg_cqi_mode_t; + +static const char rrc_cfg_cqi_mode_text[RRC_CFG_CQI_MODE_N_ITEMS][20] = {"periodic", "aperiodic"}; + +typedef struct { + uint32_t sf_mapping[80]; + uint32_t nof_subframes; + uint32_t nof_prb; + uint32_t period; + bool simultaneousAckCQI; + rrc_cfg_cqi_mode_t mode; +} rrc_cfg_cqi_t; + +typedef struct { + bool configured; + LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT lc_cfg; + LIBLTE_RRC_PDCP_CONFIG_STRUCT pdcp_cfg; + LIBLTE_RRC_RLC_CONFIG_STRUCT rlc_cfg; +} rrc_cfg_qci_t; + +#define MAX_NOF_QCI 10 + +typedef struct { + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB]; + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT mac_cnfg; + + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cfg; + LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT antenna_info; + LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM pdsch_cfg; + rrc_cfg_sr_t sr_cfg; + rrc_cfg_cqi_t cqi_cfg; + rrc_cfg_qci_t qci_cfg[MAX_NOF_QCI]; + srslte_cell_t cell; + bool enable_mbsfn; + uint32_t inactivity_timeout_ms; +}rrc_cfg_t; + +static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", + "WAIT FOR CON SETUP COMPLETE", + "WAIT FOR SECURITY MODE COMPLETE", + "WAIT FOR UE CAPABILITIY INFORMATION", + "WAIT FOR CON RECONF COMPLETE", + "RRC CONNECTED" + "RELEASE REQUEST"}; + +class rrc : public rrc_interface_pdcp, + public rrc_interface_mac, + public rrc_interface_rlc, + public rrc_interface_s1ap, + public thread +{ +public: + + rrc() : act_monitor(this), cnotifier(NULL), running(false), nof_si_messages(0) { + users.clear(); + pending_paging.clear(); + + pool = NULL; + phy = NULL; + mac = NULL; + rlc = NULL; + pdcp = NULL; + gtpu = NULL; + s1ap = NULL; + rrc_log = NULL; + + bzero(&sr_sched, sizeof(sr_sched)); + bzero(&cqi_sched, sizeof(cqi_sched)); + bzero(&cfg, sizeof(cfg)); + bzero(&sib2, sizeof(sib2)); + bzero(&paging_mutex, sizeof(paging_mutex)); + + } + + void init(rrc_cfg_t *cfg, + phy_interface_rrc *phy, + mac_interface_rrc *mac, + rlc_interface_rrc *rlc, + pdcp_interface_rrc *pdcp, + s1ap_interface_rrc *s1ap, + gtpu_interface_rrc *gtpu, + srslte::log *log_rrc); + + void stop(); + void get_metrics(rrc_metrics_t &m); + + // rrc_interface_mac + void rl_failure(uint16_t rnti); + void add_user(uint16_t rnti); + void upd_user(uint16_t new_rnti, uint16_t old_rnti); + void set_activity_user(uint16_t rnti); + bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len); + + // rrc_interface_rlc + void read_pdu_bcch_dlsch(uint32_t sib_idx, uint8_t *payload); + void read_pdu_pcch(uint8_t *payload, uint32_t buffer_size); + void max_retx_attempted(uint16_t rnti); + + // rrc_interface_s1ap + void write_dl_info(uint16_t rnti, srslte::byte_buffer_t *sdu); + void release_complete(uint16_t rnti); + bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg); + bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); + bool release_erabs(uint32_t rnti); + void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID); + + // rrc_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + + void parse_sibs(); + uint32_t get_nof_users(); + + // Notifier for user connect + class connect_notifier { + public: + virtual void user_connected(uint16_t rnti) = 0; + }; + void set_connect_notifer(connect_notifier *cnotifier); + + class activity_monitor : public thread + { + public: + activity_monitor(rrc* parent_); + void stop(); + private: + rrc* parent; + bool running; + void run_thread(); + }; + + class ue + { + public: + ue(); + bool is_connected(); + bool is_idle(); + bool is_timeout(); + void set_activity(); + + uint32_t rl_failure(); + + rrc_state_t get_state(); + + void send_connection_setup(bool is_setup = true); + void send_connection_reest(); + void send_connection_release(); + void send_connection_reest_rej(); + void send_connection_reconf(srslte::byte_buffer_t *sdu); + void send_connection_reconf_new_bearer(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + void send_connection_reconf_upd(srslte::byte_buffer_t *pdu); + void send_security_mode_command(); + void send_ue_cap_enquiry(); + void parse_ul_dcch(uint32_t lcid, srslte::byte_buffer_t* pdu); + + void handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg); + void handle_rrc_con_reest_req(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *msg); + void handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu); + void handle_rrc_reconf_complete(LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu); + void handle_security_mode_complete(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *msg); + void handle_security_mode_failure(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *msg); + void handle_ue_cap_info(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *msg); + + void set_bitrates(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *rates); + void set_security_capabilities(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *caps); + void set_security_key(uint8_t* key, uint32_t length); + + bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *e); + bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + void setup_erab(uint8_t id, LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *qos, + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *addr, uint32_t teid_out, + LIBLTE_S1AP_NAS_PDU_STRUCT *nas_pdu); + bool release_erabs(); + + void notify_s1ap_ue_ctxt_setup_complete(); + void notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + + int sr_allocate(uint32_t period, uint32_t *I_sr, uint32_t *N_pucch_sr); + void sr_get(uint32_t *I_sr, uint32_t *N_pucch_sr); + int sr_free(); + + int cqi_allocate(uint32_t period, uint32_t *pmi_idx, uint32_t *n_pucch); + void cqi_get(uint32_t *pmi_idx, uint32_t *n_pucch); + int cqi_free(); + + void send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg); + void send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, srslte::byte_buffer_t *pdu = NULL); + + uint16_t rnti; + rrc *parent; + + bool connect_notified; + + private: + srslte::byte_buffer_pool *pool; + + struct timeval t_last_activity; + + LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM establishment_cause; + + // S-TMSI for this UE + bool has_tmsi; + uint32_t m_tmsi; + uint8_t mmec; + + uint32_t rlf_cnt; + uint8_t transaction_id; + rrc_state_t state; + + std::map srbs; + std::map drbs; + + uint8_t k_enb[32]; // Provided by MME + uint8_t k_rrc_enc[32]; + uint8_t k_rrc_int[32]; + uint8_t k_up_enc[32]; + uint8_t k_up_int[32]; // Not used: only for relay nodes (3GPP 33.401 Annex A.7) + + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT bitrates; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT security_capabilities; + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT eutra_capabilities; + + typedef struct { + uint8_t id; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT qos_params; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT address; + uint32_t teid_out; + uint32_t teid_in; + }erab_t; + std::map erabs; + int sr_sched_sf_idx; + int sr_sched_prb_idx; + bool sr_allocated; + uint32_t sr_N_pucch; + uint32_t sr_I; + uint32_t cqi_pucch; + uint32_t cqi_idx; + bool cqi_allocated; + int cqi_sched_sf_idx; + int cqi_sched_prb_idx; + int get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drbid); + }; + + +private: + + std::map users; + + std::map pending_paging; + + activity_monitor act_monitor; + + LIBLTE_BYTE_MSG_STRUCT sib_buffer[LIBLTE_RRC_MAX_SIB]; + + // user connect notifier + connect_notifier *cnotifier; + + void process_release_complete(uint16_t rnti); + void process_rl_failure(uint16_t rnti); + void rem_user(uint16_t rnti); + uint32_t generate_sibs(); + void configure_mbsfn_sibs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + + void config_mac(); + void parse_ul_dcch(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + void parse_ul_ccch(uint16_t rnti, srslte::byte_buffer_t *pdu); + void configure_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + srslte::byte_buffer_pool *pool; + srslte::bit_buffer_t bit_buf; + srslte::bit_buffer_t bit_buf_paging; + srslte::byte_buffer_t erab_info; + + phy_interface_rrc *phy; + mac_interface_rrc *mac; + rlc_interface_rrc *rlc; + pdcp_interface_rrc *pdcp; + gtpu_interface_rrc *gtpu; + s1ap_interface_rrc *s1ap; + srslte::log *rrc_log; + + typedef struct{ + uint16_t rnti; + uint32_t lcid; + srslte::byte_buffer_t* pdu; + }rrc_pdu; + + const static uint32_t LCID_EXIT = 0xffff0000; + const static uint32_t LCID_REM_USER = 0xffff0001; + const static uint32_t LCID_REL_USER = 0xffff0002; + const static uint32_t LCID_RLF_USER = 0xffff0003; + const static uint32_t LCID_ACT_USER = 0xffff0004; + + bool running; + static const int RRC_THREAD_PRIO = 65; + srslte::block_queue rx_pdu_queue; + + typedef struct { + uint32_t nof_users[100][80]; + } sr_sched_t; + + sr_sched_t sr_sched; + sr_sched_t cqi_sched; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + bool enable_mbms; + rrc_cfg_t cfg; + uint32_t nof_si_messages; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + + void run_thread(); + void rem_user_thread(uint16_t rnti); + pthread_mutex_t user_mutex; + + pthread_mutex_t paging_mutex; +}; + +} // namespace srsenb + +#endif // SRSENB_RRC_H diff --git a/srsenb/hdr/upper/rrc_metrics.h b/srsenb/hdr/upper/rrc_metrics.h new file mode 100644 index 0000000..7c388da --- /dev/null +++ b/srsenb/hdr/upper/rrc_metrics.h @@ -0,0 +1,58 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_RRC_METRICS_H +#define SRSENB_RRC_METRICS_H + +#include "common_enb.h" + +namespace srsenb { + +typedef enum{ + RRC_STATE_IDLE = 0, + RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE, + RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE, + RRC_STATE_WAIT_FOR_UE_CAP_INFO, + RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE, + RRC_STATE_REGISTERED, + RRC_STATE_RELEASE_REQUEST, + RRC_STATE_N_ITEMS, +}rrc_state_t; + +struct rrc_ue_metrics_t +{ + rrc_state_t state; +}; + +struct rrc_metrics_t +{ + uint16_t n_ues; + rrc_ue_metrics_t ues[ENB_METRICS_MAX_USERS]; +}; + +} // namespace srsenb + +#endif // SRSENB_RRC_METRICS_H diff --git a/srsenb/hdr/upper/s1ap.h b/srsenb/hdr/upper/s1ap.h new file mode 100644 index 0000000..b686b65 --- /dev/null +++ b/srsenb/hdr/upper/s1ap.h @@ -0,0 +1,149 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_S1AP_H +#define SRSENB_S1AP_H + +#include + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/common/threads.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "common_enb.h" + +#include "srslte/asn1/liblte_s1ap.h" +#include "s1ap_metrics.h" + +namespace srsenb { + +typedef struct { + uint32_t enb_id; // 20-bit id (lsb bits) + uint8_t cell_id; // 8-bit cell id + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string mme_addr; + std::string gtp_bind_addr; + std::string s1c_bind_addr; + std::string enb_name; +}s1ap_args_t; + +typedef struct { + uint32_t rnti; + uint32_t eNB_UE_S1AP_ID; + uint32_t MME_UE_S1AP_ID; + bool release_requested; + uint16_t stream_id; +}ue_ctxt_t; + +class s1ap + :public s1ap_interface_rrc + ,public thread +{ +public: + bool init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_); + void stop(); + void get_metrics(s1ap_metrics_t &m); + + void run_thread(); + + // RRC interface + void initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu); + void initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec); + void write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu); + bool user_exists(uint16_t rnti); + bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio); + void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res); + void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res); + //void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps); + +private: + static const int S1AP_THREAD_PRIO = 65; + static const int MME_PORT = 36412; + static const int ADDR_FAMILY = AF_INET; + static const int SOCK_TYPE = SOCK_STREAM; + static const int PROTO = IPPROTO_SCTP; + static const int PPID = 18; + static const int NONUE_STREAM_ID = 0; + + rrc_interface_s1ap *rrc; + s1ap_args_t args; + srslte::log *s1ap_log; + srslte::byte_buffer_pool *pool; + + bool mme_connected; + bool running; + int socket_fd; // SCTP socket file descriptor + struct sockaddr_in mme_addr; // MME address + uint32_t next_eNB_UE_S1AP_ID; // Next ENB-side UE identifier + uint16_t next_ue_stream_id; // Next UE SCTP stream identifier + + // Protocol IEs sent with every UL S1AP message + LIBLTE_S1AP_TAI_STRUCT tai; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eutran_cgi; + + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT s1setupresponse; + + std::map ue_ctxt_map; + std::map enbid_to_rnti_map; + + void build_tai_cgi(); + bool connect_mme(); + bool setup_s1(); + + bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu); + bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg); + bool handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + bool handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg); + bool handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg); + + bool handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg); + bool handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg); + bool handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg); + bool handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg); + bool handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg); + bool handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); + + bool send_initialuemessage(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu, bool has_tmsi, uint32_t m_tmsi=0, uint8_t mmec=0); + bool send_ulnastransport(uint16_t rnti, srslte::byte_buffer_t *pdu); + bool send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause); + bool send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id); + bool send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_); + bool send_initial_ctxt_setup_failure(uint16_t rnti); + bool send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_); + //bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) + + bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id); + std::string get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c); + +}; + +} // namespace srsenb + + +#endif // SRSENB_S1AP_H diff --git a/srsenb/hdr/upper/s1ap_metrics.h b/srsenb/hdr/upper/s1ap_metrics.h new file mode 100644 index 0000000..b73b0af --- /dev/null +++ b/srsenb/hdr/upper/s1ap_metrics.h @@ -0,0 +1,46 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSENB_S1AP_METRICS_H +#define SRSENB_S1AP_METRICS_H + + +namespace srsenb { + +typedef enum{ + S1AP_ATTACHING = 0, // Attempting to create S1 connection + S1AP_READY, // S1 connected + S1AP_ERROR // Failure +}S1AP_STATUS_ENUM; + +struct s1ap_metrics_t +{ + S1AP_STATUS_ENUM status; +}; + +} // namespace srsenb + +#endif // SRSENB_S1AP_METRICS_H diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example new file mode 100644 index 0000000..e819cf4 --- /dev/null +++ b/srsenb/rr.conf.example @@ -0,0 +1,50 @@ +mac_cnfg = +{ + phr_cnfg = + { + dl_pathloss_change = "3dB"; // Valid: 1, 3, 6 or INFINITY + periodic_phr_timer = 50; + prohibit_phr_timer = 0; + }; + ulsch_cnfg = + { + max_harq_tx = 4; + periodic_bsr_timer = 20; // in ms + retx_bsr_timer = 320; // in ms + }; + + time_alignment_timer = -1; // -1 is infinity +}; + +phy_cnfg = +{ + phich_cnfg = + { + duration = "Normal"; + resources = "1/6"; + }; + + pusch_cnfg_ded = + { + beta_offset_ack_idx = 6; + beta_offset_ri_idx = 6; + beta_offset_cqi_idx = 6; + }; + + // PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource. + sched_request_cnfg = + { + dsr_trans_max = 64; + period = 20; // in ms + subframe = [1]; // vector of subframe indices allowed for SR transmissions + nof_prb = 2; // number of PRBs on each extreme used for SR (total prb is twice this number) + }; + cqi_report_cnfg = + { + mode = "periodic"; + simultaneousAckCQI = true; + period = 40; // in ms + subframe = [0]; + nof_prb = 2; + }; +}; diff --git a/srsenb/sib.conf.example b/srsenb/sib.conf.example new file mode 100644 index 0000000..dded32c --- /dev/null +++ b/srsenb/sib.conf.example @@ -0,0 +1,127 @@ +sib1 = +{ + intra_freq_reselection = "Allowed"; + q_rx_lev_min = -130; + //p_max = 3; + cell_barred = "Not Barred" + si_window_length = 20; + sched_info = + ( + { + si_periodicity = 16; + si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13). + // Leave empty or commented to just scheduler sib2 + } + ); + system_info_value_tag = 0; +}; + +sib2 = +{ + rr_config_common_sib = + { + rach_cnfg = + { + num_ra_preambles = 52; + preamble_init_rx_target_pwr = -104; + pwr_ramping_step = 6; // in dB + preamble_trans_max = 10; + ra_resp_win_size = 10; // in ms + mac_con_res_timer = 64; // in ms + max_harq_msg3_tx = 4; + }; + bcch_cnfg = + { + modification_period_coeff = 16; // in ms + }; + pcch_cnfg = + { + default_paging_cycle = 32; // in rf + nB = "1"; + }; + prach_cnfg = + { + root_sequence_index = 128; + prach_cnfg_info = + { + high_speed_flag = false; + prach_config_index = 3; + prach_freq_offset = 2; + zero_correlation_zone_config = 5; + }; + }; + pdsch_cnfg = + { + p_b = 0; + rs_power = 0; + }; + pusch_cnfg = + { + n_sb = 1; + hopping_mode = "inter-subframe"; + pusch_hopping_offset = 2; + enable_64_qam = false; // 64QAM PUSCH is not currently enabled + ul_rs = + { + cyclic_shift = 0; + group_assignment_pusch = 0; + group_hopping_enabled = false; + sequence_hopping_enabled = false; + }; + }; + pucch_cnfg = + { + delta_pucch_shift = 2; + n_rb_cqi = 2; + n_cs_an = 0; + n1_pucch_an = 12; + }; + ul_pwr_ctrl = + { + p0_nominal_pusch = -85; + alpha = 0.7; + p0_nominal_pucch = -107; + delta_flist_pucch = + { + format_1 = 0; + format_1b = 3; + format_2 = 1; + format_2a = 2; + format_2b = 2; + }; + delta_preamble_msg3 = 8; + }; + ul_cp_length = "Normal"; + }; + + ue_timers_and_constants = + { + t300 = 2000; // in ms + t301 = 100; // in ms + t310 = 1000; // in ms + n310 = 1; + t311 = 1000; // in ms + n311 = 1; + }; + + freqInfo = + { + ul_carrier_freq_present = true; + ul_bw_present = true; + additional_spectrum_emission = 1; + }; + + mbsfnSubframeConfigList = + { + radioframeAllocationPeriod = "1"; + subframeAllocationNumFrames = "1"; + radioframeAllocationOffset = 0; + subframeAllocation = 63; + + }; + + mbsfnSubframeConfigListLength = 0; + + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. +}; + diff --git a/srsenb/sib.conf.mbsfn.example b/srsenb/sib.conf.mbsfn.example new file mode 100644 index 0000000..3fe2ddc --- /dev/null +++ b/srsenb/sib.conf.mbsfn.example @@ -0,0 +1,153 @@ +sib1 = +{ + intra_freq_reselection = "Allowed"; + q_rx_lev_min = -130; + //p_max = 3; + cell_barred = "Not Barred" + si_window_length = 20; + sched_info = + ( + { + si_periodicity = 16; + si_mapping_info = [13]; // comma-separated array of SIB-indexes (from 3 to 13). + // Leave empty or commented to just scheduler sib2 + } + ); + system_info_value_tag = 0; +}; + +sib2 = +{ + rr_config_common_sib = + { + rach_cnfg = + { + num_ra_preambles = 52; + preamble_init_rx_target_pwr = -108; + pwr_ramping_step = 6; // in dB + preamble_trans_max = 7; + ra_resp_win_size = 10; // in ms + mac_con_res_timer = 64; // in ms + max_harq_msg3_tx = 4; + }; + bcch_cnfg = + { + modification_period_coeff = 16; // in ms + }; + pcch_cnfg = + { + default_paging_cycle = 32; // in rf + nB = "1"; + }; + prach_cnfg = + { + root_sequence_index = 128; + prach_cnfg_info = + { + high_speed_flag = false; + prach_config_index = 3; + prach_freq_offset = 0; + zero_correlation_zone_config = 11; + }; + }; + pdsch_cnfg = + { + p_b = 0; + rs_power = -4; + }; + pusch_cnfg = + { + n_sb = 1; + hopping_mode = "inter-subframe"; + pusch_hopping_offset = 2; + enable_64_qam = false; + ul_rs = + { + cyclic_shift = 0; + group_assignment_pusch = 0; + group_hopping_enabled = false; + sequence_hopping_enabled = false; + }; + }; + pucch_cnfg = + { + delta_pucch_shift = 1; + n_rb_cqi = 1; + n_cs_an = 0; + n1_pucch_an = 2; + }; + ul_pwr_ctrl = + { + p0_nominal_pusch = -108; + alpha = 1.0; + p0_nominal_pucch = -88; + delta_flist_pucch = + { + format_1 = 2; + format_1b = 3; + format_2 = 0; + format_2a = 0; + format_2b = 0; + }; + delta_preamble_msg3 = 4; + }; + ul_cp_length = "Normal"; + }; + + ue_timers_and_constants = + { + t300 = 2000; // in ms + t301 = 100; // in ms + t310 = 1000; // in ms + n310 = 1; + t311 = 1000; // in ms + n311 = 1; + }; + + freqInfo = + { + ul_carrier_freq_present = true; + ul_bw_present = true; + additional_spectrum_emission = 1; + }; + + + + mbsfnSubframeConfigList = + { + radioframeAllocationPeriod = "1"; + subframeAllocationNumFrames = "1"; + radioframeAllocationOffset = 0; + subframeAllocation = 63; + + }; + + mbsfnSubframeConfigListLength = 1; + + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. +}; + +sib13 = +{ + mbsfn_notification_config = + { + mbsfn_notification_repetition_coeff = "2"; + mbsfn_notification_offset = 0; + mbsfn_notification_sf_index = 1; + }; + mbsfn_area_info_list_size = 1; + mbsfn_area_info_list = + { + non_mbsfn_region_length = "2"; + mcch_repetition_period = "64"; + mcch_modification_period = "512"; + signalling_mcs = "2"; + mbsfn_area_id = 1; + notification_indicator = 0; + mcch_offset = 0; + sf_alloc_info = 32; + }; + + +}; + diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt new file mode 100644 index 0000000..ef66c03 --- /dev/null +++ b/srsenb/src/CMakeLists.txt @@ -0,0 +1,48 @@ + +add_subdirectory(phy) +add_subdirectory(mac) +add_subdirectory(upper) + + +# Link libstdc++ and libgcc +if(BUILD_STATIC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") +endif(BUILD_STATIC) + + +if (RPATH) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + + +add_executable(srsenb main.cc enb.cc parser.cc enb_cfg_parser.cc metrics_stdout.cc) +target_link_libraries(srsenb srsenb_upper + srsenb_mac + srsenb_phy + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + +if (RPATH) + set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") +endif (RPATH) + +install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILDENB_CMD} STREQUAL "") + message(STATUS "Added custom post-build-ENB command: ${BUILDENB_CMD}") + add_custom_command(TARGET srsenb POST_BUILD COMMAND ${BUILDENB_CMD}) +else(NOT ${BUILDENB_CMD} STREQUAL "") + message(STATUS "No post-build-ENB command defined") +endif (NOT ${BUILDENB_CMD} STREQUAL "") + +install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc new file mode 100644 index 0000000..911fb1d --- /dev/null +++ b/srsenb/src/enb.cc @@ -0,0 +1,345 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srsenb/hdr/enb.h" + +namespace srsenb { + +enb* enb::instance = NULL; +pthread_mutex_t enb_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +enb* enb::get_instance(void) +{ + pthread_mutex_lock(&enb_instance_mutex); + if(NULL == instance) { + instance = new enb(); + } + pthread_mutex_unlock(&enb_instance_mutex); + return(instance); +} +void enb::cleanup(void) +{ + srslte_dft_exit(); + srslte::byte_buffer_pool::cleanup(); + pthread_mutex_lock(&enb_instance_mutex); + if(NULL != instance) { + delete instance; + instance = NULL; + } + pthread_mutex_unlock(&enb_instance_mutex); +} + +enb::enb() : started(false) { + srslte_dft_load(); + pool = srslte::byte_buffer_pool::get_instance(ENB_POOL_SIZE); + + logger = NULL; + args = NULL; + + bzero(&rf_metrics, sizeof(rf_metrics)); +} + +enb::~enb() +{ + for (uint32_t i = 0; i < phy_log.size(); i++) { + delete (phy_log[i]); + } +} + +bool enb::init(all_args_t *args_) +{ + args = args_; + + if (!args->log.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args->log.filename, args->log.file_max_size); + logger_file.log("\n\n"); + logger = &logger_file; + } + + rf_log.init("RF ", logger); + + // Create array of pointers to phy_logs + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + srslte::log_filter *mylog = new srslte::log_filter; + char tmp[16]; + sprintf(tmp, "PHY%d",i); + mylog->init(tmp, logger, true); + phy_log.push_back(mylog); + } + mac_log.init("MAC ", logger, true); + rlc_log.init("RLC ", logger); + pdcp_log.init("PDCP", logger); + rrc_log.init("RRC ", logger); + gtpu_log.init("GTPU", logger); + s1ap_log.init("S1AP", logger); + + pool_log.init("POOL", logger); + pool_log.set_level(srslte::LOG_LEVEL_ERROR); + pool->set_log(&pool_log); + + // Init logs + rf_log.set_level(srslte::LOG_LEVEL_INFO); + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + ((srslte::log_filter*) phy_log[i])->set_level(level(args->log.phy_level)); + } + mac_log.set_level(level(args->log.mac_level)); + rlc_log.set_level(level(args->log.rlc_level)); + pdcp_log.set_level(level(args->log.pdcp_level)); + rrc_log.set_level(level(args->log.rrc_level)); + gtpu_log.set_level(level(args->log.gtpu_level)); + s1ap_log.set_level(level(args->log.s1ap_level)); + + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + ((srslte::log_filter*) phy_log[i])->set_hex_limit(args->log.phy_hex_limit); + } + mac_log.set_hex_limit(args->log.mac_hex_limit); + rlc_log.set_hex_limit(args->log.rlc_hex_limit); + pdcp_log.set_hex_limit(args->log.pdcp_hex_limit); + rrc_log.set_hex_limit(args->log.rrc_hex_limit); + gtpu_log.set_hex_limit(args->log.gtpu_hex_limit); + s1ap_log.set_hex_limit(args->log.s1ap_hex_limit); + + // Set up pcap and trace + if(args->pcap.enable) + { + mac_pcap.open(args->pcap.filename.c_str()); + mac.start_pcap(&mac_pcap); + } + + // Init layers + + /* Start Radio */ + char *dev_name = NULL; + if (args->rf.device_name.compare("auto")) { + dev_name = (char*) args->rf.device_name.c_str(); + } + + char *dev_args = NULL; + if (args->rf.device_args.compare("auto")) { + dev_args = (char*) args->rf.device_args.c_str(); + } + + if(!radio.init(dev_args, dev_name, args->enb.nof_ports)) + { + printf("Failed to find device %s with args %s\n", + args->rf.device_name.c_str(), args->rf.device_args.c_str()); + return false; + } + + // Set RF options + if (args->rf.time_adv_nsamples.compare("auto")) { + radio.set_tx_adv(atoi(args->rf.time_adv_nsamples.c_str())); + } + if (args->rf.burst_preamble.compare("auto")) { + radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str())); + } + + radio.set_manual_calibration(&args->rf_cal); + + radio.set_rx_gain(args->rf.rx_gain); + radio.set_tx_gain(args->rf.tx_gain); + + if (args->rf.dl_freq < 0) { + args->rf.dl_freq = 1e6*srslte_band_fd(args->rf.dl_earfcn); + if (args->rf.dl_freq < 0) { + fprintf(stderr, "Error getting DL frequency for EARFCN=%d\n", args->rf.dl_earfcn); + return false; + } + } + if (args->rf.ul_freq < 0) { + if (args->rf.ul_earfcn == 0) { + args->rf.ul_earfcn = srslte_band_ul_earfcn(args->rf.dl_earfcn); + } + args->rf.ul_freq = 1e6*srslte_band_fu(args->rf.ul_earfcn); + if (args->rf.ul_freq < 0) { + fprintf(stderr, "Error getting UL frequency for EARFCN=%d\n", args->rf.dl_earfcn); + return false; + } + } + ((srslte::log_filter*) phy_log[0])->console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz\n", args->rf.dl_freq/1e6, args->rf.ul_freq/1e6); + + radio.set_tx_freq(args->rf.dl_freq); + radio.set_rx_freq(args->rf.ul_freq); + + radio.register_error_handler(rf_msg); + + srslte_cell_t cell_cfg; + phy_cfg_t phy_cfg; + rrc_cfg_t rrc_cfg; + + if (parse_cell_cfg(args, &cell_cfg)) { + fprintf(stderr, "Error parsing Cell configuration\n"); + return false; + } + if (parse_sibs(args, &rrc_cfg, &phy_cfg)) { + fprintf(stderr, "Error parsing SIB configuration\n"); + return false; + } + if (parse_rr(args, &rrc_cfg)) { + fprintf(stderr, "Error parsing Radio Resources configuration\n"); + return false; + } + if (parse_drb(args, &rrc_cfg)) { + fprintf(stderr, "Error parsing DRB configuration\n"); + return false; + } + + uint32_t prach_freq_offset = rrc_cfg.sibs[1].sib.sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset; + + if (prach_freq_offset + 6 > cell_cfg.nof_prb) { + fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", prach_freq_offset); + return false; + } + + if (prach_freq_offset < rrc_cfg.cqi_cfg.nof_prb || prach_freq_offset < rrc_cfg.sr_cfg.nof_prb ) { + fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d lower than CQI offset: %d or SR offset: %d\n", + prach_freq_offset, rrc_cfg.cqi_cfg.nof_prb, rrc_cfg.sr_cfg.nof_prb); + return false; + } + + rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer; + rrc_cfg.enable_mbsfn = args->expert.enable_mbsfn; + + // Copy cell struct to rrc and phy + memcpy(&rrc_cfg.cell, &cell_cfg, sizeof(srslte_cell_t)); + memcpy(&phy_cfg.cell, &cell_cfg, sizeof(srslte_cell_t)); + + // Init all layers + phy.init(&args->expert.phy, &phy_cfg, &radio, &mac, phy_log); + mac.init(&args->expert.mac, &cell_cfg, &phy, &rlc, &rrc, &mac_log); + rlc.init(&pdcp, &rrc, &mac, &mac, &rlc_log); + pdcp.init(&rlc, &rrc, >pu, &pdcp_log); + rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, >pu, &rrc_log); + s1ap.init(args->enb.s1ap, &rrc, &s1ap_log); + gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, &pdcp, >pu_log, args->expert.enable_mbsfn); + + started = true; + return true; +} + +void enb::pregenerate_signals(bool enable) +{ + //phy.enable_pregen_signals(enable); +} + +void enb::stop() +{ + if(started) + { + s1ap.stop(); + gtpu.stop(); + phy.stop(); + mac.stop(); + usleep(50000); + + rlc.stop(); + pdcp.stop(); + rrc.stop(); + + usleep(10000); + if(args->pcap.enable) + { + mac_pcap.close(); + } + radio.stop(); + started = false; + } +} + +void enb::start_plot() { + phy.start_plot(); +} + +void enb::print_pool() { + srslte::byte_buffer_pool::get_instance()->print_all_buffers(); +} + +bool enb::get_metrics(enb_metrics_t &m) +{ + m.rf = rf_metrics; + bzero(&rf_metrics, sizeof(rf_metrics_t)); + rf_metrics.rf_error = false; // Reset error flag + + phy.get_metrics(m.phy); + mac.get_metrics(m.mac); + rrc.get_metrics(m.rrc); + s1ap.get_metrics(m.s1ap); + + m.running = started; + return true; +} + +void enb::rf_msg(srslte_rf_error_t error) +{ + enb *u = enb::get_instance(); + u->handle_rf_msg(error); +} + +void enb::handle_rf_msg(srslte_rf_error_t error) +{ + if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + rf_log.warning("Overflow\n"); + }else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) { + rf_metrics.rf_u++; + rf_metrics.rf_error = true; + rf_log.warning("Underflow\n"); + } else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) { + rf_metrics.rf_l++; + rf_metrics.rf_error = true; + rf_log.warning("Late\n"); + } else if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OTHER) { + std::string str(error.msg); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); + str.push_back('\n'); + rf_log.info("%s\n", str.c_str()); + } +} + +srslte::LOG_LEVEL_ENUM enb::level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +} // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc new file mode 100644 index 0000000..b4a4e67 --- /dev/null +++ b/srsenb/src/enb_cfg_parser.cc @@ -0,0 +1,1316 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/asn1/liblte_common.h" +#include "srslte/asn1/liblte_rrc.h" +#include "srsenb/hdr/cfg_parser.h" +#include "srslte/srslte.h" + +#include "srsenb/hdr/parser.h" +#include "enb_cfg_parser.h" + +namespace srsenb { + +int enb::parse_cell_cfg(all_args_t *args, srslte_cell_t *cell) { + cell->id = args->enb.pci; + cell->cp = SRSLTE_CP_NORM; + cell->nof_ports = args->enb.nof_ports; + cell->nof_prb = args->enb.n_prb; + + LIBLTE_RRC_PHICH_CONFIG_STRUCT phichcfg; + + parser::section phy_cnfg("phy_cnfg"); + parser::section phich_cnfg("phich_cnfg"); + phy_cnfg.add_subsection(&phich_cnfg); + phich_cnfg.add_field( + new parser::field_enum_str + ("duration", &phichcfg.dur, liblte_rrc_phich_duration_text, LIBLTE_RRC_PHICH_DURATION_N_ITEMS) + ); + phich_cnfg.add_field( + new parser::field_enum_str + ("resources", &phichcfg.res, liblte_rrc_phich_resource_text, LIBLTE_RRC_PHICH_RESOURCE_N_ITEMS) + ); + parser::parse_section(args->enb_files.rr_config, &phy_cnfg); + + cell->phich_length = (srslte_phich_length_t) (int) phichcfg.dur; + cell->phich_resources = (srslte_phich_resources_t) (int) phichcfg.res; + + if (!srslte_cell_isvalid(cell)) { + fprintf(stderr, "Invalid cell parameters: nof_prb=%d, cell_id=%d\n", args->enb.n_prb, args->enb.s1ap.cell_id); + return -1; + } + + return 0; +} + +int field_sched_info::parse(libconfig::Setting &root) +{ + data->N_sched_info = root.getLength(); + for (uint32_t i=0;iN_sched_info;i++) { + uint32_t periodicity = 0; + if (!root[i].lookupValue("si_periodicity", periodicity)) { + fprintf(stderr, "Missing field si_periodicity in sched_info=%d\n", i); + return -1; + } + int k=0; + while(ksched_info[i].si_periodicity = (LIBLTE_RRC_SI_PERIODICITY_ENUM) k; + if (root[i].exists("si_mapping_info")) { + data->sched_info[i].N_sib_mapping_info = root[i]["si_mapping_info"].getLength(); + if (data->sched_info[i].N_sib_mapping_info < LIBLTE_RRC_MAX_SIB) { + for (uint32_t j=0;jsched_info[i].N_sib_mapping_info;j++) { + uint32_t sib_index = root[i]["si_mapping_info"][j]; + if (sib_index >= 3 && sib_index <= 13) { + data->sched_info[i].sib_mapping_info[j].sib_type = (LIBLTE_RRC_SIB_TYPE_ENUM) (sib_index-3); + } else { + fprintf(stderr, "Invalid SIB index %d for si_mapping_info=%d in sched_info=%d\n", sib_index, j, i); + return -1; + } + } + } else { + fprintf(stderr, "Number of si_mapping_info values exceeds maximum (%d)\n", LIBLTE_RRC_MAX_SIB); + return -1; + } + } else { + data->sched_info[i].N_sib_mapping_info = 0; + } + } + return 0; +} + + +int field_intra_neigh_cell_list::parse(libconfig::Setting &root) +{ + data->intra_freq_neigh_cell_list_size = root.getLength(); + for (uint32_t i=0;iintra_freq_neigh_cell_list_size && iintra_freq_neigh_cell_list[i].q_offset_range = (LIBLTE_RRC_Q_OFFSET_RANGE_ENUM) k; + + int phys_cell_id = 0; + if (!root[i].lookupValue("phys_cell_id", phys_cell_id)) { + fprintf(stderr, "Missing field phys_cell_id in neigh_cell=%d\n", i); + return -1; + } + data->intra_freq_neigh_cell_list[i].phys_cell_id = (uint16) phys_cell_id; + } + return 0; +} + +int field_intra_black_cell_list::parse(libconfig::Setting &root) +{ + data->intra_freq_black_cell_list_size = root.getLength(); + for (uint32_t i=0;iintra_freq_black_cell_list_size && iintra_freq_black_cell_list[i].range = (LIBLTE_RRC_PHYS_CELL_ID_RANGE_ENUM) k; + + int start = 0; + if (!root[i].lookupValue("start", start)) { + fprintf(stderr, "Missing field start in black_cell=%d\n", i); + return -1; + } + data->intra_freq_black_cell_list[i].start = (uint16) start; + } + return 0; +} + + +int enb::parse_sib1(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data) +{ + parser::section sib1("sib1"); + + sib1.add_field( + new parser::field_enum_str + ("intra_freq_reselection", &data->intra_freq_reselection, liblte_rrc_intra_freq_reselection_text, LIBLTE_RRC_INTRA_FREQ_RESELECTION_N_ITEMS) + ); + sib1.add_field( + new parser::field("q_rx_lev_min", &data->q_rx_lev_min) + ); + sib1.add_field( + new parser::field("p_max", &data->p_max, &data->p_max_present) + ); + sib1.add_field( + new parser::field_enum_str + ("cell_barred", &data->cell_barred, liblte_rrc_cell_barred_text, LIBLTE_RRC_CELL_BARRED_N_ITEMS) + ); + sib1.add_field( + new parser::field_enum_num + ("si_window_length", &data->si_window_length, liblte_rrc_si_window_length_num, LIBLTE_RRC_SI_WINDOW_LENGTH_N_ITEMS) + ); + sib1.add_field( + new parser::field("system_info_value_tag", &data->system_info_value_tag) + ); + + // sched_info subsection uses a custom field class + parser::section sched_info("sched_info"); + sib1.add_subsection(&sched_info); + sched_info.add_field(new field_sched_info(data)); + + // Run parser with single section + return parser::parse_section(filename, &sib1); +} + +int enb::parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *data) +{ + parser::section sib2("sib2"); + + sib2.add_field( + new parser::field_enum_str + ("time_alignment_timer", &data->time_alignment_timer, + liblte_rrc_time_alignment_timer_text, LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS) + ); + + + sib2.add_field( + new parser::field + ("mbsfnSubframeConfigListLength", &data->mbsfn_subfr_cnfg_list_size) + ); + + + parser::section mbsfnSubframeConfigList("mbsfnSubframeConfigList"); + sib2.add_subsection(&mbsfnSubframeConfigList); + + mbsfnSubframeConfigList.add_field( + new parser::field + ("subframeAllocation", &data->mbsfn_subfr_cnfg_list[0].subfr_alloc) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field + ("radioframeAllocationOffset", &data->mbsfn_subfr_cnfg_list[0].radio_fr_alloc_offset) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field_enum_str + ("subframeAllocationNumFrames", &data->mbsfn_subfr_cnfg_list[0].subfr_alloc_num_frames, + liblte_rrc_subframe_allocation_num_frames_text,LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_N_ITEMS) + ); + + mbsfnSubframeConfigList.add_field( + new parser::field_enum_str + ("radioframeAllocationPeriod", &data->mbsfn_subfr_cnfg_list[0].radio_fr_alloc_period, + liblte_rrc_radio_frame_allocation_period_text, LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N_ITEMS) + ); + + parser::section freqinfo("freqInfo"); + sib2.add_subsection(&freqinfo); + freqinfo.add_field( + new parser::field + ("additional_spectrum_emission", &data->additional_spectrum_emission) + ); + freqinfo.add_field( + new parser::field ("ul_carrier_freq_present", &data->arfcn_value_eutra.present) + ); + freqinfo.add_field( + new parser::field ("ul_bw_present", &data->ul_bw.present) + ); + + // AC barring configuration + parser::section acbarring("ac_barring"); + sib2.add_subsection(&acbarring); + acbarring.set_optional(&data->ac_barring_info_present); + + acbarring.add_field( + new parser::field("ac_barring_for_emergency", &data->ac_barring_for_emergency) + ); + + parser::section acbarring_signalling("ac_barring_for_mo_signalling"); + acbarring.add_subsection(&acbarring_signalling); + acbarring_signalling.set_optional(&data->ac_barring_for_mo_signalling.enabled); + + acbarring_signalling.add_field( + new parser::field_enum_num + ("factor", &data->ac_barring_for_mo_signalling.factor, + liblte_rrc_ac_barring_factor_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_signalling.add_field( + new parser::field_enum_num + ("time", &data->ac_barring_for_mo_signalling.time, + liblte_rrc_ac_barring_time_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_signalling.add_field( + new parser::field("for_special_ac", &data->ac_barring_for_mo_signalling.for_special_ac) + ); + + parser::section acbarring_data("ac_barring_for_mo_data"); + acbarring.add_subsection(&acbarring_data); + acbarring_data.set_optional(&data->ac_barring_for_mo_data.enabled); + + acbarring_data.add_field( + new parser::field_enum_num + ("factor", &data->ac_barring_for_mo_data.factor, + liblte_rrc_ac_barring_factor_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_data.add_field( + new parser::field_enum_num + ("time", &data->ac_barring_for_mo_data.time, + liblte_rrc_ac_barring_time_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_data.add_field( + new parser::field("for_special_ac", &data->ac_barring_for_mo_data.for_special_ac) + ); + + + // UE timers and constants + parser::section uetimers("ue_timers_and_constants"); + sib2.add_subsection(&uetimers); + uetimers.add_field( + new parser::field_enum_num + ("t300", &data->ue_timers_and_constants.t300, liblte_rrc_t300_num, LIBLTE_RRC_T300_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t301", &data->ue_timers_and_constants.t301, liblte_rrc_t301_num, LIBLTE_RRC_T301_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t310", &data->ue_timers_and_constants.t310, liblte_rrc_t310_num, LIBLTE_RRC_T310_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("n310", &data->ue_timers_and_constants.n310, liblte_rrc_n310_num, LIBLTE_RRC_N310_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t311", &data->ue_timers_and_constants.t311, liblte_rrc_t311_num, LIBLTE_RRC_T311_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("n311", &data->ue_timers_and_constants.n311, liblte_rrc_n311_num, LIBLTE_RRC_N311_N_ITEMS) + ); + + + + + // Radio-resource configuration section + parser::section rr_config("rr_config_common_sib"); + sib2.add_subsection(&rr_config); + + rr_config.add_field( + new parser::field_enum_str + ("ul_cp_length", &data->rr_config_common_sib.ul_cp_length, + liblte_rrc_ul_cp_length_text, LIBLTE_RRC_UL_CP_LENGTH_N_ITEMS) + ); + + // RACH configuration + parser::section rach_cnfg("rach_cnfg"); + rr_config.add_subsection(&rach_cnfg); + + rach_cnfg.add_field( + new parser::field_enum_num + ("num_ra_preambles", &data->rr_config_common_sib.rach_cnfg.num_ra_preambles, + liblte_rrc_number_of_ra_preambles_num, LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("preamble_init_rx_target_pwr", &data->rr_config_common_sib.rach_cnfg.preamble_init_rx_target_pwr, + liblte_rrc_preamble_initial_received_target_power_num, LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("pwr_ramping_step", &data->rr_config_common_sib.rach_cnfg.pwr_ramping_step, + liblte_rrc_power_ramping_step_num, LIBLTE_RRC_POWER_RAMPING_STEP_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("preamble_trans_max", &data->rr_config_common_sib.rach_cnfg.preamble_trans_max, + liblte_rrc_preamble_trans_max_num, LIBLTE_RRC_PREAMBLE_TRANS_MAX_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("ra_resp_win_size", &data->rr_config_common_sib.rach_cnfg.ra_resp_win_size, + liblte_rrc_ra_response_window_size_num, LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("mac_con_res_timer", &data->rr_config_common_sib.rach_cnfg.mac_con_res_timer, + liblte_rrc_mac_contention_resolution_timer_num, LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field("max_harq_msg3_tx", &data->rr_config_common_sib.rach_cnfg.max_harq_msg3_tx) + ); + + parser::section groupa_cnfg("preambles_group_a_cnfg"); + rach_cnfg.add_subsection(&groupa_cnfg); + groupa_cnfg.set_optional(&data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.present); + groupa_cnfg.add_field( + new parser::field_enum_num + ("size_of_ra", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.size_of_ra, + liblte_rrc_size_of_ra_preambles_group_a_num, LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N_ITEMS) + ); + groupa_cnfg.add_field( + new parser::field_enum_num + ("msg_size", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.msg_size, + liblte_rrc_message_size_group_a_num, LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_N_ITEMS) + ); + groupa_cnfg.add_field( + new parser::field_enum_num + ("msg_pwr_offset_group_b", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.msg_pwr_offset_group_b, + liblte_rrc_message_power_offset_group_b_num, LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_N_ITEMS) + ); + + + + // BCCH configuration + parser::section bcch_cnfg("bcch_cnfg"); + rr_config.add_subsection(&bcch_cnfg); + bcch_cnfg.add_field( + new parser::field_enum_num + ("modification_period_coeff", &data->rr_config_common_sib.bcch_cnfg.modification_period_coeff, + liblte_rrc_modification_period_coeff_num, LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N_ITEMS) + ); + + // PCCH configuration + parser::section pcch_cnfg("pcch_cnfg"); + rr_config.add_subsection(&pcch_cnfg); + pcch_cnfg.add_field( + new parser::field_enum_num + ("default_paging_cycle", &data->rr_config_common_sib.pcch_cnfg.default_paging_cycle, + liblte_rrc_default_paging_cycle_num, LIBLTE_RRC_DEFAULT_PAGING_CYCLE_N_ITEMS) + ); + pcch_cnfg.add_field( + new parser::field_enum_str + ("nB", &data->rr_config_common_sib.pcch_cnfg.nB, + liblte_rrc_nb_text, LIBLTE_RRC_NB_N_ITEMS) + ); + + // PRACH configuration + parser::section prach_cnfg("prach_cnfg"); + rr_config.add_subsection(&prach_cnfg); + prach_cnfg.add_field( + new parser::field("root_sequence_index", &data->rr_config_common_sib.prach_cnfg.root_sequence_index) + ); + parser::section prach_cnfg_info("prach_cnfg_info"); + prach_cnfg.add_subsection(&prach_cnfg_info); + prach_cnfg_info.add_field( + new parser::field("high_speed_flag", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag) + ); + prach_cnfg_info.add_field( + new parser::field("prach_config_index", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index) + ); + prach_cnfg_info.add_field( + new parser::field("prach_freq_offset", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset) + ); + prach_cnfg_info.add_field( + new parser::field("zero_correlation_zone_config", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config) + ); + + // PDSCH configuration + parser::section pdsch_cnfg("pdsch_cnfg"); + rr_config.add_subsection(&pdsch_cnfg); + pdsch_cnfg.add_field( + new parser::field("p_b", &data->rr_config_common_sib.pdsch_cnfg.p_b) + ); + pdsch_cnfg.add_field( + new parser::field("rs_power", &data->rr_config_common_sib.pdsch_cnfg.rs_power) + ); + + // PUSCH configuration + parser::section pusch_cnfg("pusch_cnfg"); + rr_config.add_subsection(&pusch_cnfg); + pusch_cnfg.add_field( + new parser::field("n_sb", &data->rr_config_common_sib.pusch_cnfg.n_sb) + ); + pusch_cnfg.add_field( + new parser::field_enum_str + ("hopping_mode", &data->rr_config_common_sib.pusch_cnfg.hopping_mode, + liblte_rrc_hopping_mode_text, LIBLTE_RRC_HOOPPING_MODE_N_ITEMS) + ); + pusch_cnfg.add_field( + new parser::field + ("pusch_hopping_offset", &data->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset) + ); + pusch_cnfg.add_field( + new parser::field + ("enable_64_qam", &data->rr_config_common_sib.pusch_cnfg.enable_64_qam) + ); + + // PUSCH-ULRS configuration + parser::section ulrs_cnfg("ul_rs"); + pusch_cnfg.add_subsection(&ulrs_cnfg); + ulrs_cnfg.add_field( + new parser::field + ("cyclic_shift", &data->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift) + ); + ulrs_cnfg.add_field( + new parser::field + ("group_assignment_pusch", &data->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch) + ); + ulrs_cnfg.add_field( + new parser::field + ("group_hopping_enabled", &data->rr_config_common_sib.pusch_cnfg.ul_rs.group_hopping_enabled) + ); + ulrs_cnfg.add_field( + new parser::field + ("sequence_hopping_enabled", &data->rr_config_common_sib.pusch_cnfg.ul_rs.sequence_hopping_enabled) + ); + + // PUCCH configuration + parser::section pucch_cnfg("pucch_cnfg"); + rr_config.add_subsection(&pucch_cnfg); + pucch_cnfg.add_field( + new parser::field_enum_num + ("delta_pucch_shift", &data->rr_config_common_sib.pucch_cnfg.delta_pucch_shift, + liblte_rrc_delta_pucch_shift_num,LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS) + ); + pucch_cnfg.add_field( + new parser::field + ("n_rb_cqi", &data->rr_config_common_sib.pucch_cnfg.n_rb_cqi) + ); + pucch_cnfg.add_field( + new parser::field + ("n_cs_an", &data->rr_config_common_sib.pucch_cnfg.n_cs_an) + ); + pucch_cnfg.add_field( + new parser::field + ("n1_pucch_an", &data->rr_config_common_sib.pucch_cnfg.n1_pucch_an) + ); + + // UL PWR Ctrl configuration + parser::section ul_pwr_ctrl("ul_pwr_ctrl"); + rr_config.add_subsection(&ul_pwr_ctrl); + ul_pwr_ctrl.add_field( + new parser::field + ("p0_nominal_pusch", &data->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pusch) + ); + ul_pwr_ctrl.add_field( + new parser::field_enum_num + ("alpha", &data->rr_config_common_sib.ul_pwr_ctrl.alpha, + liblte_rrc_ul_power_control_alpha_num, LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS) + ); + ul_pwr_ctrl.add_field( + new parser::field + ("p0_nominal_pucch", &data->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pucch) + ); + ul_pwr_ctrl.add_field( + new parser::field + ("delta_preamble_msg3", &data->rr_config_common_sib.ul_pwr_ctrl.delta_preamble_msg3) + ); + + // Delta Flist PUCCH + parser::section delta_flist("delta_flist_pucch"); + ul_pwr_ctrl.add_subsection(&delta_flist); + delta_flist.add_field( + new parser::field_enum_num + ("format_1", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1, + liblte_rrc_delta_f_pucch_format_1_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_1b", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1b, + liblte_rrc_delta_f_pucch_format_1b_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2, + liblte_rrc_delta_f_pucch_format_2_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2a", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2a, + liblte_rrc_delta_f_pucch_format_2a_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2b", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2b, + liblte_rrc_delta_f_pucch_format_2b_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS) + ); + + // Run parser with single section + return parser::parse_section(filename, &sib2); +} + +int enb::parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data) +{ + parser::section sib3("sib3"); + + // CellReselectionInfoCommon + parser::section resel_common("cell_reselection_common"); + sib3.add_subsection(&resel_common); + + resel_common.add_field( + new parser::field_enum_num + ("q_hyst", &data->q_hyst, + liblte_rrc_q_hyst_num, LIBLTE_RRC_Q_HYST_N_ITEMS) + ); + + parser::section speed_resel("speed_state_resel_params"); + resel_common.add_subsection(&speed_resel); + resel_common.set_optional(&data->speed_state_resel_params.present); + + + parser::section q_hyst_sf("q_hyst_sf"); + speed_resel.add_subsection(&q_hyst_sf); + q_hyst_sf.add_field( + new parser::field_enum_num + ("medium", &data->speed_state_resel_params.q_hyst_sf.medium, + liblte_rrc_sf_medium_num, LIBLTE_RRC_SF_MEDIUM_N_ITEMS) + ); + q_hyst_sf.add_field( + new parser::field_enum_num + ("high", &data->speed_state_resel_params.q_hyst_sf.high, + liblte_rrc_sf_high_num, LIBLTE_RRC_SF_HIGH_N_ITEMS) + ); + + + parser::section mob_params("mobility_state_params"); + speed_resel.add_subsection(&mob_params); + mob_params.add_field( + new parser::field_enum_num + ("t_eval", &data->speed_state_resel_params.mobility_state_params.t_eval, + liblte_rrc_t_evaluation_num, LIBLTE_RRC_T_EVALUATION_N_ITEMS) + ); + mob_params.add_field( + new parser::field_enum_num + ("t_hyst_normal", &data->speed_state_resel_params.mobility_state_params.t_hyst_normal, + liblte_rrc_t_hyst_normal_num, LIBLTE_RRC_T_HYST_NORMAL_N_ITEMS) + ); + mob_params.add_field( + new parser::field + ("n_cell_change_medium", &data->speed_state_resel_params.mobility_state_params.n_cell_change_medium) + ); + mob_params.add_field( + new parser::field + ("n_cell_change_high", &data->speed_state_resel_params.mobility_state_params.n_cell_change_high) + ); + + // CellReselectionServingFreqInfo + parser::section resel_serving("cell_reselection_serving"); + sib3.add_subsection(&resel_serving); + + resel_serving.add_field( + new parser::field ("s_non_intra_search", &data->s_non_intra_search, &data->s_non_intra_search_present) + ); + resel_serving.add_field( + new parser::field("thresh_serving_low", &data->thresh_serving_low) + ); + resel_serving.add_field( + new parser::field("cell_resel_prio", &data->cell_resel_prio) + ); + + + // intraFreqCellReselectionInfo + parser::section intra_freq("intra_freq_reselection"); + sib3.add_subsection(&intra_freq); + + intra_freq.add_field( + new parser::field("q_rx_lev_min", &data->q_rx_lev_min) + ); + intra_freq.add_field( + new parser::field("p_max", &data->p_max, &data->p_max_present) + ); + intra_freq.add_field( + new parser::field("s_intra_search", &data->s_intra_search, &data->s_intra_search_present) + ); + intra_freq.add_field( + new parser::field_enum_num + ("allowed_meas_bw", &data->allowed_meas_bw, + liblte_rrc_allowed_meas_bandwidth_num, LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_N_ITEMS, + &data->allowed_meas_bw_present) + ); + intra_freq.add_field( + new parser::field("presence_ant_port_1", &data->presence_ant_port_1) + ); + intra_freq.add_field( + new parser::field("neigh_cell_cnfg", &data->neigh_cell_cnfg) + ); + intra_freq.add_field( + new parser::field("t_resel_eutra", &data->t_resel_eutra) + ); + parser::section t_resel_eutra_sf("t_resel_eutra_sf"); + intra_freq.add_subsection(&t_resel_eutra_sf); + t_resel_eutra_sf.set_optional(&data->t_resel_eutra_sf_present); + + t_resel_eutra_sf.add_field( + new parser::field_enum_num + ("sf_medium", &data->t_resel_eutra_sf.sf_medium, + liblte_rrc_sssf_medium_num, LIBLTE_RRC_SSSF_MEDIUM_N_ITEMS) + ); + t_resel_eutra_sf.add_field( + new parser::field_enum_num + ("sf_high", &data->t_resel_eutra_sf.sf_high, + liblte_rrc_sssf_high_num, LIBLTE_RRC_SSSF_HIGH_N_ITEMS) + ); + + // Run parser with single section + return parser::parse_section(filename, &sib3); +} + +int enb::parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data) +{ + parser::section sib4("sib4"); + + // csg-PhysCellIdRange + parser::section csg_range("csg_phys_cell_id_range"); + sib4.add_subsection(&csg_range); + csg_range.set_optional(&data->csg_phys_cell_id_range_present); + csg_range.add_field( + new parser::field_enum_num + ("range", &data->csg_phys_cell_id_range.range, + liblte_rrc_phys_cell_id_range_num, LIBLTE_RRC_PHYS_CELL_ID_RANGE_N_ITEMS) + ); + csg_range.add_field( + new parser::field("start", &data->csg_phys_cell_id_range.start) + ); + + // intraFreqNeighCellList + parser::section intra_neigh("intra_freq_neigh_cell_list"); + sib4.add_subsection(&intra_neigh); + bool dummy_bool = false; + intra_neigh.set_optional(&dummy_bool); + intra_neigh.add_field(new field_intra_neigh_cell_list(data)); + + // intraFreqBlackCellList + parser::section intra_black("intra_freq_black_cell_list"); + sib4.add_subsection(&intra_black); + intra_black.set_optional(&dummy_bool); + intra_black.add_field(new field_intra_black_cell_list(data)); + + // Run parser with single section + return parser::parse_section(filename, &sib4); +} + + +uint32_t HexToBytes(const std::string& str, uint8_t *char_value, uint32_t buff_len) { + uint32_t i=0; + for (i = 0; i < str.length() && i("hnb_name", &hnb_name, &name_enabled)); + sib9.add_field(new parser::field("hex_value", &hex_value, &hex_enabled)); + + // Run parser with single section + if (!parser::parse_section(filename, &sib9)) { + data->hnb_name_present = true; + if (name_enabled) { + strncpy((char*) data->hnb_name, hnb_name.c_str(), 47); + data->hnb_name[47] = 0; + data->hnb_name_size = strnlen(hnb_name.c_str(), 48); + } else if (hex_enabled) { + data->hnb_name_size = HexToBytes(hex_value, data->hnb_name, 48); + } else { + data->hnb_name_present = false; + } + return 0; + } else { + return -1; + } +} + +int enb::parse_sib13(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *data) +{ + parser::section sib13("sib13"); + + sib13.add_field( + new parser::field + ("mbsfn_area_info_list_size", &data->mbsfn_area_info_list_r9_size) + ); + + parser::section mbsfn_notification_config("mbsfn_notification_config"); + sib13.add_subsection(&mbsfn_notification_config); + + + mbsfn_notification_config.add_field( + new parser::field_enum_str + ("mbsfn_notification_repetition_coeff", &data->mbsfn_notification_config.repetition_coeff, liblte_rrc_notification_repetition_coeff_r9_text,LIBLTE_RRC_NOTIFICATION_REPETITION_COEFF_R9_N_ITEMS) + ); + + mbsfn_notification_config.add_field( + new parser::field + ("mbsfn_notification_offset", &data->mbsfn_notification_config.offset) + ); + + mbsfn_notification_config.add_field( + new parser::field + ("mbsfn_notification_sf_index", &data->mbsfn_notification_config.sf_index) + ); + + + parser::section mbsfn_area_info_list("mbsfn_area_info_list"); + sib13.add_subsection(&mbsfn_area_info_list); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("non_mbsfn_region_length", &data->mbsfn_area_info_list_r9[0].non_mbsfn_region_length, + liblte_rrc_non_mbsfn_region_length_text,LIBLTE_RRC_NON_MBSFN_REGION_LENGTH_N_ITEMS) + ); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("mcch_repetition_period", &data->mbsfn_area_info_list_r9[0].mcch_repetition_period_r9, + liblte_rrc_mcch_repetition_period_r9_text,LIBLTE_RRC_MCCH_REPETITION_PERIOD_N_ITEMS) + ); + + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("mcch_modification_period", &data->mbsfn_area_info_list_r9[0].mcch_modification_period_r9, + liblte_rrc_mcch_modification_period_r9_text,LIBLTE_RRC_MCCH_MODIFICATION_PERIOD_N_ITEMS) + ); + + mbsfn_area_info_list.add_field( + new parser::field_enum_str + ("signalling_mcs", &data->mbsfn_area_info_list_r9[0].signalling_mcs_r9, + liblte_rrc_mcch_signalling_mcs_r9_text,LIBLTE_RRC_MCCH_SIGNALLING_MCS_N_ITEMS) + ); + + + mbsfn_area_info_list.add_field( + new parser::field + ("mbsfn_area_id", &data->mbsfn_area_info_list_r9[0].mbsfn_area_id_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("notification_indicator", &data->mbsfn_area_info_list_r9[0].notification_indicator_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("mcch_offset", &data->mbsfn_area_info_list_r9[0].mcch_offset_r9) + ); + + mbsfn_area_info_list.add_field( + new parser::field + ("sf_alloc_info", &data->mbsfn_area_info_list_r9[0].sf_alloc_info_r9) + ); + return parser::parse_section(filename, &sib13); +} + +int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common) +{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &rrc_cfg->sibs[0].sib.sib1; + rrc_cfg->sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2 = &rrc_cfg->sibs[1].sib.sib2; + rrc_cfg->sibs[1].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = &rrc_cfg->sibs[2].sib.sib3; + rrc_cfg->sibs[2].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4 = &rrc_cfg->sibs[3].sib.sib4; + rrc_cfg->sibs[3].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9 = &rrc_cfg->sibs[8].sib.sib9; + rrc_cfg->sibs[8].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13 = &rrc_cfg->sibs[12].sib.sib13; + rrc_cfg->sibs[12].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13; + + // Read SIB1 configuration from file + bzero(sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + if (parse_sib1(args->enb_files.sib_config, sib1)) { + return -1; + } + + // Fill rest of data from enb config + sib1->cell_id = (args->enb.s1ap.enb_id << 8) + args->enb.s1ap.cell_id; + sib1->tracking_area_code = args->enb.s1ap.tac; + sib1->freq_band_indicator = srslte_band_get_band(args->rf.dl_earfcn); + sib1->N_plmn_ids = 1; + sib1->plmn_id[0].id.mcc = args->enb.s1ap.mcc; + sib1->plmn_id[0].id.mnc = args->enb.s1ap.mnc; + sib1->plmn_id[0].resv_for_oper = LIBLTE_RRC_NOT_RESV_FOR_OPER; + sib1->cell_barred = LIBLTE_RRC_CELL_NOT_BARRED; + sib1->q_rx_lev_min_offset = 0; + + // Generate SIB2 + bzero(sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + if (parse_sib2(args->enb_files.sib_config, sib2)) { + return -1; + } + + // SRS not yet supported + sib2->rr_config_common_sib.srs_ul_cnfg.present = false; + if (sib2->ul_bw.present) { + switch(args->enb.n_prb) { + case 6: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N6; + break; + case 15: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N15; + break; + case 25: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N25; + break; + case 50: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N50; + break; + case 75: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N75; + break; + case 100: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N100; + break; + } + } + if (sib2->arfcn_value_eutra.present) { + sib2->arfcn_value_eutra.value = args->rf.ul_earfcn; + } + + // Generate SIB3 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_3)) { + bzero(sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT)); + if (parse_sib3(args->enb_files.sib_config, sib3)) { + return -1; + } + } + + // Generate SIB4 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_4)) { + bzero(sib4, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT)); + if (parse_sib4(args->enb_files.sib_config, sib4)) { + return -1; + } + } + + // Generate SIB9 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_9)) { + bzero(sib9, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT)); + if (parse_sib9(args->enb_files.sib_config, sib9)) { + return -1; + } + } + + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_13_v920)) { + bzero(sib13, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT)); + if (parse_sib13(args->enb_files.sib_config, sib13)) { + return -1; + } + } + // Copy PHY common configuration + bzero(phy_config_common, sizeof(phy_cfg_t)); + memcpy(&phy_config_common->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + memcpy(&phy_config_common->pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + + return 0; +} + +bool enb::sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num) +{ + for (uint32_t i=0;ienb.transmission_mode < 1 || args->enb.transmission_mode > 4) { + ERROR("Invalid transmission mode (%d). Only indexes 1-4 are implemented.\n", args->enb.transmission_mode); + return SRSLTE_ERROR; + } else if (args->enb.transmission_mode == 1 && args->enb.nof_ports > 1) { + ERROR("Invalid number of ports (%d) for transmission mode (%d). Only one antenna port is allowed.\n", + args->enb.nof_ports, args->enb.transmission_mode); + return SRSLTE_ERROR; + } else if (args->enb.transmission_mode > 1 && args->enb.nof_ports != 2) { + ERROR("The selected number of ports (%d) are insufficient for the selected transmission mode (%d).\n", + args->enb.nof_ports, args->enb.transmission_mode); + return SRSLTE_ERROR; + } + + bzero(&rrc_cfg->antenna_info, sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + rrc_cfg->antenna_info.tx_mode = (LIBLTE_RRC_TRANSMISSION_MODE_ENUM) (args->enb.transmission_mode - 1); + + if (rrc_cfg->antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3) { + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup = LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_OPEN_LOOP; + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup_present = false; + + rrc_cfg->antenna_info.codebook_subset_restriction_choice = LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM3; + rrc_cfg->antenna_info.codebook_subset_restriction = 0b11; + rrc_cfg->antenna_info.codebook_subset_restriction_present = true; + } else if (rrc_cfg->antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup = LIBLTE_RRC_UE_TX_ANTENNA_SELECTION_CLOSED_LOOP; + rrc_cfg->antenna_info.ue_tx_antenna_selection_setup_present = true; + + rrc_cfg->antenna_info.codebook_subset_restriction_choice = LIBLTE_RRC_CODEBOOK_SUBSET_RESTRICTION_N2_TM4; + rrc_cfg->antenna_info.codebook_subset_restriction = 0b111111; + rrc_cfg->antenna_info.codebook_subset_restriction_present = true; + } + + /* Parse power allocation */ + rrc_cfg->pdsch_cfg = LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS; + for (int i = 0; i < LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS; i++) { + if (args->enb.p_a == liblte_rrc_pdsch_config_p_a_num[i]) { + rrc_cfg->pdsch_cfg = (LIBLTE_RRC_PDSCH_CONFIG_P_A_ENUM) i; + } + } + if (rrc_cfg->pdsch_cfg == LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS) { + ERROR("Invalid p_a value (%f) only -6, -4.77, -3, -1.77, 0, 1, 2, 3 values allowed.", args->enb.p_a); + return SRSLTE_ERROR; + } + + /* MAC config section */ + parser::section mac_cnfg("mac_cnfg"); + + parser::section phr_cnfg("phr_cnfg"); + mac_cnfg.add_subsection(&phr_cnfg); + mac_cnfg.set_optional(&rrc_cfg->mac_cnfg.phr_cnfg.setup_present); + phr_cnfg.add_field( + new parser::field_enum_str + ("dl_pathloss_change", &rrc_cfg->mac_cnfg.phr_cnfg.dl_pathloss_change, + liblte_rrc_dl_pathloss_change_text, LIBLTE_RRC_DL_PATHLOSS_CHANGE_N_ITEMS) + ); + phr_cnfg.add_field( + new parser::field_enum_num + ("periodic_phr_timer", &rrc_cfg->mac_cnfg.phr_cnfg.periodic_phr_timer, + liblte_rrc_periodic_phr_timer_num, LIBLTE_RRC_PERIODIC_PHR_TIMER_N_ITEMS) + ); + phr_cnfg.add_field( + new parser::field_enum_num + ("prohibit_phr_timer", &rrc_cfg->mac_cnfg.phr_cnfg.prohibit_phr_timer, + liblte_rrc_prohibit_phr_timer_num, LIBLTE_RRC_PROHIBIT_PHR_TIMER_N_ITEMS) + ); + + parser::section ulsch_cnfg("ulsch_cnfg"); + mac_cnfg.add_subsection(&ulsch_cnfg); + + rrc_cfg->mac_cnfg.ulsch_cnfg.tti_bundling = false; + ulsch_cnfg.add_field( + new parser::field_enum_num + ("max_harq_tx", &rrc_cfg->mac_cnfg.ulsch_cnfg.max_harq_tx, + liblte_rrc_max_harq_tx_num, LIBLTE_RRC_MAX_HARQ_TX_N_ITEMS, + &rrc_cfg->mac_cnfg.ulsch_cnfg.max_harq_tx_present) + ); + ulsch_cnfg.add_field( + new parser::field_enum_num + ("periodic_bsr_timer", &rrc_cfg->mac_cnfg.ulsch_cnfg.periodic_bsr_timer, + liblte_rrc_periodic_bsr_timer_num, LIBLTE_RRC_PERIODIC_BSR_TIMER_N_ITEMS, + &rrc_cfg->mac_cnfg.ulsch_cnfg.periodic_bsr_timer_present) + ); + + ulsch_cnfg.add_field( + new parser::field_enum_num + ("retx_bsr_timer", &rrc_cfg->mac_cnfg.ulsch_cnfg.retx_bsr_timer, + liblte_rrc_retransmission_bsr_timer_num, LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_N_ITEMS) + ); + + mac_cnfg.add_field( + new parser::field_enum_num + ("time_alignment_timer", &rrc_cfg->mac_cnfg.time_alignment_timer, + liblte_rrc_time_alignment_timer_num, LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS) + ); + + + /* PHY config section */ + parser::section phy_cfg("phy_cnfg"); + + parser::section pusch_cnfg_ded("pusch_cnfg_ded"); + phy_cfg.add_subsection(&pusch_cnfg_ded); + + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_ack_idx", &rrc_cfg->pusch_cfg.beta_offset_ack_idx)); + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_ri_idx", &rrc_cfg->pusch_cfg.beta_offset_ri_idx)); + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_cqi_idx", &rrc_cfg->pusch_cfg.beta_offset_cqi_idx)); + + parser::section sched_request_cnfg("sched_request_cnfg"); + phy_cfg.add_subsection(&sched_request_cnfg); + + sched_request_cnfg.add_field( + new parser::field_enum_num + ("dsr_trans_max", &rrc_cfg->sr_cfg.dsr_max, + liblte_rrc_dsr_trans_max_num, LIBLTE_RRC_DSR_TRANS_MAX_N_ITEMS) + ); + sched_request_cnfg.add_field(new parser::field ("period", &rrc_cfg->sr_cfg.period)); + sched_request_cnfg.add_field(new parser::field ("nof_prb", &rrc_cfg->sr_cfg.nof_prb)); + sched_request_cnfg.add_field(new field_sf_mapping(rrc_cfg->sr_cfg.sf_mapping, &rrc_cfg->sr_cfg.nof_subframes)); + + parser::section cqi_report_cnfg("cqi_report_cnfg"); + phy_cfg.add_subsection(&cqi_report_cnfg); + + cqi_report_cnfg.add_field( + new parser::field_enum_str + ("mode", &rrc_cfg->cqi_cfg.mode, + rrc_cfg_cqi_mode_text, RRC_CFG_CQI_MODE_N_ITEMS) + ); + cqi_report_cnfg.add_field(new parser::field ("period", &rrc_cfg->cqi_cfg.period)); + cqi_report_cnfg.add_field(new parser::field ("nof_prb", &rrc_cfg->cqi_cfg.nof_prb)); + cqi_report_cnfg.add_field(new parser::field ("simultaneousAckCQI", &rrc_cfg->cqi_cfg.simultaneousAckCQI)); + cqi_report_cnfg.add_field(new field_sf_mapping(rrc_cfg->cqi_cfg.sf_mapping, &rrc_cfg->cqi_cfg.nof_subframes)); + + // Run parser with two sections + parser p(args->enb_files.rr_config); + p.add_section(&mac_cnfg); + p.add_section(&phy_cfg); + return p.parse(); +} + +int field_sf_mapping::parse(libconfig::Setting &root) +{ + *nof_subframes = root["subframe"].getLength(); + for (uint32_t i=0;i<*nof_subframes;i++) { + sf_mapping[i] = root["subframe"][i]; + } + return 0; +} + + + + + + + +int enb::parse_drb(all_args_t* args, rrc_cfg_t* rrc_cfg) +{ + parser::section qci("qci_config"); + qci.add_field(new field_qci(rrc_cfg->qci_cfg)); + return parser::parse_section(args->enb_files.drb_config, &qci); +} + +int field_qci::parse(libconfig::Setting &root) +{ + uint32_t nof_qci = root.getLength(); + + bzero(cfg, sizeof(rrc_cfg_qci_t)*MAX_NOF_QCI); + + for (uint32_t i=0;i discard_timer + ("discard_timer", &cfg[qci].pdcp_cfg.discard_timer, + liblte_rrc_discard_timer_num, LIBLTE_RRC_DISCARD_TIMER_N_ITEMS); + if (discard_timer.parse(q["pdcp_config"])) { + cfg[qci].pdcp_cfg.discard_timer_present = false; + } else { + cfg[qci].pdcp_cfg.discard_timer_present = true; + } + + parser::field_enum_num pdcp_sn_size + ("pdcp_sn_size", &cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size, + liblte_rrc_pdcp_sn_size_num, LIBLTE_RRC_PDCP_SN_SIZE_N_ITEMS); + + if (pdcp_sn_size.parse(q["pdcp_config"])) { + cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size_present = false; + } else { + cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size_present = true; + } + + if (q["pdcp_config"].lookupValue("status_report_required", cfg[qci].pdcp_cfg.rlc_am_status_report_required)) { + cfg[qci].pdcp_cfg.rlc_am_status_report_required_present = true; + } else { + cfg[qci].pdcp_cfg.rlc_am_status_report_required_present = false; + } + + // Parse RLC section + if (q["rlc_config"].exists("ul_am")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + } else if (q["rlc_config"].exists("ul_um") && q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + } else if (q["rlc_config"].exists("ul_um") && !q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_UL; + } else if (!q["rlc_config"].exists("ul_um") && q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_DL; + } else { + fprintf(stderr, "Invalid combination of UL/DL UM/AM for qci=%d\n", qci); + return -1; + } + + // Parse RLC-UM section + if (q["rlc_config"].exists("ul_um")) { + + LIBLTE_RRC_UL_UM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.ul_um_bi_rlc; + if (cfg[qci].rlc_cfg.rlc_mode == LIBLTE_RRC_RLC_MODE_UM_UNI_UL) { + rlc_cfg = &cfg[qci].rlc_cfg.ul_um_bi_rlc; + } + + parser::field_enum_num sn_field_len + ("sn_field_length", &rlc_cfg->sn_field_len, + liblte_rrc_sn_field_length_num, LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS); + if (sn_field_len.parse(q["rlc_config"]["ul_um"])) { + fprintf(stderr, "Error can't find sn_field_length in section ul_um\n"); + } + } + + if (q["rlc_config"].exists("dl_um")) { + + LIBLTE_RRC_DL_UM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.dl_um_bi_rlc; + if (cfg[qci].rlc_cfg.rlc_mode == LIBLTE_RRC_RLC_MODE_UM_UNI_DL) { + rlc_cfg = &cfg[qci].rlc_cfg.dl_um_bi_rlc; + } + + parser::field_enum_num sn_field_len + ("sn_field_length", &rlc_cfg->sn_field_len, + liblte_rrc_sn_field_length_num, LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS); + if (sn_field_len.parse(q["rlc_config"]["dl_um"])) { + fprintf(stderr, "Error can't find sn_field_length in section dl_um\n"); + } + + parser::field_enum_num t_reordering + ("t_reordering", &rlc_cfg->t_reordering, + liblte_rrc_t_reordering_num, LIBLTE_RRC_T_REORDERING_N_ITEMS); + if (t_reordering.parse(q["rlc_config"]["dl_um"])) { + fprintf(stderr, "Error can't find t_reordering in section dl_um\n"); + } + } + + // Parse RLC-AM section + if (q["rlc_config"].exists("ul_am")) { + LIBLTE_RRC_UL_AM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.ul_am_rlc; + + parser::field_enum_num t_poll_retx + ("t_poll_retx", &rlc_cfg->t_poll_retx, + liblte_rrc_t_poll_retransmit_num, LIBLTE_RRC_T_POLL_RETRANSMIT_N_ITEMS); + if (t_poll_retx.parse(q["rlc_config"]["ul_am"])) { + fprintf(stderr, "Error can't find t_poll_retx in section ul_am\n"); + } + + parser::field_enum_num poll_pdu + ("poll_pdu", &rlc_cfg->poll_pdu, + liblte_rrc_poll_pdu_num, LIBLTE_RRC_POLL_PDU_N_ITEMS); + if (poll_pdu.parse(q["rlc_config"]["ul_am"])) { + fprintf(stderr, "Error can't find poll_pdu in section ul_am\n"); + } + + parser::field_enum_num poll_byte + ("poll_byte", &rlc_cfg->poll_byte, + liblte_rrc_poll_byte_num, LIBLTE_RRC_POLL_BYTE_N_ITEMS); + if (poll_byte.parse(q["rlc_config"]["ul_am"])) { + fprintf(stderr, "Error can't find poll_byte in section ul_am\n"); + } + + parser::field_enum_num max_retx_thresh + ("max_retx_thresh", &rlc_cfg->max_retx_thresh, + liblte_rrc_max_retx_threshold_num, LIBLTE_RRC_MAX_RETX_THRESHOLD_N_ITEMS); + if (max_retx_thresh.parse(q["rlc_config"]["ul_am"])) { + fprintf(stderr, "Error can't find max_retx_thresh in section ul_am\n"); + } + } + + if (q["rlc_config"].exists("dl_am")) { + LIBLTE_RRC_DL_AM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.dl_am_rlc; + + parser::field_enum_num t_reordering + ("t_reordering", &rlc_cfg->t_reordering, + liblte_rrc_t_reordering_num, LIBLTE_RRC_T_REORDERING_N_ITEMS); + if (t_reordering.parse(q["rlc_config"]["dl_am"])) { + fprintf(stderr, "Error can't find t_reordering in section dl_am\n"); + } + + parser::field_enum_num t_status_prohibit + ("t_status_prohibit", &rlc_cfg->t_status_prohibit, + liblte_rrc_t_status_prohibit_num, LIBLTE_RRC_T_STATUS_PROHIBIT_N_ITEMS); + if (t_status_prohibit.parse(q["rlc_config"]["dl_am"])) { + fprintf(stderr, "Error can't find t_status_prohibit in section dl_am\n"); + } + } + + + // Parse logical channel configuration section + if (!q.exists("logical_channel_config")) { + fprintf(stderr, "Error section logical_channel_config not found for qci=%d\n", qci); + return -1; + } + LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT *lc_cfg = &cfg[qci].lc_cfg; + + parser::field priority ("priority", &lc_cfg->priority); + if (priority.parse(q["logical_channel_config"])) { + fprintf(stderr, "Error can't find logical_channel_config in section priority\n"); + } + + parser::field_enum_num prioritized_bit_rate + ("prioritized_bit_rate", &lc_cfg->prioritized_bit_rate, + liblte_rrc_prioritized_bit_rate_num, LIBLTE_RRC_PRIORITIZED_BIT_RATE_N_ITEMS); + if (prioritized_bit_rate.parse(q["logical_channel_config"])) { + fprintf(stderr, "Error can't find prioritized_bit_rate in section logical_channel_config\n"); + } + + parser::field_enum_num bucket_size_duration + ("bucket_size_duration", &lc_cfg->bucket_size_duration, + liblte_rrc_bucket_size_duration_num, LIBLTE_RRC_BUCKET_SIZE_DURATION_N_ITEMS); + if (bucket_size_duration.parse(q["logical_channel_config"])) { + fprintf(stderr, "Error can't find bucket_size_duration in section logical_channel_config\n"); + } + + parser::field log_chan_group ("log_chan_group", &lc_cfg->log_chan_group); + if (log_chan_group.parse(q["logical_channel_config"])) { + lc_cfg->log_chan_group_present = false; + } else { + lc_cfg->log_chan_group_present = true; + } + + + + + cfg[qci].configured = true; + } + + return 0; +} + +} diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h new file mode 100644 index 0000000..912c9bf --- /dev/null +++ b/srsenb/src/enb_cfg_parser.h @@ -0,0 +1,120 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef ENB_CFG_PARSER_SIB1_H +#define ENB_CFG_PARSER_SIB1_H + +#include +#include +#include +#include +#include +#include +#include "srsenb/hdr/parser.h" + +#include "srsenb/hdr/upper/rrc.h" +#include "srslte/asn1/liblte_rrc.h" + +namespace srsenb { + +using namespace libconfig; + +class field_sched_info : public parser::field_itf +{ +public: + field_sched_info(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data_) { data = data_; } + ~field_sched_info() {} + int parse(Setting &root); + const char* get_name() { + return "sched_info"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data; +}; + +class field_intra_neigh_cell_list : public parser::field_itf +{ +public: + field_intra_neigh_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; } + ~field_intra_neigh_cell_list(){} + int parse(Setting &root); + const char* get_name() { + return "intra_neigh_cell_list"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data; +}; + +class field_intra_black_cell_list : public parser::field_itf +{ +public: + field_intra_black_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; } + ~field_intra_black_cell_list(){} + int parse(Setting &root); + const char* get_name() { + return "intra_black_cell_list"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data; +}; + +class field_sf_mapping : public parser::field_itf +{ +public: + field_sf_mapping(uint32_t *sf_mapping_, uint32_t *nof_subframes_) { sf_mapping = sf_mapping_; nof_subframes = nof_subframes_; } + ~field_sf_mapping(){} + int parse(Setting &root); + const char* get_name() { + return "sf_mapping"; + } + +private: + uint32_t *sf_mapping; + uint32_t *nof_subframes; +}; + +class field_qci : public parser::field_itf +{ +public: + field_qci(rrc_cfg_qci_t *cfg_) { cfg = cfg_; } + ~field_qci(){} + const char* get_name() { + return "field_cqi"; + } + + int parse(Setting &root); +private: + rrc_cfg_qci_t *cfg; +}; + + +} + +#endif + diff --git a/srsenb/src/mac/CMakeLists.txt b/srsenb/src/mac/CMakeLists.txt new file mode 100644 index 0000000..8819c0b --- /dev/null +++ b/srsenb/src/mac/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsenb_mac STATIC ${SOURCES}) +install(TARGETS srsenb_mac DESTINATION ${LIBRARY_DIR}) + diff --git a/srsenb/src/mac/mac.cc b/srsenb/src/mac/mac.cc new file mode 100644 index 0000000..fb1a71c --- /dev/null +++ b/srsenb/src/mac/mac.cc @@ -0,0 +1,996 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include +#include +#include +#include +#include + +#include "srslte/common/log.h" +#include "srsenb/hdr/mac/mac.h" + +//#define WRITE_SIB_PCAP + +namespace srsenb { + +mac::mac() : timers_db(128), timers_thread(&timers_db), tti(0), last_rnti(0), + rar_pdu_msg(sched_interface::MAX_RAR_LIST), rar_payload(), + pdu_process_thread(this) +{ + started = false; + pcap = NULL; + phy_h = NULL; + rlc_h = NULL; + rrc_h = NULL; + log_h = NULL; + + bzero(&locations, sizeof(locations)); + bzero(&cell, sizeof(cell)); + bzero(&args, sizeof(args)); + bzero(&pending_rars, sizeof(pending_rars)); + bzero(&bcch_dlsch_payload, sizeof(bcch_dlsch_payload)); + bzero(&pcch_payload_buffer, sizeof(pcch_payload_buffer)); + bzero(&bcch_softbuffer_tx, sizeof(bcch_softbuffer_tx)); + bzero(&pcch_softbuffer_tx, sizeof(pcch_softbuffer_tx)); + bzero(&rar_softbuffer_tx, sizeof(rar_softbuffer_tx)); +} + +bool mac::init(mac_args_t *args_, srslte_cell_t *cell_, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h_) +{ + started = false; + + if (cell_ && phy && rlc && log_h_ && args_) { + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; + log_h = log_h_; + + memcpy(&args, args_, sizeof(mac_args_t)); + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + scheduler.init(rrc, log_h); + // Set default scheduler (RR) + scheduler.set_metric(&sched_metric_dl_rr, &sched_metric_ul_rr); + + // Set default scheduler configuration + scheduler.set_sched_cfg(&args.sched); + + // Init softbuffer for SI messages + for (int i=0;i::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + u->start_pcap(pcap); + } +} + +/******************************************************** + * + * RLC interface + * + *******************************************************/ +int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +{ + pthread_rwlock_rdlock(&rwlock); + int ret = -1; + if (ue_db.count(rnti)) { + if(rnti != SRSLTE_MRNTI){ + ret = scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); + } else { + for(uint32_t i = 0; i < mch.num_mtch_sched; i++){ + if(lc_id == mch.mtch_sched[i].lcid){ + mch.mtch_sched[i].lcid_buffer_size = tx_queue; + } + } + ret = 0; + } + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) +{ + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + // configure BSR group in UE + ue_db[rnti]->set_lcg(lc_id, (uint32_t) cfg->group); + ret = scheduler.bearer_ue_cfg(rnti, lc_id, cfg); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) +{ + pthread_rwlock_rdlock(&rwlock); + int ret = -1; + if (ue_db.count(rnti)) { + ret = scheduler.bearer_ue_rem(rnti, lc_id); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +void mac::phy_config_enabled(uint16_t rnti, bool enabled) +{ + scheduler.phy_config_enabled(rnti, enabled); +} + +// Update UE configuration +int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) +{ + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + + // Add RNTI to the PHY (pregerate signals) now instead of after PRACH + if (!ue_db[rnti]->is_phy_added) { + ue_db[rnti]->is_phy_added = true; + Info("Registering rnti=0x%x to PHY...\n", rnti); + // Register new user in PHY + if (phy_h->add_rnti(rnti)) { + Error("Registering new ue rnti=0x%x to PHY\n", rnti); + } + Info("Done registering rnti=0x%x to PHY...\n", rnti); + } + + // Update Scheduler configuration + if (scheduler.ue_cfg(rnti, cfg)) { + Error("Registering new UE rnti=0x%x to SCHED\n", rnti); + } else { + ret = 0; + } + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +// Removes UE from DB +int mac::ue_rem(uint16_t rnti) +{ + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + scheduler.ue_rem(rnti); + phy_h->rem_rnti(rnti); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + if (ret) { + return ret; + } + pthread_rwlock_wrlock(&rwlock); + if (ue_db.count(rnti)) { + delete ue_db[rnti]; + ue_db.erase(rnti); + Info("User rnti=0x%x removed from MAC/PHY\n", rnti); + } else { + Error("User rnti=0x%x already removed\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return 0; +} + +int mac::cell_cfg(sched_interface::cell_cfg_t* cell_cfg) +{ + memcpy(&this->cell_config, cell_cfg, sizeof(sched_interface::cell_cfg_t)); + return scheduler.cell_cfg(cell_cfg); +} + +void mac::get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]) +{ + pthread_rwlock_rdlock(&rwlock); + int cnt=0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + if(iter->first != SRSLTE_MRNTI) { + u->metrics_read(&metrics[cnt]); + cnt++; + } + } + pthread_rwlock_unlock(&rwlock); +} + + +/******************************************************** + * + * PHY interface + * + *******************************************************/ + +void mac::rl_failure(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + uint32_t nof_fails = ue_db[rnti]->rl_failure(); + if (nof_fails >= (uint32_t) args.link_failure_nof_err && args.link_failure_nof_err > 0) { + Info("Detected Uplink failure for rnti=0x%x\n", rnti); + rrc_h->rl_failure(rnti); + ue_db[rnti]->rl_failure_reset(); + } + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +void mac::rl_ok(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti]->rl_failure_reset(); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +int mac::ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) +{ + pthread_rwlock_rdlock(&rwlock); + log_h->step(tti); + uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, tb_idx, ack); + ue_db[rnti]->metrics_tx(ack, nof_bytes); + + if (ack) { + if (nof_bytes > 64) { // do not count RLC status messages only + rrc_h->set_activity_user(rnti); + log_h->debug("DL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes); + } + } + pthread_rwlock_unlock(&rwlock); + return 0; +} + +int mac::crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc) +{ + log_h->step(tti); + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti]->set_tti(tti); + + ue_db[rnti]->metrics_rx(crc, nof_bytes); + + // push the pdu through the queue if received correctly + if (crc) { + ue_db[rnti]->push_pdu(tti, nof_bytes); + pdu_process_thread.notify(); + } else { + ue_db[rnti]->deallocate_pdu(tti); + } + + ret = scheduler.ul_crc_info(tti, rnti, crc); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::set_dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) { + log_h->step(tti); + + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + scheduler.dl_ant_info(rnti, dl_ant_info); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::ri_info(uint32_t tti, uint16_t rnti, uint32_t ri_value) +{ + log_h->step(tti); + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + scheduler.dl_ri_info(tti, rnti, ri_value); + ue_db[rnti]->metrics_dl_ri(ri_value); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) +{ + log_h->step(tti); + pthread_rwlock_rdlock(&rwlock); + int ret = -1; + if (ue_db.count(rnti)) { + scheduler.dl_pmi_info(tti, rnti, pmi_value); + ue_db[rnti]->metrics_dl_pmi(pmi_value); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + log_h->step(tti); + int ret = -1; + + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + scheduler.dl_cqi_info(tti, rnti, cqi_value); + ue_db[rnti]->metrics_dl_cqi(cqi_value); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::snr_info(uint32_t tti, uint16_t rnti, float snr) +{ + log_h->step(tti); + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + uint32_t cqi = srslte_cqi_from_snr(snr); + scheduler.ul_cqi_info(tti, rnti, cqi, 0); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::sr_detected(uint32_t tti, uint16_t rnti) +{ + log_h->step(tti); + int ret = -1; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + scheduler.ul_sr_info(tti, rnti); + ret = 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int mac::rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv) +{ + log_h->step(tti); + + // Find empty slot for pending rars + uint32_t ra_id=0; + while(pending_rars[ra_id].temp_crnti && ra_idconfig(last_rnti, cell.nof_prb, &scheduler, rrc_h, rlc_h, log_h); + + // Set PCAP if available + if (pcap) { + ue_db[last_rnti]->start_pcap(pcap); + } + + pthread_rwlock_unlock(&rwlock); + + // Save RA info + pending_rars[ra_id].preamble_idx = preamble_idx; + pending_rars[ra_id].ta_cmd = 2*time_adv; + pending_rars[ra_id].temp_crnti = last_rnti; + + // Add new user to the scheduler so that it can RX/TX SRB0 + sched_interface::ue_cfg_t uecfg; + bzero(&uecfg, sizeof(sched_interface::ue_cfg_t)); + uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + if (scheduler.ue_cfg(last_rnti, &uecfg)) { + // Release pending RAR + bzero(&pending_rars[ra_id], sizeof(pending_rar_t)); + Error("Registering new user rnti=0x%x to SCHED\n", last_rnti); + return -1; + } + + // Register new user in RRC + rrc_h->add_user(last_rnti); + // Trigger scheduler RACH + scheduler.dl_rach_info(tti, ra_id, last_rnti, 7); + + log_h->info("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + tti, preamble_idx, time_adv, last_rnti); + log_h->console("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + tti, preamble_idx, time_adv, last_rnti); + + // Increae RNTI counter + last_rnti++; + if (last_rnti >= 60000) { + last_rnti = 70; + } + return 0; +} + +int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) +{ + log_h->step(tti); + + if (!started) { + return 0; + } + + if (!dl_sched_res) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Run scheduler with current info + sched_interface::dl_sched_res_t sched_result; + bzero(&sched_result, sizeof(sched_interface::dl_sched_res_t)); + if (scheduler.dl_sched(tti, &sched_result) < 0) { + Error("Running scheduler\n"); + return SRSLTE_ERROR; + } + + int n = 0; + + pthread_rwlock_rdlock(&rwlock); + + // Copy data grants + for (uint32_t i=0;isched_grants[n].rnti = rnti; + dl_sched_res->sched_grants[n].dci_format = sched_result.data[i].dci_format; + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.data[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.data[i].dci_location, sizeof(srslte_dci_location_t)); + + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + dl_sched_res->sched_grants[n].softbuffers[tb] = + ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.harq_process, tb); + + if (sched_result.data[i].nof_pdu_elems[tb] > 0) { + /* Get PDU if it's a new transmission */ + dl_sched_res->sched_grants[n].data[tb] = ue_db[rnti]->generate_pdu(tb, + sched_result.data[i].pdu[tb], + sched_result.data[i].nof_pdu_elems[tb], + sched_result.data[i].tbs[tb]); + + if (!dl_sched_res->sched_grants[n].data[tb]) { + Error("Error! PDU was not generated (rnti=0x%04x, tb=%d)\n", rnti, tb); + sched_result.data[i].dci.tb_en[tb] = false; + } + + if (pcap) { + pcap->write_dl_crnti(dl_sched_res->sched_grants[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti); + } + + } else { + /* TB not enabled OR no data to send: set pointers to NULL */ + dl_sched_res->sched_grants[n].data[tb] = NULL; + } + } + n++; + } else { + Warning("Invalid DL scheduling result. User 0x%x does not exist\n", rnti); + } + } + + // No more uses of shared ue_db beyond here + pthread_rwlock_unlock(&rwlock); + + // Copy RAR grants + for (uint32_t i=0;isched_grants[n].rnti = sched_result.rar[i].rarnti; + dl_sched_res->sched_grants[n].dci_format = SRSLTE_DCI_FORMAT1A; // Force Format 1A + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.rar[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.rar[i].dci_location, sizeof(srslte_dci_location_t)); + + // Set softbuffer (there are no retx in RAR but a softbuffer is required) + dl_sched_res->sched_grants[n].softbuffers[0] = &rar_softbuffer_tx; + + // Assemble PDU + dl_sched_res->sched_grants[n].data[0] = assemble_rar(sched_result.rar[i].grants, sched_result.rar[i].nof_grants, i, sched_result.rar[i].tbs); + + + if (pcap) { + pcap->write_dl_ranti(dl_sched_res->sched_grants[n].data[0], sched_result.rar[i].tbs, dl_sched_res->sched_grants[n].rnti, true, tti); + } + + n++; + } + + // Copy SI and Paging grants + for (uint32_t i=0;isched_grants[n].rnti = (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH ) ? SRSLTE_SIRNTI : SRSLTE_PRNTI; + dl_sched_res->sched_grants[n].dci_format = SRSLTE_DCI_FORMAT1A; // Force Format 1A + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.bc[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.bc[i].dci_location, sizeof(srslte_dci_location_t)); + + // Set softbuffer + if (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH) { + dl_sched_res->sched_grants[n].softbuffers[0] = &bcch_softbuffer_tx[sched_result.bc[i].index]; + dl_sched_res->sched_grants[n].data[0] = assemble_si(sched_result.bc[i].index); +#ifdef WRITE_SIB_PCAP + if (pcap) { + pcap->write_dl_sirnti(dl_sched_res->sched_grants[n].data[0], sched_result.bc[i].tbs, true, tti); + } +#endif + } else { + dl_sched_res->sched_grants[n].softbuffers[0] = &pcch_softbuffer_tx; + dl_sched_res->sched_grants[n].data[0] = pcch_payload_buffer; + rlc_h->read_pdu_pcch(pcch_payload_buffer, pcch_payload_buffer_len); + + if (pcap) { + pcap->write_dl_pch(dl_sched_res->sched_grants[n].data[0], sched_result.bc[i].tbs, true, tti); + } + } + + n++; + } + + dl_sched_res->nof_grants = n; + + // Number of CCH symbols + dl_sched_res->cfi = sched_result.cfi; + + return SRSLTE_SUCCESS; +} + +void mac::build_mch_sched(uint32_t tbs) +{ + int sfs_per_sched_period = mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9; + int bytes_per_sf = tbs/8 - 6;// leave 6 bytes for header + + int total_space_avail_bytes = sfs_per_sched_period*bytes_per_sf; + + int total_bytes_to_tx = 0; + + + // calculate total bytes to be scheduled + for (uint32_t i = 0; i < mch.num_mtch_sched; i++) { + total_bytes_to_tx += mch.mtch_sched[i].lcid_buffer_size; + mch.mtch_sched[i].stop = 0; + } + + int last_mtch_stop = 0; + + if(total_bytes_to_tx >= total_space_avail_bytes){ + for(uint32_t i = 0; i < mch.num_mtch_sched;i++){ + double ratio = mch.mtch_sched[i].lcid_buffer_size/total_bytes_to_tx; + float assigned_sfs = floor(sfs_per_sched_period*ratio); + mch.mtch_sched[i].stop = last_mtch_stop + (uint32_t)assigned_sfs; + last_mtch_stop = mch.mtch_sched[i].stop; + } + }else { + for(uint32_t i = 0; i < mch.num_mtch_sched;i++){ + float assigned_sfs = ceil(((float)mch.mtch_sched[i].lcid_buffer_size)/((float)bytes_per_sf)); + mch.mtch_sched[i].stop = last_mtch_stop + (uint32_t)assigned_sfs; + last_mtch_stop = mch.mtch_sched[i].stop; + } + } +} + +int mac::get_mch_sched(bool is_mcch, dl_sched_t *dl_sched_res) +{ + + srslte_ra_mcs_t mcs; + srslte_ra_mcs_t mcs_data; + mcs.idx = this->sib13.mbsfn_area_info_list_r9[0].signalling_mcs_r9; + mcs_data.idx = this->mcch.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9; + srslte_dl_fill_ra_mcs(&mcs, this->cell_config.cell.nof_prb); + srslte_dl_fill_ra_mcs(&mcs_data, this->cell_config.cell.nof_prb); + if(is_mcch){ + + build_mch_sched(mcs_data.tbs); + mch.mcch_payload = mcch_payload_buffer; + mch.current_sf_allocation_num = 1; + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", mch.mtch_sched[0].lcid, mch.mtch_sched[0].stop, tti); + for(uint32_t i = 0; i < mch.num_mtch_sched; i++) { + mch.pdu[i].lcid = srslte::sch_subh::MCH_SCHED_INFO; + // mch.mtch_sched[i].lcid = 1+i; + } + + mch.pdu[mch.num_mtch_sched].lcid = 0; + mch.pdu[mch.num_mtch_sched].nbytes = current_mcch_length; + dl_sched_res->sched_grants[0].rnti = SRSLTE_MRNTI; + dl_sched_res->sched_grants[0].data[0] = ue_db[SRSLTE_MRNTI]->generate_mch_pdu(mch, mch.num_mtch_sched + 1, mcs.tbs/8); + + } else { + uint32_t current_lcid = 1; + uint32_t mtch_index = 0; + uint32_t mtch_stop = mch.mtch_sched[mch.num_mtch_sched -1].stop; + + for(uint32_t i = 0;i < mch.num_mtch_sched;i++) { + if(mch.current_sf_allocation_num <= mch.mtch_sched[i].stop){ + current_lcid = mch.mtch_sched[i].lcid; + mtch_index = i; + break; + } + } + if(mch.current_sf_allocation_num <= mtch_stop) { + int requested_bytes = (mcs_data.tbs/8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size)?(mch.mtch_sched[mtch_index].lcid_buffer_size):((mcs_data.tbs/8) - 2); + int bytes_received = ue_db[SRSLTE_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; + mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; + dl_sched_res->sched_grants[0].rnti = SRSLTE_MRNTI; + if(bytes_received){ + dl_sched_res->sched_grants[0].data[0] = ue_db[SRSLTE_MRNTI]->generate_mch_pdu(mch, 1, mcs_data.tbs/8); + } + } else { + dl_sched_res->sched_grants[0].rnti = 0; + dl_sched_res->sched_grants[0].data[0] = NULL; + } + mch.current_sf_allocation_num++; + } + + return SRSLTE_SUCCESS; +} + +uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len) +{ + uint8_t grant_buffer[64]; + if (pdu_len < rar_payload_len) { + srslte::rar_pdu *pdu = &rar_pdu_msg[rar_idx]; + pdu->init_tx(rar_payload[rar_idx], pdu_len); + for (uint32_t i=0;inew_subh()) { + /* Search pending RAR */ + int idx = grants[i].ra_id; + pdu->get()->set_rapid(pending_rars[idx].preamble_idx); + pdu->get()->set_ta_cmd(pending_rars[idx].ta_cmd); + pdu->get()->set_temp_crnti(pending_rars[idx].temp_crnti); + pdu->get()->set_sched_grant(grant_buffer); + bzero(&pending_rars[idx], sizeof(pending_rar_t)); + } + } + pdu->write_packet(rar_payload[rar_idx]); + return rar_payload[rar_idx]; + } else { + Error("Assembling RAR: pdu_len > rar_payload_len (%d>%d)\n", pdu_len, rar_payload_len); + return NULL; + } +} + +uint8_t* mac::assemble_si(uint32_t index) +{ + rlc_h->read_pdu_bcch_dlsch(index, bcch_dlsch_payload); + return bcch_dlsch_payload; +} + +int mac::get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res) +{ + + log_h->step(tti); + + if (!started) { + return 0; + } + + if (!ul_sched_res) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Run scheduler with current info + sched_interface::ul_sched_res_t sched_result; + bzero(&sched_result, sizeof(sched_interface::ul_sched_res_t)); + if (scheduler.ul_sched(tti, &sched_result)<0) { + Error("Running scheduler\n"); + return SRSLTE_ERROR; + } + + pthread_rwlock_rdlock(&rwlock); + + // Copy DCI grants + ul_sched_res->nof_grants = 0; + int n = 0; + for (uint32_t i=0;i 0) { + // Get UE + uint16_t rnti = sched_result.pusch[i].rnti; + + if (ue_db.count(rnti)) { + // Copy grant info + ul_sched_res->sched_grants[n].rnti = rnti; + ul_sched_res->sched_grants[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; + ul_sched_res->sched_grants[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; + memcpy(&ul_sched_res->sched_grants[n].grant, &sched_result.pusch[i].dci, sizeof(srslte_ra_ul_dci_t)); + memcpy(&ul_sched_res->sched_grants[n].location, &sched_result.pusch[i].dci_location, sizeof(srslte_dci_location_t)); + + ul_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_rx_softbuffer(tti); + + if (sched_result.pusch[n].current_tx_nb == 0) { + srslte_softbuffer_rx_reset_tbs(ul_sched_res->sched_grants[n].softbuffer, sched_result.pusch[i].tbs*8); + } + ul_sched_res->sched_grants[n].data = ue_db[rnti]->request_buffer(tti, sched_result.pusch[i].tbs); + ul_sched_res->nof_grants++; + n++; + } else { + Warning("Invalid DL scheduling result. User 0x%x does not exist\n", rnti); + } + + } else { + Warning("Grant %d for rnti=0x%x has zero TBS\n", i, sched_result.pusch[i].rnti); + } + } + + // No more uses of ue_db beyond here + pthread_rwlock_unlock(&rwlock); + + // Copy PHICH actions + for (uint32_t i=0;iphich[i].ack = sched_result.phich[i].phich == sched_interface::ul_sched_phich_t::ACK; + ul_sched_res->phich[i].rnti = sched_result.phich[i].rnti; + } + ul_sched_res->nof_phich = sched_result.nof_phich_elems; + return SRSLTE_SUCCESS; +} + +void mac::tti_clock() +{ + timers_thread.tti_clock(); +} + +/******************************************************** + * + * Interface for upper layer timers + * + *******************************************************/ +uint32_t mac::timer_get_unique_id() +{ + return timers_db.get_unique_id(); +} + +void mac::timer_release_id(uint32_t timer_id) +{ + timers_db.release_id(timer_id); +} + +/* Front-end to upper-layer timers */ +srslte::timers::timer* mac::timer_get(uint32_t timer_id) +{ + return timers_db.get(timer_id); +} + + +/******************************************************** + * + * Class to run timers with normal priority + * + *******************************************************/ +void mac::timer_thread::run_thread() +{ + running=true; + ttisync.set_producer_cntr(0); + ttisync.resync(); + while(running) { + ttisync.wait(); + timers->step_all(); + } +} + +void mac::timer_thread::stop() +{ + running=false; + ttisync.increase(); + wait_thread_finish(); +} + +void mac::timer_thread::tti_clock() +{ + ttisync.increase(); +} + + + +/******************************************************** + * + * Class that runs a thread to process DL MAC PDUs from + * DEMUX unit + * + *******************************************************/ +mac::pdu_process::pdu_process(pdu_process_handler *h) : running(false) { + handler = h; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + have_data = false; + start(MAC_PDU_THREAD_PRIO); +} + +void mac::pdu_process::stop() +{ + pthread_mutex_lock(&mutex); + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void mac::pdu_process::notify() +{ + pthread_mutex_lock(&mutex); + have_data = true; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); +} + +void mac::pdu_process::run_thread() +{ + running = true; + while(running) { + have_data = handler->process_pdus(); + if (!have_data) { + pthread_mutex_lock(&mutex); + while(!have_data && running) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + } + } +} + +bool mac::process_pdus() +{ + pthread_rwlock_rdlock(&rwlock); + bool ret = false; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + uint16_t rnti = iter->first; + ret = ret | u->process_pdus(); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + + +void mac::write_mcch(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13, LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) +{ + bzero(&mcch_payload_buffer[0],sizeof(uint8_t)*3000); + + + LIBLTE_BIT_MSG_STRUCT bitbuffer; + liblte_rrc_pack_mcch_msg(mcch, &bitbuffer); + memcpy(&this->mcch ,mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + mch.num_mtch_sched = this->mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size; + for(uint32_t i = 0; i < mch.num_mtch_sched; i++){ + mch.mtch_sched[i].lcid = this->mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[i].logicalchannelid_r9; + } + + memcpy(&this->sib2,sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + memcpy(&this->sib2,sib13, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT)); // TODO: consolidate relevant parts into 1 struct + current_mcch_length = floor(bitbuffer.N_bits/8); + if(bitbuffer.N_bits%8 != 0) { + current_mcch_length++; + } + int rlc_header_len = 1; + current_mcch_length = current_mcch_length + rlc_header_len; + srslte_bit_pack_vector(&bitbuffer.msg[0], &mcch_payload_buffer[rlc_header_len], bitbuffer.N_bits); + ue_db[SRSLTE_MRNTI] = new ue; + ue_db[SRSLTE_MRNTI]->config(SRSLTE_MRNTI, cell.nof_prb, &scheduler, rrc_h, rlc_h, log_h); + + rrc_h->add_user(SRSLTE_MRNTI); +} + +} + + + diff --git a/srsenb/src/mac/scheduler.cc b/srsenb/src/mac/scheduler.cc new file mode 100644 index 0000000..8737d91 --- /dev/null +++ b/srsenb/src/mac/scheduler.cc @@ -0,0 +1,1130 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "srsenb/hdr/mac/scheduler.h" + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsenb { + + +/******************************************************* + * + * Initialization and sched configuration functions + * + *******************************************************/ +sched::sched() : bc_aggr_level(0), rar_aggr_level(0), avail_rbg(0), P(0), start_rbg(0), si_n_rbg(0), rar_n_rbg(0), + nof_rbg(0), sf_idx(0), sfn(0), current_cfi(0) { + current_tti = 0; + log_h = NULL; + dl_metric = NULL; + ul_metric = NULL; + rrc = NULL; + + bzero(&cfg, sizeof(cfg)); + bzero(®s, sizeof(regs)); + bzero(&used_cce, sizeof(used_cce)); + bzero(&sched_cfg, sizeof(sched_cfg)); + bzero(&common_locations, sizeof(common_locations)); + bzero(&pdsch_re, sizeof(pdsch_re)); + + for (int i = 0; i < 3; i++) { + bzero(rar_locations[i], sizeof(sched_ue::sched_dci_cce_t) * 10); + } + reset(); + + pthread_rwlock_init(&rwlock, NULL); +} + +sched::~sched() +{ + srslte_regs_free(®s); + pthread_rwlock_wrlock(&rwlock); + pthread_rwlock_unlock(&rwlock); + pthread_rwlock_destroy(&rwlock); +} + +void sched::init(rrc_interface_mac *rrc_, srslte::log* log) +{ + sched_cfg.pdsch_max_mcs = 28; + sched_cfg.pdsch_mcs = -1; + sched_cfg.pusch_max_mcs = 28; + sched_cfg.pusch_mcs = -1; + sched_cfg.nof_ctrl_symbols = 3; + log_h = log; + rrc = rrc_; + reset(); +} + +int sched::reset() +{ + bzero(pending_msg3, sizeof(pending_msg3_t)*10); + bzero(pending_rar, sizeof(sched_rar_t)*SCHED_MAX_PENDING_RAR); + bzero(pending_sibs, sizeof(sched_sib_t)*MAX_SIBS); + configured = false; + pthread_rwlock_wrlock(&rwlock); + ue_db.clear(); + pthread_rwlock_unlock(&rwlock); + return 0; +} + +void sched::set_sched_cfg(sched_interface::sched_args_t* sched_cfg_) +{ + if (sched_cfg_) { + memcpy(&sched_cfg, sched_cfg_, sizeof(sched_args_t)); + } +} + +void sched::set_metric(sched::metric_dl* dl_metric_, sched::metric_ul* ul_metric_) +{ + dl_metric = dl_metric_; + ul_metric = ul_metric_; +} + +int sched::cell_cfg(sched_interface::cell_cfg_t* cell_cfg) +{ + // Basic cell config checks + if (cell_cfg->si_window_ms == 0) { + Error("SCHED: Invalid si-window length 0 ms\n"); + return -1; + } + + memcpy(&cfg, cell_cfg, sizeof(sched_interface::cell_cfg_t)); + + // Get DCI locations + if (srslte_regs_init(®s, cfg.cell)) { + Error("Getting DCI locations\n"); + return SRSLTE_ERROR; + } + + P = srslte_ra_type0_P(cfg.cell.nof_prb); + si_n_rbg = ceilf((float) 4/P); + rar_n_rbg = ceilf((float) 3/P); + nof_rbg = (uint32_t) ceil((float) cfg.cell.nof_prb/P); + + // Compute Common locations for DCI for each CFI + for (uint32_t cfi=0;cfi<3;cfi++) { + generate_cce_location(®s, &common_locations[cfi], cfi+1); + } + + // Compute UE locations for RA-RNTI + for (int cfi=0;cfi<3;cfi++) { + for (int sf_idx=0;sf_idx<10;sf_idx++) { + uint16_t ra_rnti = 1+sf_idx; + generate_cce_location(®s, &rar_locations[cfi][sf_idx], cfi+1, sf_idx); + } + } + configured = true; + + return 0; +} + + +/******************************************************* + * + * FAPI-like main sched interface. Wrappers to UE object + * + *******************************************************/ + +int sched::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *ue_cfg) +{ + // Add or config user + pthread_rwlock_rdlock(&rwlock); + ue_db[rnti].set_cfg(rnti, ue_cfg, &cfg, ®s, log_h); + ue_db[rnti].set_max_mcs(sched_cfg.pusch_max_mcs, sched_cfg.pdsch_max_mcs); + ue_db[rnti].set_fixed_mcs(sched_cfg.pusch_mcs, sched_cfg.pdsch_mcs); + pthread_rwlock_unlock(&rwlock); + + return 0; +} + +int sched::ue_rem(uint16_t rnti) +{ + int ret = 0; + pthread_rwlock_wrlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db.erase(rnti); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +bool sched::ue_exists(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + bool ret = (ue_db.count(rnti) == 1); + pthread_rwlock_unlock(&rwlock); + return ret; +} + +void sched::phy_config_enabled(uint16_t rnti, bool enabled) +{ + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].phy_config_enabled(current_tti, enabled); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_bearer_cfg(lc_id, cfg); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].rem_bearer(lc_id); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +uint32_t sched::get_dl_buffer(uint16_t rnti) +{ + uint32_t ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ret = ue_db[rnti].get_pending_dl_new_data(current_tti); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +uint32_t sched::get_ul_buffer(uint16_t rnti) +{ + uint32_t ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ret = ue_db[rnti].get_pending_ul_new_data(current_tti); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].dl_buffer_state(lc_id, tx_queue, retx_queue); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].mac_buffer_state(ce_code); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_ant_info(uint16_t rnti, LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *dl_ant_info) { + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_ant_info(dl_ant_info); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t tb_idx, bool ack) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ret = ue_db[rnti].set_ack_info(tti, tb_idx, ack); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::ul_crc_info(uint32_t tti, uint16_t rnti, bool crc) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_ul_crc(tti, crc); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_ri(tti, cqi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t pmi_value) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_pmi(tti, pmi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + int ret = 0; + pthread_rwlock_rdlock(&rwlock); + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_cqi(tti, cqi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int sched::dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size) +{ + for (int i=0;i 0) { + x = (i-1)*cfg.si_window_ms; + sf = x%10; + } + if ((sfn%(cfg.sibs[i].period_rf)) == x/10 && sf_idx == sf) { + pending_sibs[i].is_in_window = true; + pending_sibs[i].window_start = current_tti; + pending_sibs[i].n_tx = 0; + } + } else { + if (i > 0) { + if (srslte_tti_interval(current_tti, pending_sibs[i].window_start) > cfg.si_window_ms) { + pending_sibs[i].is_in_window = false; + pending_sibs[i].window_start = 0; + } + } else { + // SIB1 is always in window + if (pending_sibs[0].n_tx == 4) { + pending_sibs[0].n_tx = 0; + } + } + } + } + } + + for (int i=0;i si_n_rbg) + { + uint32_t nof_tx = 4; + if (i > 0) { + if (cfg.si_window_ms <= 10) { + nof_tx = 1; + } else if (cfg.si_window_ms <= 20) { + nof_tx = 2; + } else if (cfg.si_window_ms <= 30) { + nof_tx = 3; + } else { + nof_tx = 4; + } + } + uint32_t n_sf = (current_tti-pending_sibs[i].window_start); + if ((i == 0 && (sfn%2) == 0 && sf_idx == 5) || + (i > 0 && n_sf >= (cfg.si_window_ms/nof_tx)*pending_sibs[i].n_tx && sf_idx==9)) + { + uint32_t rv = get_rvidx(pending_sibs[i].n_tx); + + // Try to allocate DCI first + if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) { + int tbs = generate_format1a(start_rbg*P, si_n_rbg*P, cfg.sibs[i].len, rv, &bc[nof_bc_elems].dci); + if (tbs >= (int) cfg.sibs[i].len) { + bc[nof_bc_elems].index = i; + bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::BCCH; + bc[nof_bc_elems].tbs = tbs; + + Debug("SCHED: SIB%d, start_rb=%d, n_rb=%d, rv=%d, len=%d, period=%d, mcs=%d\n", + i+1, start_rbg*P, si_n_rbg*P, rv, cfg.sibs[i].len, cfg.sibs[i].period_rf, bc[nof_bc_elems].dci.mcs_idx); + + pending_sibs[i].n_tx++; + + nof_bc_elems++; + avail_rbg -= si_n_rbg; + start_rbg += si_n_rbg; + } else { + Error("Could not allocate DCI Format1A for SIB%d, len=%d\n", i+1, cfg.sibs[i].len); + } + } else { + Warning("SCHED: Could not schedule DCI for SIB=%d, L=%d\n", i+1, bc_aggr_level); + } + } + } + } + + // Schedule Paging + if (rrc) { + uint32_t paging_payload = 0; + if (rrc->is_paging_opportunity(current_tti, &paging_payload)) { + if (avail_rbg > si_n_rbg && paging_payload) + { + if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) { + int tbs = generate_format1a(start_rbg*P, si_n_rbg*P, paging_payload, 0, &bc[nof_bc_elems].dci); + if (tbs > 0) { + + bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::PCCH; + bc[nof_bc_elems].tbs = tbs; + nof_bc_elems++; + + Info("SCHED: PCH start_rb=%d, tbs=%d, mcs=%d\n", start_rbg, tbs, bc[nof_bc_elems].dci.mcs_idx); + + avail_rbg -= si_n_rbg; + start_rbg += si_n_rbg; + + } + } + } + } + } + + return nof_bc_elems; +} + + +// Schedules RAR +int sched::dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST]) +{ + + int nof_rar_elems = 0; + for (uint32_t i=0;i 0 && avail_rbg >= rar_n_rbg) + { + /* Check if we are still within the RAR window, otherwise discard it */ + if (current_tti <= (pending_rar[i].rar_tti + cfg.prach_rar_window + 3)%10240 && current_tti >= pending_rar[i].rar_tti + 3) + { + // Try to schedule DCI for this RAR + if (generate_dci(&rar[nof_rar_elems].dci_location, &rar_locations[current_cfi-1][sf_idx], rar_aggr_level)) { + + /* Find all pending RARs with same transmission TTI */ + uint32_t tti = pending_rar[i].rar_tti; + uint32_t rar_sfidx = (tti+1)%10; + uint32_t buf_rar = 0; + uint32_t nof_grants = 0; + for (int j=0;jinfo("SCHED: RAR, ra_id=%d, rnti=0x%x, rarnti_idx=%d, start_rb=%d, n_rb=%d, rar_grant_rba=%d, rar_grant_mcs=%d\n", + pending_rar[j].ra_id, pending_rar[j].rnti, rar_sfidx, start_rbg*P, rar_n_rbg*P, + rar[nof_rar_elems].grants[nof_grants].grant.rba, + rar[nof_rar_elems].grants[nof_grants].grant.trunc_mcs); + } else { + log_h->warning("Only 1 RA is responded at a time. Found %d for TTI=%d\n", nof_grants+1, tti); + } + nof_grants++; + } + } + + rar[nof_rar_elems].nof_grants = nof_grants; + rar[nof_rar_elems].rarnti = rar_sfidx; + + if (generate_format1a(start_rbg*P, rar_n_rbg*P, buf_rar, 0, &rar[nof_rar_elems].dci) >= 0) { + rar[nof_rar_elems].tbs = buf_rar; + nof_rar_elems++; + avail_rbg -= rar_n_rbg; + start_rbg += rar_n_rbg; + } else { + Error("SCHED: Allocating Format1A grant\n"); + } + + } else { + log_h->warning("SCHED: Could not schedule DCI for RAR tti=%d, L=%d\n", pending_rar[i].rar_tti, rar_aggr_level); + } + } else { + log_h->console("SCHED: Could not transmit RAR within the window (RA TTI=%d, Window=%d, Now=%d)\n", + pending_rar[i].rar_tti, cfg.prach_rar_window, current_tti); + pending_rar[i].buf_rar = 0; + pending_rar[i].rar_tti = 0; + } + } + } + return nof_rar_elems; +} + +// Schedules data to users +int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]) +{ + uint32_t nof_ctrl_symbols = (cfg.cell.nof_prb<10)?(current_cfi+1):current_cfi; + dl_metric->new_tti(ue_db, start_rbg, avail_rbg, nof_ctrl_symbols, current_tti); + + int nof_data_elems = 0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + uint32_t data_before = user->get_pending_dl_new_data(current_tti); + dl_harq_proc *h = dl_metric->get_user_allocation(user); + srslte_dci_format_t dci_format = user->get_dci_format(); + data[nof_data_elems].dci_format = dci_format; + + uint32_t aggr_level = user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, cfg.cell.nof_prb, cfg.cell.nof_ports)); + if (h) { + // Try to schedule DCI first + if (generate_dci(&data[nof_data_elems].dci_location, + user->get_locations(current_cfi, sf_idx), + aggr_level, user)) + { + bool is_newtx = h->is_empty(0) && h->is_empty(1) ; + int tbs = 0; + switch(dci_format) { + case SRSLTE_DCI_FORMAT1: + tbs = user->generate_format1(h, &data[nof_data_elems], current_tti, current_cfi); + break; + case SRSLTE_DCI_FORMAT2: + tbs = user->generate_format2(h, &data[nof_data_elems], current_tti, current_cfi); + break; + case SRSLTE_DCI_FORMAT2A: + tbs = user->generate_format2a(h, &data[nof_data_elems], current_tti, current_cfi); + break; + default: + Error("DCI format (%d) not implemented\n", dci_format); + } + if (tbs > 0) { + log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, n_rtx=%d, tbs=%d, buffer=%d/%d, tb_en={%s,%s}\n", + !is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(), + data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, h->nof_retx(0) + h->nof_retx(1), + tbs, + data_before, user->get_pending_dl_new_data(current_tti), + data[nof_data_elems].dci.tb_en[0]?"y":"n", + data[nof_data_elems].dci.tb_en[1]?"y":"n"); + nof_data_elems++; + } else { + log_h->warning("SCHED: Error DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, tbs=%d, buffer=%d\n", + !is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(), + data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, + tbs, user->get_pending_dl_new_data(current_tti)); + } + } else { + for(uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + h->reset(tb); + } + Warning("SCHED: Could not schedule DL DCI for rnti=0x%x, pid=%d, cfi=%d\n", rnti, h->get_id(), current_cfi); + } + } + + // Reset blocked PIDs + user->reset_timeout_dl_harq(current_tti); + + } + + + + return nof_data_elems; +} + +// Downlink Scheduler +int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result) +{ + if (!configured) { + return 0; + } + + /* If ul_sched() not yet called this tti, reset CCE state */ + if (current_tti != tti) { + bzero(used_cce, MAX_CCE*sizeof(bool)); + } + + /* Initialize variables */ + current_tti = tti; + sfn = tti/10; + sf_idx = tti%10; + avail_rbg = nof_rbg; + start_rbg = 0; + current_cfi = sched_cfg.nof_ctrl_symbols; + bc_aggr_level = 2; + rar_aggr_level = 2; + bzero(sched_result, sizeof(sched_interface::dl_sched_res_t)); + + pthread_rwlock_rdlock(&rwlock); + + /* Schedule Broadcast data */ + sched_result->nof_bc_elems += dl_sched_bc(sched_result->bc); + + /* Schedule RAR */ + sched_result->nof_rar_elems += dl_sched_rar(sched_result->rar); + + /* Schedule pending RLC data */ + sched_result->nof_data_elems += dl_sched_data(sched_result->data); + + pthread_rwlock_unlock(&rwlock); + + /* Set CFI */ + sched_result->cfi = current_cfi; + + return 0; +} + +// Uplink sched +int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched_result) +{ + typedef std::map::iterator it_t; + + if (!configured) { + return 0; + } + + if (cfg.prach_freq_offset + 6 > cfg.cell.nof_prb) { + fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", cfg.prach_freq_offset); + return -1; + } + + /* If dl_sched() not yet called this tti (this tti is +4ms advanced), reset CCE state */ + if (TTI_TX(current_tti) != tti) { + bzero(used_cce, MAX_CCE*sizeof(bool)); + } + + /* Initialize variables */ + current_tti = tti; + sfn = tti/10; + if (tti > HARQ_DELAY_MS) { + sf_idx = (tti-HARQ_DELAY_MS)%10; + } else { + sf_idx = (tti+10240-HARQ_DELAY_MS)%10; + } + int nof_dci_elems = 0; + int nof_phich_elems = 0; + + // current_cfi is set in dl_sched() + bzero(sched_result, sizeof(sched_interface::ul_sched_res_t)); + ul_metric->reset_allocation(cfg.cell.nof_prb); + + pthread_rwlock_rdlock(&rwlock); + + // Get HARQ process for this TTI + for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + user->has_pucch = false; + + ul_harq_proc *h = user->get_ul_harq(current_tti); + + /* Indicate PHICH acknowledgment if needed */ + if (h->has_pending_ack()) { + sched_result->phich[nof_phich_elems].phich = h->get_ack(0)?ul_sched_phich_t::ACK:ul_sched_phich_t::NACK; + sched_result->phich[nof_phich_elems].rnti = rnti; + nof_phich_elems++; + } + } + + // Update available allocation if there's a pending RAR + if (pending_msg3[tti%10].enabled) { + ul_harq_proc::ul_alloc_t msg3 = {pending_msg3[tti%10].n_prb, pending_msg3[tti%10].L}; + if(ul_metric->update_allocation(msg3)) { + log_h->debug("SCHED: Allocated msg3 RBs within (%d,%d)\n", msg3.RB_start, msg3.RB_start + msg3.L); + } + else { + log_h->warning("SCHED: Could not allocate msg3 within (%d,%d)\n", msg3.RB_start, msg3.RB_start + msg3.L); + } + } + + // Allocate PUCCH resources + if (cfg.nrb_pucch >= 0) { + ul_harq_proc::ul_alloc_t pucch = {0, (uint32_t) cfg.nrb_pucch}; + if(!ul_metric->update_allocation(pucch)) { + log_h->warning("SCHED: Failed to allocate PUCCH\n"); + } + pucch.RB_start = cfg.cell.nof_prb-cfg.nrb_pucch; + pucch.L = (uint32_t) cfg.nrb_pucch; + if(!ul_metric->update_allocation(pucch)) { + log_h->warning("SCHED: Failed to allocate PUCCH\n"); + } else { + log_h->debug("Allocating PUCCH (%d,%d)\n", pucch.RB_start, pucch.RB_start+pucch.L); + } + } else { + for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + uint32_t prb_idx[2] = {0, 0}; + if (user->get_pucch_sched(current_tti, prb_idx)) { + user->has_pucch = true; + // allocate PUCCH + for (int i=0;i<2;i++) { + ul_harq_proc::ul_alloc_t pucch = {prb_idx[i], 1}; + ul_metric->update_allocation(pucch); + } + } + } + } + + // reserve PRBs for PRACH + if(srslte_prach_tti_opportunity_config(cfg.prach_config, tti, -1)) { + ul_harq_proc::ul_alloc_t prach = {cfg.prach_freq_offset, 6}; + if(!ul_metric->update_allocation(prach)) { + log_h->warning("SCHED: Failed to allocate PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L); + } + else { + log_h->debug("SCHED: Allocated PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L); + } + } + + ul_metric->new_tti(ue_db, cfg.cell.nof_prb, current_tti); + + // Now allocate PUSCH + for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + ul_harq_proc *h = NULL; + + // Check if there are pending Msg3 transmissions + bool is_rar = false; + if (pending_msg3[tti%10].enabled && pending_msg3[tti%10].rnti == rnti) { + h = user->get_ul_harq(tti); + if (h) { + ul_harq_proc::ul_alloc_t alloc; + alloc.L = pending_msg3[tti%10].L; + alloc.RB_start = pending_msg3[tti%10].n_prb; + h->set_alloc(alloc); + h->set_rar_mcs(pending_msg3[tti%10].mcs); + is_rar = true; + pending_msg3[tti%10].enabled = false; + } else { + Warning("No HARQ pid available for transmission of Msg3\n"); + } + } else { + h = ul_metric->get_user_allocation(user); + } + if (h) + { + ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); + bool is_newtx = h->is_empty(0); + bool needs_pdcch = !h->is_adaptive_retx() && !is_rar; + + // Set number of retx + if (is_newtx) { + if (is_rar) { + h->set_max_retx(cfg.maxharq_msg3tx); + } else { + h->set_max_retx(user->get_max_retx()); + } + } + + // Generate PDCCH except for RAR and non-adaptive retx + if (needs_pdcch) { + uint32_t aggr_level = user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, cfg.cell.nof_prb, cfg.cell.nof_ports)); + if (!generate_dci(&sched_result->pusch[nof_dci_elems].dci_location, + user->get_locations(current_cfi, sf_idx), + aggr_level)) + { + h->reset(0); + log_h->warning("SCHED: Could not schedule UL DCI rnti=0x%x, pid=%d, L=%d, sf_idx=%d\n", + rnti, h->get_id(), aggr_level, sf_idx); + + sched_result->pusch[nof_dci_elems].needs_pdcch = false; + } else { + sched_result->pusch[nof_dci_elems].needs_pdcch = true; + } + } else { + sched_result->pusch[nof_dci_elems].needs_pdcch = false; + } + + // Generate grant unless DCI could not be generated and was required + if (sched_result->pusch[nof_dci_elems].needs_pdcch == needs_pdcch) { + uint32_t pending_data_before = user->get_pending_ul_new_data(current_tti); + if (user->generate_format0(h, &sched_result->pusch[nof_dci_elems], current_tti, user->needs_cqi(tti, true)) > 0) + { + + if (is_newtx) { + // Un-trigger SR + user->unset_sr(); + } + + log_h->info("SCHED: %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=(%d,%d), n_rtx=%d, tbs=%d, bsr=%d (%d-%d)\n", + is_rar?"RAR":"UL", + is_newtx?"tx":"retx", + rnti, h->get_id(), + sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce, + alloc.RB_start, alloc.RB_start+alloc.L, h->nof_retx(0), sched_result->pusch[nof_dci_elems].tbs, + user->get_pending_ul_new_data(current_tti),pending_data_before, user->get_pending_ul_old_data()); + + nof_dci_elems++; + } else { + log_h->warning("SCHED: Error %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=(%d,%d), tbs=%d, bsr=%d\n", + is_rar?"RAR":"UL", + is_newtx?"tx":"retx", + rnti, h->get_id(), + sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce, + alloc.RB_start, alloc.RB_start+alloc.L, sched_result->pusch[nof_dci_elems].tbs, + user->get_pending_ul_new_data(current_tti)); + } + } + } + } + + // Update pending data counters after this TTI + for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue *) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + user->get_ul_harq(current_tti)->reset_pending_data(); + } + + pthread_rwlock_unlock(&rwlock); + + sched_result->nof_dci_elems = nof_dci_elems; + sched_result->nof_phich_elems = nof_phich_elems; + + return SRSLTE_SUCCESS; +} + + +/******************************************************* + * + * Helper functions + * + *******************************************************/ + +void sched::generate_cce_location(srslte_regs_t *regs_, sched_ue::sched_dci_cce_t* location, + uint32_t cfi, uint32_t sf_idx, uint16_t rnti) +{ + srslte_dci_location_t loc[64]; + uint32_t nloc = 0; + if (rnti == 0) { + nloc = srslte_pdcch_common_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi), + loc, 64); + } else { + nloc = srslte_pdcch_ue_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi), + loc, 64, sf_idx, rnti); + } + + for (uint32_t l=0;l<=3;l++) { + int n=0; + for (uint32_t i=0;icce_start[l][n] = loc[i].ncce; + n++; + } + } + location->nof_loc[l] = n; + } + +} + + +#define NCCE(L) (1<nof_loc[aggr_level]) { + Error("In generate_dci(): No locations for aggr_level=%d\n", aggr_level); + return false; + } + uint32_t nof_cand = 0; + uint32_t test_cand = rand()%locations->nof_loc[aggr_level]; + bool allocated=false; + + while(nof_candnof_loc[aggr_level] && !allocated) { + uint32_t ncce = locations->cce_start[aggr_level][test_cand]; + bool used = false; + if (user) { + used = user->pucch_sr_collision(current_tti, ncce); + } + for (int j=0;jnof_loc[aggr_level]) { + test_cand = 0; + } + nof_cand++; + } else { + for (int j=0;jL = aggr_level; + sched_location->ncce = locations->cce_start[aggr_level][test_cand]; + } + + return allocated; +} + +int sched::generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs_bytes, uint32_t rv, srslte_ra_dl_dci_t *dci) +{ + /* Calculate I_tbs for this TBS */ + int tbs = tbs_bytes*8; + int i; + int mcs = -1; + for (i=0;i<27;i++) { + if (srslte_ra_tbs_from_idx(i, 2) >= tbs) { + dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_2; + mcs = i; + tbs = srslte_ra_tbs_from_idx(i, 2); + break; + } else if (srslte_ra_tbs_from_idx(i, 3) >= tbs) { + dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_3; + mcs = i; + tbs = srslte_ra_tbs_from_idx(i, 3); + break; + } + } + if (i == 28) { + Error("Can't allocate Format 1A for TBS=%d\n", tbs); + return -1; + } + + Debug("ra_tbs=%d/%d, tbs_bytes=%d, tbs=%d, mcs=%d\n", + srslte_ra_tbs_from_idx(mcs, 2),srslte_ra_tbs_from_idx(mcs, 3),tbs_bytes,tbs,mcs); + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + dci->type2_alloc.mode = srslte_ra_type2_t::SRSLTE_RA_TYPE2_LOC; + dci->type2_alloc.L_crb = l_crb; + dci->type2_alloc.RB_start = rb_start; + dci->harq_process = 0; + dci->mcs_idx = mcs; + dci->rv_idx = rv; + dci->tb_en[0] = true; + dci->tb_en[1] = false; + + return tbs; +} + + + + + +} + + diff --git a/srsenb/src/mac/scheduler_harq.cc b/srsenb/src/mac/scheduler_harq.cc new file mode 100644 index 0000000..c860c7a --- /dev/null +++ b/srsenb/src/mac/scheduler_harq.cc @@ -0,0 +1,274 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "srsenb/hdr/mac/scheduler.h" + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsenb { + +/****************************************************** + * + * These classes manage the HARQ Processes. + * There is a common class and two child classes for UL and DL. + * + ******************************************************/ + +void harq_proc::config(uint32_t id_, uint32_t max_retx_, srslte::log* log_h_) +{ + log_h = log_h_; + id = id_; + max_retx = max_retx_; + for (int i = 0; i < SRSLTE_MAX_TB; i++) { + ndi[i] = false; + } +} + +void harq_proc::set_max_retx(uint32_t max_retx_) { + log_h->debug("Set max_retx=%d pid=%d\n", max_retx_, id); + max_retx = max_retx_; +} + +uint32_t harq_proc::get_id() +{ + return id; +} + +void harq_proc::reset(uint32_t tb_idx) +{ + active[tb_idx] = false; + ack[tb_idx] = true; + ack_received[tb_idx] = false; + n_rtx[tb_idx] = 0; + tti = 0; + last_mcs[tb_idx] = -1; + last_tbs[tb_idx] = -1; + tx_cnt[tb_idx] = 0; +} + +bool harq_proc::is_empty(uint32_t tb_idx) +{ + return !active[tb_idx] || (active[tb_idx] && ack[tb_idx] && ack_received[tb_idx]); +} + +bool harq_proc::has_pending_retx_common(uint32_t tb_idx) +{ + return !ack[tb_idx] && n_rtx[tb_idx] < max_retx; +} + +uint32_t harq_proc::get_tti() +{ + return (uint32_t) tti; +} + +bool harq_proc::get_ack(uint32_t tb_idx) +{ + return ack[tb_idx]; +} + +void harq_proc::set_ack(uint32_t tb_idx, bool ack_) +{ + ack[tb_idx] = ack_; + ack_received[tb_idx] = true; + log_h->debug("ACK=%d received pid=%d, tb_idx=%d, n_rtx=%d, max_retx=%d\n", ack_, id, tb_idx, n_rtx[tb_idx], max_retx); + if (n_rtx[tb_idx] + 1 >= max_retx) { + Warning("SCHED: discarting TB %d pid=%d, tti=%d, maximum number of retx exceeded (%d)\n", tb_idx, id, tti, max_retx); + active[tb_idx] = false; + } +} + +void harq_proc::new_tx_common(uint32_t tb_idx, uint32_t tti_, int mcs, int tbs) +{ + reset(tb_idx); + ndi[tb_idx] = !ndi[tb_idx]; + tti = tti_; + tx_cnt[tb_idx]++; + last_mcs[tb_idx] = mcs; + last_tbs[tb_idx] = tbs; + + if (max_retx) { + active[tb_idx] = true; + } else { + active[tb_idx] = false; // Can reuse this process if no retx are allowed + } +} + +void harq_proc::new_retx(uint32_t tb_idx, uint32_t tti_, int *mcs, int *tbs) +{ + ack_received[tb_idx] = false; + tti = tti_; + n_rtx[tb_idx]++; + if (mcs) { + *mcs = last_mcs[tb_idx]; + } + if (tbs) { + *tbs = last_tbs[tb_idx]; + } +} + +uint32_t harq_proc::nof_tx(uint32_t tb_idx) +{ + return tx_cnt[tb_idx]; +} + +uint32_t harq_proc::nof_retx(uint32_t tb_idx) +{ + return n_rtx[tb_idx]; +} + +bool harq_proc::get_ndi(uint32_t tb_idx) +{ + return ndi[tb_idx]; +} + +/****************************************************** + * UE::DL HARQ class * + ******************************************************/ + +void dl_harq_proc::new_tx(uint32_t tb_idx, uint32_t tti, int mcs, int tbs, uint32_t n_cce_) +{ + n_cce = n_cce_; + new_tx_common(tb_idx, tti, mcs, tbs); +} + +uint32_t dl_harq_proc::get_n_cce() +{ + return n_cce; +} + +uint32_t dl_harq_proc::get_rbgmask() +{ + return rbgmask; +} + +void dl_harq_proc::set_rbgmask(uint32_t new_mask) +{ + rbgmask = new_mask; +} + +bool dl_harq_proc::has_pending_retx(uint32_t tb_idx, uint32_t current_tti) +{ + return srslte_tti_interval(current_tti, tti) >= (2*HARQ_DELAY_MS) && has_pending_retx_common(tb_idx); +} + +int dl_harq_proc::get_tbs(uint32_t tb_idx) +{ + return last_tbs[tb_idx]; +} + + + +/****************************************************** + * UE::UL HARQ class * + ******************************************************/ + +ul_harq_proc::ul_alloc_t ul_harq_proc::get_alloc() +{ + return allocation; +} + +void ul_harq_proc::set_alloc(ul_harq_proc::ul_alloc_t alloc) +{ + is_adaptive = false; + memcpy(&allocation, &alloc, sizeof(ul_alloc_t)); +} + +void ul_harq_proc::re_alloc(ul_harq_proc::ul_alloc_t alloc) +{ + is_adaptive = true; + memcpy(&allocation, &alloc, sizeof(ul_alloc_t)); +} + +bool ul_harq_proc::is_adaptive_retx() +{ + return is_adaptive; +} + +void ul_harq_proc::new_tx(uint32_t tti_, int mcs, int tbs) +{ + need_ack = true; + new_tx_common(0, tti_, mcs, tbs); + pending_data = tbs; +} + + +bool ul_harq_proc::has_pending_ack() +{ + bool ret = need_ack; + + // Reset if already received a positive ACK + if (active[0] && ack[0]) { + active[0] = false; + } + if (!active[0]) { + need_ack = false; + } + return ret; +} + + + +void ul_harq_proc::reset_pending_data() +{ + if (!active[0]) { + pending_data = 0; + } +} + + +uint32_t ul_harq_proc::get_pending_data() +{ + return (uint32_t) pending_data; +} + +void ul_harq_proc::set_rar_mcs(uint32_t mcs) +{ + rar_mcs = mcs; + has_rar_mcs = true; +} + +bool ul_harq_proc::get_rar_mcs(int *mcs) +{ + if (has_rar_mcs) { + if (mcs) { + *mcs = (int) rar_mcs; + } + has_rar_mcs = false; + return true; + } + return false; +} + + + +} diff --git a/srsenb/src/mac/scheduler_metric.cc b/srsenb/src/mac/scheduler_metric.cc new file mode 100644 index 0000000..517590c --- /dev/null +++ b/srsenb/src/mac/scheduler_metric.cc @@ -0,0 +1,408 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srsenb/hdr/mac/scheduler_harq.h" +#include "srsenb/hdr/mac/scheduler_metric.h" + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsenb { + + + +/***************************************************************** + * + * Downlink Metric + * + *****************************************************************/ + +uint32_t dl_metric_rr::calc_rbg_mask(bool mask[MAX_RBG]) +{ + // Build RBG bitmask + uint32_t rbg_bitmask = 0; + for (uint32_t n=0;n 0) { + if ((mask & 1) == 1) { + count++; + } + mask >>= 1; + } + return count; +} + +uint32_t dl_metric_rr::get_required_rbg(sched_ue *user, uint32_t tti) +{ + dl_harq_proc *h = user->get_pending_dl_harq(tti); + if (h) { + return count_rbg(h->get_rbgmask()); + } + uint32_t pending_data = user->get_pending_dl_new_data(current_tti); + return user->prb_to_rbg(user->get_required_prb_dl(pending_data, nof_ctrl_symbols)); +} + +void dl_metric_rr::new_tti(std::map &ue_db, uint32_t start_rbg, uint32_t nof_rbg, uint32_t nof_ctrl_symbols_, uint32_t tti) +{ + total_rbg = start_rbg+nof_rbg; + for (uint32_t i=0;i::iterator iter = ue_db.begin(); + std::advance(iter,priority_idx); + for(uint32_t ue_count = 0 ; ue_count < ue_db.size() ; ++iter, ++ue_count) { + if(iter==ue_db.end()) { + iter = ue_db.begin(); // wrap around + } + sched_ue *user = (sched_ue*) &iter->second; + user->dl_next_alloc = apply_user_allocation(user); + } +} + +bool dl_metric_rr::new_allocation(uint32_t nof_rbg, uint32_t *rbgmask) { + bool mask_bit[MAX_RBG]; + bzero(mask_bit, sizeof(bool)*MAX_RBG); + + for (uint32_t i=0;i 0;i++) { + if (used_rbg[i]) { + mask_bit[i] = false; + } else { + mask_bit[i] = true; + nof_rbg--; + } + } + if (rbgmask) { + *rbgmask = calc_rbg_mask(mask_bit); + } + return (nof_rbg == 0); +} + +void dl_metric_rr::update_allocation(uint32_t new_mask) { + used_rbg_mask |= new_mask; + for (uint32_t n=0;nget_pending_dl_harq(current_tti); + uint32_t req_bytes = user->get_pending_dl_new_data_total(current_tti); + + // Schedule retx if we have space +#if ASYNC_DL_SCHED + if (h) { +#else + if (h && !h->is_empty()) { +#endif + uint32_t retx_mask = h->get_rbgmask(); + // If can schedule the same mask, do it + if (!allocation_is_valid(retx_mask)) { + update_allocation(retx_mask); + return h; + } + + // If not, try to find another mask in the current tti + uint32_t nof_rbg = count_rbg(retx_mask); + if (nof_rbg < available_rbg) { + if (new_allocation(nof_rbg, &retx_mask)) { + update_allocation(retx_mask); + h->set_rbgmask(retx_mask); + return h; + } + } + } + // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID +#if ASYNC_DL_SCHED + h = user->get_empty_dl_harq(); + if (h) { +#else + if (h && h->is_empty()) { +#endif + // Allocate resources based on pending data + if (req_bytes) { + uint32_t pending_rbg = user->prb_to_rbg(user->get_required_prb_dl(req_bytes, nof_ctrl_symbols)); + uint32_t newtx_mask = 0; + new_allocation(pending_rbg, &newtx_mask); + if (newtx_mask) { + update_allocation(newtx_mask); + h->set_rbgmask(newtx_mask); + return h; + } + } + } + + return NULL; +} + +dl_harq_proc* dl_metric_rr::get_user_allocation(sched_ue *user) +{ + return user->dl_next_alloc; +} + + + + + + + + + +/***************************************************************** + * + * Uplink Metric + * + *****************************************************************/ + +void ul_metric_rr::reset_allocation(uint32_t nof_rb_) +{ + nof_rb = nof_rb_; + bzero(used_rb, nof_rb*sizeof(bool)); +} + +void ul_metric_rr::new_tti(std::map &ue_db, uint32_t nof_rb_, uint32_t tti) +{ + typedef std::map::iterator it_t; + current_tti = tti; + + if(ue_db.size()==0) + return; + + for(it_t it = ue_db.begin(); it != ue_db.end(); ++it) { + it->second.ul_next_alloc = NULL; + } + + // give priority in a time-domain RR basis + uint32_t priority_idx = (current_tti+(uint32_t)ue_db.size()/2) % (uint32_t)ue_db.size(); // make DL and UL interleaved + + // allocate reTxs first + it_t iter = ue_db.begin(); + for(uint32_t ue_count = 0 ; ue_count < ue_db.size() ; ++iter, ++ue_count) { + if(iter==ue_db.end()) { + iter = ue_db.begin(); // wrap around + } + sched_ue *user = (sched_ue *) &iter->second; + user->ul_next_alloc = allocate_user_retx_prbs(user); + } + + // give priority in a time-domain RR basis + iter = ue_db.begin(); + std::advance(iter,priority_idx); + for(uint32_t ue_count = 0 ; ue_count < ue_db.size() ; ++iter, ++ue_count) { + if(iter==ue_db.end()) { + iter = ue_db.begin(); // wrap around + } + sched_ue *user = (sched_ue*) &iter->second; + if (!user->ul_next_alloc) { + user->ul_next_alloc = allocate_user_newtx_prbs(user); + } + } +} + +bool ul_metric_rr::allocation_is_valid(ul_harq_proc::ul_alloc_t alloc) +{ + if (alloc.RB_start+alloc.L > nof_rb) { + return false; + } + for (uint32_t n=alloc.RB_start;nL < L;n++) { + if (!used_rb[n] && alloc->L == 0) { + alloc->RB_start = n; + } + if (!used_rb[n]) { + alloc->L++; + } else if (alloc->L > 0) { + // avoid edges + if (n < 3) { + alloc->RB_start = 0; + alloc->L = 0; + } else { + break; + } + } + } + if (alloc->L==0) { + return false; + } + + // Make sure L is allowed by SC-FDMA modulation + while (!srslte_dft_precoding_valid_prb(alloc->L)) { + alloc->L--; + } + return alloc->L == L; +} + +bool ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc) +{ + if(allocation_is_valid(alloc)) { + for (uint32_t n=alloc.RB_start;nget_ul_harq(current_tti); + + // if there are procedures and we have space + if(!h->is_empty(0)) { + ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); + + // If can schedule the same mask, do it + if (update_allocation(alloc)) { + return h; + } + + // If not, try to find another mask in the current tti + if (new_allocation(alloc.L, &alloc)) { + if(not update_allocation(alloc)) { + printf("SCHED: Computed UL allocation is not valid!\n"); + } + h->set_alloc(alloc); + return h; + } + } + return NULL; +} + +ul_harq_proc* ul_metric_rr::allocate_user_newtx_prbs(sched_ue* user) +{ + uint32_t pending_data = user->get_pending_ul_new_data(current_tti); + ul_harq_proc *h = user->get_ul_harq(current_tti); + + // find an empty PID + if (h->is_empty(0) and pending_data) { + uint32_t pending_rb = user->get_required_prb_ul(pending_data); + ul_harq_proc::ul_alloc_t alloc; + new_allocation(pending_rb, &alloc); + if (alloc.L) { + if(!update_allocation(alloc)) { + printf("SCHED: Computed UL allocation is not valid!\n"); + } + h->set_alloc(alloc); + return h; + } + } + return NULL; +} + +ul_harq_proc* ul_metric_rr::apply_user_allocation(sched_ue *user, bool retx_only) { + // Time-domain RR scheduling + uint32_t pending_data = user->get_pending_ul_new_data(current_tti); + ul_harq_proc *h = user->get_ul_harq(current_tti); + + // Schedule retx if we have space + if (!h->is_empty(0)) { + ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); + + // If can schedule the same mask, do it + if (allocation_is_valid(alloc)) { + update_allocation(alloc); + return h; + } + + // If not, try to find another mask in the current tti + if (new_allocation(alloc.L, &alloc)) { + update_allocation(alloc); + h->set_alloc(alloc); + return h; + } + } + + if (retx_only) { + return NULL; + } + + // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID + if (h->is_empty(0)) { + // Allocate resources based on pending data + if (pending_data) { + uint32_t pending_rb = user->get_required_prb_ul(pending_data); + ul_harq_proc::ul_alloc_t alloc; + new_allocation(pending_rb, &alloc); + if (alloc.L) { + update_allocation(alloc); + h->set_alloc(alloc); + return h; + } + } + } + return NULL; +} + +ul_harq_proc* ul_metric_rr::get_user_allocation(sched_ue *user) +{ + return user->ul_next_alloc; +} + +} diff --git a/srsenb/src/mac/scheduler_ue.cc b/srsenb/src/mac/scheduler_ue.cc new file mode 100644 index 0000000..c4cc38e --- /dev/null +++ b/srsenb/src/mac/scheduler_ue.cc @@ -0,0 +1,1234 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "srsenb/hdr/mac/scheduler_ue.h" +#include "srsenb/hdr/mac/scheduler.h" + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +/****************************************************** + * UE class * + ******************************************************/ + +namespace srsenb { + + +/******************************************************* + * + * Initialization and configuration functions + * + *******************************************************/ + +sched_ue::sched_ue() : dl_next_alloc(NULL), ul_next_alloc(NULL), has_pucch(false), power_headroom(0), rnti(0), max_mcs_dl(0), max_mcs_ul(0), + fixed_mcs_ul(0), fixed_mcs_dl(0), phy_config_dedicated_enabled(false) +{ + log_h = NULL; + + bzero(&cell, sizeof(cell)); + bzero(&lch, sizeof(lch)); + bzero(&dci_locations, sizeof(dci_locations)); + bzero(&dl_harq, sizeof(dl_harq)); + bzero(&ul_harq, sizeof(ul_harq)); + bzero(&dl_ant_info, sizeof(dl_ant_info)); + + pthread_mutex_init(&mutex, NULL); + reset(); +} + +sched_ue::~sched_ue() { + pthread_mutex_lock(&mutex); + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); +} + +void sched_ue::set_cfg(uint16_t rnti_, sched_interface::ue_cfg_t *cfg_, sched_interface::cell_cfg_t *cell_cfg, + srslte_regs_t *regs, srslte::log *log_h_) +{ + reset(); + + pthread_mutex_lock(&mutex); + rnti = rnti_; + log_h = log_h_; + memcpy(&cell, &cell_cfg->cell, sizeof(srslte_cell_t)); + P = srslte_ra_type0_P(cell.nof_prb); + + max_mcs_dl = 28; + max_mcs_ul = 28; + + if (cfg_) { + memcpy(&cfg, cfg_, sizeof(sched_interface::ue_cfg_t)); + } + + Info("SCHED: Added user rnti=0x%x\n", rnti); + // Config HARQ processes + for (int i=0;i 4) { + len -= 4; + } + if (lcid < sched_interface::MAX_LC) { + if (bearer_is_ul(&lch[lcid])) { + if (lch[lcid].bsr > (int) len) { + lch[lcid].bsr -= len; + } else { + lch[lcid].bsr = 0; + } + } + } + Debug("SCHED: recv_len=%d, lcid=%d, bsr={%d,%d,%d,%d}\n", len, lcid, + lch[0].bsr, lch[1].bsr, lch[2].bsr, lch[3].bsr); + + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_ul_crc(uint32_t tti, bool crc_res) +{ + pthread_mutex_lock(&mutex); + get_ul_harq(tti)->set_ack(0, crc_res); + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_dl_ri(uint32_t tti, uint32_t ri) +{ + pthread_mutex_lock(&mutex); + dl_ri = ri; + dl_ri_tti = tti; + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_dl_pmi(uint32_t tti, uint32_t pmi) +{ + pthread_mutex_lock(&mutex); + dl_pmi = pmi; + dl_pmi_tti = tti; + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_dl_cqi(uint32_t tti, uint32_t cqi) +{ + pthread_mutex_lock(&mutex); + dl_cqi = cqi; + dl_cqi_tti = tti; + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_dl_ant_info(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT *d) +{ + pthread_mutex_lock(&mutex); + memcpy(&dl_ant_info, d, sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + pthread_mutex_unlock(&mutex); +} + +void sched_ue::set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code) +{ + pthread_mutex_lock(&mutex); + ul_cqi = cqi; + ul_cqi_tti = tti; + pthread_mutex_unlock(&mutex); +} + +void sched_ue::tpc_inc() { + pthread_mutex_lock(&mutex); + if (power_headroom > 0) { + next_tpc_pusch = 3; + next_tpc_pucch = 3; + } + log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti); + pthread_mutex_unlock(&mutex); +} + +void sched_ue::tpc_dec() { + pthread_mutex_lock(&mutex); + next_tpc_pusch = 0; + next_tpc_pucch = 0; + log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti); + pthread_mutex_unlock(&mutex); +} + +/******************************************************* + * + * Functions used to generate DCI grants + * + *******************************************************/ + + +// Generates a Format1 grant +int sched_ue::generate_format1(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + pthread_mutex_lock(&mutex); + + srslte_ra_dl_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_dl_dci_t)); + + uint32_t sf_idx = tti%10; + + int mcs = 0; + int tbs = 0; + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci->type0_alloc.rbg_bitmask = h->get_rbgmask(); + + + // If this is the first transmission for this UE, make room for MAC Contention Resolution ID + bool need_conres_ce = false; + if (is_first_dl_tx()) { + need_conres_ce = true; + } + if (h->is_empty(0)) { + + uint32_t req_bytes = get_pending_dl_new_data_unlocked(tti); + + uint32_t nof_prb = format1_count_prb(h->get_rbgmask(), cell.nof_prb); + srslte_ra_dl_grant_t grant; + srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb); + uint32_t nof_ctrl_symbols = cfi+(cell.nof_prb<10?1:0); + uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols); + if (fixed_mcs_dl < 0) { + tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb)/8; + mcs = fixed_mcs_dl; + } + + h->new_tx(0, tti, mcs, tbs, data->dci_location.ncce); + + // Allocate MAC ConRes CE + if (need_conres_ce) { + data->pdu[0][0].lcid = srslte::sch_subh::CON_RES_ID; + data->nof_pdu_elems[0]++; + Info("SCHED: Added MAC Contention Resolution CE for rnti=0x%x\n", rnti); + } + + int rem_tbs = tbs; + int x = 0; + do { + x = alloc_pdu(rem_tbs, &data->pdu[0][data->nof_pdu_elems[0]]); + rem_tbs -= x; + if (x) { + data->nof_pdu_elems[0]++; + } + } while(rem_tbs > 0 && x > 0); + + Debug("SCHED: Alloc format1 new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes); + } else { + h->new_retx(0, tti, &mcs, &tbs); + Debug("SCHED: Alloc format1 previous mcs=%d, tbs=%d\n", mcs, tbs); + } + + data->rnti = rnti; + + if (tbs > 0) { + dci->harq_process = h->get_id(); + dci->mcs_idx = (uint32_t) mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx(0)); + dci->ndi = h->get_ndi(0); + dci->tpc_pucch = (uint8_t) next_tpc_pucch; + next_tpc_pucch = 1; + data->tbs[0] = (uint32_t) tbs; + data->tbs[1] = 0; + dci->tb_en[0] = true; + dci->tb_en[1] = false; + } + pthread_mutex_unlock(&mutex); + return tbs; +} + +// Generates a Format2a grant +int sched_ue::generate_format2a(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + pthread_mutex_lock(&mutex); + int ret = generate_format2a_unlocked(h, data, tti, cfi); + pthread_mutex_unlock(&mutex); + return ret; +} + +// Generates a Format2a grant +int sched_ue::generate_format2a_unlocked(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + bool tb_en[SRSLTE_MAX_TB] = {false}; + + srslte_ra_dl_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_dl_dci_t)); + + uint32_t sf_idx = tti%10; + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci->type0_alloc.rbg_bitmask = h->get_rbgmask(); + + uint32_t nof_prb = format1_count_prb(h->get_rbgmask(), cell.nof_prb); + uint32_t nof_ctrl_symbols = cfi + (cell.nof_prb < 10 ? 1 : 0); + srslte_ra_dl_grant_t grant; + srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb); + uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols); + bool no_retx = true; + + if (dl_ri == 0) { + if (h->is_empty(1)) { + /* One layer, tb1 buffer is empty, send tb0 only */ + tb_en[0] = true; + } else { + /* One layer, tb1 buffer is not empty, send tb1 only */ + tb_en[1] = true; + } + } else { + /* Two layers, retransmit what TBs that have not been Acknowledged */ + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (!h->is_empty(tb)) { + tb_en[tb] = true; + no_retx = false; + } + } + /* Two layers, no retransmissions... */ + if (no_retx) { + tb_en[0] = true; + tb_en[1] = true; + } + } + + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + uint32_t req_bytes = get_pending_dl_new_data_unlocked(tti); + int mcs = 0; + int tbs = 0; + + if (!h->is_empty(tb)) { + h->new_retx(tb, tti, &mcs, &tbs); + Debug("SCHED: Alloc format2/2a previous mcs=%d, tbs=%d\n", mcs, tbs); + } else if (tb_en[tb] && req_bytes && no_retx) { + if (fixed_mcs_dl < 0) { + tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx((uint32_t) srslte_ra_tbs_idx_from_mcs((uint32_t) fixed_mcs_dl), nof_prb) / 8; + mcs = fixed_mcs_dl; + } + h->new_tx(tb, tti, mcs, tbs, data->dci_location.ncce); + + int rem_tbs = tbs; + int x = 0; + do { + x = alloc_pdu(rem_tbs, &data->pdu[tb][data->nof_pdu_elems[tb]]); + rem_tbs -= x; + if (x) { + data->nof_pdu_elems[tb]++; + } + } while (rem_tbs > 0 && x > 0); + + Debug("SCHED: Alloc format2/2a new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes); + } + + /* Fill DCI TB dedicated fields */ + if (tbs > 0) { + if (tb == 0) { + dci->mcs_idx = (uint32_t) mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx(tb)); + dci->ndi = h->get_ndi(tb); + } else { + dci->mcs_idx_1 = (uint32_t) mcs; + dci->rv_idx_1 = sched::get_rvidx(h->nof_retx(tb)); + dci->ndi_1 = h->get_ndi(tb); + } + data->tbs[tb] = (uint32_t) tbs; + dci->tb_en[tb] = true; + } else { + data->tbs[tb] = 0; + dci->tb_en[tb] = false; + } + } + + /* Fill common fields */ + data->rnti = rnti; + dci->harq_process = h->get_id(); + dci->tpc_pucch = (uint8_t) next_tpc_pucch; + next_tpc_pucch = 1; + + int ret = data->tbs[0] + data->tbs[1]; + return ret; +} + + +// Generates a Format2 grant +int sched_ue::generate_format2(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + + pthread_mutex_lock(&mutex); + + /* Call Format 2a (common) */ + int ret = generate_format2a_unlocked(h, data, tti, cfi); + + /* Compute precoding information */ + if (SRSLTE_RA_DL_GRANT_NOF_TB(&data->dci) == 1) { + data->dci.pinfo = (uint8_t) (dl_pmi + 1) % (uint8_t) 5; + } else { + data->dci.pinfo = (uint8_t) (dl_pmi & 1); + } + + pthread_mutex_unlock(&mutex); + + return ret; +} + + +int sched_ue::generate_format0(ul_harq_proc *h, + sched_interface::ul_sched_data_t *data, + uint32_t tti, + bool cqi_request) +{ + pthread_mutex_lock(&mutex); + + srslte_ra_ul_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_ul_dci_t)); + + int mcs = 0; + int tbs = 0; + + ul_harq_proc::ul_alloc_t allocation = h->get_alloc(); + + bool is_newtx = true; + if (h->get_rar_mcs(&mcs)) { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; + h->new_tx(tti, mcs, tbs); + } else if (h->is_empty(0)) { + + uint32_t req_bytes = get_pending_ul_new_data_unlocked(tti); + + uint32_t N_srs = 0; + uint32_t nof_re = (2*(SRSLTE_CP_NSYMB(cell.cp)-1) - N_srs)*allocation.L*SRSLTE_NRE; + if (fixed_mcs_ul < 0) { + tbs = alloc_tbs_ul(allocation.L, nof_re, req_bytes, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), allocation.L)/8; + mcs = fixed_mcs_ul; + } + + h->new_tx(tti, mcs, tbs); + + } else { + h->new_retx(0, tti, &mcs, NULL); + is_newtx = false; + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; + } + + data->rnti = rnti; + data->tbs = tbs; + + if (tbs > 0) { + dci->type2_alloc.L_crb = allocation.L; + dci->type2_alloc.RB_start = allocation.RB_start; + dci->rv_idx = sched::get_rvidx(h->nof_retx(0)); + if (!is_newtx && h->is_adaptive_retx()) { + dci->mcs_idx = 28+dci->rv_idx; + } else { + dci->mcs_idx = mcs; + } + dci->ndi = h->get_ndi(0); + dci->cqi_request = cqi_request; + dci->freq_hop_fl = srslte_ra_ul_dci_t::SRSLTE_RA_PUSCH_HOP_DISABLED; + dci->tpc_pusch = next_tpc_pusch; + next_tpc_pusch = 1; + } + + pthread_mutex_unlock(&mutex); + + return tbs; +} + +/******************************************************* + * + * Functions used by scheduler or scheduler metric objects + * + *******************************************************/ + +bool sched_ue::bearer_is_ul(ue_bearer_t *lch) { + return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::UL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH; +} + +bool sched_ue::bearer_is_dl(ue_bearer_t *lch) { + return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::DL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH; +} + +uint32_t sched_ue::get_max_retx() { + return cfg.maxharq_tx; +} + +bool sched_ue::is_first_dl_tx() +{ + for (int i=0;i 0) { + return false; + } + } + return true; +} + +bool sched_ue::needs_cqi(uint32_t tti, bool will_be_sent) +{ + pthread_mutex_lock(&mutex); + bool ret = needs_cqi_unlocked(tti, will_be_sent); + pthread_mutex_unlock(&mutex); + return ret; +} + +// Private lock-free implemenentation +bool sched_ue::needs_cqi_unlocked(uint32_t tti, bool will_be_sent) +{ + bool ret = false; + if (phy_config_dedicated_enabled && + cfg.aperiodic_cqi_period && + get_pending_dl_new_data_unlocked(tti) > 0) + { + uint32_t interval = srslte_tti_interval(tti, dl_cqi_tti); + bool needscqi = interval >= cfg.aperiodic_cqi_period; + if (needscqi) { + uint32_t interval_sent = srslte_tti_interval(tti, cqi_request_tti); + if (interval_sent >= 16) { + if (will_be_sent) { + cqi_request_tti = tti; + } + Debug("SCHED: Needs_cqi, last_sent=%d, will_be_sent=%d\n", cqi_request_tti, will_be_sent); + ret = true; + } + } + } + return ret; +} + +uint32_t sched_ue::get_pending_dl_new_data(uint32_t tti) +{ + pthread_mutex_lock(&mutex); + uint32_t pending_data = get_pending_dl_new_data_unlocked(tti); + pthread_mutex_unlock(&mutex); + return pending_data; +} + +/// Use this function in the dl-metric to get the bytes to be scheduled. It accounts for the UE data, +/// the RAR resources, and headers +/// \param tti +/// \return number of bytes to be allocated +uint32_t sched_ue::get_pending_dl_new_data_total(uint32_t tti) +{ + pthread_mutex_lock(&mutex); + uint32_t req_bytes = get_pending_dl_new_data_unlocked(tti); + if(req_bytes>0) { + req_bytes += (req_bytes < 128) ? 2 : 3; // consider the header + if(is_first_dl_tx()) { + req_bytes += 6; // count for RAR + } + } + pthread_mutex_unlock(&mutex); + return req_bytes; +} + +// Private lock-free implementation +uint32_t sched_ue::get_pending_dl_new_data_unlocked(uint32_t tti) +{ + uint32_t pending_data = 0; + for (int i=0;i pending_ul_data) { + pending_data -= pending_ul_data; + } else { + pending_data = 0; + } + if (pending_data) { + Debug("SCHED: pending_data=%d, pending_ul_data=%d, bsr={%d,%d,%d,%d}\n", pending_data,pending_ul_data, + lch[0].bsr, lch[1].bsr, lch[2].bsr, lch[3].bsr); + } + return pending_data; +} + +// Private lock-free implementation +uint32_t sched_ue::get_pending_ul_old_data_unlocked() +{ + uint32_t pending_data = 0; + for (int i=0;i 0) { + nbytes = tbs; + } else if (tbs < 0) { + pthread_mutex_unlock(&mutex); + return 0; + } + } + + pthread_mutex_unlock(&mutex); + return n; +} + +uint32_t sched_ue::get_required_prb_ul(uint32_t req_bytes) +{ + int mcs = 0; + int tbs = 0; + uint32_t nbytes = 0; + uint32_t N_srs = 0; + + uint32_t n = 0; + if (req_bytes == 0) { + return 0; + } + + pthread_mutex_lock(&mutex); + + for (n=1;n 0) { + nbytes = tbs; + } + } + + while (!srslte_dft_precoding_valid_prb(n) && n<=cell.nof_prb) { + n++; + } + + pthread_mutex_unlock(&mutex); + + return n; +} + +bool sched_ue::is_sr_triggered() +{ + return sr; +} + +void sched_ue::reset_timeout_dl_harq(uint32_t tti) { + for (int i=0;i 50) { + log_h->info("SCHED: pid=%d is old. tti_pid=%d, now is %d, resetting\n", i, dl_harq[i].get_tti(), tti); + dl_harq[i].reset(0); + dl_harq[i].reset(1); + } + } + } +} + +/* Gets HARQ process with oldest pending retx */ +dl_harq_proc* sched_ue::get_pending_dl_harq(uint32_t tti) +{ +#if ASYNC_DL_SCHED + + pthread_mutex_lock(&mutex); + + int oldest_idx=-1; + uint32_t oldest_tti = 0; + for (int i=0;i oldest_tti) { + oldest_idx = i; + oldest_tti = x; + } + } + } + dl_harq_proc *h = NULL; + if (oldest_idx >= 0) { + h = &dl_harq[oldest_idx]; + } + + pthread_mutex_unlock(&mutex); + + return h; + +#else + return &dl_harq[tti%SCHED_MAX_HARQ_PROC]; +#endif +} + +dl_harq_proc* sched_ue::get_empty_dl_harq() +{ + pthread_mutex_lock(&mutex); + + dl_harq_proc *h = NULL; + for (int i=0;i max_coderate); + Debug("SCHED: CQI=%d, l=%d, nof_bits=%d, coderate=%.2f, max_coderate=%.2f\n", dl_cqi, l, nof_bits, coderate, max_coderate); + pthread_mutex_unlock(&mutex); + return l; +} + +sched_ue::sched_dci_cce_t* sched_ue::get_locations(uint32_t cfi, uint32_t sf_idx) +{ + if (cfi > 0 && cfi <= 3) { + return &dci_locations[cfi-1][sf_idx]; + } else { + Error("SCHED: Invalid CFI=%d\n", cfi); + return &dci_locations[0][sf_idx]; + } +} + +/* Allocates first available RLC PDU */ +int sched_ue::alloc_pdu(int tbs_bytes, sched_interface::dl_sched_pdu_t* pdu) +{ + // TODO: Implement lcid priority (now lowest index is lowest priority) + int x = 0; + int i = 0; + for (i=0;ilcid = i-1; + pdu->nbytes = x; + Debug("SCHED: Allocated lcid=%d, nbytes=%d, tbs_bytes=%d\n", pdu->lcid, pdu->nbytes, tbs_bytes); + } + return x; +} + +uint32_t sched_ue::format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb) { + uint32_t P = srslte_ra_type0_P(cell_nof_prb); + uint32_t nb = (int) ceilf((float) cell_nof_prb / P); + + uint32_t nof_prb = 0; + for (uint32_t i = 0; i < nb; i++) { + if (bitmask & (1 << (nb - i - 1))) { + for (uint32_t j = 0; j < P; j++) { + if (i*P+j < cell_nof_prb) { + nof_prb++; + } + } + } + } + return nof_prb; +} + +int sched_ue::cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t max_Qm, uint32_t *mcs) { + float max_coderate = srslte_cqi_to_coderate(cqi); + int sel_mcs = max_mcs+1; + float coderate = 99; + float eff_coderate = 99; + uint32_t Qm = 1; + int tbs = 0; + + do { + sel_mcs--; + uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(sel_mcs); + tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb); + coderate = srslte_coderate(tbs, nof_re); + Qm = SRSLTE_MIN(max_Qm, srslte_mod_bits_x_symbol(srslte_ra_mod_from_mcs(sel_mcs))); + eff_coderate = coderate/Qm; + } while((sel_mcs > 0 && coderate > max_coderate) || eff_coderate > 0.930); + if (mcs) { + *mcs = (uint32_t) sel_mcs; + } + return tbs; +} + +int sched_ue::alloc_tbs_dl(uint32_t nof_prb, + uint32_t nof_re, + uint32_t req_bytes, + int *mcs) +{ + return alloc_tbs(nof_prb, nof_re, req_bytes, false, mcs); +} + +int sched_ue::alloc_tbs_ul(uint32_t nof_prb, + uint32_t nof_re, + uint32_t req_bytes, + int *mcs) +{ + return alloc_tbs(nof_prb, nof_re, req_bytes, true, mcs); +} + + /* In this scheduler we tend to use all the available bandwidth and select the MCS + * that approximates the minimum between the capacity and the requested rate + */ +int sched_ue::alloc_tbs(uint32_t nof_prb, + uint32_t nof_re, + uint32_t req_bytes, + bool is_ul, + int *mcs) +{ + uint32_t sel_mcs = 0; + + uint32_t cqi = is_ul?ul_cqi:dl_cqi; + uint32_t max_mcs = is_ul?max_mcs_ul:max_mcs_dl; + uint32_t max_Qm = is_ul?4:6; // Allow 16-QAM in PUSCH Only + + // TODO: Compute real spectral efficiency based on PUSCH-UCI configuration + + int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, max_Qm, &sel_mcs)/8; + + /* If less bytes are requested, lower the MCS */ + if (tbs > (int) req_bytes && req_bytes > 0) { + uint32_t req_tbs_idx = srslte_ra_tbs_to_table_idx(req_bytes*8, nof_prb); + uint32_t req_mcs = srslte_ra_mcs_from_tbs_idx(req_tbs_idx); + if (req_mcs < sel_mcs) { + sel_mcs = req_mcs; + tbs = srslte_ra_tbs_from_idx(req_tbs_idx, nof_prb)/8; + } + } + // Avoid the unusual case n_prb=1, mcs=6 tbs=328 (used in voip) + if (nof_prb == 1 && sel_mcs == 6) { + sel_mcs--; + } + + if (mcs && tbs >= 0) { + *mcs = (int) sel_mcs; + } + + return tbs; +} + + +} diff --git a/srsenb/src/mac/ue.cc b/srsenb/src/mac/ue.cc new file mode 100644 index 0000000..9a54245 --- /dev/null +++ b/srsenb/src/mac/ue.cc @@ -0,0 +1,482 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include + +#include "srslte/interfaces/enb_interfaces.h" +#include "srsenb/hdr/mac/ue.h" + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + + +namespace srsenb { + +void ue::config(uint16_t rnti_, uint32_t nof_prb, sched_interface *sched_, rrc_interface_mac *rrc_, rlc_interface_mac *rlc_, srslte::log *log_h_) +{ + rnti = rnti_; + rlc = rlc_; + rrc = rrc_; + log_h = log_h_; + sched = sched_; + pdus.init(this, log_h); + + for (int i=0;i 0) { + if (!pending_buffers[tti%NOF_HARQ_PROCESSES]) { + ret = pdus.request(len); + pending_buffers[tti%NOF_HARQ_PROCESSES] = ret; + } else { + log_h->console("Error requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES); + log_h->error("Requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES); + } + } else { + log_h->warning("Requesting buffer for zero bytes\n"); + } + return ret; +} + +bool ue::process_pdus() +{ + return pdus.process_pdus(); +} + +void ue::set_tti(uint32_t tti) { + last_tti = tti; +} + +#include + +void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srslte::pdu_queue::channel_t channel, uint32_t tstamp) +{ + // Unpack ULSCH MAC PDU + mac_msg_ul.init_rx(nof_bytes, true); + mac_msg_ul.parse_packet(pdu); + + if (pcap) { + pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti); + } + + pdus.deallocate(pdu); + + uint32_t lcid_most_data = 0; + int most_data = -99; + + while(mac_msg_ul.next()) { + assert(mac_msg_ul.get()); + if (mac_msg_ul.get()->is_sdu()) { + // Route logical channel + log_h->debug_hex(mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size(), + "PDU: rnti=0x%x, lcid=%d, %d bytes\n", + rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size()); + + + /* In some cases, an uplink transmission with only CQI has all zeros and gets routed to RRC + * Compute the checksum if lcid=0 and avoid routing in that case + */ + bool route_pdu = true; + if (mac_msg_ul.get()->get_sdu_lcid() == 0) { + uint8_t *x = mac_msg_ul.get()->get_sdu_ptr(); + uint32_t sum = 0; + for (uint32_t i = 0; i < mac_msg_ul.get()->get_payload_size(); i++) { + sum += x[i]; + } + if (sum == 0) { + route_pdu = false; + Warning("Received all zero PDU\n"); + } + } + + if (route_pdu) { + rlc->write_pdu(rnti, + mac_msg_ul.get()->get_sdu_lcid(), + mac_msg_ul.get()->get_sdu_ptr(), + mac_msg_ul.get()->get_payload_size()); + } + + // Indicate scheduler to update BSR counters + //sched->ul_recv_len(rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size()); + + // Indicate RRC about successful activity if valid RLC message is received + if (mac_msg_ul.get()->get_payload_size() > 64) { // do not count RLC status messages only + rrc->set_activity_user(rnti); + log_h->debug("UL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes); + } + + if ((int) mac_msg_ul.get()->get_payload_size() > most_data) { + most_data = (int) mac_msg_ul.get()->get_payload_size(); + lcid_most_data = mac_msg_ul.get()->get_sdu_lcid(); + } + + // Save contention resolution if lcid == 0 + if (mac_msg_ul.get()->get_sdu_lcid() == 0 && route_pdu) { + 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]; + } + } else { + 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()); + } + } + + // If BSR is not received means that new data has arrived and there is no space for BSR transmission + if (!bsr_received && lcid_most_data > 2) { + // Add BSR to the LCID for which most data was received + sched->ul_bsr(rnti, lcid_most_data, 256, false); // false adds BSR instead of setting + Debug("BSR not received. Giving extra grant\n"); + } + + Debug("MAC PDU processed\n"); + +} + +void ue::deallocate_pdu(uint32_t tti) +{ + if (pending_buffers[tti%NOF_HARQ_PROCESSES]) { + pdus.deallocate(pending_buffers[tti%NOF_HARQ_PROCESSES]); + pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL; + } else { + log_h->console("Error deallocating buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES); + } +} + +void ue::push_pdu(uint32_t tti, uint32_t len) +{ + if (pending_buffers[tti%NOF_HARQ_PROCESSES]) { + pdus.push(pending_buffers[tti%NOF_HARQ_PROCESSES], len); + pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL; + } else { + log_h->console("Error pushing buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES); + } +} + +bool ue::process_ce(srslte::sch_subh *subh) { + 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->ce_type()) { + case srslte::sch_subh::PHR_REPORT: + phr = subh->get_phr(); + Info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr); + sched->ul_phr(rnti, (int) phr); + metrics_phr(phr); + break; + case srslte::sch_subh::CRNTI: + old_rnti = subh->get_c_rnti(); + Info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti); + if (sched->ue_exists(old_rnti)) { + rrc->upd_user(rnti, old_rnti); + rnti = old_rnti; + } else { + Error("Updating user C-RNTI: rnti=0x%x already released\n", old_rnti); + } + break; + case srslte::sch_subh::TRUNC_BSR: + case srslte::sch_subh::SHORT_BSR: + idx = subh->get_bsr(buff_size); + if(idx == -1){ + Error("Invalid Index Passed to lc groups\n"); + break; + } + for (uint32_t i=0;iul_bsr(rnti, lc_groups[idx][i], buff_size[idx]); + } + Info("CE: Received %s BSR rnti=0x%x, lcg=%d, value=%d\n", + subh->ce_type()==srslte::sch_subh::SHORT_BSR?"Short":"Trunc", rnti, idx, buff_size[idx]); + is_bsr = true; + break; + case srslte::sch_subh::LONG_BSR: + subh->get_bsr(buff_size); + for (idx=0;idx<4;idx++) { + for (uint32_t i=0;iul_bsr(rnti, lc_groups[idx][i], buff_size[idx]); + } + } + is_bsr = true; + 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::sch_subh::PADDING: + Debug("CE: Received padding for rnti=0x%x\n", rnti); + break; + default: + Error("CE: Invalid lcid=0x%x\n", subh->ce_type()); + break; + } + return is_bsr; +} + + +int ue::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes) +{ + return rlc->read_pdu(rnti, lcid, payload, requested_bytes); +} + +void ue::allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t total_sdu_len) +{ + int sdu_space = pdu->get_sdu_space(); + if (sdu_space > 0) { + int sdu_len = SRSLTE_MIN(total_sdu_len, (uint32_t) sdu_space); + int n=1; + while(sdu_len > 3 && n > 0) { + if (pdu->new_subh()) { // there is space for a new subheader + log_h->debug("SDU: set_sdu(), lcid=%d, sdu_len=%d, sdu_space=%d\n", lcid, sdu_len, sdu_space); + n = pdu->get()->set_sdu(lcid, sdu_len, this); + if (n > 0) { // new SDU could be added + sdu_len -= n; + log_h->debug("SDU: rnti=0x%x, lcid=%d, nbytes=%d, rem_len=%d\n", + rnti, lcid, n, sdu_len); + } else { + Debug("Could not add SDU lcid=%d nbytes=%d, space=%d\n", lcid, sdu_len, sdu_space); + pdu->del_subh(); + } + } else { + n=0; + } + } + } +} + +void ue::allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid) +{ + switch((srslte::sch_subh::cetype) lcid) { + case srslte::sch_subh::CON_RES_ID: + if (pdu->new_subh()) { + if (pdu->get()->set_con_res_id(conres_id)) { + Info("CE: Added Contention Resolution ID=0x%lx\n", conres_id); + } else { + Error("CE: Setting Contention Resolution ID CE\n"); + } + } else { + Error("CE: Setting Contention Resolution ID CE. No space for a subheader\n"); + } + break; + default: + Error("CE: Allocating CE=0x%x. Not supported\n", lcid); + break; + } +} + +uint8_t* ue::generate_pdu(uint32_t tb_idx, sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], + uint32_t nof_pdu_elems, uint32_t grant_size) +{ + uint8_t *ret = NULL; + pthread_mutex_lock(&mutex); + if (rlc) { + mac_msg_dl.init_tx(tx_payload_buffer[tb_idx], grant_size, false); + for (uint32_t i=0;iset_next_mch_sched_info(sched.mtch_sched[i].lcid,sched.mtch_sched[i].stop); + } else if (sched.pdu[i].lcid == 0) { + mch_mac_msg_dl.new_subh(); + mch_mac_msg_dl.get()->set_sdu(0, sched.pdu[i].nbytes, sched.mcch_payload); + } else if (sched.pdu[i].lcid <= srslte::mch_subh::MTCH_MAX_LCID) { + mch_mac_msg_dl.new_subh(); + mch_mac_msg_dl.get()->set_sdu(sched.pdu[i].lcid, sched.pdu[i].nbytes,sched.mtch_sched[i].mtch_payload); + } + } + ret = mch_mac_msg_dl.write_packet(log_h); + pthread_mutex_unlock(&mutex); + return ret; +} + + + +/******* METRICS interface ***************/ +void ue::metrics_read(mac_metrics_t* metrics_) +{ + metrics.rnti = rnti; + metrics.ul_buffer = sched->get_ul_buffer(rnti); + metrics.dl_buffer = sched->get_dl_buffer(rnti); + + memcpy(metrics_, &metrics, sizeof(mac_metrics_t)); + + phr_counter = 0; + dl_cqi_counter = 0; + bzero(&metrics, sizeof(mac_metrics_t)); +} + +void ue::metrics_phr(float phr) { + metrics.phr = SRSLTE_VEC_CMA(phr, metrics.phr, phr_counter); + phr_counter++; +} + +void ue::metrics_dl_ri(uint32_t dl_ri) { + if (metrics.dl_ri == 0.0f) { + metrics.dl_ri = (float) dl_ri + 1.0f; + } else { + metrics.dl_ri = SRSLTE_VEC_EMA((float) dl_ri + 1.0f, metrics.dl_ri, 0.5f); + } + dl_ri_counter++; +} + +void ue::metrics_dl_pmi(uint32_t dl_ri) { + metrics.dl_pmi = SRSLTE_VEC_CMA((float) dl_ri, metrics.dl_pmi, dl_pmi_counter); + dl_pmi_counter++; +} + +void ue::metrics_dl_cqi(uint32_t dl_cqi) { + metrics.dl_cqi = SRSLTE_VEC_CMA((float) dl_cqi, metrics.dl_cqi, dl_cqi_counter); + dl_cqi_counter++; +} + +void ue::metrics_rx(bool crc, uint32_t tbs) +{ + if (crc) { + metrics.rx_brate += tbs*8; + } else { + metrics.rx_errors++; + } + metrics.rx_pkts++; +} + +void ue::metrics_tx(bool crc, uint32_t tbs) +{ + if (crc) { + metrics.tx_brate += tbs*8; + } else { + metrics.tx_errors++; + } + metrics.tx_pkts++; +} + + +} + diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc new file mode 100644 index 0000000..3622f8d --- /dev/null +++ b/srsenb/src/main.cc @@ -0,0 +1,437 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include + +#include "srslte/common/config_file.h" + +#include +#include +#include +#include +#include + +#include "srsenb/hdr/enb.h" +#include "srsenb/hdr/metrics_stdout.h" + +using namespace std; +using namespace srsenb; +namespace bpo = boost::program_options; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void parse_args(all_args_t *args, int argc, char* argv[]) { + + string enb_id; + string cell_id; + string tac; + string mcc; + string mnc; + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + + ("enb.enb_id", bpo::value(&enb_id)->default_value("0x0"), "eNodeB ID") + ("enb.name", bpo::value(&args->enb.s1ap.enb_name)->default_value("srsenb01"), "eNodeB Name") + ("enb.cell_id", bpo::value(&cell_id)->default_value("0x0"), "Cell ID") + ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") + ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") + ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") + ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") + ("enb.nof_ports", bpo::value(&args->enb.nof_ports)->default_value(1), "Number of ports") + ("enb.tm", bpo::value(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)") + ("enb.p_a", bpo::value(&args->enb.p_a)->default_value(0.0f), "Power allocation rho_a (-6, -4.77, -3, -1.77, 0, 1, 2, 3)") + + ("enb_files.sib_config", bpo::value(&args->enb_files.sib_config)->default_value("sib.conf"), "SIB configuration files") + ("enb_files.rr_config", bpo::value(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files") + ("enb_files.drb_config", bpo::value(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files") + + ("rf.dl_earfcn", bpo::value(&args->rf.dl_earfcn)->default_value(3400), "Downlink EARFCN") + ("rf.ul_earfcn", bpo::value(&args->rf.ul_earfcn)->default_value(0), "Uplink EARFCN (Default based on Downlink EARFCN)") + ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(50), "Front-end receiver gain") + ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(70), "Front-end transmitter gain") + ("rf.dl_freq", bpo::value(&args->rf.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") + ("rf.ul_freq", bpo::value(&args->rf.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") + + ("rf.device_name", bpo::value(&args->rf.device_name)->default_value("auto"), "Front-end device name") + ("rf.device_args", bpo::value(&args->rf.device_args)->default_value("auto"), "Front-end device arguments") + ("rf.time_adv_nsamples", bpo::value(&args->rf.time_adv_nsamples)->default_value("auto"), "Transmission time advance") + ("rf.burst_preamble_us", bpo::value(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance") + + ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") + ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") + + ("gui.enable", bpo::value(&args->gui.enable)->default_value(false), "Enable GUI plots") + + ("log.phy_level", bpo::value(&args->log.phy_level), "PHY log level") + ("log.phy_hex_limit", bpo::value(&args->log.phy_hex_limit), "PHY log hex dump limit") + ("log.phy_lib_level", bpo::value(&args->log.phy_lib_level)->default_value("none"), "PHY lib log level") + ("log.mac_level", bpo::value(&args->log.mac_level), "MAC log level") + ("log.mac_hex_limit", bpo::value(&args->log.mac_hex_limit), "MAC log hex dump limit") + ("log.rlc_level", bpo::value(&args->log.rlc_level), "RLC log level") + ("log.rlc_hex_limit", bpo::value(&args->log.rlc_hex_limit), "RLC log hex dump limit") + ("log.pdcp_level", bpo::value(&args->log.pdcp_level), "PDCP log level") + ("log.pdcp_hex_limit",bpo::value(&args->log.pdcp_hex_limit), "PDCP log hex dump limit") + ("log.rrc_level", bpo::value(&args->log.rrc_level), "RRC log level") + ("log.rrc_hex_limit", bpo::value(&args->log.rrc_hex_limit), "RRC log hex dump limit") + ("log.gtpu_level", bpo::value(&args->log.gtpu_level), "GTPU log level") + ("log.gtpu_hex_limit",bpo::value(&args->log.gtpu_hex_limit), "GTPU log hex dump limit") + ("log.s1ap_level", bpo::value(&args->log.s1ap_level), "S1AP log level") + ("log.s1ap_hex_limit",bpo::value(&args->log.s1ap_hex_limit), "S1AP log hex dump limit") + + ("log.all_level", bpo::value(&args->log.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log.filename)->default_value("/tmp/ue.log"),"Log filename") + ("log.file_max_size", bpo::value(&args->log.file_max_size)->default_value(-1), "Maximum file size (in kilobytes). When passed, multiple files are created. Default -1 (single file)") + + /* MCS section */ + ("scheduler.pdsch_mcs", + bpo::value(&args->expert.mac.sched.pdsch_mcs)->default_value(-1), + "Optional fixed PDSCH MCS (ignores reported CQIs if specified)") + ("scheduler.pdsch_max_mcs", + bpo::value(&args->expert.mac.sched.pdsch_max_mcs)->default_value(-1), + "Optional PDSCH MCS limit") + ("scheduler.pusch_mcs", + bpo::value(&args->expert.mac.sched.pusch_mcs)->default_value(-1), + "Optional fixed PUSCH MCS (ignores reported CQIs if specified)") + ("scheduler.pusch_max_mcs", + bpo::value(&args->expert.mac.sched.pusch_max_mcs)->default_value(-1), + "Optional PUSCH MCS limit") + ("scheduler.nof_ctrl_symbols", + bpo::value(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3), + "Number of control symbols") + + + /* Expert section */ + + ("expert.metrics_period_secs", + bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), + "Periodicity for metrics in seconds") + + ("expert.pregenerate_signals", + bpo::value(&args->expert.phy.pregenerate_signals)->default_value(false), + "Pregenerate uplink signals after attach. Improves CPU performance.") + + ("expert.pusch_max_its", + bpo::value(&args->expert.phy.pusch_max_its)->default_value(4), + "Maximum number of turbo decoder iterations") + + ("expert.tx_amplitude", + bpo::value(&args->expert.phy.tx_amplitude)->default_value(0.6), + "Transmit amplitude factor") + + ("expert.nof_phy_threads", + bpo::value(&args->expert.phy.nof_phy_threads)->default_value(2), + "Number of PHY threads") + + ("expert.link_failure_nof_err", + bpo::value(&args->expert.mac.link_failure_nof_err)->default_value(100), + "Number of PUSCH failures after which a radio-link failure is triggered") + + ("expert.max_prach_offset_us", + bpo::value(&args->expert.phy.max_prach_offset_us)->default_value(30), + "Maximum allowed RACH offset (in us)") + + ("expert.equalizer_mode", + bpo::value(&args->expert.phy.equalizer_mode)->default_value("mmse"), + "Equalizer mode") + + ("expert.estimator_fil_w", + bpo::value(&args->expert.phy.estimator_fil_w)->default_value(0.1), + "Chooses the coefficients for the 3-tap channel estimator centered filter.") + + ("expert.rrc_inactivity_timer", + bpo::value(&args->expert.rrc_inactivity_timer)->default_value(60000), + "Inactivity timer in ms") + + ("expert.enable_mbsfn", + bpo::value(&args->expert.enable_mbsfn)->default_value(false), + "enables mbms in the enodeb") + + ("expert.print_buffer_state", + bpo::value(&args->expert.print_buffer_state)->default_value(false), + "Prints on the console the buffer state every 10 seconds") + + ("rf_calibration.tx_corr_dc_gain", bpo::value(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), "TX DC offset gain correction") + ("rf_calibration.tx_corr_dc_phase", bpo::value(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), "TX DC offset phase correction") + ("rf_calibration.tx_corr_iq_i", bpo::value(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), "TX IQ imbalance inphase correction") + ("rf_calibration.tx_corr_iq_q", bpo::value(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), "TX IQ imbalance quadrature correction") + + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "eNodeB configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + // print version number and exit + if (vm.count("version")) { + cout << "Version " << + srslte_get_version_major() << "." << + srslte_get_version_minor() << "." << + srslte_get_version_patch() << endl; + exit(0); + } + + // if no config file given, check users home path + if (!vm.count("config_file")) { + if (!config_exists(config_file, "enb.conf")) { + cout << "Failed to read eNB configuration file " << config_file << " - exiting" << endl; + exit(1); + } + } + + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + + // Convert hex strings + { + std::stringstream sstr; + sstr << std::hex << vm["enb.enb_id"].as(); + sstr >> args->enb.s1ap.enb_id; + } + { + std::stringstream sstr; + sstr << std::hex << vm["enb.cell_id"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + args->enb.s1ap.cell_id = tmp; + } + { + std::stringstream sstr; + sstr << std::hex << vm["enb.tac"].as(); + sstr >> args->enb.s1ap.tac; + } + + // Convert MCC/MNC strings + if(!srslte::string_to_mcc(mcc, &args->enb.s1ap.mcc)) { + cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if(!srslte::string_to_mnc(mnc, &args->enb.s1ap.mnc)) { + cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } + + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.phy_level")) { + args->log.phy_level = args->log.all_level; + } + if (!vm.count("log.phy_lib_level")) { + args->log.phy_lib_level = args->log.all_level; + } + if(!vm.count("log.mac_level")) { + args->log.mac_level = args->log.all_level; + } + if(!vm.count("log.rlc_level")) { + args->log.rlc_level = args->log.all_level; + } + if(!vm.count("log.pdcp_level")) { + args->log.pdcp_level = args->log.all_level; + } + if(!vm.count("log.rrc_level")) { + args->log.rrc_level = args->log.all_level; + } + if(!vm.count("log.gtpu_level")) { + args->log.gtpu_level = args->log.all_level; + } + if(!vm.count("log.s1ap_level")) { + args->log.s1ap_level = args->log.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.phy_hex_limit")) { + args->log.phy_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.mac_hex_limit")) { + args->log.mac_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rlc_hex_limit")) { + args->log.rlc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.pdcp_hex_limit")) { + args->log.pdcp_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rrc_hex_limit")) { + args->log.rrc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.gtpu_hex_limit")) { + args->log.gtpu_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.s1ap_hex_limit")) { + args->log.s1ap_hex_limit = args->log.all_hex_limit; + } + } + + // Check remaining eNB config files + if (!config_exists(args->enb_files.sib_config, "sib.conf")) { + cout << "Failed to read SIB configuration file " << args->enb_files.sib_config << " - exiting" << endl; + exit(1); + } + + if (!config_exists(args->enb_files.rr_config, "rr.conf")) { + cout << "Failed to read RR configuration file " << args->enb_files.rr_config << " - exiting" << endl; + exit(1); + } + + if (!config_exists(args->enb_files.drb_config, "drb.conf")) { + cout << "Failed to read DRB configuration file " << args->enb_files.drb_config << " - exiting" << endl; + exit(1); + } +} + +static int sigcnt = 0; +static bool running = true; +static bool do_metrics = false; + +void sig_int_handler(int signo) +{ + sigcnt++; + running = false; + printf("Stopping srsENB... Press Ctrl+C %d more times to force stop\n", 10-sigcnt); + if (sigcnt >= 10) { + exit(-1); + } +} + +void *input_loop(void *m) +{ + metrics_stdout *metrics = (metrics_stdout*) m; + char key; + while(running) { + cin >> key; + if (cin.eof() || cin.bad()) { + cout << "Closing stdin thread." << endl; + break; + } else { + if('t' == key) { + do_metrics = !do_metrics; + if(do_metrics) { + cout << "Enter t to stop trace." << endl; + } else { + cout << "Enter t to restart trace." << endl; + } + metrics->toggle_print(do_metrics); + } + } + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + signal(SIGINT, sig_int_handler); + signal(SIGTERM, sig_int_handler); + all_args_t args; + metrics_stdout metrics; + enb *enb = enb::get_instance(); + + srslte_debug_handle_crash(argc, argv); + + cout << "--- Software Radio Systems LTE eNodeB ---" << endl << endl; + + parse_args(&args, argc, argv); + if(!enb->init(&args)) { + exit(1); + } + metrics.init(enb, args.expert.metrics_period_secs); + + pthread_t input; + pthread_create(&input, NULL, &input_loop, &metrics); + + bool plot_started = false; + bool signals_pregenerated = false; + if(running) { + if (!plot_started && args.gui.enable) { + enb->start_plot(); + plot_started = true; + } + } + int cnt=0; + while (running) { + if (args.expert.print_buffer_state) { + cnt++; + if (cnt==1000) { + cnt=0; + enb->print_pool(); + } + } + usleep(10000); + } + pthread_cancel(input); + metrics.stop(); + enb->stop(); + enb->cleanup(); + cout << "--- exiting ---" << endl; + exit(0); +} diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc new file mode 100644 index 0000000..826a03c --- /dev/null +++ b/srsenb/src/metrics_stdout.cc @@ -0,0 +1,227 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/metrics_stdout.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +namespace srsenb{ + +#define MAX(a,b) (a>b?a:b) + +char const * const prefixes[2][9] = +{ + { "", "m", "u", "n", "p", "f", "a", "z", "y", }, + { "", "k", "M", "G", "T", "P", "E", "Z", "Y", }, +}; + +metrics_stdout::metrics_stdout() : started(false) ,do_print(false), metrics_report_period(0.0f),n_reports(10) +{ + enb_ = NULL; + bzero(&metrics_thread, sizeof(metrics_thread)); + bzero(&metrics, sizeof(metrics)); +} + +bool metrics_stdout::init(enb_metrics_interface *u, float report_period_secs) +{ + enb_ = u; + metrics_report_period = report_period_secs; + + started = true; + pthread_create(&metrics_thread, NULL, &metrics_thread_start, this); + return true; +} + +void metrics_stdout::stop() +{ + if(started) + { + started = false; + pthread_join(metrics_thread, NULL); + } +} + +void metrics_stdout::toggle_print(bool b) +{ + do_print = b; +} + +void* metrics_stdout::metrics_thread_start(void *m_) +{ + metrics_stdout *m = (metrics_stdout*)m_; + m->metrics_thread_run(); + return NULL; +} + +void metrics_stdout::metrics_thread_run() +{ + while(started) + { + usleep(metrics_report_period*1e6); + if(enb_->get_metrics(metrics)) { + if (metrics.rrc.n_ues > 0) { + print_metrics(); + } + } else { + print_disconnect(); + } + } +} + +void metrics_stdout::print_metrics() +{ + std::ios::fmtflags f(cout.flags()); // For avoiding Coverity defect: Not restoring ostream format + + if(!do_print) + return; + + if(++n_reports > 10) + { + n_reports = 0; + cout << endl; + cout << "------DL------------------------------UL----------------------------------" << endl; + cout << "rnti cqi ri mcs brate bler snr phr mcs brate bler bsr" << endl; + } + if (metrics.rrc.n_ues > 0) { + + for (int i=0;i metrics.mac[i].tx_pkts) { + printf("tx caution errors %d > %d\n", metrics.mac[i].tx_errors, metrics.mac[i].tx_pkts); + } + if (metrics.mac[i].rx_errors > metrics.mac[i].rx_pkts) { + printf("rx caution errors %d > %d\n", metrics.mac[i].rx_errors, metrics.mac[i].rx_pkts); + } + + cout << std::hex << metrics.mac[i].rnti << " "; + cout << float_to_string(MAX(0.1,metrics.mac[i].dl_cqi), 2); + cout << float_to_string(metrics.mac[i].dl_ri, 1); + if(not isnan(metrics.phy[i].dl.mcs)) { + cout << float_to_string(MAX(0.1,metrics.phy[i].dl.mcs), 2); + } else { + cout << float_to_string(0,2); + } + if (metrics.mac[i].tx_brate > 0 && metrics_report_period) { + cout << float_to_eng_string(MAX(0.1,(float) metrics.mac[i].tx_brate/metrics_report_period), 2); + } else { + cout << float_to_string(0, 2) << ""; + } + if (metrics.mac[i].tx_pkts > 0 && metrics.mac[i].tx_errors) { + cout << float_to_string(MAX(0.1,(float) 100*metrics.mac[i].tx_errors/metrics.mac[i].tx_pkts), 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + if(not isnan(metrics.phy[i].ul.sinr)) { + cout << float_to_string(MAX(0.1,metrics.phy[i].ul.sinr), 2); + } else { + cout << float_to_string(0,2); + } + cout << float_to_string(metrics.mac[i].phr, 2); + if(not isnan(metrics.phy[i].ul.mcs)) { + cout << float_to_string(MAX(0.1,metrics.phy[i].ul.mcs), 2); + } else { + cout << float_to_string(0,2); + } + if (metrics.mac[i].rx_brate > 0 && metrics_report_period) { + cout << float_to_eng_string(MAX(0.1,(float) metrics.mac[i].rx_brate/metrics_report_period), 2); + } else { + cout << float_to_string(0, 2) << ""; + } + if (metrics.mac[i].rx_pkts > 0 && metrics.mac[i].rx_errors > 0) { + cout << float_to_string(MAX(0.1,(float) 100*metrics.mac[i].rx_errors/metrics.mac[i].rx_pkts), 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + cout << float_to_eng_string(metrics.mac[i].ul_buffer, 2); + cout << endl; + } + } else { + cout << "--- No users ---" << endl; + } + if(metrics.rf.rf_error) { + printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); + } + + cout.flags(f); // For avoiding Coverity defect: Not restoring ostream format +} + +void metrics_stdout::print_disconnect() +{ + if(do_print) { + cout << "--- disconnected ---" << endl; + } +} + +std::string metrics_stdout::float_to_string(float f, int digits) +{ + std::ostringstream os; + int precision; + if(isnan(f) or abs(f) < 0.0001) { + f = 0.0; + precision = digits-1; + } + else { + precision = digits - (int)(log10(fabs(f))-2*DBL_EPSILON); + } + os << std::setw(6) << std::fixed << std::setprecision(precision) << f; + return os.str(); +} + +std::string metrics_stdout::float_to_eng_string(float f, int digits) +{ + const int degree = (f == 0.0) ? 0 : lrint( floor( log10( fabs( f ) ) / 3) ); + + std::string factor; + + if ( abs( degree ) < 9 ) + { + if(degree < 0) + factor = prefixes[0][ abs( degree ) ]; + else + factor = prefixes[1][ abs( degree ) ]; + } else { + return "failed"; + } + + const double scaled = f * pow( 1000.0, -degree ); + if (degree != 0) { + return float_to_string(scaled, digits) + factor; + } else { + return " " + float_to_string(scaled, digits) + factor; + } +} + +} // namespace srsue diff --git a/srsenb/src/parser.cc b/srsenb/src/parser.cc new file mode 100644 index 0000000..8a85789 --- /dev/null +++ b/srsenb/src/parser.cc @@ -0,0 +1,146 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/parser.h" +#include + +namespace srsenb { + +using namespace libconfig; + +int parser::parse_section(std::string filename, parser::section *s) +{ + parser p(filename); + p.add_section(s); + return p.parse(); +} + +parser::parser(std::string filename_) +{ + filename = filename_; +} + +void parser::add_section(parser::section* s) +{ + sections.push_back(s); +} + +int parser::parse() +{ + // open file + Config cfg; + + try + { + cfg.readFile(filename.c_str()); + } + catch(const FileIOException &fioex) + { + std::cerr << "I/O error while reading file: " << filename << std::endl; + return(-1); + } + catch(const ParseException &pex) + { + std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() + << " - " << pex.getError() << std::endl; + return(-1); + } + + for (std::list::iterator ci = sections.begin(); ci != sections.end(); ++ci) { + section *s = *ci; + if (s->parse(cfg.getRoot())) { + return -1; + } + } + return 0; +} + +parser::section::section(std::string name_) +{ + name = name_; + enabled_value = NULL; +} + +// Fields are allocated dynamically, free all fields added to the section +parser::section::~section() +{ + for (std::list::iterator ci = fields.begin(); ci != fields.end(); ++ci) { + delete *ci; + } +} + +void parser::section::add_field(field_itf* f) +{ + fields.push_back(f); +} + +void parser::section::add_subsection(parser::section* s) +{ + sub_sections.push_back(s); +} + +void parser::section::set_optional(bool* enabled_value_) +{ + enabled_value = enabled_value_; +} + +int parser::section::parse(Setting &root) +{ + try { + for (std::list::iterator ci = fields.begin(); ci != fields.end(); ++ci) { + field_itf *f = *ci; + if (f->parse(root[name.c_str()])) { + fprintf(stderr, "Error parsing field %s in section %s\n", f->get_name(), name.c_str()); + return -1; + } + } + for (std::list::iterator ci = sub_sections.begin(); ci != sub_sections.end(); ++ci) { + section *s = *ci; + if (s->parse(root[name.c_str()])) { + fprintf(stderr, "Error parsing section %s\n", name.c_str()); + return -1; + } + } + if (enabled_value) { + *enabled_value = true; + } + } catch(const SettingNotFoundException ex) { + if (enabled_value) { + *enabled_value = false; + return 0; + } else { + std::cerr << "Error section " << name.c_str() << " not found." << std::endl; + return -1; + } + } + return 0; +} + + + + + +} diff --git a/srsenb/src/phy/CMakeLists.txt b/srsenb/src/phy/CMakeLists.txt new file mode 100644 index 0000000..5d46d32 --- /dev/null +++ b/srsenb/src/phy/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsenb_phy STATIC ${SOURCES}) + +if(ENABLE_GUI AND SRSGUI_FOUND) + target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES}) +endif(ENABLE_GUI AND SRSGUI_FOUND) + +install(TARGETS srsenb_phy DESTINATION ${LIBRARY_DIR}) + diff --git a/srsenb/src/phy/phch_common.cc b/srsenb/src/phy/phch_common.cc new file mode 100644 index 0000000..536b0a3 --- /dev/null +++ b/srsenb/src/phy/phch_common.cc @@ -0,0 +1,358 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/common/threads.h" +#include "srslte/common/log.h" +#include +#include "srsenb/hdr/phy/txrx.h" + +#include + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +void phch_common::set_nof_mutex(uint32_t nof_mutex_) { + nof_mutex = nof_mutex_; + assert(nof_mutex <= max_mutex); +} + +void phch_common::reset() { + bzero(ul_grants, sizeof(mac_interface_phy::ul_sched_t)*TTIMOD_SZ); + bzero(dl_grants, sizeof(mac_interface_phy::dl_sched_t)*TTIMOD_SZ); +} + +bool phch_common::init(srslte_cell_t *cell_, srslte::radio* radio_h_, mac_interface_phy *mac_) +{ + radio = radio_h_; + mac = mac_; + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + pthread_mutex_init(&user_mutex, NULL); + + is_first_of_burst = true; + is_first_tx = true; + for (uint32_t i=0;iset_tti(tx_mutex_cnt); + radio->tx((void **) buffer, nof_samples, tx_time); + + // Trigger next transmission + pthread_mutex_unlock(&tx_mutex[(tx_mutex_cnt+1)%nof_mutex]); + + // Trigger MAC clock + mac->tti_clock(); +} + +void phch_common::ue_db_clear(uint32_t sf_idx) +{ + for(std::map::iterator iter=common_ue_db.begin(); iter!=common_ue_db.end(); ++iter) { + pending_ack_t *p = &((common_ue*)&iter->second)->pending_ack; + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + p->is_pending[sf_idx][tb_idx] = false; + } + } +} + +void phch_common::ue_db_add_rnti(uint16_t rnti) +{ + pthread_mutex_lock(&user_mutex); + add_rnti(rnti); + pthread_mutex_unlock(&user_mutex); +} + +// Private function not mutexed +void phch_common::add_rnti(uint16_t rnti) +{ + for (int sf_idx=0;sf_idxmcch_offset_r9; + period = liblte_rrc_mcch_repetition_period_r9_num[area_info->mcch_repetition_period_r9]; + + if((sfn%period == offset) && mcch_table[sf] > 0) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + cfg->mbsfn_mcs = liblte_rrc_mcch_signalling_mcs_r9_num[area_info->signalling_mcs_r9]; + cfg->mbsfn_encode = true; + cfg->is_mcch = true; + return true; + } + } + return false; +} + +bool phch_common::is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + // Set some defaults + cfg->mbsfn_area_id = 0; + cfg->non_mbsfn_region_length = 1; + cfg->mbsfn_mcs = 2; + cfg->mbsfn_encode = false; + cfg->is_mcch = false; + // Check for MCCH + if (is_mcch_subframe(cfg, phy_tti)) { + return true; + } + + // Not MCCH, check for MCH + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &mbsfn.mbsfn_area_info; + + offset = subfr_cnfg->radio_fr_alloc_offset; + period = liblte_rrc_radio_frame_allocation_period_num[subfr_cnfg->radio_fr_alloc_period]; + + if (LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == subfr_cnfg->subfr_alloc_num_frames) { + if ((sfn%period == offset) && (mch_table[sf] > 0)) { + if (sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + if (mcch_configured) { + // Iterate through PMCH configs to see which one applies in the current frame + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch = &mbsfn.mcch; + uint32_t sf_alloc_idx = sfn%liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[mcch->commonsf_allocperiod_r9]; + for (uint32_t i=0; ipmch_infolist_r9_size; i++) { + //if(sf_alloc_idx < mch_period_stop) { + cfg->mbsfn_mcs = mcch->pmch_infolist_r9[i].pmch_config_r9.datamcs_r9; + cfg->mbsfn_encode = true; + //} + } + + } + } + return true; + } + } else if (LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR == subfr_cnfg->subfr_alloc_num_frames) { + uint8_t idx = sfn%period; + if ((idx >= offset) && (idx < offset+4)) { + if (mch_table[(idx*10)+sf] > 0){ + if (sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + // TODO: check for MCCH configuration, set MCS and decode + + } + return true; + } + } + } + + return false; +} + +void phch_common::get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + if(is_mch_subframe(cfg, phy_tti)) { + cfg->sf_type = SUBFRAME_TYPE_MBSFN; + }else{ + cfg->sf_type = SUBFRAME_TYPE_REGULAR; + } +} +} \ No newline at end of file diff --git a/srsenb/src/phy/phch_worker.cc b/srsenb/src/phy/phch_worker.cc new file mode 100644 index 0000000..36a260b --- /dev/null +++ b/srsenb/src/phy/phch_worker.cc @@ -0,0 +1,1231 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" + +#include "srsenb/hdr/phy/phch_worker.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +using namespace std; + +// Enable this to log SI +//#define LOG_THIS(a) 1 + +// Enable this one to skip SI-RNTI +#define LOG_THIS(rnti) (rnti != 0xFFFF) + + +/* Define GUI-related things */ +#ifdef ENABLE_GUI +#include "srsgui/srsgui.h" +#include +#include +#include +#include +#include + +void init_plots(srsenb::phch_worker *worker); +pthread_t plot_thread; +sem_t plot_sem; +static int plot_worker_id = -1; +#else +#warning Compiling without srsGUI support +#endif +/*********************************************/ + + + +//#define DEBUG_WRITE_FILE + +namespace srsenb { + + +phch_worker::phch_worker() +{ + phy = NULL; + + bzero(&enb_dl, sizeof(enb_dl)); + bzero(&enb_ul, sizeof(enb_ul)); + bzero(&tx_time, sizeof(tx_time)); + + reset(); +} + +#ifdef DEBUG_WRITE_FILE +FILE *f; +#endif + +void phch_worker::init(phch_common* phy_, srslte::log *log_h_) +{ + phy = phy_; + log_h = log_h_; + + pthread_mutex_init(&mutex, NULL); + + // Init cell here + for(int p = 0; p < SRSLTE_MAX_PORTS; p++) { + signal_buffer_rx[p] = (cf_t *) srslte_vec_malloc(2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + if (!signal_buffer_rx[p]) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + bzero(signal_buffer_rx[p], 2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + signal_buffer_tx[p] = (cf_t *) srslte_vec_malloc(2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + if (!signal_buffer_tx[p]) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + bzero(signal_buffer_tx[p], 2 * SRSLTE_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t)); + } + if (srslte_enb_dl_init(&enb_dl, signal_buffer_tx, phy->cell.nof_prb)) { + fprintf(stderr, "Error initiating ENB DL\n"); + return; + } + if (srslte_enb_dl_set_cell(&enb_dl, phy->cell)) { + fprintf(stderr, "Error initiating ENB DL\n"); + return; + } + if (srslte_enb_ul_init(&enb_ul, signal_buffer_rx[0], phy->cell.nof_prb)) { + fprintf(stderr, "Error initiating ENB UL\n"); + return; + } + if (srslte_enb_ul_set_cell(&enb_ul, + phy->cell, + NULL, + &phy->pusch_cfg, + &phy->hopping_cfg, + &phy->pucch_cfg)) + { + fprintf(stderr, "Error initiating ENB UL\n"); + return; + } + + /* Setup SI-RNTI in PHY */ + add_rnti(SRSLTE_SIRNTI); + + /* Setup P-RNTI in PHY */ + add_rnti(SRSLTE_PRNTI); + + /* Setup RA-RNTI in PHY */ + for (int i=0;i<10;i++) { + add_rnti(1+i); + } + + + if (srslte_softbuffer_tx_init(&temp_mbsfn_softbuffer, phy->cell.nof_prb)) { + fprintf(stderr, "Error initiating soft buffer\n"); + exit(-1); + } + + srslte_softbuffer_tx_reset(&temp_mbsfn_softbuffer); + srslte_pucch_set_threshold(&enb_ul.pucch, 0.5); + srslte_sch_set_max_noi(&enb_ul.pusch.ul_sch, phy->params.pusch_max_its); + srslte_enb_dl_set_amp(&enb_dl, phy->params.tx_amplitude); + + Info("Worker %d configured cell %d PRB\n", get_id(), phy->cell.nof_prb); + + initiated = true; + running = true; + +#ifdef DEBUG_WRITE_FILE + f = fopen("test.dat", "w"); +#endif +} + +void phch_worker::stop() +{ + running = false; + pthread_mutex_lock(&mutex); + + int cnt = 0; + while(is_worker_running && cnt<100) { + usleep(1000); + cnt++; + } + + if (!is_worker_running) { + srslte_softbuffer_tx_free(&temp_mbsfn_softbuffer); + srslte_enb_dl_free(&enb_dl); + srslte_enb_ul_free(&enb_ul); + for (int p = 0; p < SRSLTE_MAX_PORTS; p++) { + if (signal_buffer_rx[p]) { + free(signal_buffer_rx[p]); + } + if (signal_buffer_tx[p]) { + free(signal_buffer_tx[p]); + } + } + } else { + printf("Warning could not stop properly PHY\n"); + } + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); +} +void phch_worker::reset() +{ + initiated = false; + ue_db.clear(); +} + +cf_t* phch_worker::get_buffer_rx(uint32_t antenna_idx) +{ + return signal_buffer_rx[antenna_idx]; +} + +void phch_worker::set_time(uint32_t tti_, uint32_t tx_mutex_cnt_, srslte_timestamp_t tx_time_) +{ + tti_rx = tti_; + tti_tx_dl = TTI_TX(tti_rx); + tti_tx_ul = TTI_RX_ACK(tti_rx); + + sf_rx = tti_rx%10; + sf_tx = tti_tx_dl%10; + + t_tx_dl = TTIMOD(tti_tx_dl); + t_rx = TTIMOD(tti_rx); + t_tx_ul = TTIMOD(tti_tx_ul); + + tx_mutex_cnt = tx_mutex_cnt_; + memcpy(&tx_time, &tx_time_, sizeof(srslte_timestamp_t)); +} + +int phch_worker::add_rnti(uint16_t rnti) +{ + + if (srslte_enb_dl_add_rnti(&enb_dl, rnti)) { + return -1; + } + if (srslte_enb_ul_add_rnti(&enb_ul, rnti)) { + return -1; + } + + // Create user + ue_db[rnti].rnti = rnti; + + return SRSLTE_SUCCESS; + +} + +uint32_t phch_worker::get_nof_rnti() { + return ue_db.size(); +} + +void phch_worker::set_conf_dedicated_ack(uint16_t rnti, bool ack){ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + ue_db[rnti].dedicated_ack = ack; + } else { + Error("Setting dedicated ack: rnti=0x%x does not exist\n", rnti); + } + pthread_mutex_unlock(&mutex); +} + +void phch_worker::set_config_dedicated(uint16_t rnti, + srslte_refsignal_srs_cfg_t *srs_cfg, + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) +{ + bool pucch_cqi_ack = dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi; + bool pucch_ri = dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present; + + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + /* PUSCH UCI and scheduling configuration */ + srslte_uci_cfg_t uci_cfg; + ZERO_OBJECT(uci_cfg); + + if (dedicated->pusch_cnfg_ded_present && dedicated->sched_request_cnfg_present) { + uci_cfg.I_offset_ack = dedicated->pusch_cnfg_ded.beta_offset_ack_idx; + uci_cfg.I_offset_cqi = dedicated->pusch_cnfg_ded.beta_offset_cqi_idx; + uci_cfg.I_offset_ri = dedicated->pusch_cnfg_ded.beta_offset_ri_idx; + + srslte_pucch_sched_t pucch_sched; + ZERO_OBJECT(pucch_sched); + + pucch_sched.N_pucch_1 = phy->pucch_cfg.n1_pucch_an; + pucch_sched.n_pucch_2 = dedicated->cqi_report_cnfg.report_periodic.pucch_resource_idx; + pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx; + srslte_enb_ul_cfg_ue(&enb_ul, rnti, &uci_cfg, &pucch_sched, srs_cfg); + + ue_db[rnti].I_sr = dedicated->sched_request_cnfg.sr_cnfg_idx; + ue_db[rnti].I_sr_en = true; + } + + /* CQI Reporting */ + if (dedicated->cqi_report_cnfg.report_periodic_setup_present) { + ue_db[rnti].pmi_idx = dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx; + ue_db[rnti].cqi_en = true; + ue_db[rnti].pucch_cqi_ack = pucch_cqi_ack; + } else { + ue_db[rnti].pmi_idx = 0; + ue_db[rnti].cqi_en = false; + } + + /* RI reporting */ + if (pucch_ri) { + ue_db[rnti].ri_idx = dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx; + ue_db[rnti].ri_en = true; + } else { + ue_db[rnti].ri_idx = 0; + ue_db[rnti].ri_en = false; + } + + if (dedicated->antenna_info_present) { + /* If default antenna info then follow 3GPP 36.331 clause 9.2.4 Default physical channel configuration */ + if (dedicated->antenna_info_default_value) { + if (enb_dl.cell.nof_ports == 1) { + ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_1; + } else { + ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_2; + } + ue_db[rnti].dedicated.antenna_info_explicit_value.codebook_subset_restriction_present = false; + ue_db[rnti].dedicated.antenna_info_explicit_value.ue_tx_antenna_selection_setup_present = false; + ue_db[rnti].ri_idx = 0; + ue_db[rnti].ri_en = false; + } else { + /* Physical channel reconfiguration according to 3GPP 36.331 clause 5.3.10.6 */ + memcpy(&ue_db[rnti].dedicated.antenna_info_explicit_value, + &dedicated->antenna_info_explicit_value, + sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + if (dedicated->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_3 && + dedicated->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_4 && + ue_db[rnti].ri_en) { + ue_db[rnti].ri_idx = 0; + ue_db[rnti].ri_en = false; + } + } + } + + /* Set PDSCH power allocation */ + if (dedicated->pdsch_cnfg_ded_present) { + ue_db[rnti].dedicated.pdsch_cnfg_ded_present = true; + ue_db[rnti].dedicated.pdsch_cnfg_ded = dedicated->pdsch_cnfg_ded; + } + } else { + Error("Setting config dedicated: rnti=0x%x does not exist\n", rnti); + } + pthread_mutex_unlock(&mutex); +} + +void phch_worker::rem_rnti(uint16_t rnti) +{ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + ue_db.erase(rnti); + + srslte_enb_dl_rem_rnti(&enb_dl, rnti); + srslte_enb_ul_rem_rnti(&enb_ul, rnti); + + // remove any pending grant for each subframe + for (uint32_t i=0;iul_grants[i].nof_grants;j++) { + if (phy->ul_grants[i].sched_grants[j].rnti == rnti) { + phy->ul_grants[i].sched_grants[j].rnti = 0; + } + } + for (uint32_t j=0;jdl_grants[i].nof_grants;j++) { + if (phy->dl_grants[i].sched_grants[j].rnti == rnti) { + phy->dl_grants[i].sched_grants[j].rnti = 0; + } + } + } + } else { + Error("Removing user: rnti=0x%x does not exist\n", rnti); + } + pthread_mutex_unlock(&mutex); +} + +void phch_worker::work_imp() +{ + if (!running) { + return; + } + + subframe_cfg_t sf_cfg; + phy->get_sf_config(&sf_cfg, tti_tx_dl);// TODO difference between tti_tx_dl and t_tx_dl + + pthread_mutex_lock(&mutex); + is_worker_running = true; + + mac_interface_phy::ul_sched_t *ul_grants = phy->ul_grants; + mac_interface_phy::dl_sched_t *dl_grants = phy->dl_grants; + mac_interface_phy *mac = phy->mac; + + log_h->step(tti_rx); + + Debug("Worker %d running\n", get_id()); + + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + uint16_t rnti = (uint16_t) iter->first; + ue_db[rnti].has_grant_tti = -1; + } + + // Process UL signal + srslte_enb_ul_fft(&enb_ul); + + // Decode pending UL grants for the tti they were scheduled + decode_pusch(ul_grants[t_rx].sched_grants, ul_grants[t_rx].nof_grants); + + // Decode remaining PUCCH ACKs not associated with PUSCH transmission and SR signals + decode_pucch(); + + // Get DL scheduling for the TX TTI from MAC + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + if (mac->get_dl_sched(tti_tx_dl, &dl_grants[t_tx_dl]) < 0) { + Error("Getting DL scheduling from MAC\n"); + goto unlock; + } + } else { + dl_grants[t_tx_dl].cfi = sf_cfg.non_mbsfn_region_length; + srslte_enb_dl_set_non_mbsfn_region(&enb_dl, sf_cfg.non_mbsfn_region_length); + if(mac->get_mch_sched(sf_cfg.is_mcch, &dl_grants[t_tx_dl])){ + Error("Getting MCH packets from MAC\n"); + goto unlock; + } + } + + if (dl_grants[t_tx_dl].cfi < 1 || dl_grants[t_tx_dl].cfi > 3) { + Error("Invalid CFI=%d\n", dl_grants[t_tx_dl].cfi); + goto unlock; + } + + // Get UL scheduling for the TX TTI from MAC + if (mac->get_ul_sched(tti_tx_ul, &ul_grants[t_tx_ul]) < 0) { + Error("Getting UL scheduling from MAC\n"); + goto unlock; + } + + // Put base signals (references, PBCH, PCFICH and PSS/SSS) into the resource grid + srslte_enb_dl_clear_sf(&enb_dl); + srslte_enb_dl_set_cfi(&enb_dl, dl_grants[t_tx_dl].cfi); + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + srslte_enb_dl_put_base(&enb_dl, tti_tx_dl); + } else if (sf_cfg.mbsfn_encode){ + srslte_enb_dl_put_mbsfn_base(&enb_dl, tti_tx_dl); + } + + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + // Put UL/DL grants to resource grid. PDSCH data will be encoded as well. + encode_pdcch_dl(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); + encode_pdsch(dl_grants[t_tx_dl].sched_grants, dl_grants[t_tx_dl].nof_grants); + }else { + srslte_ra_dl_grant_t phy_grant; + phy_grant.mcs[0].idx = sf_cfg.mbsfn_mcs; + encode_pmch(&dl_grants[t_tx_dl].sched_grants[0], &phy_grant); + } + + encode_pdcch_ul(ul_grants[t_tx_ul].sched_grants, ul_grants[t_tx_ul].nof_grants); + // Put pending PHICH HARQ ACK/NACK indications into subframe + encode_phich(ul_grants[t_tx_ul].phich, ul_grants[t_tx_ul].nof_phich); + + // Prepare for receive ACK for DL grants in t_tx_dl+4 + phy->ue_db_clear(TTIMOD(TTI_TX(t_tx_dl))); + + for (uint32_t i=0;i= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + /* For each TB */ + for (uint32_t tb_idx = 0; tb_idx < SRSLTE_MAX_TB; tb_idx++) { + /* If TB enabled, set pending ACK */ + if (dl_grants[t_tx_dl].sched_grants[i].grant.tb_en[tb_idx]) { + phy->ue_db_set_ack_pending(TTIMOD(TTI_TX(t_tx_dl)), + rnti, + tb_idx, + dl_grants[t_tx_dl].sched_grants[i].location.ncce); + } + } + } + } + + // Generate signal and transmit + if(sf_cfg.sf_type == SUBFRAME_TYPE_REGULAR) { + srslte_enb_dl_gen_signal(&enb_dl); + } else { + srslte_enb_dl_gen_signal_mbsfn(&enb_dl); + } + + pthread_mutex_unlock(&mutex); + + Debug("Sending to radio\n"); + phy->worker_end(tx_mutex_cnt, signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time); + + is_worker_running = false; + +#ifdef DEBUG_WRITE_FILE + fwrite(signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t), 1, f); +#endif + +#ifdef DEBUG_WRITE_FILE + if (tti_tx_dl == 10) { + fclose(f); + exit(-1); + } +#endif + + /* Tell the plotting thread to draw the plots */ +#ifdef ENABLE_GUI + if ((int) get_id() == plot_worker_id) { + sem_post(&plot_sem); + } +#endif + +unlock: + if (is_worker_running) { + is_worker_running = false; + pthread_mutex_unlock(&mutex); + } + +} + + +int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch) +{ + srslte_uci_data_t uci_data; + ZERO_OBJECT(uci_data); + + uint32_t wideband_cqi_value = 0, wideband_pmi = 0; + bool wideband_pmi_present = false; + + uint32_t n_rb_ho = 0; + for (uint32_t i=0;iue_db_is_ack_pending(t_rx, rnti, tb); + if (acks_pending[tb]) { + uci_data.uci_ack_len++; + } + } + + // Configure PUSCH CQI channel + srslte_cqi_value_t cqi_value; + ZERO_OBJECT(cqi_value); + + bool cqi_enabled = false; + + if (ue_db[rnti].cqi_en && ue_db[rnti].ri_en && srslte_ri_send(ue_db[rnti].pmi_idx, ue_db[rnti].ri_idx, tti_rx) ) { + uci_data.uci_ri_len = 1; /* Asumes only 1 bit for RI */ + uci_data.ri_periodic_report = true; + } else if (ue_db[rnti].cqi_en && srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + cqi_enabled = true; + if (ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + cqi_value.wideband.pmi_present = true; + cqi_value.wideband.rank_is_not_one = phy->ue_db_get_ri(rnti) > 0; + } + } else if (grants[i].grant.cqi_request) { + cqi_value.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + if (ue_db[rnti].dedicated.antenna_info_present && ( + ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3 || + ue_db[rnti].dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4 + )) { + cqi_value.subband_hl.ri_present = true; + } + cqi_value.subband_hl.N = (phy->cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(phy->cell.nof_prb) : 0; + cqi_value.subband_hl.four_antenna_ports = (phy->cell.nof_ports == 4); + cqi_value.subband_hl.pmi_present = (ue_db[rnti].dedicated.cqi_report_cnfg.report_mode_aperiodic == LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31); + cqi_value.subband_hl.rank_is_not_one = phy->ue_db_get_ri(rnti) > 0; + cqi_enabled = true; + } + + // mark this tti as having an ul grant to avoid pucch + ue_db[rnti].has_grant_tti = tti_rx; + + srslte_ra_ul_grant_t phy_grant; + int res = -1; + if (!srslte_ra_ul_dci_to_grant(&grants[i].grant, enb_ul.cell.nof_prb, n_rb_ho, &phy_grant)) { + + // Handle Format0 adaptive retx + // Use last TBS for this TB in case of mcs>28 + if (phy_grant.mcs.idx > 28) { + phy_grant.mcs.tbs = phy->ue_db_get_last_ul_tbs(rnti, tti_rx); + Info("RETX: mcs=%d, old_tbs=%d pid=%d\n", phy_grant.mcs.idx, phy_grant.mcs.tbs, TTI_TX(tti_rx)%(2*HARQ_DELAY_MS)); + } + phy->ue_db_set_last_ul_tbs(rnti, tti_rx, phy_grant.mcs.tbs); + + if (phy_grant.mcs.mod == SRSLTE_MOD_LAST) { + phy_grant.mcs.mod = phy->ue_db_get_last_ul_mod(rnti, tti_rx); + phy_grant.Qm = srslte_mod_bits_x_symbol(phy_grant.mcs.mod); + } + phy->ue_db_set_last_ul_mod(rnti, tti_rx, phy_grant.mcs.mod); + + + if (phy_grant.mcs.mod == SRSLTE_MOD_64QAM) { + phy_grant.mcs.mod = SRSLTE_MOD_16QAM; + } + phy_grant.Qm = SRSLTE_MIN(phy_grant.Qm, 4); + res = srslte_enb_ul_get_pusch(&enb_ul, &phy_grant, grants[i].softbuffer, + rnti, grants[i].rv_idx, + grants[i].current_tx_nb, + grants[i].data, + (cqi_enabled) ? &cqi_value : NULL, + &uci_data, + sf_rx); + } else { + Error("Computing PUSCH grant\n"); + return SRSLTE_ERROR; + } + + #ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); + #endif + + bool crc_res = (res == 0); + + // Save PHICH scheduling for this user. Each user can have just 1 PUSCH grant per TTI + ue_db[rnti].phich_info.n_prb_lowest = enb_ul.pusch_cfg.grant.n_prb_tilde[0]; + ue_db[rnti].phich_info.n_dmrs = phy_grant.ncs_dmrs; + + char cqi_str[SRSLTE_CQI_STR_MAX_CHAR]; + if (cqi_enabled) { + if (ue_db[rnti].cqi_en) { + wideband_cqi_value = cqi_value.wideband.wideband_cqi; + if (cqi_value.wideband.pmi_present) { + wideband_pmi_present = true; + wideband_pmi = cqi_value.wideband.pmi; + } + } else if (grants[i].grant.cqi_request) { + wideband_cqi_value = cqi_value.subband_hl.wideband_cqi_cw0; + if (cqi_value.subband_hl.pmi_present) { + wideband_pmi_present = true; + wideband_pmi = cqi_value.subband_hl.pmi; + if (cqi_value.subband_hl.rank_is_not_one) { + Info("PUSCH: Aperiodic ri~1, CQI=%02d/%02d, pmi=%d for %d subbands\n", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.wideband_cqi_cw1, + cqi_value.subband_hl.pmi, cqi_value.subband_hl.N); + } else { + Info("PUSCH: Aperiodic ri=1, CQI=%02d, pmi=%d for %d subbands\n", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.pmi, cqi_value.subband_hl.N); + } + } else { + Info("PUSCH: Aperiodic ri%s, CQI=%02d for %d subbands\n", + cqi_value.subband_hl.rank_is_not_one?"~1":"=1", + cqi_value.subband_hl.wideband_cqi_cw0, cqi_value.subband_hl.N); + } + } + srslte_cqi_value_tostring(&cqi_value, cqi_str, SRSLTE_CQI_STR_MAX_CHAR); + } + + float snr_db = 10*log10(srslte_chest_ul_get_snr(&enb_ul.chest)); + + /* + if (!crc_res && enb_ul.pusch_cfg.grant.L_prb == 1 && enb_ul.pusch_cfg.grant.n_prb[0] == 0 && snr_db > 5) { + srslte_vec_save_file("sf_symbols", enb_ul.sf_symbols, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM)); + srslte_vec_save_file("ce", enb_ul.ce, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM)); + srslte_vec_save_file("d", enb_ul.pusch.d, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + srslte_vec_save_file("ce2", enb_ul.pusch.ce, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + srslte_vec_save_file("z", enb_ul.pusch.z, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + printf("saved sf_idx=%d, mcs=%d, tbs=%d, rnti=%d, rv=%d, snr=%.1f\n", tti%10, + grants[i].grant.mcs_idx, enb_ul.pusch_cfg.cb_segm.tbs, rnti, grants[i].rv_idx, snr_db); + exit(-1); + } + */ + log_h->info_hex(grants[i].data, phy_grant.mcs.tbs / 8, + "PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s%s%s%s\n", + rnti, phy_grant.n_prb[0], phy_grant.n_prb[0]+phy_grant.L_prb, + phy_grant.mcs.tbs / 8, phy_grant.mcs.idx, grants[i].grant.rv_idx, + snr_db, + srslte_pusch_last_noi(&enb_ul.pusch), + crc_res ? "OK" : "KO", + (acks_pending[0] || acks_pending[1]) ? ", ack=" : "", + (acks_pending[0]) ? (uci_data.uci_ack ? "1" : "0") : "", + (acks_pending[1]) ? (uci_data.uci_ack_2 ? "1" : "0") : "", + uci_data.uci_cqi_len > 0 ? cqi_str : "", + uci_data.uci_ri_len > 0 ? ((uci_data.uci_ri == 0) ? ", ri=0" : ", ri=1") : "", + timestr); + + // Notify MAC of RL status + if (grants[i].grant.rv_idx == 0) { + if (res && snr_db < PUSCH_RL_SNR_DB_TH) { + Debug("PUSCH: Radio-Link failure snr=%.1f dB\n", snr_db); + phy->mac->rl_failure(rnti); + } else { + phy->mac->rl_ok(rnti); + } + } + + // Notify MAC new received data and HARQ Indication value + phy->mac->crc_info(tti_rx, rnti, phy_grant.mcs.tbs/8, crc_res); + uint32_t ack_idx = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (acks_pending[tb]) { + bool ack = ((ack_idx++ == 0) ? uci_data.uci_ack : uci_data.uci_ack_2); + bool valid = (crc_res || snr_db > PUSCH_RL_SNR_DB_TH); + phy->mac->ack_info(tti_rx, rnti, tb, ack && valid); + } + } + + // Notify MAC of UL SNR, DL CQI and DL RI + if (snr_db >= PUSCH_RL_SNR_DB_TH) { + phy->mac->snr_info(tti_rx, rnti, snr_db); + } + if (uci_data.uci_cqi_len>0 && crc_res) { + phy->mac->cqi_info(tti_rx, rnti, wideband_cqi_value); + } + if (uci_data.uci_ri_len > 0 && crc_res) { + phy->mac->ri_info(tti_rx, rnti, uci_data.uci_ri); + phy->ue_db_set_ri(rnti, uci_data.uci_ri); + } + if (wideband_pmi_present && crc_res) { + phy->mac->pmi_info(tti_rx, rnti, wideband_pmi); + } + + // Save metrics stats + ue_db[rnti].metrics_ul(phy_grant.mcs.idx, 0, snr_db, srslte_pusch_last_noi(&enb_ul.pusch)); + } + } + return SRSLTE_SUCCESS; +} + + +int phch_worker::decode_pucch() +{ + srslte_uci_data_t uci_data; + + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + uint16_t rnti = (uint16_t) iter->first; + + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END && ue_db[rnti].has_grant_tti != (int) tti_rx) { + // Check if user needs to receive PUCCH + bool needs_pucch = false, needs_ack[SRSLTE_MAX_TB] = {false}, needs_sr = false, needs_cqi = false; + uint32_t last_n_pdcch = 0; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + + if (ue_db[rnti].I_sr_en) { + if (srslte_ue_ul_sr_send_tti(ue_db[rnti].I_sr, tti_rx)) { + needs_pucch = true; + needs_sr = true; + uci_data.scheduling_request = true; + } + } + + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + needs_ack[tb] = phy->ue_db_is_ack_pending(t_rx, rnti, tb, &last_n_pdcch); + if (needs_ack[tb]) { + needs_pucch = true; + uci_data.uci_ack_len++; + } + } + srslte_cqi_value_t cqi_value; + ZERO_OBJECT(cqi_value); + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated = &ue_db[rnti].dedicated; + LIBLTE_RRC_TRANSMISSION_MODE_ENUM tx_mode = dedicated->antenna_info_explicit_value.tx_mode; + + if (ue_db[rnti].cqi_en && (ue_db[rnti].pucch_cqi_ack || !needs_ack[0] || !needs_ack[1])) { + if (ue_db[rnti].ri_en && srslte_ri_send(ue_db[rnti].pmi_idx, ue_db[rnti].ri_idx, tti_rx)) { + needs_pucch = true; + uci_data.uci_ri_len = 1; + uci_data.ri_periodic_report = true; + } else if (srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { + needs_pucch = true; + needs_cqi = true; + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + if (tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + cqi_value.wideband.pmi_present = true; + cqi_value.wideband.rank_is_not_one = phy->ue_db_get_ri(rnti) > 0; + } + uci_data.uci_cqi_len = (uint32_t) srslte_cqi_size(&cqi_value); + } + } + + if (needs_pucch) { + if (srslte_enb_ul_get_pucch(&enb_ul, rnti, last_n_pdcch, sf_rx, &uci_data)) { + fprintf(stderr, "Error getting PUCCH\n"); + return SRSLTE_ERROR; + } + /* If only one ACK is required, it can be for TB0 or TB1 */ + uint32_t ack_idx = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (needs_ack[tb]) { + bool ack = ((ack_idx++ == 0) ? uci_data.uci_ack : uci_data.uci_ack_2); + bool valid = srslte_pucch_get_last_corr(&enb_ul.pucch) >= PUCCH_RL_CORR_TH; + phy->mac->ack_info(tti_rx, rnti, tb, ack && valid); + } + } + if (uci_data.scheduling_request) { + phy->mac->sr_detected(tti_rx, rnti); + } + + char cqi_ri_str[64] = {0}; + if (srslte_pucch_get_last_corr(&enb_ul.pucch) > PUCCH_RL_CORR_TH) { + if (uci_data.ri_periodic_report) { + phy->mac->ri_info(tti_rx, rnti, uci_data.uci_ri); + phy->ue_db_set_ri(rnti, uci_data.uci_ri); + sprintf(cqi_ri_str, ", ri=%d", uci_data.uci_ri); + } else if (uci_data.uci_cqi_len && needs_cqi) { + srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); + phy->mac->cqi_info(tti_rx, rnti, cqi_value.wideband.wideband_cqi); + sprintf(cqi_ri_str, ", cqi=%d", cqi_value.wideband.wideband_cqi); + + if (cqi_value.type == SRSLTE_CQI_TYPE_WIDEBAND && cqi_value.wideband.pmi_present) { + phy->mac->pmi_info(tti_rx, rnti, cqi_value.wideband.pmi); + sprintf(cqi_ri_str, "%s, pmi=%d", cqi_ri_str, cqi_value.wideband.pmi); + } + } + } + log_h->info("PUCCH: rnti=0x%x, corr=%.2f, n_pucch=%d, n_prb=%d%s%s%s%s\n", + rnti, + srslte_pucch_get_last_corr(&enb_ul.pucch), + enb_ul.pucch.last_n_pucch, enb_ul.pucch.last_n_prb, + (uci_data.uci_ack_len)?(uci_data.uci_ack?", ack=1":", ack=0"):"", + (uci_data.uci_ack_len > 1)?(uci_data.uci_ack_2?"1":"0"):"", + needs_sr?(uci_data.scheduling_request?", sr=yes":", sr=no"):"", + (needs_cqi || uci_data.ri_periodic_report)?cqi_ri_str:""); + + + // Notify MAC of RL status + if (!needs_sr) { + if (srslte_pucch_get_last_corr(&enb_ul.pucch) < PUCCH_RL_CORR_TH) { + Debug("PUCCH: Radio-Link failure corr=%.1f\n", srslte_pucch_get_last_corr(&enb_ul.pucch)); + phy->mac->rl_failure(rnti); + } else { + phy->mac->rl_ok(rnti); + } + } + } + } + } + return 0; +} + + +int phch_worker::encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks) +{ + for (uint32_t i=0;irnti; + if (rnti) { + if (srslte_enb_dl_put_pdcch_dl(&enb_dl, &grants[i].grant, grant->dci_format, grants[i].location, rnti, sf_tx)) { + fprintf(stderr, "Error putting PDCCH %d\n",i); + return SRSLTE_ERROR; + } + + if (LOG_THIS(rnti)) { + Info("PDCCH: DL DCI %s rnti=0x%x, cce_index=%d, L=%d, tti_tx_dl=%d\n", srslte_dci_format_string(grant->dci_format), + rnti, grants[i].location.ncce, (1<tb_en[0] = true; + phy_grant->tb_en[1] = false; + phy_grant->nof_prb = enb_dl.cell.nof_prb; + phy_grant->sf_type = SRSLTE_SF_MBSFN; + srslte_dl_fill_ra_mcs(&phy_grant->mcs[0], enb_dl.cell.nof_prb); + phy_grant->Qm[0] = srslte_mod_bits_x_symbol(phy_grant->mcs[0].mod); + for(int i = 0; i < 2; i++){ + for(uint32_t j = 0; j < phy_grant->nof_prb; j++){ + phy_grant->prb_idx[i][j] = true; + } + } + srslte_enb_dl_put_pmch(&enb_dl, phy_grant, &temp_mbsfn_softbuffer, sf_tx, &grant->data[0][0]); + return SRSLTE_SUCCESS; +} + +int phch_worker::encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants) { + + /* Scales the Resources Elements affected by the power allocation (p_b) */ + srslte_enb_dl_prepare_power_allocation(&enb_dl); + + for (uint32_t i = 0; i < nof_grants; i++) { + uint16_t rnti = grants[i].rnti; + if (rnti) { + + bool rnti_is_user = true; + if (rnti == SRSLTE_SIRNTI || rnti == SRSLTE_PRNTI || rnti == SRSLTE_MRNTI) { + rnti_is_user = false; + } + /* Mimo type (tx scheme) shall be single or tx diversity by default */ + srslte_mimo_type_t mimo_type = (enb_dl.cell.nof_ports == 1) ? SRSLTE_MIMO_TYPE_SINGLE_ANTENNA + : SRSLTE_MIMO_TYPE_TX_DIVERSITY; + srslte_ra_dl_grant_t phy_grant; + srslte_ra_dl_dci_to_grant(&grants[i].grant, enb_dl.cell.nof_prb, rnti, &phy_grant); + + char grant_str[64]; + switch (grants[i].grant.alloc_type) { + case SRSLTE_RA_ALLOC_TYPE0: + sprintf(grant_str, "mask=0x%x", grants[i].grant.type0_alloc.rbg_bitmask); + break; + case SRSLTE_RA_ALLOC_TYPE1: + sprintf(grant_str, "mask=0x%x", grants[i].grant.type1_alloc.vrb_bitmask); + break; + default: + sprintf(grant_str, "rb_start=%d", grants[i].grant.type2_alloc.RB_start); + break; + } + + + srslte_dci_format_t dci_format = grants[i].dci_format; + switch (dci_format) { + case SRSLTE_DCI_FORMAT1: + case SRSLTE_DCI_FORMAT1A: + /* Do nothing, it keeps default */ + break; + case SRSLTE_DCI_FORMAT2A: + if (SRSLTE_RA_DL_GRANT_NOF_TB(&phy_grant) == 1) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else if (SRSLTE_RA_DL_GRANT_NOF_TB(&phy_grant) == 2) { + mimo_type = SRSLTE_MIMO_TYPE_CDD; + } + break; + case SRSLTE_DCI_FORMAT2: + if (SRSLTE_RA_DL_GRANT_NOF_TB(&phy_grant) == 1) { + if (phy_grant.pinfo == 0) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } + } else if (SRSLTE_RA_DL_GRANT_NOF_TB(&phy_grant) == 2) { + mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } + break; + case SRSLTE_DCI_FORMAT0: + case SRSLTE_DCI_FORMAT1C: + case SRSLTE_DCI_FORMAT1B: + case SRSLTE_DCI_FORMAT1D: + case SRSLTE_DCI_FORMAT2B: + default: + Error("Not implemented/Undefined DCI format (%d)\n", dci_format); + } + + if (LOG_THIS(rnti)) { + uint8_t x = 0; + uint8_t *ptr = grants[i].data[0]; + uint32_t len = phy_grant.mcs[0].tbs / (uint32_t) 8; + if (!ptr) { + ptr = &x; + len = 1; + } + char pinfo_str[16] = {0}; + if (dci_format == SRSLTE_DCI_FORMAT2) { + snprintf(pinfo_str, 15, ", pinfo=%x", phy_grant.pinfo); + } + char tbstr[SRSLTE_MAX_TB][128]; + for (int tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (phy_grant.tb_en[tb]) { + snprintf(tbstr[tb], 128, ", TB%d: tbs=%d, mcs=%d, rv=%d", + tb, + phy_grant.mcs[tb].tbs / 8, + phy_grant.mcs[tb].idx, + (tb == 0) ? grants[i].grant.rv_idx : grants[i].grant.rv_idx_1); + } else { + tbstr[tb][0] = '\0'; + } + } + log_h->info_hex(ptr, len, + "PDSCH: rnti=0x%x, l_crb=%2d, %s, harq=%d, tti_tx_dl=%d, tx_scheme=%s%s%s%s\n", + rnti, phy_grant.nof_prb, grant_str, grants[i].grant.harq_process, tti_tx_dl, + srslte_mimotype2str(mimo_type), pinfo_str, tbstr[0], tbstr[1]); + } + + int rv[SRSLTE_MAX_CODEWORDS] = {grants[i].grant.rv_idx, grants[i].grant.rv_idx_1}; + + /* Set power allocation */ + float rho_a = ((enb_dl.cell.nof_ports == 1) ? 1.0f : sqrtf(2.0f)), rho_b = 1.0f; + uint32_t pdsch_cnfg_ded = ue_db[rnti].dedicated.pdsch_cnfg_ded; + if (pdsch_cnfg_ded < (uint32_t) LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS) { + float rho_a_db = liblte_rrc_pdsch_config_p_a_num[pdsch_cnfg_ded]; + rho_a *= powf(10.0f, rho_a_db / 20.0f); + } + if (phy->pdsch_p_b < 4) { + uint32_t idx0 = (phy->cell.nof_ports == 1) ? 0 : 1; + float cell_specific_ratio = pdsch_cfg_cell_specific_ratio_table[idx0][phy->pdsch_p_b]; + rho_b = sqrtf(cell_specific_ratio); + } + srslte_enb_dl_set_power_allocation(&enb_dl, rho_a, rho_b); + if (srslte_enb_dl_put_pdsch(&enb_dl, &phy_grant, grants[i].softbuffers, rnti, rv, sf_tx, grants[i].data, mimo_type)) { + fprintf(stderr, "Error putting PDSCH %d\n", i); + return SRSLTE_ERROR; + } + + // Save metrics stats + ue_db[rnti].metrics_dl(phy_grant.mcs[0].idx); + } + } + + srslte_enb_dl_apply_power_allocation(&enb_dl); + + return SRSLTE_SUCCESS; +} + + + +/************ METRICS interface ********************/ +uint32_t phch_worker::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]) +{ + uint32_t cnt=0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = (ue*) &iter->second; + uint16_t rnti = iter->first; + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + u->metrics_read(&metrics[cnt]); + cnt++; + } + } + return cnt; +} + +void phch_worker::ue::metrics_read(phy_metrics_t* metrics_) +{ + memcpy(metrics_, &metrics, sizeof(phy_metrics_t)); + bzero(&metrics, sizeof(phy_metrics_t)); +} + +void phch_worker::ue::metrics_dl(uint32_t mcs) +{ + metrics.dl.mcs = SRSLTE_VEC_CMA(mcs, metrics.dl.mcs, metrics.dl.n_samples); + metrics.dl.n_samples++; +} + +void phch_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters) +{ + metrics.ul.mcs = SRSLTE_VEC_CMA((float) mcs, metrics.ul.mcs, metrics.ul.n_samples); + metrics.ul.sinr = SRSLTE_VEC_CMA((float) sinr, metrics.ul.sinr, metrics.ul.n_samples); + metrics.ul.rssi = SRSLTE_VEC_CMA((float) sinr, metrics.ul.rssi, metrics.ul.n_samples); + metrics.ul.turbo_iters = SRSLTE_VEC_CMA((float) turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples); + metrics.ul.n_samples++; +} + + + +void phch_worker::start_plot() { +#ifdef ENABLE_GUI + if (plot_worker_id == -1) { + plot_worker_id = get_id(); + log_h->console("Starting plot for worker_id=%d\n", plot_worker_id); + init_plots(this); + } else { + log_h->console("Trying to start a plot but already started by worker_id=%d\n", plot_worker_id); + } +#else + log_h->console("Trying to start a plot but plots are disabled (ENABLE_GUI constant in phch_worker.cc)\n"); +#endif +} + + +int phch_worker::read_ce_abs(float *ce_abs) { + uint32_t i=0; + int sz = srslte_symbol_sz(phy->cell.nof_prb); + bzero(ce_abs, sizeof(float)*sz); + int g = (sz - 12*phy->cell.nof_prb)/2; + for (i = 0; i < 12*phy->cell.nof_prb; i++) { + ce_abs[g+i] = 20 * log10(cabs(enb_ul.ce[i])); + if (isinf(ce_abs[g+i])) { + ce_abs[g+i] = -80; + } + } + return sz; +} + +int phch_worker::read_ce_arg(float *ce_arg) { + uint32_t i=0; + int sz = srslte_symbol_sz(phy->cell.nof_prb); + bzero(ce_arg, sizeof(float)*sz); + int g = (sz - 12*phy->cell.nof_prb)/2; + for (i = 0; i < 12*phy->cell.nof_prb; i++) { + ce_arg[g+i] = cargf(enb_ul.ce[i]) * 180.0f / (float) M_PI; + if (isinf(ce_arg[g+i])) { + ce_arg[g+i] = -80; + } + } + return sz; +} + +int phch_worker::read_pusch_d(cf_t* pdsch_d) +{ + int nof_re = 400;//enb_ul.pusch_cfg.nbits.nof_re + memcpy(pdsch_d, enb_ul.pusch.d, nof_re*sizeof(cf_t)); + return nof_re; +} + +int phch_worker::read_pucch_d(cf_t* pdsch_d) +{ + int nof_re = SRSLTE_PUCCH_MAX_BITS/2;//enb_ul.pusch_cfg.nbits.nof_re + memcpy(pdsch_d, enb_ul.pucch.z_tmp, nof_re*sizeof(cf_t)); + return nof_re; +} + + +} + + +/*********************************************************** + * + * PLOT TO VISUALIZE THE CHANNEL RESPONSEE + * + ***********************************************************/ + + +#ifdef ENABLE_GUI +plot_real_t pce, pce_arg; +plot_scatter_t pconst; +plot_scatter_t pconst2; +#define SCATTER_PUSCH_BUFFER_LEN (20*6*SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)) +#define SCATTER_PUSCH_PLOT_LEN 4000 +float tmp_plot[SCATTER_PUSCH_BUFFER_LEN]; +float tmp_plot_arg[SCATTER_PUSCH_BUFFER_LEN]; +cf_t tmp_plot2[SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)]; +cf_t tmp_pucch_plot[SRSLTE_PUCCH_MAX_BITS/2]; + +void *plot_thread_run(void *arg) { + srsenb::phch_worker *worker = (srsenb::phch_worker*) arg; + + sdrgui_init_title("srsENB"); + plot_real_init(&pce); + plot_real_setTitle(&pce, (char*) "Channel Response - Magnitude"); + plot_real_setLabels(&pce, (char*) "Index", (char*) "dB"); + plot_real_setYAxisScale(&pce, -40, 40); + + plot_real_init(&pce_arg); + plot_real_setTitle(&pce_arg, (char*) "Channel Response - Argument"); + plot_real_setLabels(&pce_arg, (char*) "Angle", (char*) "deg"); + plot_real_setYAxisScale(&pce_arg, -180, 180); + + plot_scatter_init(&pconst); + plot_scatter_setTitle(&pconst, (char*) "PUSCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pconst, -4, 4); + plot_scatter_setYAxisScale(&pconst, -4, 4); + + plot_scatter_init(&pconst2); + plot_scatter_setTitle(&pconst2, (char*) "PUCCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pconst2, -4, 4); + plot_scatter_setYAxisScale(&pconst2, -4, 4); + + plot_real_addToWindowGrid(&pce, (char*)"srsenb", 0, 0); + plot_real_addToWindowGrid(&pce_arg, (char*)"srsenb", 1, 0); + plot_scatter_addToWindowGrid(&pconst, (char*)"srsenb", 0, 1); + plot_scatter_addToWindowGrid(&pconst2, (char*)"srsenb", 1, 1); + + int n, n_arg, n_pucch; + int readed_pusch_re=0; + while(1) { + sem_wait(&plot_sem); + + n = worker->read_pusch_d(tmp_plot2); + n_pucch = worker->read_pucch_d(tmp_pucch_plot); + plot_scatter_setNewData(&pconst, tmp_plot2, n); + plot_scatter_setNewData(&pconst2, tmp_pucch_plot, n_pucch); + + n = worker->read_ce_abs(tmp_plot); + plot_real_setNewData(&pce, tmp_plot, n); + + n_arg = worker->read_ce_arg(tmp_plot_arg); + plot_real_setNewData(&pce_arg, tmp_plot_arg, n_arg); + + } + return NULL; +} + + +void init_plots(srsenb::phch_worker *worker) { + + if (sem_init(&plot_sem, 0, 0)) { + perror("sem_init"); + exit(-1); + } + + pthread_attr_t attr; + struct sched_param param; + param.sched_priority = 0; + pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + pthread_attr_setschedparam(&attr, ¶m); + if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) { + perror("pthread_create"); + exit(-1); + } +} +#endif + + + + diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc new file mode 100644 index 0000000..ee7c1e8 --- /dev/null +++ b/srsenb/src/phy/phy.cc @@ -0,0 +1,259 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" +#include "srsenb/hdr/phy/phy.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +phy::phy() : workers_pool(MAX_WORKERS), + workers(MAX_WORKERS), + workers_common(txrx::MUTEX_X_WORKER*MAX_WORKERS), + nof_workers(0) +{ + radio_handler = NULL; + bzero(&prach_cfg, sizeof(prach_cfg)); +} + +void phy::parse_config(phy_cfg_t* cfg) +{ + + // PRACH configuration + prach_cfg.config_idx = cfg->prach_cnfg.prach_cnfg_info.prach_config_index; + prach_cfg.hs_flag = cfg->prach_cnfg.prach_cnfg_info.high_speed_flag; + prach_cfg.root_seq_idx = cfg->prach_cnfg.root_sequence_index; + prach_cfg.zero_corr_zone = cfg->prach_cnfg.prach_cnfg_info.zero_correlation_zone_config; + prach_cfg.freq_offset = cfg->prach_cnfg.prach_cnfg_info.prach_freq_offset; + + // PUSCH DMRS configuration + workers_common.pusch_cfg.cyclic_shift = cfg->pusch_cnfg.ul_rs.cyclic_shift; + workers_common.pusch_cfg.delta_ss = cfg->pusch_cnfg.ul_rs.group_assignment_pusch; + workers_common.pusch_cfg.group_hopping_en = cfg->pusch_cnfg.ul_rs.group_hopping_enabled; + workers_common.pusch_cfg.sequence_hopping_en = cfg->pusch_cnfg.ul_rs.sequence_hopping_enabled; + + // PUSCH hopping configuration + workers_common.hopping_cfg.hop_mode = cfg->pusch_cnfg.hopping_mode == LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME ? + srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTRA_SF : + srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTER_SF; ; + workers_common.hopping_cfg.n_sb = cfg->pusch_cnfg.n_sb; + workers_common.hopping_cfg.hopping_offset = cfg->pusch_cnfg.pusch_hopping_offset; + + // PUCCH configuration + workers_common.pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[cfg->pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + workers_common.pucch_cfg.N_cs = cfg->pucch_cnfg.n_cs_an; + workers_common.pucch_cfg.n_rb_2 = cfg->pucch_cnfg.n_rb_cqi; + workers_common.pucch_cfg.srs_configured = false; + workers_common.pucch_cfg.n1_pucch_an = cfg->pucch_cnfg.n1_pucch_an; + + // PDSCH configuration + workers_common.pdsch_p_b = cfg->pdsch_cnfg.p_b; +} + +bool phy::init(phy_args_t *args, + phy_cfg_t *cfg, + srslte::radio* radio_handler_, + mac_interface_phy *mac, + srslte::log_filter* log_h) +{ + + std::vector log_vec; + this->log_h = log_h; + for (int i=0;inof_phy_threads;i++) { + log_vec.push_back(log_h); + } + init(args, cfg, radio_handler_, mac, log_vec); + return true; +} + +bool phy::init(phy_args_t *args, + phy_cfg_t *cfg, + srslte::radio* radio_handler_, + mac_interface_phy *mac, + std::vector log_vec) +{ + + mlockall(MCL_CURRENT | MCL_FUTURE); + + radio_handler = radio_handler_; + nof_workers = args->nof_phy_threads; + this->log_h = (srslte::log*)log_vec[0]; + workers_common.params = *args; + + workers_common.init(&cfg->cell, radio_handler, mac); + + parse_config(cfg); + + // Add workers to workers pool and start threads + for (uint32_t i=0;icell, &prach_cfg, mac, (srslte::log*) log_vec[0], PRACH_WORKER_THREAD_PRIO); + prach.set_max_prach_offset_us(args->max_prach_offset_us); + + // Warning this must be initialized after all workers have been added to the pool + tx_rx.init(radio_handler, &workers_pool, &workers_common, &prach, (srslte::log*) log_vec[0], SF_RECV_THREAD_PRIO); + + return true; +} + +void phy::stop() +{ + tx_rx.stop(); + workers_common.stop(); + for (uint32_t i=0;iPHY interface **********/ +int phy::add_rnti(uint16_t rnti) +{ + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + workers_common.ue_db_add_rnti(rnti); + } + for (uint32_t i=0;i= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + workers_common.ue_db_rem_rnti(rnti); + } + for (uint32_t i=0;iPHY interface **********/ + +void phy::set_conf_dedicated_ack(uint16_t rnti, bool ack) +{ + for (uint32_t i = 0; i < nof_workers; i++) { + workers[i].set_conf_dedicated_ack(rnti, ack); + } +} + +void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) +{ + for (uint32_t i=0;imbsfn_subfr_cnfg_list_size > 1) { + Warning("SIB2 has %d MBSFN subframe configs - only 1 supported\n", sib2->mbsfn_subfr_cnfg_list_size); + } + if(sib2->mbsfn_subfr_cnfg_list_size > 0) { + memcpy(&phy_rrc_config.mbsfn.mbsfn_subfr_cnfg, &sib2->mbsfn_subfr_cnfg_list[0], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); + } + + memcpy(&phy_rrc_config.mbsfn.mbsfn_notification_cnfg, &sib13->mbsfn_notification_config, sizeof(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT)); + if(sib13->mbsfn_area_info_list_r9_size > 1) { + Warning("SIB13 has %d MBSFN area info elements - only 1 supported\n", sib13->mbsfn_area_info_list_r9_size); + } + if(sib13->mbsfn_area_info_list_r9_size > 0) { + memcpy(&phy_rrc_config.mbsfn.mbsfn_area_info, &sib13->mbsfn_area_info_list_r9[0], sizeof(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT)); + } + + memcpy(&phy_rrc_config.mbsfn.mcch, &mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + + workers_common.configure_mbsfn(&phy_rrc_config.mbsfn); +} + +// Start GUI +void phy::start_plot() { + ((phch_worker) workers[0]).start_plot(); +} + +} diff --git a/srsenb/src/phy/prach_worker.cc b/srsenb/src/phy/prach_worker.cc new file mode 100644 index 0000000..a42849b --- /dev/null +++ b/srsenb/src/phy/prach_worker.cc @@ -0,0 +1,166 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/srslte.h" +#include "srsenb/hdr/phy/prach_worker.h" + +namespace srsenb { + +int prach_worker::init(srslte_cell_t *cell_, srslte_prach_cfg_t *prach_cfg_, mac_interface_phy* mac_, srslte::log* log_h_, int priority) +{ + log_h = log_h_; + mac = mac_; + memcpy(&prach_cfg, prach_cfg_, sizeof(srslte_prach_cfg_t)); + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + max_prach_offset_us = 50; + + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + + if (srslte_prach_init_cfg(&prach, &prach_cfg, cell.nof_prb)) { + fprintf(stderr, "Error initiating PRACH\n"); + return -1; + } + + srslte_prach_set_detect_factor(&prach, 60); + + nof_sf = (uint32_t) ceilf(prach.T_tot*1000); + + signal_buffer_rx = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + if (!signal_buffer_rx) { + perror("malloc"); + return -1; + } + + start(priority); + initiated = true; + + sf_cnt = 0; + pending_tti = 0; + processed_tti = 0; + return 0; +} + +void prach_worker::stop() +{ + srslte_prach_free(&prach); + + if (signal_buffer_rx) { + free(signal_buffer_rx); + } + pthread_mutex_lock(&mutex); + processed_tti = 99999; + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void prach_worker::set_max_prach_offset_us(float delay_us) +{ + max_prach_offset_us = delay_us; +} + +int prach_worker::new_tti(uint32_t tti_rx, cf_t* buffer_rx) +{ + // Save buffer only if it's a PRACH TTI + if (srslte_prach_tti_opportunity(&prach, tti_rx, -1) || sf_cnt) { + memcpy(&signal_buffer_rx[sf_cnt*SRSLTE_SF_LEN_PRB(cell.nof_prb)], buffer_rx, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + sf_cnt++; + if (sf_cnt == nof_sf) { + sf_cnt = 0; + if ((int) pending_tti != processed_tti) { + log_h->warning("PRACH thread did not finish processing TTI=%d\n", pending_tti); + } + pthread_mutex_lock(&mutex); + if (tti_rx+1 > nof_sf) { + pending_tti = tti_rx+1-nof_sf; + } else { + pending_tti = 10240+(tti_rx+1-nof_sf); + } + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + } + } + return 0; +} + + +int prach_worker::run_tti(uint32_t tti_rx) +{ + if (srslte_prach_tti_opportunity(&prach, tti_rx, -1)) + { + // Detect possible PRACHs + if (srslte_prach_detect_offset(&prach, + prach_cfg.freq_offset, + &signal_buffer_rx[prach.N_cp], + nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb)-prach.N_cp, + prach_indices, + prach_offsets, + prach_p2avg, + &prach_nof_det)) + { + log_h->error("Error detecting PRACH\n"); + return SRSLTE_ERROR; + } + + if (prach_nof_det) { + for (uint32_t i=0;iinfo("PRACH: %d/%d, preamble=%d, offset=%.1f us, peak2avg=%.1f, max_offset=%.1f us\n", + i, prach_nof_det, prach_indices[i], prach_offsets[i]*1e6, prach_p2avg[i], max_prach_offset_us); + + if (prach_offsets[i]*1e6 < max_prach_offset_us) { + mac->rach_detected(tti_rx, prach_indices[i], (uint32_t) (prach_offsets[i]*1e6)); + } + } + } + } + return 0; +} + +void prach_worker::run_thread() +{ + running = true; + while(running) { + pthread_mutex_lock(&mutex); + while(processed_tti == (int) pending_tti) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + log_h->debug("Processing pending_tti=%d\n", pending_tti); + if (running) { + if (run_tti(pending_tti)) { + running = false; + } + processed_tti = pending_tti; + } + } +} + + +} diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc new file mode 100644 index 0000000..7f6503b --- /dev/null +++ b/srsenb/src/phy/txrx.cc @@ -0,0 +1,151 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" + +#include "srsenb/hdr/phy/txrx.h" +#include "srsenb/hdr/phy/phch_worker.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +txrx::txrx() : tx_mutex_cnt(0), nof_tx_mutex(0), tti(0) { + running = false; + radio_h = NULL; + log_h = NULL; + workers_pool = NULL; + worker_com = NULL; + prach = NULL; +} + +bool txrx::init(srslte::radio* radio_h_, srslte::thread_pool* workers_pool_, phch_common* worker_com_, prach_worker *prach_, srslte::log* log_h_, uint32_t prio_) +{ + radio_h = radio_h_; + log_h = log_h_; + workers_pool = workers_pool_; + worker_com = worker_com_; + prach = prach_; + tx_mutex_cnt = 0; + running = true; + + nof_tx_mutex = MUTEX_X_WORKER*workers_pool->get_nof_workers(); + worker_com->set_nof_mutex(nof_tx_mutex); + + start(prio_); + return true; +} + +void txrx::stop() +{ + running = false; + wait_thread_finish(); +} + +void txrx::run_thread() +{ + phch_worker *worker = NULL; + cf_t *buffer[SRSLTE_MAX_PORTS] = {NULL}; + srslte_timestamp_t rx_time, tx_time; + uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->cell.nof_prb); + + float samp_rate = srslte_sampling_freq_hz(worker_com->cell.nof_prb); +#if 0 + if (30720%((int) samp_rate/1000) == 0) { + radio_h->set_master_clock_rate(30.72e6); + } else { + radio_h->set_master_clock_rate(23.04e6); + } +#else + if (samp_rate < 10e6) { + radio_h->set_master_clock_rate(4 * samp_rate); + } else { + radio_h->set_master_clock_rate(samp_rate); + } +#endif + + log_h->console("Setting Sampling frequency %.2f MHz\n", (float) samp_rate/1000000); + + // Configure radio + radio_h->set_rx_srate(samp_rate); + radio_h->set_tx_srate(samp_rate); + + log_h->info("Starting RX/TX thread nof_prb=%d, sf_len=%d\n",worker_com->cell.nof_prb, sf_len); + + + // Set TTI so that first TX is at tti=0 + tti = 10235; + + printf("\n==== eNodeB started ===\n"); + printf("Type to view trace\n"); + // Main loop + while (running) { + tti = (tti+1)%10240; + worker = (phch_worker*) workers_pool->wait_worker(tti); + if (worker) { + for (int p = 0; p < SRSLTE_MAX_PORTS; p++){ + buffer[p] = worker->get_buffer_rx(p); + } + + radio_h->rx_now((void **) buffer, sf_len, &rx_time); + + /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ + srslte_timestamp_copy(&tx_time, &rx_time); + srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); + + Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + tti, tx_mutex_cnt, + tx_time.full_secs, tx_time.frac_secs, + worker->get_id()); + + worker->set_time(tti, tx_mutex_cnt, tx_time); + tx_mutex_cnt = (tx_mutex_cnt+1)%nof_tx_mutex; + + // Trigger phy worker execution + workers_pool->start_worker(worker); + + // Trigger prach worker execution + prach->new_tti(tti, buffer[0]); + + } else { + // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here + running = false; + } + } +} + + + +} diff --git a/srsenb/src/upper/CMakeLists.txt b/srsenb/src/upper/CMakeLists.txt new file mode 100644 index 0000000..59116ba --- /dev/null +++ b/srsenb/src/upper/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsenb_upper STATIC ${SOURCES}) +install(TARGETS srsenb_upper DESTINATION ${LIBRARY_DIR}) + diff --git a/srsenb/src/upper/gtpu.cc b/srsenb/src/upper/gtpu.cc new file mode 100644 index 0000000..1d90fd3 --- /dev/null +++ b/srsenb/src/upper/gtpu.cc @@ -0,0 +1,410 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/gtpu.h" +#include "srsenb/hdr/upper/gtpu.h" +#include +#include +#include +#include + +using namespace srslte; +namespace srsenb { + +gtpu::gtpu():mchthread() +{ + pdcp = NULL; + gtpu_log = NULL; + pool = NULL; + + pthread_mutex_init(&mutex, NULL); + +} + +bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_interface_gtpu* pdcp_, srslte::log* gtpu_log_, bool enable_mbsfn) +{ + pdcp = pdcp_; + gtpu_log = gtpu_log_; + gtp_bind_addr = gtp_bind_addr_; + mme_addr = mme_addr_; + + pool = byte_buffer_pool::get_instance(); + + // Set up sink socket + snk_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (snk_fd < 0) { + gtpu_log->error("Failed to create sink socket\n"); + return false; + } + int enable = 1; +#if defined (SO_REUSEADDR) + if (setsockopt(snk_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + gtpu_log->error("setsockopt(SO_REUSEADDR) failed\n"); +#endif +#if defined (SO_REUSEPORT) + if (setsockopt(snk_fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) + gtpu_log->error("setsockopt(SO_REUSEPORT) failed\n"); +#endif + + + // Set up source socket + src_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (src_fd < 0) { + gtpu_log->error("Failed to create source socket\n"); + return false; + } +#if defined (SO_REUSEADDR) + if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) + gtpu_log->error("setsockopt(SO_REUSEADDR) failed\n"); +#endif +#if defined (SO_REUSEPORT) + if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) + gtpu_log->error("setsockopt(SO_REUSEPORT) failed\n"); +#endif + + struct sockaddr_in bindaddr; + bzero(&bindaddr, sizeof(struct sockaddr_in)); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = inet_addr(gtp_bind_addr.c_str()); + bindaddr.sin_port = htons(GTPU_PORT); + + if (bind(src_fd, (struct sockaddr *)&bindaddr, sizeof(struct sockaddr_in))) { + gtpu_log->error("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT); + gtpu_log->console("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT); + return false; + } + + // Setup a thread to receive packets from the src socket + start(THREAD_PRIO); + + // Start MCH thread if enabled + this->enable_mbsfn = enable_mbsfn; + if(enable_mbsfn) { + mchthread.init(pdcp, gtpu_log); + } + return true; +} + +void gtpu::stop() +{ + + if(enable_mbsfn){ + mchthread.stop(); + } + + if (run_enable) { + run_enable = false; + // Wait thread to exit gracefully otherwise might leave a mutex locked + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; + } + if (running) { + thread_cancel(); + } + wait_thread_finish(); + } + + if (snk_fd) { + close(snk_fd); + } + if (src_fd) { + close(src_fd); + } +} + +// gtpu_interface_pdcp +void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu) +{ + gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d, n_bytes=%d", rnti, lcid, pdu->N_bytes); + gtpu_header_t header; + header.flags = 0x30; + header.message_type = 0xFF; + header.length = pdu->N_bytes; + header.teid = rnti_bearers[rnti].teids_out[lcid]; + + struct sockaddr_in servaddr; + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(rnti_bearers[rnti].spgw_addrs[lcid]); + servaddr.sin_port = htons(GTPU_PORT); + + gtpu_write_header(&header, pdu, gtpu_log); + if (sendto(snk_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))<0) { + perror("sendto"); + } + + pool->deallocate(pdu); +} + +/* Warning: This function is called before calling gtpu::init() during MCCH initialization. + * If access to any element created in init (such as gtpu_log) is required, it must be considered + * the case of it being NULL. + */ +void gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, uint32_t *teid_in) +{ + // Allocate a TEID for the incoming tunnel + rntilcid_to_teidin(rnti, lcid, teid_in); + if (gtpu_log) { + gtpu_log->info("Adding bearer for rnti: 0x%x, lcid: %d, addr: 0x%x, teid_out: 0x%x, teid_in: 0x%x\n", rnti, lcid, addr, teid_out, *teid_in); + } + + // Initialize maps if it's a new RNTI + if(rnti_bearers.count(rnti) == 0) { + for(int i=0;iinfo("Removing bearer for rnti: 0x%x, lcid: %d\n", rnti, lcid); + + rnti_bearers[rnti].teids_in[lcid] = 0; + rnti_bearers[rnti].teids_out[lcid] = 0; + + // Remove RNTI if all bearers are removed + bool rem = true; + for(int i=0;ierror("Fatal Error: Couldn't allocate buffer in gtpu::run_thread().\n"); + return; + } + run_enable = true; + + running=true; + while(run_enable) { + + pdu->reset(); + gtpu_log->debug("Waiting for read...\n"); + int n = 0; + do{ + n = recv(src_fd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0); + } while (n == -1 && errno == EAGAIN); + + if (n < 0) { + gtpu_log->error("Failed to read from socket\n"); + } + + pdu->N_bytes = (uint32_t) n; + + gtpu_header_t header; + gtpu_read_header(pdu, &header,gtpu_log); + + uint16_t rnti = 0; + uint16_t lcid = 0; + teidin_to_rntilcid(header.teid, &rnti, &lcid); + + pthread_mutex_lock(&mutex); + bool user_exists = (rnti_bearers.count(rnti) > 0); + pthread_mutex_unlock(&mutex); + + if(!user_exists) { + gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti); + continue; + } + + if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { + gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); + continue; + } + + gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes); + + pdcp->write_sdu(rnti, lcid, pdu); + + do { + pdu = pool_allocate; + if (!pdu) { + gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); + usleep(10000); + } + } while(!pdu); + } + running = false; +} + +/**************************************************************************** +* TEID to RNIT/LCID helper functions +***************************************************************************/ +void gtpu::teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid) +{ + *lcid = teidin & 0xFFFF; + *rnti = (teidin >> 16) & 0xFFFF; +} + +void gtpu::rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin) +{ + *teidin = (rnti << 16) | lcid; +} + + +/**************************************************************************** +* Class to run the MCH thread +***************************************************************************/ +bool gtpu::mch_thread::init(pdcp_interface_gtpu *pdcp, srslte::log *gtpu_log) +{ + pool = byte_buffer_pool::get_instance(); + this->pdcp = pdcp; + this->gtpu_log = gtpu_log; + + struct sockaddr_in bindaddr; + + // Set up sink socket + m1u_sd = socket(AF_INET, SOCK_DGRAM, 0); + if (m1u_sd < 0) { + gtpu_log->error("Failed to create M1-U sink socket\n"); + return false; + } + + /* Bind socket */ + bzero((char *)&bindaddr, sizeof(struct sockaddr_in)); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); //Multicast sockets require bind to INADDR_ANY + bindaddr.sin_port = htons(GTPU_PORT+1); + size_t addrlen = sizeof(bindaddr); + + if (bind(m1u_sd, (struct sockaddr *) &bindaddr, sizeof(bindaddr)) < 0) { + gtpu_log->error("Failed to bind multicast socket\n"); + return false; + } + + /* Send an ADD MEMBERSHIP message via setsockopt */ + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr("239.255.0.1"); //Multicast address of the service + mreq.imr_interface.s_addr = inet_addr("127.0.1.200"); //Address of the IF the socket will listen to. + if (setsockopt(m1u_sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + gtpu_log->error("Register musticast group for M1-U\n"); + return false; + } + gtpu_log->info("M1-U initialized\n"); + + initiated = true; + lcid_counter = 1; + + // Start thread + start(MCH_THREAD_PRIO); + return true; +} + +void gtpu::mch_thread::run_thread() +{ + if (!initiated) { + fprintf(stderr, "Fatal error running mch_thread without initialization\n"); + return; + } + + byte_buffer_t *pdu; + int n; + socklen_t addrlen; + sockaddr_in src_addr; + + bzero((char *)&src_addr, sizeof(src_addr)); + src_addr.sin_family = AF_INET; + src_addr.sin_addr.s_addr = htonl(INADDR_ANY); + src_addr.sin_port = htons(GTPU_PORT+1); + addrlen = sizeof(src_addr); + + run_enable = true; + running=true; + + pdu = pool->allocate(); + + // Warning: Use mutex here if creating multiple services each with a different thread + uint16_t lcid = lcid_counter; + lcid_counter++; + + while(run_enable) { + + pdu->reset(); + do{ + n = recvfrom(m1u_sd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0, (struct sockaddr *) &src_addr, &addrlen); + } while (n == -1 && errno == EAGAIN); + + pdu->N_bytes = (uint32_t) n; + + gtpu_header_t header; + gtpu_read_header(pdu, &header, gtpu_log); + + pdcp->write_sdu(SRSLTE_MRNTI, lcid, pdu); + do { + pdu = pool_allocate; + if (!pdu) { + gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); + usleep(10000); + } + } while(!pdu); + } + running = false; +} + +void gtpu::mch_thread::stop() +{ + if (run_enable) { + run_enable = false; + // Wait thread to exit gracefully otherwise might leave a mutex locked + int cnt = 0; + while(running && cnt < 100) { + usleep(10000); + cnt++; + } + if (running) { + thread_cancel(); + } + wait_thread_finish(); + } +} + +} // namespace srsenb diff --git a/srsenb/src/upper/pdcp.cc b/srsenb/src/upper/pdcp.cc new file mode 100644 index 0000000..6f07e4e --- /dev/null +++ b/srsenb/src/upper/pdcp.cc @@ -0,0 +1,192 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/upper/pdcp.h" +#include "srsenb/hdr/upper/common_enb.h" + +namespace srsenb { + +void pdcp::init(rlc_interface_pdcp* rlc_, rrc_interface_pdcp* rrc_, gtpu_interface_pdcp* gtpu_, srslte::log* pdcp_log_) +{ + rlc = rlc_; + rrc = rrc_; + gtpu = gtpu_; + log_h = pdcp_log_; + + pool = srslte::byte_buffer_pool::get_instance(); + + pthread_rwlock_init(&rwlock, NULL); +} + +void pdcp::stop() +{ + pthread_rwlock_wrlock(&rwlock); + for(std::map::iterator iter=users.begin(); iter!=users.end(); ++iter) { + clear_user(&iter->second); + } + users.clear(); + pthread_rwlock_unlock(&rwlock); + pthread_rwlock_destroy(&rwlock); +} + +void pdcp::add_user(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti) == 0) { + srslte::pdcp *obj = new srslte::pdcp; + obj->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].gtpu_itf, log_h, RB_ID_SRB0, SECURITY_DIRECTION_DOWNLINK); + users[rnti].rlc_itf.rnti = rnti; + users[rnti].gtpu_itf.rnti = rnti; + users[rnti].rrc_itf.rnti = rnti; + + users[rnti].rrc_itf.rrc = rrc; + users[rnti].rlc_itf.rlc = rlc; + users[rnti].gtpu_itf.gtpu = gtpu; + users[rnti].pdcp = obj; + } + pthread_rwlock_unlock(&rwlock); +} + +// Private unlocked deallocation of user +void pdcp::clear_user(user_interface *ue) +{ + ue->pdcp->stop(); + delete ue->pdcp; + ue->pdcp = NULL; +} + +void pdcp::rem_user(uint16_t rnti) +{ + pthread_rwlock_wrlock(&rwlock); + if (users.count(rnti)) { + clear_user(&users[rnti]); + users.erase(rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_pdcp_config_t cfg) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + if(rnti != SRSLTE_MRNTI){ + users[rnti].pdcp->add_bearer(lcid, cfg); + } else { + users[rnti].pdcp->add_bearer_mrb(lcid, cfg); + } + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::reset(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].pdcp->reset(); + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::config_security(uint16_t rnti, uint32_t lcid, uint8_t* k_rrc_enc_, uint8_t* k_rrc_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].pdcp->config_security(lcid, k_rrc_enc_, k_rrc_int_, cipher_algo_, integ_algo_); + users[rnti].pdcp->enable_integrity(lcid); + users[rnti].pdcp->enable_encryption(lcid); + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].pdcp->write_pdu(lcid, sdu); + } else { + pool->deallocate(sdu); + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + if(rnti != SRSLTE_MRNTI){ + users[rnti].pdcp->write_sdu(lcid, sdu); + }else { + users[rnti].pdcp->write_sdu_mch(lcid, sdu); + } + } else { + pool->deallocate(sdu); + } + pthread_rwlock_unlock(&rwlock); +} + +void pdcp::user_interface_gtpu::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + gtpu->write_pdu(rnti, lcid, pdu); +} + +void pdcp::user_interface_rlc::write_sdu(uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + + rlc->write_sdu(rnti, lcid, sdu); +} + +bool pdcp::user_interface_rlc::rb_is_um(uint32_t lcid) { + return rlc->rb_is_um(rnti, lcid); +} + +void pdcp::user_interface_rrc::write_pdu(uint32_t lcid, srslte::byte_buffer_t* pdu) +{ + rrc->write_pdu(rnti, lcid, pdu); +} + +void pdcp::user_interface_rrc::write_pdu_bcch_bch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void pdcp::user_interface_rrc::write_pdu_bcch_dlsch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void pdcp::user_interface_rrc::write_pdu_pcch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti); +} + +std::string pdcp::user_interface_rrc::get_rb_name(uint32_t lcid) +{ + return std::string(rb_id_text[lcid]); +} + +} diff --git a/srsenb/src/upper/rlc.cc b/srsenb/src/upper/rlc.cc new file mode 100644 index 0000000..6c97391 --- /dev/null +++ b/srsenb/src/upper/rlc.cc @@ -0,0 +1,251 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/upper/rlc.h" +#include "srsenb/hdr/upper/common_enb.h" + +namespace srsenb { + +void rlc::init(pdcp_interface_rlc* pdcp_, rrc_interface_rlc* rrc_, mac_interface_rlc *mac_, + srslte::mac_interface_timers *mac_timers_, srslte::log* log_h_) +{ + pdcp = pdcp_; + rrc = rrc_, + log_h = log_h_; + mac = mac_; + mac_timers = mac_timers_; + + pool = srslte::byte_buffer_pool::get_instance(); + + pthread_rwlock_init(&rwlock, NULL); +} + +void rlc::stop() +{ + pthread_rwlock_wrlock(&rwlock); + for(std::map::iterator iter=users.begin(); iter!=users.end(); ++iter) { + clear_user(&iter->second); + } + users.clear(); + pthread_rwlock_unlock(&rwlock); + pthread_rwlock_destroy(&rwlock); +} + +void rlc::add_user(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti) == 0) { + srslte::rlc *obj = new srslte::rlc; + obj->init(&users[rnti], &users[rnti], &users[rnti], log_h, mac_timers, RB_ID_SRB0); + users[rnti].rnti = rnti; + users[rnti].pdcp = pdcp; + users[rnti].rrc = rrc; + users[rnti].rlc = obj; + users[rnti].parent = this; + } + pthread_rwlock_unlock(&rwlock); +} + +// Private unlocked deallocation of user +void rlc::clear_user(user_interface *ue) +{ + ue->rlc->stop(); + delete ue->rlc; + ue->rlc = NULL; +} +void rlc::rem_user(uint16_t rnti) +{ + pthread_rwlock_wrlock(&rwlock); + if (users.count(rnti)) { + clear_user(&users[rnti]); + users.erase(rnti); + } else { + log_h->error("Removing rnti=0x%x. Already removed\n", rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::clear_buffer(uint16_t rnti) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].rlc->empty_queue(); + for (int i=0;irlc_buffer_state(rnti, i, 0, 0); + } + log_h->info("Cleared buffer rnti=0x%x\n", rnti); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::add_bearer(uint16_t rnti, uint32_t lcid) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].rlc->add_bearer(lcid); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::add_bearer(uint16_t rnti, uint32_t lcid, srslte::srslte_rlc_config_t cnfg) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].rlc->add_bearer(lcid, cnfg); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::add_bearer_mrb(uint16_t rnti, uint32_t lcid) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].rlc->add_bearer_mrb_enb(lcid); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) +{ + rrc->read_pdu_pcch(payload, buffer_size); +} + +int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +{ + int ret; + uint32_t tx_queue; + + pthread_rwlock_rdlock(&rwlock); + if(users.count(rnti)) { + if(rnti != SRSLTE_MRNTI) { + ret = users[rnti].rlc->read_pdu(lcid, payload, nof_bytes); + tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + } else { + ret = users[rnti].rlc->read_pdu_mch(lcid, payload, nof_bytes); + tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); + } + // In the eNodeB, there is no polling for buffer state from the scheduler, thus + // communicate buffer state every time a PDU is read + + uint32_t retx_queue = 0; + log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + }else{ + ret = SRSLTE_ERROR; + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +void rlc::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +{ + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + users[rnti].rlc->write_pdu(lcid, payload, nof_bytes); + + // In the eNodeB, there is no polling for buffer state from the scheduler, thus + // communicate buffer state every time a new PDU is written + uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + uint32_t retx_queue = 0; + log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + } + pthread_rwlock_unlock(&rwlock); +} + +void rlc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t *payload) +{ + // RLC is transparent for BCCH + rrc->read_pdu_bcch_dlsch(sib_index, payload); +} + +void rlc::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + + uint32_t tx_queue; + + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + if(rnti != SRSLTE_MRNTI){ + users[rnti].rlc->write_sdu_nb(lcid, sdu); + tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + }else { + users[rnti].rlc->write_sdu_mch(lcid, sdu); + tx_queue = users[rnti].rlc->get_total_mch_buffer_state(lcid); + } + // In the eNodeB, there is no polling for buffer state from the scheduler, thus + // communicate buffer state every time a new SDU is written + + uint32_t retx_queue = 0; + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + } else { + pool->deallocate(sdu); + } + pthread_rwlock_unlock(&rwlock); +} + +bool rlc::rb_is_um(uint16_t rnti, uint32_t lcid) { + bool ret = false; + pthread_rwlock_rdlock(&rwlock); + if (users.count(rnti)) { + ret = users[rnti].rlc->rb_is_um(lcid); + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +void rlc::user_interface::max_retx_attempted() +{ + rrc->max_retx_attempted(rnti); +} + +void rlc::user_interface::write_pdu(uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + pdcp->write_pdu(rnti, lcid, sdu); +} + +void rlc::user_interface::write_pdu_bcch_bch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void rlc::user_interface::write_pdu_bcch_dlsch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void rlc::user_interface::write_pdu_pcch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti); +} + +std::string rlc::user_interface::get_rb_name(uint32_t lcid) +{ + return std::string(rb_id_text[lcid]); +} + +} diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc new file mode 100644 index 0000000..2e481ce --- /dev/null +++ b/srsenb/src/upper/rrc.cc @@ -0,0 +1,1973 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srslte/interfaces/sched_interface.h" +#include "srslte/asn1/liblte_rrc.h" +#include "srsenb/hdr/upper/rrc.h" +#include "srslte/srslte.h" +#include "srslte/asn1/liblte_mme.h" + + +using srslte::byte_buffer_t; +using srslte::bit_buffer_t; + +namespace srsenb { + +void rrc::init(rrc_cfg_t *cfg_, + phy_interface_rrc* phy_, + mac_interface_rrc* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + s1ap_interface_rrc *s1ap_, + gtpu_interface_rrc* gtpu_, + srslte::log* log_rrc) +{ + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + gtpu = gtpu_; + s1ap = s1ap_; + rrc_log = log_rrc; + cnotifier = NULL; + + running = false; + pool = srslte::byte_buffer_pool::get_instance(); + + memcpy(&cfg, cfg_, sizeof(rrc_cfg_t)); + + if(cfg.sibs[12].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13 && cfg_->enable_mbsfn) { + configure_mbsfn_sibs(&cfg.sibs[1].sib.sib2,&cfg.sibs[12].sib.sib13); + } + + nof_si_messages = generate_sibs(); + config_mac(); + + pthread_mutex_init(&user_mutex, NULL); + pthread_mutex_init(&paging_mutex, NULL); + + act_monitor.start(RRC_THREAD_PRIO); + bzero(&sr_sched, sizeof(sr_sched_t)); + + start(RRC_THREAD_PRIO); +} + +void rrc::set_connect_notifer(connect_notifier *cnotifier) +{ + this->cnotifier = cnotifier; +} + +void rrc::stop() +{ + if(running) { + running = false; + rrc_pdu p = {0, LCID_EXIT, NULL}; + rx_pdu_queue.push(p); + wait_thread_finish(); + } + act_monitor.stop(); + pthread_mutex_lock(&user_mutex); + users.clear(); + pthread_mutex_unlock(&user_mutex); + pthread_mutex_destroy(&user_mutex); + pthread_mutex_destroy(&paging_mutex); +} + + +/******************************************************************************* + Public functions + + All public functions must be mutexed. +*******************************************************************************/ + +void rrc::get_metrics(rrc_metrics_t &m) +{ + if (running) { + pthread_mutex_lock(&user_mutex); + m.n_ues = 0; + for(std::map::iterator iter=users.begin(); m.n_ues < ENB_METRICS_MAX_USERS &&iter!=users.end(); ++iter) { + ue *u = (ue*) &iter->second; + if(iter->first != SRSLTE_MRNTI){ + m.ues[m.n_ues++].state = u->get_state(); + } + } + pthread_mutex_unlock(&user_mutex); + } +} + +/******************************************************************************* + MAC interface + + Those functions that shall be called from a phch_worker should push the command + to the queue and process later +*******************************************************************************/ + +void rrc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t* payload) +{ + if (sib_index < LIBLTE_RRC_MAX_SIB) { + memcpy(payload, sib_buffer[sib_index].msg, sib_buffer[sib_index].N_bytes); + } +} + +void rrc::rl_failure(uint16_t rnti) +{ + rrc_pdu p = {rnti, LCID_RLF_USER, NULL}; + rx_pdu_queue.push(p); +} + +void rrc::set_activity_user(uint16_t rnti) +{ + rrc_pdu p = {rnti, LCID_ACT_USER, NULL}; + rx_pdu_queue.push(p); +} + +void rrc::rem_user_thread(uint16_t rnti) +{ + rrc_pdu p = {rnti, LCID_REM_USER, NULL}; + rx_pdu_queue.push(p); +} + +uint32_t rrc::get_nof_users() { + return users.size(); +} + +void rrc::max_retx_attempted(uint16_t rnti) +{ + +} + +// This function is called from PRACH worker (can wait) +void rrc::add_user(uint16_t rnti) +{ + pthread_mutex_lock(&user_mutex); + if (users.count(rnti) == 0) { + + users[rnti].parent = this; + users[rnti].rnti = rnti; + rlc->add_user(rnti); + pdcp->add_user(rnti); + rrc_log->info("Added new user rnti=0x%x\n", rnti); + } else { + rrc_log->error("Adding user rnti=0x%x (already exists)\n", rnti); + } + + if(rnti == SRSLTE_MRNTI){ + srslte::srslte_pdcp_config_t cfg; + cfg.is_control = false; + cfg.is_data = true; + cfg.direction = SECURITY_DIRECTION_DOWNLINK; + uint32_t teid_in = 1; + + for(uint32_t i = 0; i add_bearer_mrb(SRSLTE_MRNTI,lcid); + pdcp->add_bearer(SRSLTE_MRNTI,lcid,cfg); + gtpu->add_bearer(SRSLTE_MRNTI,lcid, 1, 1, &teid_in); + } + } + + pthread_mutex_unlock(&user_mutex); +} + +/* Function called by MAC after the reception of a C-RNTI CE indicating that the UE still has a + * valid RNTI. + * Called by MAC reader thread (can wait to process) + */ +void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti) +{ + // Remove new_rnti + rem_user_thread(new_rnti); + + // Send Reconfiguration to old_rnti if is RRC_CONNECT or RRC Release if already released here + pthread_mutex_lock(&user_mutex); + if (users.count(old_rnti) == 1) { + if (users[old_rnti].is_connected()) { + users[old_rnti].send_connection_reconf_upd(pool_allocate); + } else { + users[old_rnti].send_connection_release(); + } + } + pthread_mutex_unlock(&user_mutex); +} + + + +/******************************************************************************* + PDCP interface +*******************************************************************************/ +void rrc::write_pdu(uint16_t rnti, uint32_t lcid, byte_buffer_t* pdu) +{ + rrc_pdu p = {rnti, lcid, pdu}; + rx_pdu_queue.push(p); +} + +/******************************************************************************* + S1AP interface +*******************************************************************************/ +void rrc::write_dl_info(uint16_t rnti, byte_buffer_t* sdu) +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + bzero(&dl_dcch_msg, sizeof(LIBLTE_RRC_DL_DCCH_MSG_STRUCT)); + + pthread_mutex_lock(&user_mutex); + + if (users.count(rnti) == 1) { + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER; + memcpy(dl_dcch_msg.msg.dl_info_transfer.dedicated_info.msg, sdu->msg, sdu->N_bytes); + dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes = sdu->N_bytes; + + sdu->reset(); + + users[rnti].send_dl_dcch(&dl_dcch_msg, sdu); + + } else { + rrc_log->error("Rx SDU for unknown rnti=0x%x\n", rnti); + } + + pthread_mutex_unlock(&user_mutex); +} + +void rrc::release_complete(uint16_t rnti) { + rrc_pdu p = {rnti, LCID_REL_USER, NULL}; + rx_pdu_queue.push(p); +} + +bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) +{ + pthread_mutex_lock(&user_mutex); + + rrc_log->info("Adding initial context for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + pthread_mutex_unlock(&user_mutex); + return false; + } + + if(msg->CSFallbackIndicator_present) { + rrc_log->warning("Not handling CSFallbackIndicator\n"); + } + if(msg->AdditionalCSFallbackIndicator_present) { + rrc_log->warning("Not handling AdditionalCSFallbackIndicator\n"); + } + if(msg->CSGMembershipStatus_present) { + rrc_log->warning("Not handling CSGMembershipStatus\n"); + } + if(msg->GUMMEI_ID_present) { + rrc_log->warning("Not handling GUMMEI_ID\n"); + } + if(msg->HandoverRestrictionList_present) { + rrc_log->warning("Not handling HandoverRestrictionList\n"); + } + if(msg->ManagementBasedMDTAllowed_present) { + rrc_log->warning("Not handling ManagementBasedMDTAllowed\n"); + } + if(msg->ManagementBasedMDTPLMNList_present) { + rrc_log->warning("Not handling ManagementBasedMDTPLMNList\n"); + } + if(msg->MME_UE_S1AP_ID_2_present) { + rrc_log->warning("Not handling MME_UE_S1AP_ID_2\n"); + } + if(msg->RegisteredLAI_present) { + rrc_log->warning("Not handling RegisteredLAI\n"); + } + if(msg->SRVCCOperationPossible_present) { + rrc_log->warning("Not handling SRVCCOperationPossible\n"); + } + if(msg->SubscriberProfileIDforRFP_present) { + rrc_log->warning("Not handling SubscriberProfileIDforRFP\n"); + } + if(msg->TraceActivation_present) { + rrc_log->warning("Not handling TraceActivation\n"); + } + if(msg->UERadioCapability_present) { + rrc_log->warning("Not handling UERadioCapability\n"); + } + + // UEAggregateMaximumBitrate + users[rnti].set_bitrates(&msg->uEaggregateMaximumBitrate); + + // UESecurityCapabilities + users[rnti].set_security_capabilities(&msg->UESecurityCapabilities); + + // SecurityKey + uint8_t key[32]; + liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key); + users[rnti].set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN/8); + + // Send RRC security mode command + users[rnti].send_security_mode_command(); + + // Setup E-RABs + users[rnti].setup_erabs(&msg->E_RABToBeSetupListCtxtSUReq); + + pthread_mutex_unlock(&user_mutex); + + return true; +} + +bool rrc::setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) +{ + pthread_mutex_lock(&user_mutex); + + rrc_log->info("Setting up erab(s) for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + pthread_mutex_unlock(&user_mutex); + return false; + } + + if(msg->uEaggregateMaximumBitrate_present) { + // UEAggregateMaximumBitrate + users[rnti].set_bitrates(&msg->uEaggregateMaximumBitrate); + } + + // Setup E-RABs + users[rnti].setup_erabs(&msg->E_RABToBeSetupListBearerSUReq); + + pthread_mutex_unlock(&user_mutex); + + return true; +} + +bool rrc::release_erabs(uint32_t rnti) +{ + pthread_mutex_lock(&user_mutex); + rrc_log->info("Releasing E-RABs for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + pthread_mutex_unlock(&user_mutex); + return false; + } + + bool ret = users[rnti].release_erabs(); + pthread_mutex_unlock(&user_mutex); + return ret; +} + + + +/******************************************************************************* + Paging functions + These functions use a different mutex because access different shared variables + than user map +*******************************************************************************/ + +void rrc::add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) +{ + pthread_mutex_lock(&paging_mutex); + if (pending_paging.count(ueid) == 0) { + pending_paging[ueid] = UEPagingID; + } else { + rrc_log->warning("Received Paging for UEID=%d but not yet transmitted\n", ueid); + } + pthread_mutex_unlock(&paging_mutex); +} + +// Described in Section 7 of 36.304 +bool rrc::is_paging_opportunity(uint32_t tti, uint32_t *payload_len) +{ + int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; + + if (pending_paging.empty()) { + return false; + } + + pthread_mutex_lock(&paging_mutex); + + LIBLTE_RRC_PCCH_MSG_STRUCT pcch_msg; + bzero(&pcch_msg, sizeof(LIBLTE_RRC_PCCH_MSG_STRUCT)); + + // Default paging cycle, should get DRX from user + uint32_t T = liblte_rrc_default_paging_cycle_num[cfg.sibs[1].sib.sib2.rr_config_common_sib.pcch_cnfg.default_paging_cycle]; + uint32_t Nb = T*liblte_rrc_nb_num[cfg.sibs[1].sib.sib2.rr_config_common_sib.pcch_cnfg.nB]; + + uint32_t N = T1?Nb/T:1; + uint32_t sfn = tti/10; + + std::vector ue_to_remove; + + int n=0; + for(std::map::iterator iter=pending_paging.begin(); n < LIBLTE_RRC_MAX_PAGE_REC && iter!=pending_paging.end(); ++iter) { + LIBLTE_S1AP_UEPAGINGID_STRUCT u = (LIBLTE_S1AP_UEPAGINGID_STRUCT) iter->second; + uint32_t ueid = ((uint32_t) iter->first)%1024; + uint32_t i_s = (ueid/N) % Ns; + + if ((sfn % T) == (T/N) * (ueid % N)) { + + int sf_idx = sf_pattern[i_s%4][(Ns-1)%4]; + if (sf_idx < 0) { + rrc_log->error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d\n", Ns, i_s, ueid); + } else if ((uint32_t) sf_idx == (tti%10)) { + + if (u.choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_IMSI) { + pcch_msg.paging_record_list[n].ue_identity.ue_identity_type = LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_IMSI; + memcpy(pcch_msg.paging_record_list[n].ue_identity.imsi, u.choice.iMSI.buffer, u.choice.iMSI.n_octets); + pcch_msg.paging_record_list[n].ue_identity.imsi_size = u.choice.iMSI.n_octets; + printf("Warning IMSI paging not tested\n"); + } else { + pcch_msg.paging_record_list[n].ue_identity.ue_identity_type = LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_S_TMSI; + pcch_msg.paging_record_list[n].ue_identity.s_tmsi.mmec = u.choice.s_TMSI.mMEC.buffer[0]; + uint32_t m_tmsi = 0; + for (int i=0;iinfo("Assembled paging for ue_id=%d, tti=%d\n", ueid, tti); + } + } + } + + for (uint32_t i=0;i 0) { + pcch_msg.paging_record_list_size = n; + liblte_rrc_pack_pcch_msg(&pcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf_paging); + uint32_t N_bytes = (bit_buf_paging.N_bits-1)/8+1; + + if (payload_len) { + *payload_len = N_bytes; + } + rrc_log->info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d\n", + pcch_msg.paging_record_list_size, N_bytes, bit_buf_paging.N_bits); + return true; + } + + return false; +} + +void rrc::read_pdu_pcch(uint8_t *payload, uint32_t buffer_size) +{ + pthread_mutex_lock(&paging_mutex); + uint32_t N_bytes = (bit_buf_paging.N_bits-1)/8+1; + if (N_bytes <= buffer_size) { + srslte_bit_pack_vector(bit_buf_paging.msg, payload, bit_buf_paging.N_bits); + } + pthread_mutex_unlock(&paging_mutex); +} + + +/******************************************************************************* + Private functions + All private functions are not mutexed and must be called from a mutexed enviornment + from either a public function or the internal thread +*******************************************************************************/ + +void rrc::parse_ul_ccch(uint16_t rnti, byte_buffer_t *pdu) +{ + uint16_t old_rnti = 0; + + if (pdu) { + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + bzero(&ul_ccch_msg, sizeof(LIBLTE_RRC_UL_CCCH_MSG_STRUCT)); + + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + liblte_rrc_unpack_ul_ccch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &ul_ccch_msg); + + rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB0 - Rx: %s", + liblte_rrc_ul_ccch_msg_type_text[ul_ccch_msg.msg_type]); + + switch (ul_ccch_msg.msg_type) { + case LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ: + if (users.count(rnti)) { + users[rnti].handle_rrc_con_req(&ul_ccch_msg.msg.rrc_con_req); + } else { + rrc_log->error("Received ConnectionSetup for rnti=0x%x without context\n", rnti); + } + break; + case LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ: + rrc_log->debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s\n", + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti, + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.phys_cell_id, + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.short_mac_i, + liblte_rrc_con_reest_req_cause_text[ul_ccch_msg.msg.rrc_con_reest_req.cause] + ); + if (users[rnti].is_idle()) { + old_rnti = ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti; + if (users.count(old_rnti)) { + rrc_log->error("Not supported: ConnectionReestablishment for rnti=0x%x. Sending Connection Reject\n", old_rnti); + users[rnti].send_connection_reest_rej(); + s1ap->user_release(old_rnti, LIBLTE_S1AP_CAUSERADIONETWORK_RELEASE_DUE_TO_EUTRAN_GENERATED_REASON); + } else { + rrc_log->error("Received ConnectionReestablishment for rnti=0x%x without context\n", old_rnti); + users[rnti].send_connection_reest_rej(); + } + // remove temporal rnti + rrc_log->warning("Received ConnectionReestablishment for rnti=0x%x. Removing temporal rnti=0x%x\n", old_rnti, rnti); + rem_user_thread(rnti); + } else { + rrc_log->error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE\n", rnti); + } + break; + default: + rrc_log->error("UL CCCH message not recognised\n"); + break; + } + + pool->deallocate(pdu); + } +} + +void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) +{ + if (pdu) { + if (users.count(rnti)) { + users[rnti].parse_ul_dcch(lcid, pdu); + } else { + rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + } + } +} + +void rrc::process_rl_failure(uint16_t rnti) +{ + if (users.count(rnti) == 1) { + uint32_t n_rfl = users[rnti].rl_failure(); + if (n_rfl == 1) { + rrc_log->info("Radio-Link failure detected rnti=0x%x\n", rnti); + if (s1ap->user_exists(rnti)) { + if (!s1ap->user_release(rnti, LIBLTE_S1AP_CAUSERADIONETWORK_RADIO_CONNECTION_WITH_UE_LOST)) { + rrc_log->info("Removing rnti=0x%x\n", rnti); + } + } else { + rrc_log->warning("User rnti=0x%x context not existing in S1AP. Removing user\n", rnti); + // Remove user from separate thread to wait to close all resources + rem_user_thread(rnti); + } + } else { + rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); + } + } else { + rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + } +} + +void rrc::process_release_complete(uint16_t rnti) +{ + rrc_log->info("Received Release Complete rnti=0x%x\n", rnti); + if (users.count(rnti) == 1) { + if (!users[rnti].is_idle()) { + rlc->clear_buffer(rnti); + users[rnti].send_connection_release(); + // There is no RRCReleaseComplete message from UE thus wait ~50 subframes for tx + usleep(50000); + } + rem_user_thread(rnti); + } else { + rrc_log->error("Received ReleaseComplete for unknown rnti=0x%x\n", rnti); + } +} + +void rrc::rem_user(uint16_t rnti) +{ + pthread_mutex_lock(&user_mutex); + if (users.count(rnti) == 1) { + rrc_log->console("Disconnecting rnti=0x%x.\n", rnti); + rrc_log->info("Disconnecting rnti=0x%x.\n", rnti); + + /* First remove MAC and GTPU to stop processing DL/UL traffic for this user + */ + mac->ue_rem(rnti); // MAC handles PHY + gtpu->rem_user(rnti); + + // Now remove RLC and PDCP + rlc->rem_user(rnti); + pdcp->rem_user(rnti); + + // And deallocate resources from RRC + users[rnti].sr_free(); + users[rnti].cqi_free(); + + users.erase(rnti); + rrc_log->info("Removed user rnti=0x%x\n", rnti); + } else { + rrc_log->error("Removing user rnti=0x%x (does not exist)\n", rnti); + } + pthread_mutex_unlock(&user_mutex); +} + +void rrc::config_mac() +{ + // Fill MAC scheduler configuration for SIBs + sched_interface::cell_cfg_t sched_cfg; + bzero(&sched_cfg, sizeof(sched_interface::cell_cfg_t)); + for (uint32_t i=0;iinfo("Allocating %d PRBs for PUCCH\n", sched_cfg.nrb_pucch); + + // Copy Cell configuration + memcpy(&sched_cfg.cell, &cfg.cell, sizeof(srslte_cell_t)); + + // Configure MAC scheduler + mac->cell_cfg(&sched_cfg); +} + +uint32_t rrc::generate_sibs() +{ + // nof_messages includes SIB2 by default, plus all configured SIBs + uint32_t nof_messages = 1+cfg.sibs[0].sib.sib1.N_sched_info; + LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info = cfg.sibs[0].sib.sib1.sched_info; + + // msg is array of SI messages, each SI message msg[i] may contain multiple SIBs + // all SIBs in a SI message msg[i] share the same periodicity + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *msg = (LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT*)calloc(nof_messages+1, sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + + // Copy SIB1 to first SI message + msg[0].N_sibs = 1; + memcpy(&msg[0].sibs[0], &cfg.sibs[0], sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT)); + + // Copy rest of SIBs + for (uint32_t sched_info_elem = 0; sched_info_elem < nof_messages; sched_info_elem++) { + uint32_t msg_index = sched_info_elem + 1; // first msg is SIB1, therefore start with second + uint32_t current_msg_element_offset = 0; + + msg[msg_index].N_sibs = 0; + + // SIB2 always in second SI message + if (msg_index == 1) { + msg[msg_index].N_sibs++; + memcpy(&msg[msg_index].sibs[0], &cfg.sibs[1], sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT)); + current_msg_element_offset = 1; // make sure "other SIBs" do not overwrite this SIB2 + // Save SIB2 + memcpy(&sib2, &cfg.sibs[1].sib.sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + } else { + current_msg_element_offset = 0; // no SIB2, no offset + } + + // Add other SIBs to this message, if any + for (uint32_t mapping = 0; mapping < sched_info[sched_info_elem].N_sib_mapping_info; mapping++) { + msg[msg_index].N_sibs++; + // current_msg_element_offset skips SIB2 if necessary + memcpy(&msg[msg_index].sibs[mapping + current_msg_element_offset], + &cfg.sibs[(int) sched_info[sched_info_elem].sib_mapping_info[mapping].sib_type+2], + sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT)); + } + } + + // Pack payload for all messages + for (uint32_t msg_index = 0; msg_index < nof_messages; msg_index++) { + LIBLTE_BIT_MSG_STRUCT bitbuffer; + liblte_rrc_pack_bcch_dlsch_msg(&msg[msg_index], &bitbuffer); + srslte_bit_pack_vector(bitbuffer.msg, sib_buffer[msg_index].msg, bitbuffer.N_bits); + sib_buffer[msg_index].N_bytes = (bitbuffer.N_bits-1)/8+1; + } + + free(msg); + return nof_messages; +} + +void rrc::configure_mbsfn_sibs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + // Temp assignment of MCCH, this will eventually come from a cfg file + mcch.pmch_infolist_r9_size = 1; + mcch.commonsf_allocpatternlist_r9_size = 1; + mcch.commonsf_allocperiod_r9 = LIBLTE_RRC_MBSFN_COMMON_SF_ALLOC_PERIOD_R9_RF64; + mcch.commonsf_allocpatternlist_r9[0].radio_fr_alloc_offset = 0; + mcch.commonsf_allocpatternlist_r9[0].radio_fr_alloc_period = LIBLTE_RRC_RADIO_FRAME_ALLOCATION_PERIOD_N1; + mcch.commonsf_allocpatternlist_r9[0].subfr_alloc = 32+31; + mcch.commonsf_allocpatternlist_r9[0].subfr_alloc_num_frames = LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE; + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size = 1; + + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].logicalchannelid_r9 = 1; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].sessionid_r9_present = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_explicit = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mcc = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_id_r9.mnc = 3; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.plmn_index_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[0].tmgi_r9.serviceid_r9 = 0; + + if(mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size > 1) { + + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].logicalchannelid_r9 = 2; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].sessionid_r9 = 1; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].sessionid_r9_present = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_explicit = true; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_r9.mcc = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_id_r9.mnc = 3; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.plmn_index_r9 = 0; + mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9[1].tmgi_r9.serviceid_r9 = 1; + + } + mcch.pmch_infolist_r9[0].pmch_config_r9.datamcs_r9 = 10; + mcch.pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9 = LIBLTE_RRC_MCH_SCHEDULING_PERIOD_R9_RF64; + mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9 = 64*6; + + + + phy->configure_mbsfn(sib2,sib13,mcch); + mac->write_mcch(sib2,sib13,&mcch); +} + +void rrc::configure_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + // TODO: add k_up_enc, k_up_int support to PDCP + pdcp->config_security(rnti, lcid, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); +} + +/******************************************************************************* + RRC thread +*******************************************************************************/ + +void rrc::run_thread() +{ + rrc_pdu p; + running = true; + + while(running) { + p = rx_pdu_queue.wait_pop(); + if (p.pdu) { + rrc_log->info_hex(p.pdu->msg, p.pdu->N_bytes, "Rx %s PDU", rb_id_text[p.lcid]); + } + + // Mutex these calls even though it's a private function + if (users.count(p.rnti) == 1) { + switch(p.lcid) + { + case RB_ID_SRB0: + parse_ul_ccch(p.rnti, p.pdu); + break; + case RB_ID_SRB1: + case RB_ID_SRB2: + parse_ul_dcch(p.rnti, p.lcid, p.pdu); + break; + case LCID_REM_USER: + rem_user(p.rnti); + break; + case LCID_REL_USER: + process_release_complete(p.rnti); + break; + case LCID_RLF_USER: + process_rl_failure(p.rnti); + break; + case LCID_ACT_USER: + if (users.count(p.rnti) == 1) { + users[p.rnti].set_activity(); + } + break; + case LCID_EXIT: + rrc_log->info("Exiting thread\n"); + break; + default: + rrc_log->error("Rx PDU with invalid bearer id: %d", p.lcid); + break; + } + } else { + rrc_log->warning("Discarding PDU for removed rnti=0x%x\n", p.rnti); + } + } +} + + + +/******************************************************************************* + Activity monitor class +*******************************************************************************/ + +rrc::activity_monitor::activity_monitor(rrc* parent_) +{ + running = true; + parent = parent_; +} + +void rrc::activity_monitor::stop() +{ + if (running) { + running = false; + thread_cancel(); + wait_thread_finish(); + } +} + +void rrc::activity_monitor::run_thread() +{ + while(running) + { + usleep(10000); + pthread_mutex_lock(&parent->user_mutex); + uint16_t rem_rnti = 0; + for(std::map::iterator iter=parent->users.begin(); rem_rnti == 0 && iter!=parent->users.end(); ++iter) { + if(iter->first != SRSLTE_MRNTI){ + ue *u = (ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + if (parent->cnotifier && u->is_connected() && !u->connect_notified) { + parent->cnotifier->user_connected(rnti); + u->connect_notified = true; + } + + if (u->is_timeout()) { + parent->rrc_log->info("User rnti=0x%x timed out. Exists in s1ap=%s\n", rnti, parent->s1ap->user_exists(rnti)?"yes":"no"); + rem_rnti = rnti; + } + } + } + if (rem_rnti) { + if (parent->s1ap->user_exists(rem_rnti)) { + parent->s1ap->user_release(rem_rnti, LIBLTE_S1AP_CAUSERADIONETWORK_USER_INACTIVITY); + } else { + if(rem_rnti != SRSLTE_MRNTI) + parent->rem_user_thread(rem_rnti); + } + } + pthread_mutex_unlock(&parent->user_mutex); + } +} + + + + + +/******************************************************************************* + UE class + + Every function in UE class is called from a mutex environment thus does not + need extra protection. +*******************************************************************************/ +rrc::ue::ue() +{ + parent = NULL; + set_activity(); + has_tmsi = false; + connect_notified = false; + transaction_id = 0; + sr_allocated = false; + sr_sched_sf_idx = 0; + sr_sched_prb_idx = 0; + sr_N_pucch = 0; + sr_I = 0; + cqi_allocated = false; + cqi_pucch = 0; + cqi_idx = 0; + cqi_sched_sf_idx = 0; + cqi_sched_prb_idx = 0; + rlf_cnt = 0; + state = RRC_STATE_IDLE; + pool = srslte::byte_buffer_pool::get_instance(); +} + +rrc_state_t rrc::ue::get_state() +{ + return state; +} + +uint32_t rrc::ue::rl_failure() { + rlf_cnt++; + return rlf_cnt; +} + +void rrc::ue::set_activity() +{ + gettimeofday(&t_last_activity, NULL); + if (parent) { + if (parent->rrc_log) { + parent->rrc_log->debug("Activity registered rnti=0x%x\n", rnti); + } + } +} + +bool rrc::ue::is_connected() { + return state == RRC_STATE_REGISTERED; +} + +bool rrc::ue::is_idle() { + return state == RRC_STATE_IDLE; +} + +bool rrc::ue::is_timeout() +{ + + if (!parent) { + return false; + } + + struct timeval t[3]; + uint32_t deadline_s = 0; + uint32_t deadline_us = 0; + const char *deadline_str = NULL; + memcpy(&t[1], &t_last_activity, sizeof(struct timeval)); + gettimeofday(&t[2], NULL); + get_time_interval(t); + + switch(state) { + case RRC_STATE_IDLE: + deadline_s = 0; + deadline_us = (parent->sib2.rr_config_common_sib.rach_cnfg.max_harq_msg3_tx + 1)* 8 * 1000; + deadline_str = "RRCConnectionSetup"; + break; + case RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE: + deadline_s = 1; + deadline_us = 0; + deadline_str = "RRCConnectionSetupComplete"; + break; + case RRC_STATE_RELEASE_REQUEST: + deadline_s = 4; + deadline_us = 0; + deadline_str = "RRCReleaseRequest"; + break; + default: + deadline_s = parent->cfg.inactivity_timeout_ms/1000; + deadline_us = (parent->cfg.inactivity_timeout_ms%1000)*1000; + deadline_str = "Activity"; + break; + } + + if (deadline_str) { + int64_t deadline = deadline_s*1e6 + deadline_us; + int64_t elapsed = t[0].tv_sec*1e6 + t[0].tv_usec; + if (elapsed > deadline && elapsed > 0) { + parent->rrc_log->warning("User rnti=0x%x expired %s deadline: %ld:%ld>%d:%d us\n", + rnti, deadline_str, + t[0].tv_sec, t[0].tv_usec, + deadline_s, deadline_us); + memcpy(&t_last_activity, &t[2], sizeof(struct timeval)); + state = RRC_STATE_RELEASE_REQUEST; + return true; + } + } + return false; +} + +void rrc::ue::parse_ul_dcch(uint32_t lcid, byte_buffer_t *pdu) +{ + + set_activity(); + + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + + srslte_bit_unpack_vector(pdu->msg, parent->bit_buf.msg, pdu->N_bytes*8); + parent->bit_buf.N_bits = pdu->N_bytes*8; + liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&parent->bit_buf, &ul_dcch_msg); + + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "%s - Rx %s\n", + rb_id_text[lcid], liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + transaction_id = 0; + pdu->reset(); + + switch(ul_dcch_msg.msg_type) { + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE: + handle_rrc_con_setup_complete(&ul_dcch_msg.msg.rrc_con_setup_complete, pdu); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER: + memcpy(pdu->msg, ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes); + pdu->N_bytes = ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes; + parent->s1ap->write_pdu(rnti, pdu); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE: + handle_rrc_reconf_complete(&ul_dcch_msg.msg.rrc_con_reconfig_complete, pdu); + parent->rrc_log->console("User 0x%x connected\n", rnti); + state = RRC_STATE_REGISTERED; + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE: + handle_security_mode_complete(&ul_dcch_msg.msg.security_mode_complete); + // Skipping send_ue_cap_enquiry() procedure for now + // state = RRC_STATE_WAIT_FOR_UE_CAP_INFO; + notify_s1ap_ue_ctxt_setup_complete(); + send_connection_reconf(pdu); + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_FAILURE: + handle_security_mode_failure(&ul_dcch_msg.msg.security_mode_failure); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO: + handle_ue_cap_info(&ul_dcch_msg.msg.ue_capability_info); + send_connection_reconf(pdu); + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + break; + default: + parent->rrc_log->error("Msg: %s not supported\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + break; + } +} + +void rrc::ue::handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg) +{ + set_activity(); + + if(msg->ue_id_type == LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI) { + mmec = msg->ue_id.s_tmsi.mmec; + m_tmsi = msg->ue_id.s_tmsi.m_tmsi; + has_tmsi = true; + } + establishment_cause = msg->cause; + send_connection_setup(); + state = RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE; +} + +void rrc::ue::handle_rrc_con_reest_req(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *msg) +{ + //TODO: Check Short-MAC-I value + parent->rrc_log->error("Not Supported: ConnectionReestablishment. \n"); + +} + +void rrc::ue::handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu) +{ + parent->rrc_log->info("RRCConnectionSetupComplete transaction ID: %d\n", msg->rrc_transaction_id); + + // TODO: msg->selected_plmn_id - used to select PLMN from SIB1 list + // TODO: if(msg->registered_mme_present) - the indicated MME should be used from a pool + + memcpy(pdu->msg, msg->dedicated_info_nas.msg, msg->dedicated_info_nas.N_bytes); + pdu->N_bytes = msg->dedicated_info_nas.N_bytes; + + // Acknowledge Dedicated Configuration + parent->phy->set_conf_dedicated_ack(rnti, true); + parent->mac->phy_config_enabled(rnti, true); + + if(has_tmsi) { + parent->s1ap->initial_ue(rnti, (LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM)establishment_cause, pdu, m_tmsi, mmec); + } else { + parent->s1ap->initial_ue(rnti, (LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM)establishment_cause, pdu); + } + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; +} + +void rrc::ue::handle_rrc_reconf_complete(LIBLTE_RRC_CONNECTION_RECONFIGURATION_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu) +{ + parent->rrc_log->info("RRCReconfigurationComplete transaction ID: %d\n", msg->rrc_transaction_id); + + + // Acknowledge Dedicated Configuration + parent->phy->set_conf_dedicated_ack(rnti, true); + parent->mac->phy_config_enabled(rnti, true); +} + +void rrc::ue::handle_security_mode_complete(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *msg) +{ + parent->rrc_log->info("SecurityModeComplete transaction ID: %d\n", msg->rrc_transaction_id); +} + +void rrc::ue::handle_security_mode_failure(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *msg) +{ + parent->rrc_log->info("SecurityModeFailure transaction ID: %d\n", msg->rrc_transaction_id); +} + +void rrc::ue::handle_ue_cap_info(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *msg) +{ + parent->rrc_log->info("UECapabilityInformation transaction ID: %d\n", msg->rrc_transaction_id); + for(uint32_t i=0; iN_ue_caps; i++) { + if(msg->ue_capability_rat[i].rat_type != LIBLTE_RRC_RAT_TYPE_EUTRA) { + parent->rrc_log->warning("Not handling UE capability information for RAT type %s\n", + liblte_rrc_rat_type_text[msg->ue_capability_rat[i].rat_type]); + } else { + memcpy(&eutra_capabilities, &msg->ue_capability_rat[0], sizeof(LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT)); + parent->rrc_log->info("UE rnti: 0x%x category: %d\n", rnti, msg->ue_capability_rat[0].eutra_capability.ue_category); + } + } + + // TODO: Add liblte_rrc support for unpacking UE cap info and repacking into + // inter-node UERadioAccessCapabilityInformation (36.331 v10.0.0 Section 10.2.2). + // This is then passed to S1AP for transfer to EPC. + // parent->s1ap->ue_capabilities(rnti, &eutra_capabilities); +} + +void rrc::ue::set_bitrates(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *rates) +{ + memcpy(&bitrates, rates, sizeof(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT)); +} + +void rrc::ue::set_security_capabilities(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *caps) +{ + memcpy(&security_capabilities, caps, sizeof(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT)); +} + +void rrc::ue::set_security_key(uint8_t* key, uint32_t length) +{ + memcpy(k_enb, key, length); + parent->rrc_log->info_hex(k_enb, 32, "Key eNodeB (k_enb)"); + // Select algos (TODO: use security capabilities and config preferences) + cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0; + integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA1; + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); + + parent->configure_security(rnti, RB_ID_SRB1, + k_rrc_enc, k_rrc_int, + k_up_enc, k_up_int, + cipher_algo, integ_algo); +} + +bool rrc::ue::setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *e) +{ + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab = &e->buffer[i]; + if(erab->ext) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->iE_Extensions_present) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->transportLayerAddress.n_bits > 32) { + parent->rrc_log->error("IPv6 addresses not currently supported\n"); + return false; + } + + uint32_t teid_out; + uint8_to_uint32(erab->gTP_TEID.buffer, &teid_out); + LIBLTE_S1AP_NAS_PDU_STRUCT *nas_pdu = erab->nAS_PDU_present ? &erab->nAS_PDU : NULL; + setup_erab(erab->e_RAB_ID.E_RAB_ID, &erab->e_RABlevelQoSParameters, + &erab->transportLayerAddress, teid_out, nas_pdu); + } + return true; +} + +bool rrc::ue::setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + if(erab->ext) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->iE_Extensions_present) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->transportLayerAddress.n_bits > 32) { + parent->rrc_log->error("IPv6 addresses not currently supported\n"); + return false; + } + + uint32_t teid_out; + uint8_to_uint32(erab->gTP_TEID.buffer, &teid_out); + setup_erab(erab->e_RAB_ID.E_RAB_ID, &erab->e_RABlevelQoSParameters, + &erab->transportLayerAddress, teid_out, &erab->nAS_PDU); + } + + // Work in progress + notify_s1ap_ue_erab_setup_response(e); + send_connection_reconf_new_bearer(e); + return true; +} + +void rrc::ue::setup_erab(uint8_t id, LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT *qos, + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT *addr, uint32_t teid_out, + LIBLTE_S1AP_NAS_PDU_STRUCT *nas_pdu) +{ + erabs[id].id = id; + memcpy(&erabs[id].qos_params, qos, sizeof(LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT)); + memcpy(&erabs[id].address, addr, sizeof(LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT)); + erabs[id].teid_out = teid_out; + + uint8_t* bit_ptr = addr->buffer; + uint32_t addr_ = liblte_bits_2_value(&bit_ptr, addr->n_bits); + uint8_t lcid = id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1) + parent->gtpu->add_bearer(rnti, lcid, addr_, erabs[id].teid_out, &(erabs[id].teid_in)); + + if(nas_pdu) { + memcpy(parent->erab_info.msg, nas_pdu->buffer, nas_pdu->n_octets); + parent->erab_info.N_bytes = nas_pdu->n_octets; + } +} + +bool rrc::ue::release_erabs() +{ + typedef std::map::iterator it_t; + for(it_t it=erabs.begin(); it!=erabs.end(); ++it) { + // TODO: notify GTPU layer + } + erabs.clear(); + return true; +} + +void rrc::ue::notify_s1ap_ue_ctxt_setup_complete() +{ + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT res; + res.ext = false; + res.E_RABFailedToSetupListCtxtSURes_present = false; + res.CriticalityDiagnostics_present = false; + + res.E_RABSetupListCtxtSURes.len = 0; + res.E_RABFailedToSetupListCtxtSURes.len = 0; + + typedef std::map::iterator it_t; + for(it_t it=erabs.begin(); it!=erabs.end(); ++it) { + uint32_t j = res.E_RABSetupListCtxtSURes.len++; + res.E_RABSetupListCtxtSURes.buffer[j].ext = false; + res.E_RABSetupListCtxtSURes.buffer[j].iE_Extensions_present = false; + res.E_RABSetupListCtxtSURes.buffer[j].e_RAB_ID.ext = false; + res.E_RABSetupListCtxtSURes.buffer[j].e_RAB_ID.E_RAB_ID = it->second.id; + uint32_to_uint8(it->second.teid_in, res.E_RABSetupListCtxtSURes.buffer[j].gTP_TEID.buffer); + } + + parent->s1ap->ue_ctxt_setup_complete(rnti, &res); +} + +void rrc::ue::notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT res; + res.ext=false; + res.E_RABSetupListBearerSURes.len = 0; + res.E_RABFailedToSetupListBearerSURes.len = 0; + + res.CriticalityDiagnostics_present = false; + res.E_RABFailedToSetupListBearerSURes_present = false; + + for(uint32_t i=0; ilen; i++) { + res.E_RABSetupListBearerSURes_present = true; + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + uint32_t j = res.E_RABSetupListBearerSURes.len++; + res.E_RABSetupListBearerSURes.buffer[j].ext = false; + res.E_RABSetupListBearerSURes.buffer[j].iE_Extensions_present = false; + res.E_RABSetupListBearerSURes.buffer[j].e_RAB_ID.ext = false; + res.E_RABSetupListBearerSURes.buffer[j].e_RAB_ID.E_RAB_ID = id; + uint32_to_uint8(erabs[id].teid_in, res.E_RABSetupListBearerSURes.buffer[j].gTP_TEID.buffer); + } + + parent->s1ap->ue_erab_setup_complete(rnti, &res); +} + +void rrc::ue::send_connection_reest_rej() +{ + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ; + + send_dl_ccch(&dl_ccch_msg); + +} + +void rrc::ue::send_connection_setup(bool is_setup) +{ + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT* rr_cfg = NULL; + if (is_setup) { + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP; + dl_ccch_msg.msg.rrc_con_setup.rrc_transaction_id = (transaction_id++)%4; + rr_cfg = &dl_ccch_msg.msg.rrc_con_setup.rr_cnfg; + } else { + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST; + dl_ccch_msg.msg.rrc_con_reest.rrc_transaction_id = (transaction_id++)%4; + rr_cfg = &dl_ccch_msg.msg.rrc_con_reest.rr_cnfg; + } + + // Add SRB1 to cfg + rr_cfg->srb_to_add_mod_list_size = 1; + rr_cfg->srb_to_add_mod_list[0].srb_id = 1; + rr_cfg->srb_to_add_mod_list[0].lc_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].lc_default_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].rlc_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].rlc_default_cnfg_present = true; + + // mac-MainConfig + rr_cfg->mac_main_cnfg_present = true; + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg = &rr_cfg->mac_main_cnfg.explicit_value; + mac_cfg->ulsch_cnfg_present = true; + memcpy(&mac_cfg->ulsch_cnfg, &parent->cfg.mac_cnfg.ulsch_cnfg, sizeof(LIBLTE_RRC_ULSCH_CONFIG_STRUCT)); + mac_cfg->drx_cnfg_present = false; + mac_cfg->phr_cnfg_present = true; + memcpy(&mac_cfg->phr_cnfg, &parent->cfg.mac_cnfg.phr_cnfg, sizeof(LIBLTE_RRC_PHR_CONFIG_STRUCT)); + mac_cfg->time_alignment_timer = parent->cfg.mac_cnfg.time_alignment_timer; + + // physicalConfigDedicated + rr_cfg->phy_cnfg_ded_present = true; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &rr_cfg->phy_cnfg_ded; + bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + phy_cfg->pusch_cnfg_ded_present = true; + memcpy(&phy_cfg->pusch_cnfg_ded, &parent->cfg.pusch_cfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT)); + phy_cfg->sched_request_cnfg_present = true; + phy_cfg->sched_request_cnfg.setup_present = true; + phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + + phy_cfg->antenna_info_default_value = true; + phy_cfg->antenna_info_present = false; + + if (is_setup) { + if (sr_allocate(parent->cfg.sr_cfg.period, &phy_cfg->sched_request_cnfg.sr_cnfg_idx, &phy_cfg->sched_request_cnfg.sr_pucch_resource_idx)) { + parent->rrc_log->error("Allocating SR resources for rnti=%d\n", rnti); + return; + } + } else { + phy_cfg->sched_request_cnfg.sr_cnfg_idx = sr_I; + phy_cfg->sched_request_cnfg.sr_pucch_resource_idx = sr_N_pucch; + } + // Power control + phy_cfg->ul_pwr_ctrl_ded_present = true; + phy_cfg->ul_pwr_ctrl_ded.p0_ue_pusch = 0; + phy_cfg->ul_pwr_ctrl_ded.delta_mcs_en = LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + phy_cfg->ul_pwr_ctrl_ded.accumulation_en = true; + phy_cfg->ul_pwr_ctrl_ded.p0_ue_pucch = 0, + phy_cfg->ul_pwr_ctrl_ded.p_srs_offset = 3; + + // PDSCH + phy_cfg->pdsch_cnfg_ded_present = true; + phy_cfg->pdsch_cnfg_ded = parent->cfg.pdsch_cfg; + + // PUCCH + phy_cfg->pucch_cnfg_ded_present = true; + phy_cfg->pucch_cnfg_ded.ack_nack_repetition_n1_pucch_an = 0; + + phy_cfg->cqi_report_cnfg_present = true; + if(parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + } else { + phy_cfg->cqi_report_cnfg.report_periodic_present = true; + phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = false; + if (is_setup) { + if (cqi_allocate(parent->cfg.cqi_cfg.period, + &phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + &phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx)) + { + parent->rrc_log->error("Allocating CQI resources for rnti=%d\n", rnti); + return; + } + } else { + phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx = cqi_pucch; + phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx = cqi_idx; + } + } + phy_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = 0; + + + // Add SRB1 to Scheduler + srsenb::sched_interface::ue_cfg_t sched_cfg; + bzero(&sched_cfg, sizeof(srsenb::sched_interface::ue_cfg_t)); + sched_cfg.maxharq_tx = liblte_rrc_max_harq_tx_num[parent->cfg.mac_cnfg.ulsch_cnfg.max_harq_tx]; + sched_cfg.continuous_pusch = false; + sched_cfg.aperiodic_cqi_period = parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC?parent->cfg.cqi_cfg.period:0; + sched_cfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sched_cfg.ue_bearers[1].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sched_cfg.sr_I = sr_I; + sched_cfg.sr_N_pucch = sr_N_pucch; + sched_cfg.sr_enabled = true; + sched_cfg.cqi_pucch = cqi_pucch; + sched_cfg.cqi_idx = cqi_idx; + sched_cfg.cqi_enabled = parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_PERIODIC; + sched_cfg.pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + sched_cfg.pucch_cfg.N_cs = parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + sched_cfg.pucch_cfg.n_rb_2 = parent->sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi; + sched_cfg.pucch_cfg.n1_pucch_an = parent->sib2.rr_config_common_sib.pucch_cnfg.n1_pucch_an; + + // Configure MAC + parent->mac->ue_cfg(rnti, &sched_cfg); + + // Configure SRB1 in RLC + parent->rlc->add_bearer(rnti, 1); + + // Configure SRB1 in PDCP + srslte::srslte_pdcp_config_t pdcp_cnfg; + pdcp_cnfg.is_control = true; + pdcp_cnfg.direction = SECURITY_DIRECTION_DOWNLINK; + parent->pdcp->add_bearer(rnti, 1, pdcp_cnfg); + + // Configure PHY layer + parent->phy->set_config_dedicated(rnti, phy_cfg); + parent->phy->set_conf_dedicated_ack(rnti, false); + parent->mac->set_dl_ant_info(rnti, &phy_cfg->antenna_info_explicit_value); + parent->mac->phy_config_enabled(rnti, false); + + rr_cfg->drb_to_add_mod_list_size = 0; + rr_cfg->drb_to_release_list_size = 0; + rr_cfg->rlf_timers_and_constants_present = false; + rr_cfg->sps_cnfg_present = false; + + send_dl_ccch(&dl_ccch_msg); +} + +void rrc::ue::send_connection_reest() +{ + send_connection_setup(false); +} + +void rrc::ue::send_connection_release() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE; + dl_dcch_msg.msg.rrc_con_release.rrc_transaction_id = (transaction_id++)%4; + dl_dcch_msg.msg.rrc_con_release.release_cause = LIBLTE_RRC_RELEASE_CAUSE_OTHER; + + send_dl_dcch(&dl_dcch_msg); +} + +int rrc::ue::get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drb_id) +{ + uint32_t lc_id = drb_id + 2; + uint32_t erab_id = lc_id + 2; + uint32_t qci = erabs[erab_id].qos_params.qCI.QCI; + + if (qci >= MAX_NOF_QCI) { + parent->rrc_log->error("Invalid QCI=%d for ERAB_id=%d, DRB_id=%d\n", qci, erab_id, drb_id); + return -1; + } + + if (!parent->cfg.qci_cfg[qci].configured) { + parent->rrc_log->error("QCI=%d not configured\n", qci); + return -1; + } + + // Add DRB1 to the message + drb->drb_id = drb_id; + drb->lc_id = lc_id; + drb->lc_id_present = true; + drb->eps_bearer_id = erab_id; + drb->eps_bearer_id_present = true; + + drb->lc_cnfg_present = true; + drb->lc_cnfg.ul_specific_params_present = true; + drb->lc_cnfg.log_chan_sr_mask_present = false; + drb->lc_cnfg.ul_specific_params.log_chan_group_present = true; + memcpy(&drb->lc_cnfg.ul_specific_params, &parent->cfg.qci_cfg[qci].lc_cfg, sizeof(LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT)); + + drb->pdcp_cnfg_present = true; + memcpy(&drb->pdcp_cnfg, &parent->cfg.qci_cfg[qci].pdcp_cfg, sizeof(LIBLTE_RRC_PDCP_CONFIG_STRUCT)); + + drb->rlc_cnfg_present = true; + memcpy(&drb->rlc_cnfg, &parent->cfg.qci_cfg[qci].rlc_cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + + return 0; +} + +void rrc::ue::send_connection_reconf_upd(srslte::byte_buffer_t *pdu) +{ + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + bzero(&dl_dcch_msg, sizeof(LIBLTE_RRC_DL_DCCH_MSG_STRUCT)); + + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT* rr_cfg = &dl_dcch_msg.msg.rrc_con_reconfig.rr_cnfg_ded; + + dl_dcch_msg.msg.rrc_con_reconfig.rr_cnfg_ded_present = true; + + rr_cfg->phy_cnfg_ded_present = true; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &rr_cfg->phy_cnfg_ded; + bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + phy_cfg->sched_request_cnfg_present = true; + phy_cfg->sched_request_cnfg.setup_present = true; + phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + + phy_cfg->cqi_report_cnfg_present = true; + if (cqi_allocated) { + cqi_get(&phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + &phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx); + phy_cfg->cqi_report_cnfg.report_periodic_present = true; + phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; + if (parent->cfg.antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3 || + parent->cfg.antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx = 483; /* TODO: HARDCODED! Add to UL scheduler */ + } else { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + } + } else { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; + if (phy_cfg->antenna_info_present && + parent->cfg.antenna_info.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31; + } else { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + } + } + parent->phy->set_config_dedicated(rnti, phy_cfg); + + sr_get(&phy_cfg->sched_request_cnfg.sr_cnfg_idx, &phy_cfg->sched_request_cnfg.sr_pucch_resource_idx); + + pdu->reset(); + + send_dl_dcch(&dl_dcch_msg, pdu); + + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + +} + +void rrc::ue::send_connection_reconf(srslte::byte_buffer_t *pdu) +{ + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT* conn_reconf = &dl_dcch_msg.msg.rrc_con_reconfig; + conn_reconf->rr_cnfg_ded_present = true; + conn_reconf->rr_cnfg_ded.mac_main_cnfg_present = false; + conn_reconf->rr_cnfg_ded.phy_cnfg_ded_present = false; + conn_reconf->rr_cnfg_ded.rlf_timers_and_constants_present = false; + conn_reconf->rr_cnfg_ded.sps_cnfg_present = false; + conn_reconf->rr_cnfg_ded.drb_to_release_list_size = 0; + conn_reconf->meas_cnfg_present = false; + conn_reconf->mob_ctrl_info_present = false; + conn_reconf->sec_cnfg_ho_present = false; + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &conn_reconf->rr_cnfg_ded.phy_cnfg_ded; + bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + conn_reconf->rr_cnfg_ded.phy_cnfg_ded_present = true; + + if (parent->cfg.antenna_info.tx_mode > LIBLTE_RRC_TRANSMISSION_MODE_1) { + memcpy(&phy_cfg->antenna_info_explicit_value, &parent->cfg.antenna_info, + sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + phy_cfg->antenna_info_present = true; + phy_cfg->antenna_info_default_value = false; + } + + // Configure PHY layer + phy_cfg->cqi_report_cnfg_present = true; + if(parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; + if (phy_cfg->antenna_info_present && + phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31; + } else { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + } + } else { + cqi_get(&phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + &phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx); + phy_cfg->cqi_report_cnfg.report_periodic_present = true; + phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; + if (phy_cfg->antenna_info_present && + (phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3 || + phy_cfg->antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4)) { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx = 483; + } else { + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + } + } + phy_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = 0; + + parent->phy->set_config_dedicated(rnti, phy_cfg); + parent->phy->set_conf_dedicated_ack(rnti, false); + parent->mac->set_dl_ant_info(rnti, &phy_cfg->antenna_info_explicit_value); + parent->mac->phy_config_enabled(rnti, false); + + // Add SRB2 to the message + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list_size = 1; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].srb_id = 2; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].lc_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].lc_default_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].rlc_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].rlc_default_cnfg_present = true; + + // Get DRB1 configuration + if (get_drbid_config(&conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0], 1)) { + parent->rrc_log->error("Getting DRB1 configuration\n"); + printf("The QCI %d for DRB1 is invalid or not configured.\n", erabs[5].qos_params.qCI.QCI); + return; + } else { + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size = 1; + } + + // Add SRB2 and DRB1 to the scheduler + srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; + bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + bearer_cfg.group = 0; + parent->mac->bearer_ue_cfg(rnti, 2, &bearer_cfg); + bearer_cfg.group = conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].lc_cnfg.ul_specific_params.log_chan_group; + parent->mac->bearer_ue_cfg(rnti, 3, &bearer_cfg); + + // Configure SRB2 in RLC and PDCP + parent->rlc->add_bearer(rnti, 2); + + // Configure SRB2 in PDCP + srslte::srslte_pdcp_config_t pdcp_cnfg; + pdcp_cnfg.direction = SECURITY_DIRECTION_DOWNLINK; + pdcp_cnfg.is_control = true; + pdcp_cnfg.is_data = false; + parent->pdcp->add_bearer(rnti, 2, pdcp_cnfg); + + // Configure DRB1 in RLC + parent->rlc->add_bearer(rnti, 3, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].rlc_cnfg); + + // Configure DRB1 in PDCP + pdcp_cnfg.is_control = false; + pdcp_cnfg.is_data = true; + if (conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg.rlc_um_pdcp_sn_size_present) { + if(LIBLTE_RRC_PDCP_SN_SIZE_7_BITS == conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg.rlc_um_pdcp_sn_size) { + pdcp_cnfg.sn_len = 7; + } + } + parent->pdcp->add_bearer(rnti, 3, pdcp_cnfg); + + // DRB1 has already been configured in GTPU through bearer setup + + // Add NAS Attach accept + conn_reconf->N_ded_info_nas = 1; + conn_reconf->ded_info_nas_list[0].N_bytes = parent->erab_info.N_bytes; + memcpy(conn_reconf->ded_info_nas_list[0].msg, parent->erab_info.msg, parent->erab_info.N_bytes); + + // Reuse same PDU + pdu->reset(); + + send_dl_dcch(&dl_dcch_msg, pdu); + + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; +} + +void rrc::ue::send_connection_reconf_new_bearer(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + srslte::byte_buffer_t *pdu = pool_allocate; + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT* conn_reconf = &dl_dcch_msg.msg.rrc_con_reconfig; + conn_reconf->rr_cnfg_ded_present = true; + conn_reconf->rr_cnfg_ded.mac_main_cnfg_present = false; + conn_reconf->rr_cnfg_ded.phy_cnfg_ded_present = false; + conn_reconf->rr_cnfg_ded.rlf_timers_and_constants_present = false; + conn_reconf->rr_cnfg_ded.sps_cnfg_present = false; + conn_reconf->rr_cnfg_ded.drb_to_release_list_size = 0; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list_size = 0; + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size = 0; + conn_reconf->meas_cnfg_present = false; + conn_reconf->mob_ctrl_info_present = false; + conn_reconf->sec_cnfg_ho_present = false; + + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + uint8_t lcid = id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1) + + // Get DRB configuration + if (get_drbid_config(&conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i], lcid-2)) { + parent->rrc_log->error("Getting DRB configuration\n"); + printf("ERROR: The QCI %d is invalid or not configured.\n", erabs[lcid+4].qos_params.qCI.QCI); + return; + } else { + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size++; + } + + // Add DRB to the scheduler + srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; + bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + parent->mac->bearer_ue_cfg(rnti, lcid, &bearer_cfg); + + // Configure DRB in RLC + parent->rlc->add_bearer(rnti, lcid, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i].rlc_cnfg); + // Configure DRB in PDCP + parent->pdcp->add_bearer(rnti, lcid, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i].pdcp_cnfg); + // DRB has already been configured in GTPU through bearer setup + + // Add NAS message + conn_reconf->ded_info_nas_list[conn_reconf->N_ded_info_nas].N_bytes = parent->erab_info.N_bytes; + memcpy(conn_reconf->ded_info_nas_list[conn_reconf->N_ded_info_nas].msg, parent->erab_info.msg, parent->erab_info.N_bytes); + conn_reconf->N_ded_info_nas++; + } + + send_dl_dcch(&dl_dcch_msg, pdu); +} + +void rrc::ue::send_security_mode_command() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND; + + LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT* comm = &dl_dcch_msg.msg.security_mode_cmd; + comm->rrc_transaction_id = (transaction_id++)%4; + + // TODO: select these based on UE capabilities and preference order + comm->sec_algs.cipher_alg = (LIBLTE_RRC_CIPHERING_ALGORITHM_ENUM)cipher_algo; + comm->sec_algs.int_alg = (LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_ENUM)integ_algo; + + send_dl_dcch(&dl_dcch_msg); +} + +void rrc::ue::send_ue_cap_enquiry() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY; + + LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT* enq = &dl_dcch_msg.msg.ue_cap_enquiry; + enq->rrc_transaction_id = (transaction_id++)%4; + + enq->N_ue_cap_reqs = 1; + enq->ue_capability_request[0] = LIBLTE_RRC_RAT_TYPE_EUTRA; + + send_dl_dcch(&dl_dcch_msg); +} + +/********************** HELPERS ***************************/ + +void rrc::ue::send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg) +{ + // Allocate a new PDU buffer, pack the message and send to PDCP + byte_buffer_t *pdu = pool_allocate; + if (pdu) { + liblte_rrc_pack_dl_ccch_msg(dl_ccch_msg, (LIBLTE_BIT_MSG_STRUCT*) &parent->bit_buf); + srslte_bit_pack_vector(parent->bit_buf.msg, pdu->msg, parent->bit_buf.N_bits); + pdu->N_bytes = 1+(parent->bit_buf.N_bits-1)/8; + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB0 - rnti=0x%x, Sending: %s\n", + rnti, + liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg->msg_type]); + + parent->pdcp->write_sdu(rnti, RB_ID_SRB0, pdu); + + } else { + parent->rrc_log->error("Allocating pdu\n"); + } +} + +void rrc::ue::send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, byte_buffer_t *pdu) +{ + if (!pdu) { + pdu = pool_allocate; + } + if (pdu) { + liblte_rrc_pack_dl_dcch_msg(dl_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*) &parent->bit_buf); + srslte_bit_pack_vector(parent->bit_buf.msg, pdu->msg, parent->bit_buf.N_bits); + pdu->N_bytes = 1+(parent->bit_buf.N_bits-1)/8; + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB1 - rnti=0x%x, Sending: %s\n", + rnti, + liblte_rrc_dl_dcch_msg_type_text[dl_dcch_msg->msg_type]); + + parent->pdcp->write_sdu(rnti, RB_ID_SRB1, pdu); + + } else { + parent->rrc_log->error("Allocating pdu\n"); + } +} + +int rrc::ue::sr_free() +{ + if (sr_allocated) { + if (parent->sr_sched.nof_users[sr_sched_prb_idx][sr_sched_sf_idx] > 0) { + parent->sr_sched.nof_users[sr_sched_prb_idx][sr_sched_sf_idx]--; + } else { + parent->rrc_log->warning("Removing SR resources: no users in time-frequency slot (%d, %d)\n", sr_sched_prb_idx, sr_sched_sf_idx); + } + parent->rrc_log->info("Deallocated SR resources for time-frequency slot (%d, %d)\n", sr_sched_prb_idx, sr_sched_sf_idx); + } + return 0; +} + +void rrc::ue::sr_get(uint32_t *I_sr, uint32_t *N_pucch_sr) +{ + *I_sr = sr_I; + *N_pucch_sr = sr_N_pucch; +} + +int rrc::ue::sr_allocate(uint32_t period, uint32_t *I_sr, uint32_t *N_pucch_sr) +{ + uint32_t c = SRSLTE_CP_ISNORM(parent->cfg.cell.cp)?3:2; + uint32_t delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift]; + + uint32_t max_users = 12*c/delta_pucch_shift; + + // Find freq-time resources with least number of users + int i_min=0, j_min=0; + uint32_t min_users = 1e6; + for (uint32_t i=0;icfg.sr_cfg.nof_prb;i++) { + for (uint32_t j=0;jcfg.sr_cfg.nof_subframes;j++) { + if (parent->sr_sched.nof_users[i][j] < min_users) { + i_min = i; + j_min = j; + min_users = parent->sr_sched.nof_users[i][j]; + } + } + } + + if (parent->sr_sched.nof_users[i_min][j_min] > max_users) { + parent->rrc_log->error("Not enough PUCCH resources to allocate Scheduling Request\n"); + return -1; + } + + // Compute I_sr + if (period != 5 && period != 10 && period != 20 && period != 40 && period != 80) { + parent->rrc_log->error("Invalid SchedulingRequest period %d ms\n", period); + return -1; + } + if (parent->cfg.sr_cfg.sf_mapping[j_min] < period) { + *I_sr = period - 5 + parent->cfg.sr_cfg.sf_mapping[j_min]; + } else { + parent->rrc_log->error("Allocating SR: invalid sf_idx=%d for period=%d\n", parent->cfg.sr_cfg.sf_mapping[j_min], period); + return -1; + } + + // Compute N_pucch_sr + *N_pucch_sr = i_min*max_users + parent->sr_sched.nof_users[i_min][j_min]; + if (parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an) { + *N_pucch_sr += parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + } + + // Allocate user + parent->sr_sched.nof_users[i_min][j_min]++; + sr_sched_prb_idx = i_min; + sr_sched_sf_idx = j_min; + sr_allocated = true; + sr_I = *I_sr; + sr_N_pucch = *N_pucch_sr; + + parent->rrc_log->info("Allocated SR resources for time-frequency slot (%d, %d), N_pucch_sr=%d, I_sr=%d\n", + sr_sched_prb_idx, sr_sched_sf_idx, *N_pucch_sr, *I_sr); + + return 0; +} + +int rrc::ue::cqi_free() +{ + if (cqi_allocated) { + if (parent->cqi_sched.nof_users[cqi_sched_prb_idx][cqi_sched_sf_idx] > 0) { + parent->cqi_sched.nof_users[cqi_sched_prb_idx][cqi_sched_sf_idx]--; + } else { + parent->rrc_log->warning("Removing CQI resources: no users in time-frequency slot (%d, %d)\n", cqi_sched_prb_idx, cqi_sched_sf_idx); + } + parent->rrc_log->info("Deallocated CQI resources for time-frequency slot (%d, %d)\n", cqi_sched_prb_idx, cqi_sched_sf_idx); + } + return 0; +} + +void rrc::ue::cqi_get(uint32_t *pmi_idx, uint32_t *n_pucch) +{ + *pmi_idx = cqi_idx; + *n_pucch = cqi_pucch; +} + +int rrc::ue::cqi_allocate(uint32_t period, uint32_t *pmi_idx, uint32_t *n_pucch) +{ + uint32_t c = SRSLTE_CP_ISNORM(parent->cfg.cell.cp)?3:2; + uint32_t delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift]; + + uint32_t max_users = 12*c/delta_pucch_shift; + + // Find freq-time resources with least number of users + int i_min=0, j_min=0; + uint32_t min_users = 1e6; + for (uint32_t i=0;icfg.cqi_cfg.nof_prb;i++) { + for (uint32_t j=0;jcfg.cqi_cfg.nof_subframes;j++) { + if (parent->cqi_sched.nof_users[i][j] < min_users) { + i_min = i; + j_min = j; + min_users = parent->cqi_sched.nof_users[i][j]; + } + } + } + + if (parent->cqi_sched.nof_users[i_min][j_min] > max_users) { + parent->rrc_log->error("Not enough PUCCH resources to allocate Scheduling Request\n"); + return -1; + } + + // Compute I_sr + if (period != 2 && period != 5 && period != 10 && period != 20 && period != 40 && period != 80 && + period != 160 && period != 32 && period != 64 && period != 128) { + parent->rrc_log->error("Invalid CQI Report period %d ms\n", period); + return -1; + } + if (parent->cfg.cqi_cfg.sf_mapping[j_min] < period) { + if (period != 32 && period != 64 && period != 128) { + if (period > 2) { + *pmi_idx = period - 3 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else { + *pmi_idx = parent->cfg.cqi_cfg.sf_mapping[j_min]; + } + } else { + if (period == 32) { + *pmi_idx = 318 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else if (period == 64) { + *pmi_idx = 350 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else if (period == 128) { + *pmi_idx = 414 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } + } + } else { + parent->rrc_log->error("Allocating SR: invalid sf_idx=%d for period=%d\n", parent->cfg.cqi_cfg.sf_mapping[j_min], period); + return -1; + } + + // Compute n_pucch_2 + *n_pucch = i_min*max_users + parent->cqi_sched.nof_users[i_min][j_min]; + if (parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an) { + *n_pucch += parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + } + + // Allocate user + parent->cqi_sched.nof_users[i_min][j_min]++; + cqi_sched_prb_idx = i_min; + cqi_sched_sf_idx = j_min; + cqi_allocated = true; + cqi_idx = *pmi_idx; + cqi_pucch = *n_pucch; + + parent->rrc_log->info("Allocated CQI resources for time-frequency slot (%d, %d), n_pucch_2=%d, pmi_cfg_idx=%d\n", + cqi_sched_prb_idx, cqi_sched_sf_idx, *n_pucch, *pmi_idx); + + return 0; +} + +} diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc new file mode 100644 index 0000000..1e2b259 --- /dev/null +++ b/srsenb/src/upper/s1ap.cc @@ -0,0 +1,1042 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 "srsenb/hdr/upper/s1ap.h" +#include "srsenb/hdr/upper/common_enb.h" + +#include +#include +#include //for close(), sleep() +#include +#include +#include +#include +#include //for inet_ntop() + +namespace srsenb{ + +bool s1ap::init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_) +{ + rrc = rrc_; + args = args_; + s1ap_log = s1ap_log_; + + pool = srslte::byte_buffer_pool::get_instance(); + mme_connected = false; + running = false; + next_eNB_UE_S1AP_ID = 1; + next_ue_stream_id = 1; + + build_tai_cgi(); + + start(S1AP_THREAD_PRIO); + + return true; +} + +void s1ap::stop() +{ + if(running) { + running = false; + thread_cancel(); + wait_thread_finish(); + } + + if(close(socket_fd) == -1) { + s1ap_log->error("Failed to close SCTP socket\n"); + } + return; +} + +void s1ap::get_metrics(s1ap_metrics_t &m) +{ + if(!running) { + m.status = S1AP_ERROR; + return; + } + if(mme_connected) { + m.status = S1AP_READY; + }else{ + m.status = S1AP_ATTACHING; + } + return; +} + +void s1ap::run_thread() +{ + srslte::byte_buffer_t *pdu = pool->allocate("s1ap::run_thread"); + if (!pdu) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n"); + return; + } + + uint32_t sz = SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET; + running = true; + + // Connect to MME + while(running && !connect_mme()) { + s1ap_log->error("Failed to connect to MME - retrying in 10 seconds\n"); + s1ap_log->console("Failed to connect to MME - retrying in 10 seconds\n"); + sleep(10); + } + if(!setup_s1()) { + s1ap_log->error("S1 setup failed\n"); + s1ap_log->console("S1 setup failed\n"); + running = false; + return; + } + + // S1AP rx loop + while(running) { + pdu->reset(); + pdu->N_bytes = recv(socket_fd, pdu->msg, sz, 0); + + if(pdu->N_bytes <= 0) { + mme_connected = false; + do { + s1ap_log->error("Disconnected - attempting reconnection in 10 seconds\n"); + s1ap_log->console("Disconnected - attempting reconnection in 10 seconds\n"); + sleep(10); + } while(running && !connect_mme()); + + if(!setup_s1()) { + s1ap_log->error("S1 setup failed\n"); + s1ap_log->console("S1 setup failed\n"); + running = false; + return; + } + } + + s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received S1AP PDU"); + handle_s1ap_rx_pdu(pdu); + } +} + +// Generate common S1AP protocol IEs from config args +void s1ap::build_tai_cgi() +{ + uint32_t plmn; + uint32_t tmp32; + uint16_t tmp16; + + // TAI + tai.ext = false; + tai.iE_Extensions_present = false; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + tai.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + tai.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + tai.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + tmp16 = htons(args.tac); + memcpy(tai.tAC.buffer, (uint8_t*)&tmp16, 2); + + // EUTRAN_CGI + eutran_cgi.ext = false; + eutran_cgi.iE_Extensions_present = false; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + eutran_cgi.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + eutran_cgi.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + eutran_cgi.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + + tmp32 = htonl(args.enb_id); + uint8_t enb_id_bits[4*8]; + liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits); + uint8_t cell_id_bits[1*8]; + liblte_unpack(&args.cell_id, 1, cell_id_bits); + memcpy(eutran_cgi.cell_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + memcpy(&eutran_cgi.cell_ID.buffer[LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], cell_id_bits, 8); +} + +/******************************************************************************* +/* RRC interface +********************************************************************************/ +void s1ap::initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu) +{ + ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++; + ue_ctxt_map[rnti].stream_id = 1; + ue_ctxt_map[rnti].release_requested = false; + enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti; + send_initialuemessage(rnti, cause, pdu, false); +} + +void s1ap::initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec) +{ + ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++; + ue_ctxt_map[rnti].stream_id = 1; + ue_ctxt_map[rnti].release_requested = false; + enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti; + send_initialuemessage(rnti, cause, pdu, true, m_tmsi, mmec); +} + +void s1ap::write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu) +{ + s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received RRC SDU"); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return; + } + + send_ulnastransport(rnti, pdu); +} + +bool s1ap::user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) +{ + s1ap_log->info("User inactivity - RNTI:0x%x\n", rnti); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return false; + } + + if(ue_ctxt_map[rnti].release_requested) { + s1ap_log->warning("UE context for RNTI:0x%x is in zombie state. Releasing...\n", rnti); + ue_ctxt_map.erase(rnti); + rrc->release_complete(rnti); + return false; + } + + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = cause_radio; + + ue_ctxt_map[rnti].release_requested = true; + return send_uectxtreleaserequest(rnti, &cause); +} + +bool s1ap::user_exists(uint16_t rnti) +{ + return ue_ctxt_map.end() != ue_ctxt_map.find(rnti); +} + +void s1ap::ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) +{ + if(res->E_RABSetupListCtxtSURes.len > 0) { + send_initial_ctxt_setup_response(rnti, res); + } else { + send_initial_ctxt_setup_failure(rnti); + } +} + +void s1ap::ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) +{ + send_erab_setup_response(rnti, res); +} + +//void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) +//{ + +//} + +/******************************************************************************* +/* S1AP connection helpers +********************************************************************************/ + +bool s1ap::connect_mme() +{ + socket_fd = 0; + + s1ap_log->info("Connecting to MME %s:%d\n", args.mme_addr.c_str(), MME_PORT); + + if((socket_fd = socket(ADDR_FAMILY, SOCK_TYPE, PROTO)) == -1) { + s1ap_log->error("Failed to create S1AP socket\n"); + return false; + } + + // Bind to the local address + struct sockaddr_in local_addr; + memset(&local_addr, 0, sizeof(struct sockaddr_in)); + local_addr.sin_family = ADDR_FAMILY; + local_addr.sin_port = 0; // Any local port will do + if(inet_pton(AF_INET, args.s1c_bind_addr.c_str(), &(local_addr.sin_addr)) != 1) { + s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.s1c_bind_addr.c_str()); + return false; + } + bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)); + + // Connect to the MME address + memset(&mme_addr, 0, sizeof(struct sockaddr_in)); + mme_addr.sin_family = ADDR_FAMILY; + mme_addr.sin_port = htons(MME_PORT); + if(inet_pton(AF_INET, args.mme_addr.c_str(), &(mme_addr.sin_addr)) != 1) { + s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.mme_addr.c_str()); + return false; + } + + if(connect(socket_fd, (struct sockaddr*)&mme_addr, sizeof(mme_addr)) == -1) { + s1ap_log->error("Failed to establish socket connection to MME\n"); + return false; + } + + s1ap_log->info("SCTP socket established with MME\n"); + return true; +} + +bool s1ap::setup_s1() +{ + uint32_t tmp32; + uint16_t tmp16; + srslte::byte_buffer_t msg; + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage; + + init->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST; + + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *s1setup = &init->choice.S1SetupRequest; + s1setup->ext = false; + s1setup->CSG_IdList_present = false; + + s1setup->Global_ENB_ID.ext = false; + s1setup->Global_ENB_ID.iE_Extensions_present = false; + uint32_t plmn; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + s1setup->Global_ENB_ID.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + s1setup->Global_ENB_ID.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + s1setup->Global_ENB_ID.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + + s1setup->Global_ENB_ID.ext = false; + s1setup->Global_ENB_ID.eNB_ID.ext = false; + s1setup->Global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID; + tmp32 = htonl(args.enb_id); + uint8_t enb_id_bits[4*8]; + liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits); + memcpy(s1setup->Global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + + s1setup->eNBname_present = true; + s1setup->eNBname.ext = false; + if(args.enb_name.length() >= 150) { + args.enb_name.resize(150-1); + } + memcpy(s1setup->eNBname.buffer, args.enb_name.c_str(), args.enb_name.length()); + s1setup->eNBname.n_octets = args.enb_name.length(); + + s1setup->SupportedTAs.len = 1; + s1setup->SupportedTAs.buffer[0].ext = false; + s1setup->SupportedTAs.buffer[0].iE_Extensions_present = false; + tmp16 = htons(args.tac); + memcpy(s1setup->SupportedTAs.buffer[0].tAC.buffer, (uint8_t*)&tmp16, 2); + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.len = 1; + tmp32 = htonl(plmn); + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[0] = ((uint8_t*)&tmp32)[1]; + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[1] = ((uint8_t*)&tmp32)[2]; + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[2] = ((uint8_t*)&tmp32)[3]; + + s1setup->DefaultPagingDRX.ext = false; + s1setup->DefaultPagingDRX.e = LIBLTE_S1AP_PAGINGDRX_V128; // Todo: add to args, config file + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending s1SetupRequest"); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, NONUE_STREAM_ID, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send s1SetupRequest\n"); + return false; + } + + return true; +} + +/******************************************************************************* +/* S1AP message handlers +********************************************************************************/ + +bool s1ap::handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT rx_pdu; + + if(liblte_s1ap_unpack_s1ap_pdu((LIBLTE_BYTE_MSG_STRUCT*)pdu, &rx_pdu) != LIBLTE_SUCCESS) { + s1ap_log->error("Failed to unpack received PDU\n"); + return false; + } + + switch(rx_pdu.choice_type) { + case LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE: + return handle_initiatingmessage(&rx_pdu.choice.initiatingMessage); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME: + return handle_successfuloutcome(&rx_pdu.choice.successfulOutcome); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME: + return handle_unsuccessfuloutcome(&rx_pdu.choice.unsuccessfulOutcome); + break; + default: + s1ap_log->error("Unhandled PDU type %d\n", rx_pdu.choice_type); + return false; + } + + return true; +} + +bool s1ap::handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT: + return handle_dlnastransport(&msg->choice.DownlinkNASTransport); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST: + return handle_initialctxtsetuprequest(&msg->choice.InitialContextSetupRequest); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND: + return handle_uectxtreleasecommand(&msg->choice.UEContextReleaseCommand); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING: + return handle_paging(&msg->choice.Paging); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST: + return handle_erabsetuprequest(&msg->choice.E_RABSetupRequest); + default: + s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE: + return handle_s1setupresponse(&msg->choice.S1SetupResponse); + default: + s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE: + return handle_s1setupfailure(&msg->choice.S1SetupFailure); + default: + s1ap_log->error("Unhandled unsuccessful outcome message: %s\n", liblte_s1ap_unsuccessfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg) +{ + s1ap_log->info("Received S1SetupResponse\n"); + s1setupresponse = *msg; + mme_connected = true; + return true; +} + +bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg) +{ + s1ap_log->info("Received DownlinkNASTransport\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + + if(msg->HandoverRestrictionList_present) { + s1ap_log->warning("Not handling HandoverRestrictionList\n"); + } + if(msg->SubscriberProfileIDforRFP_present) { + s1ap_log->warning("Not handling SubscriberProfileIDforRFP\n"); + } + + srslte::byte_buffer_t *pdu = pool_allocate; + if (pdu) { + memcpy(pdu->msg, msg->NAS_PDU.buffer, msg->NAS_PDU.n_octets); + pdu->N_bytes = msg->NAS_PDU.n_octets; + rrc->write_dl_info(rnti, pdu); + return true; + } else { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n"); + return false; + } +} + +bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) +{ + s1ap_log->info("Received InitialContextSetupRequest\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) { + s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n", + ue_ctxt_map[rnti].MME_UE_S1AP_ID, + msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + } + + // Setup UE ctxt in RRC + if(!rrc->setup_ue_ctxt(rnti, msg)) { + return false; + } + + return true; +} + +bool s1ap::handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg) +{ + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + uint8_t *ptr = msg->UEIdentityIndexValue.buffer; + uint32_t ueid = srslte_bit_pack(&ptr, 10); + + rrc->add_paging_id(ueid, msg->UEPagingID); + return true; +} + +bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) +{ + s1ap_log->info("Received ERABSetupRequest\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) { + s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n", + ue_ctxt_map[rnti].MME_UE_S1AP_ID, + msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + } + + // Setup UE ctxt in RRC + if(!rrc->setup_ue_erabs(rnti, msg)) { + return false; + } + + return true; +} + +bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg) +{ + s1ap_log->info("Received UEContextReleaseCommand\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(msg->UE_S1AP_IDs.ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + + uint16_t rnti = 0; + if(msg->UE_S1AP_IDs.choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) { + + if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.iE_Extensions_present) { + s1ap_log->warning("Not handling S1AP message iE_Extensions\n"); + } + uint32_t enb_ue_id = msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(enb_ue_id)) { + s1ap_log->warning("eNB_UE_S1AP_ID:%d not found - discarding message\n", enb_ue_id); + return false; + } + rnti = enbid_to_rnti_map[enb_ue_id]; + enbid_to_rnti_map.erase(enb_ue_id); + + } else { // LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID + + uint32_t mme_ue_id = msg->UE_S1AP_IDs.choice.mME_UE_S1AP_ID.MME_UE_S1AP_ID; + uint32_t enb_ue_id; + if(!find_mme_ue_id(mme_ue_id, &rnti, &enb_ue_id)) { + s1ap_log->warning("UE for MME_UE_S1AP_ID:%d not found - discarding message\n", mme_ue_id); + return false; + } + enbid_to_rnti_map.erase(enb_ue_id); + } + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("UE context for RNTI:0x%x not found - discarding message\n", rnti); + return false; + } + + rrc->release_erabs(rnti); + send_uectxtreleasecomplete(rnti, ue_ctxt_map[rnti].MME_UE_S1AP_ID, ue_ctxt_map[rnti].eNB_UE_S1AP_ID); + ue_ctxt_map.erase(rnti); + s1ap_log->info("UE context for RNTI:0x%x released\n", rnti); + rrc->release_complete(rnti); + return true; +} + +bool s1ap::handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg) { + std::string cause = get_cause(&msg->Cause); + s1ap_log->error("S1 Setup Failure. Cause: %s\n", cause.c_str()); + s1ap_log->console("S1 Setup Failure. Cause: %s\n", cause.c_str()); + return true; +} + +/******************************************************************************* +/* S1AP message senders +********************************************************************************/ + +bool s1ap::send_initialuemessage(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::byte_buffer_t *pdu, bool has_tmsi, uint32_t m_tmsi, uint8_t mmec) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALUEMESSAGE; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE; + + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *initue = &init->choice.InitialUEMessage; + initue->ext = false; + initue->CellAccessMode_present = false; + initue->CSG_Id_present = false; + initue->GUMMEIType_present = false; + initue->GUMMEI_ID_present = false; + initue->GW_TransportLayerAddress_present = false; + initue->LHN_ID_present = false; + initue->RelayNode_Indicator_present = false; + initue->SIPTO_L_GW_TransportLayerAddress_present = false; + initue->S_TMSI_present = false; + initue->Tunnel_Information_for_BBF_present = false; + + // S_TMSI + if(has_tmsi) { + initue->S_TMSI_present = true; + initue->S_TMSI.ext = false; + initue->S_TMSI.iE_Extensions_present = false; + + uint32_to_uint8(m_tmsi, initue->S_TMSI.m_TMSI.buffer); + initue->S_TMSI.mMEC.buffer[0] = mmec; + } + + // ENB_UE_S1AP_ID + initue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // NAS_PDU + memcpy(initue->NAS_PDU.buffer, pdu->msg, pdu->N_bytes); + initue->NAS_PDU.n_octets = pdu->N_bytes; + + // TAI + memcpy(&initue->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT)); + + // EUTRAN_CGI + memcpy(&initue->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT)); + + // RRC Establishment Cause + initue->RRC_Establishment_Cause.ext = false; + initue->RRC_Establishment_Cause.e = cause; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending InitialUEMessage for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send InitialUEMessage for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_ulnastransport(uint16_t rnti, srslte::byte_buffer_t *pdu) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT; + + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ultx = &init->choice.UplinkNASTransport; + ultx->ext = false; + ultx->GW_TransportLayerAddress_present = false; + ultx->LHN_ID_present = false; + ultx->SIPTO_L_GW_TransportLayerAddress_present = false; + + // MME_UE_S1AP_ID + ultx->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + // ENB_UE_S1AP_ID + ultx->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // NAS_PDU + memcpy(ultx->NAS_PDU.buffer, pdu->msg, pdu->N_bytes); + ultx->NAS_PDU.n_octets = pdu->N_bytes; + + // EUTRAN_CGI + memcpy(&ultx->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT)); + + // TAI + memcpy(&ultx->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT)); + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UplinkNASTransport for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASEREQUEST; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST; + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *req = &init->choice.UEContextReleaseRequest; + req->ext = false; + req->GWContextReleaseIndication_present = false; + + // MME_UE_S1AP_ID + req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + // ENB_UE_S1AP_ID + req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // Cause + memcpy(&req->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT)); + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseRequest for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UEContextReleaseRequest for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE; + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *comp = &succ->choice.UEContextReleaseComplete; + comp->ext = false; + comp->CriticalityDiagnostics_present = false; + comp->UserLocationInformation_present = false; + + comp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_id; + comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_id; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseComplete for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UEContextReleaseComplete for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool_allocate; + if (!buf) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_initial_ctxt_setup_response().\n"); + return false; + } + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE; + + // Copy in the provided response message + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res = &succ->choice.InitialContextSetupResponse; + memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT)); + + // Fill in the GTP bind address for all bearers + for(uint32_t i=0;iE_RABSetupListCtxtSURes.len; i++) { + uint8_t addr[4]; + inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr); + liblte_unpack(addr, 4, res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer); + res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.n_bits = 32; + res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.ext = false; + } + + // Fill in the MME and eNB IDs + res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupResponse for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + + pool->deallocate(buf); + + if(n_sent == -1) { + s1ap_log->error("Failed to send InitialContextSetupResponse for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool_allocate; + if (!buf) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_erab_setup_response().\n"); + return false; + } + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_E_RABSETUP; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE; + + // Copy in the provided response message + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res = &succ->choice.E_RABSetupResponse; + memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT)); + + // Fill in the GTP bind address for all bearers + for(uint32_t i=0;iE_RABSetupListBearerSURes.len; i++) { + uint8_t addr[4]; + inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr); + liblte_unpack(addr, 4, res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.buffer); + res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.n_bits = 32; + res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.ext = false; + } + + // Fill in the MME and eNB IDs + res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending E_RABSetupResponse for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + + pool->deallocate(buf); + + if(n_sent == -1) { + s1ap_log->error("Failed to send E_RABSetupResponse for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool_allocate; + if (!buf) { + s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_initial_ctxt_setup_failure().\n"); + return false; + } + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME; + + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *unsucc = &tx_pdu.choice.unsuccessfulOutcome; + unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE; + + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *fail = &unsucc->choice.InitialContextSetupFailure; + fail->ext = false; + fail->CriticalityDiagnostics_present = false; + + fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + fail->Cause.ext = false; + fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + fail->Cause.choice.radioNetwork.ext = false; + fail->Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupFailure for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + + pool->deallocate(buf); + + if(n_sent == -1) { + s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + + +//bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) +//{ +// srslte::byte_buffer_t msg; + +// LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; +// tx_pdu.ext = false; +// tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + +// LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; +// init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT; +// init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION; + +// LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *caps = &init->choice.UECapabilityInfoIndication; +// caps->ext = false; +// caps->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; +// caps->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; +// // TODO: caps->UERadioCapability. + +// liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); +// s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UERadioCapabilityInfo for RNTI:0x%x", rnti); + +// ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, +// (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), +// htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); +// if(n_sent == -1) { +// s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); +// return false; +// } + +// return true; +//} + +/******************************************************************************* +/* General helpers +********************************************************************************/ + +bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id) +{ + typedef std::map::iterator it_t; + for(it_t it=ue_ctxt_map.begin(); it!=ue_ctxt_map.end(); ++it) { + if(it->second.MME_UE_S1AP_ID == mme_ue_id) { + *rnti = it->second.rnti; + *enb_ue_id = it->second.eNB_UE_S1AP_ID; + return true; + } + } + return false; +} + +std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) +{ + std::string cause = liblte_s1ap_cause_choice_text[c->choice_type]; + cause += " - "; + switch(c->choice_type) { + case LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK: + cause += liblte_s1ap_causeradionetwork_text[c->choice.radioNetwork.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT: + cause += liblte_s1ap_causetransport_text[c->choice.transport.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_NAS: + cause += liblte_s1ap_causenas_text[c->choice.nas.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL: + cause += liblte_s1ap_causeprotocol_text[c->choice.protocol.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_MISC: + cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; + break; + default: + cause += "unkown"; + break; + } + return cause; +} + +} // namespace srsenb diff --git a/srsenb/test/CMakeLists.txt b/srsenb/test/CMakeLists.txt new file mode 100644 index 0000000..6a86702 --- /dev/null +++ b/srsenb/test/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_subdirectory(mac) +add_subdirectory(upper) \ No newline at end of file diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt new file mode 100644 index 0000000..9048b4e --- /dev/null +++ b/srsenb/test/mac/CMakeLists.txt @@ -0,0 +1,9 @@ + +# Scheduler test +add_executable(scheduler_test scheduler_test.cc) +target_link_libraries(scheduler_test srsenb_mac + srsenb_phy + srslte_common + srslte_phy + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) diff --git a/srsenb/test/mac/scheduler_test.cc b/srsenb/test/mac/scheduler_test.cc new file mode 100644 index 0000000..50d4cb4 --- /dev/null +++ b/srsenb/test/mac/scheduler_test.cc @@ -0,0 +1,161 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 + +#include "srsenb/hdr/mac/mac.h" +#include "srsenb/hdr/phy/phy.h" + +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/common/log_filter.h" +#include "srslte/radio/radio.h" +#include "srslte/phy/utils/debug.h" + + + + +uint8_t sib1_payload[18] = {0x60,0x40,0x04,0x03,0x00,0x01,0x1a,0x2d,0x00,0x18,0x02,0x81,0x80,0x42,0x0c,0x80,0x00,0x00}; +uint8_t sib2_payload[41] = {0x00,0x80,0x1c,0x31,0x18,0x6f,0xe1,0x20,0x00,0x35,0x84,0x8c, + 0xe2,0xd0,0x00,0x02,0x00,0x78,0xee,0x31,0x6a,0xa5,0x37,0x30, + 0xa0,0x70,0xc9,0x49,0xfa,0x8d,0xd2,0x78,0x1a,0x02,0x77,0x4a, + 0x92,0x40,0x00,0x00,0x00}; + +// Define dummy RLC always transmitts +class rlc : public srsenb::rlc_interface_mac +{ +public: + uint32_t get_buffer_state(uint16_t rnti, uint32_t lcid) + { + return 1; + } + + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + for (uint32_t i=0;i 50) { + running = false; + } + my_sched.dl_sched(tti, &sched_result_dl); + my_sched.ul_sched(tti, &sched_result_ul); + tti = (tti+1)%10240; + if (tti >= 4) { + my_sched.ul_crc_info(tti, rnti, tti%2); + } + } + + +} diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt new file mode 100644 index 0000000..243da5c --- /dev/null +++ b/srsenb/test/upper/CMakeLists.txt @@ -0,0 +1,17 @@ +# IP tx/rx program test +add_executable(ip_test_enb ip_test.cc) +target_link_libraries(ip_test_enb srsenb_upper + srsenb_mac + srsenb_phy + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES}) + +# Simple PLMN -> MCC/MNC test +add_executable(plmn_test plmn_test.cc) +target_link_libraries(plmn_test srsenb_upper srslte_asn1 ) + diff --git a/srsenb/test/upper/ip_test.cc b/srsenb/test/upper/ip_test.cc new file mode 100644 index 0000000..b6480d2 --- /dev/null +++ b/srsenb/test/upper/ip_test.cc @@ -0,0 +1,684 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srsenb/hdr/mac/mac.h" +#include "srsenb/hdr/phy/phy.h" +#include "srslte/common/threads.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/common.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/upper/rlc.h" +#include "srslte/radio/radio.h" +#include "srslte/phy/utils/debug.h" + +#define START_TUNTAP +#define USE_RADIO + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +#define LCID 3 + +typedef struct { + float rx_freq; + float tx_freq; + float rx_gain; + float tx_gain; + bool enable_gui; + int time_adv; + std::string ip_address; +}prog_args_t; + +uint32_t srsapps_verbose = 1; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->rx_freq = 2.505e9; + args->tx_freq = 2.625e9; + args->rx_gain = 50.0; + args->tx_gain = 70.0; + args->enable_gui = false; + args->time_adv = -1; // calibrated for b210 + args->ip_address = "192.168.3.1"; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGIrfFdv] \n", prog); + printf("\t-f RX frequency [Default %.1f MHz]\n", args->rx_freq/1e6); + printf("\t-F TX frequency [Default %.1f MHz]\n", args->tx_freq/1e6); + printf("\t-g RX gain [Default %.1f]\n", args->rx_gain); + printf("\t-G TX gain [Default %.1f]\n", args->tx_gain); + printf("\t-I IP address [Default %s]\n", args->ip_address.c_str()); + printf("\t-t time advance (in samples) [Default %d]\n", args->time_adv); + printf("\t-d Enable gui [Default disabled]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFItdv")) != -1) { + switch (opt) { + case 'd': + args->enable_gui = true; + break; + case 'g': + args->rx_gain = atof(argv[optind]); + break; + case 'G': + args->tx_gain = atof(argv[optind]); + break; + case 'f': + args->rx_freq = atof(argv[optind]); + break; + case 'F': + args->tx_freq = atof(argv[optind]); + break; + case 'I': + args->ip_address = argv[optind]; + break; + case 't': + args->time_adv = atoi(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rx_freq < 0 || args->tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +LIBLTE_BYTE_MSG_STRUCT sib_buffer[2]; + +int setup_if_addr(char *ip_addr); + +class tester : public srsue::pdcp_interface_rlc, + public srsue::rrc_interface_rlc, + public srsue::ue_interface, + public srsenb::rlc_interface_mac, + public srsenb::rrc_interface_mac, + public thread +{ +public: + + tester() { + rnti = 0; + } + + void init(srslte::rlc *rlc_, srsenb::mac *mac_, srsenb::phy *phy_, srslte::log *log_h_, std::string ip_address) { + log_h = log_h_; + rlc = rlc_; + mac = mac_; + phy = phy_; + + tun_fd = 0; + +#ifdef START_TUNTAP + if (init_tuntap((char*) ip_address.c_str())) { + log_h->error("Initiating IP address\n"); + } +#endif + + pool = srslte::byte_buffer_pool::get_instance(); + + // Start reader thread + running=true; + start(); + } + + void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu) {} + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu) {} + void write_pdu_pcch(srslte::byte_buffer_t *sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu){} + void max_retx_attempted(){} + void add_user(uint16_t rnti) {} + void release_user(uint16_t rnti) {} + void upd_user(uint16_t rnti, uint16_t old_rnti) {} + void set_activity_user(uint16_t rnti) {} + bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len) {return false;} + void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) {} + std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); } + + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu) + { + int n = write(tun_fd, sdu->msg, sdu->N_bytes); + if (n != (int) sdu->N_bytes) { + log_h->error("TUN/TAP write failure n=%d, nof_bytes=%d\n", n, sdu->N_bytes); + return; + } + log_h->debug_hex(sdu->msg, sdu->N_bytes, + "Wrote %d bytes to TUN/TAP\n", + sdu->N_bytes); + pool->deallocate(sdu); + } + + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + return rlc->read_pdu(lcid, payload, nof_bytes); + } + + void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t payload[srsenb::sched_interface::MAX_SIB_PAYLOAD_LEN]) + { + if (sib_index < 2) { + memcpy(payload, sib_buffer[sib_index].msg, sib_buffer[sib_index].N_bytes); + } + } + + void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + srslte::byte_buffer_t *sdu = NULL; + log_h->info("Received PDU rnti=0x%x, lcid=%d, nof_bytes=%d\n", rnti, lcid, nof_bytes); + switch(lcid) { + case LCID: + rlc->write_pdu(lcid, payload, nof_bytes); + break; + case 0: + log_h->info("Received ConnectionRequest from rnti=0x%x\n", rnti); + + // Configure User in MAC + srsenb::sched_interface::ue_cfg_t uecfg; + bzero(&uecfg, sizeof(srsenb::sched_interface::ue_cfg_t)); + uecfg.maxharq_tx = 5; + uecfg.continuous_pusch = false; + uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + uecfg.ue_bearers[LCID].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + mac->ue_cfg(rnti, &uecfg); + + // configure DRB1 as UM + LIBLTE_RRC_RLC_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS100; + cfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + rlc->add_bearer(LCID, &cfg); + + // Send dummy ConnectionSetup. MAC will send contention resolution ID automatically. + log_h->info("Sending ConnectionSetup\n"); + sdu = pool_allocate; + sdu->msg[0] = 0xab; + sdu->N_bytes = 1; + rlc->write_sdu(0, sdu); + + // Indicate RLC status to mac + mac->rlc_buffer_state(rnti, 0, 1, 0); + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + dedicated.pusch_cnfg_ded.beta_offset_ack_idx = 5; + dedicated.pusch_cnfg_ded.beta_offset_ri_idx = 12; + dedicated.pusch_cnfg_ded.beta_offset_cqi_idx = 15; + dedicated.pusch_cnfg_ded_present = true; + dedicated.sched_request_cnfg.dsr_trans_max = LIBLTE_RRC_DSR_TRANS_MAX_N4; + dedicated.sched_request_cnfg.sr_pucch_resource_idx = 0; + dedicated.sched_request_cnfg.sr_cnfg_idx = 35; + dedicated.sched_request_cnfg_present = true; + phy->set_config_dedicated(rnti, &dedicated); + + usleep(500); + break; + default: + log_h->error("Received message for lcid=%d\n", lcid); + break; + } + } + + void rl_failure(uint16_t rnti) + { + log_h->console("Disconnecting rnti=0x%x.\n", rnti); + mac->ue_rem(rnti); + rlc->reset(); + } + +private: + int tun_fd; + bool running; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srslte::rlc *rlc; + srsenb::mac *mac; + srsenb::phy *phy; + uint16_t rnti; + bool read_enable; + + int init_tuntap(char *ip_address) { + read_enable = true; + tun_fd = setup_if_addr(ip_address); + if (tun_fd<0) { + fprintf(stderr, "Error setting up IP %s\n", ip_address); + return -1; + } + printf("Created tun/tap interface at IP %s\n", ip_address); + return 0; + } + + void run_thread() { + struct iphdr *ip_pkt; + uint32_t idx = 0; + int32_t N_bytes = 0; + srslte::byte_buffer_t *pdu = pool_allocate; + + log_h->info("TUN/TAP reader thread running\n"); + + int first=1; + while(running) { + if (tun_fd > 0) { + pdu->msg[0] = 0x0; + N_bytes = read(tun_fd, &pdu->msg[idx], SRSLTE_MAX_BUFFER_SIZE_BYTES-SRSLTE_BUFFER_HEADER_OFFSET - idx); + } + if(N_bytes > 0) + { + if (read_enable && pdu->msg[0] != 0x60) { + + pdu->N_bytes = idx + N_bytes; + ip_pkt = (struct iphdr*)pdu->msg; + + log_h->debug_hex(pdu->msg, pdu->N_bytes, + "Read %d bytes from TUN/TAP\n", + N_bytes); + + // Check if entire packet was received + if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) + { + // Send PDU directly to RLC + pdu->set_timestamp(); + rlc->write_sdu(LCID, pdu); + + // Indicate RLC status to mac + mac->rlc_buffer_state(rnti, LCID, rlc->get_buffer_state(LCID), 0); + + pdu = pool_allocate; + idx = 0; + } else{ + idx += N_bytes; + } + } + }else{ + log_h->error("Failed to read from TUN interface - gw receive thread exiting.\n"); + break; + } + } + } +}; + + +// Create classes +srslte::logger_file logger; +srslte::log_filter log_phy; +srslte::log_filter log_mac; +srslte::log_filter log_rlc; +srslte::log_filter log_tester; +srsenb::phy my_phy; +srsenb::mac my_mac; +srslte::rlc my_rlc; +srslte::radio my_radio; + +// Local classes for testing +tester my_tester; + + +void generate_cell_configuration(srsenb::sched_interface::cell_cfg_t *mac_cfg, srsenb::phy_cfg_t *phy_cfg) +{ + // Main cell configuration + srslte_cell_t cell; + cell.id = 0; + cell.cp = SRSLTE_CP_NORM; + cell.nof_ports = 1; + cell.nof_prb = 25; + cell.phich_length = SRSLTE_PHICH_NORM; + cell.phich_resources = SRSLTE_PHICH_R_1; + + // Generate SIB1 + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT msg[2]; + bzero(&msg[0], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + bzero(&msg[1], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + + msg[0].N_sibs = 1; + msg[0].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &msg[0].sibs[0].sib.sib1; + + sib1->cell_id = 0x1234; + sib1->tracking_area_code = 0x1234; + sib1->freq_band_indicator = 2; + sib1->N_plmn_ids = 1; + sib1->plmn_id[0].id.mcc = 1; + sib1->plmn_id[0].id.mnc = 1; + sib1->plmn_id[0].resv_for_oper = LIBLTE_RRC_NOT_RESV_FOR_OPER; + sib1->cell_barred = LIBLTE_RRC_CELL_NOT_BARRED; + sib1->intra_freq_reselection = LIBLTE_RRC_INTRA_FREQ_RESELECTION_ALLOWED; + sib1->q_rx_lev_min = -140; + sib1->q_rx_lev_min_offset = 1; + sib1->p_max = 10; + sib1->p_max_present = true; + sib1->si_window_length = LIBLTE_RRC_SI_WINDOW_LENGTH_MS40; + sib1->N_sched_info = 1; + sib1->sched_info[0].si_periodicity = LIBLTE_RRC_SI_PERIODICITY_RF16; + sib1->sched_info[0].N_sib_mapping_info = 0; + sib1->system_info_value_tag = 8; + + // Generate SIB2 + msg[1].N_sibs = 2; + msg[1].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2; + msg[1].sibs[1].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2 = &msg[1].sibs[0].sib.sib2; + + // RACH configuration + sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles = LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N64; + sib2->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.present = false; + sib2->rr_config_common_sib.rach_cnfg.preamble_init_rx_target_pwr = LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N90; + sib2->rr_config_common_sib.rach_cnfg.pwr_ramping_step = LIBLTE_RRC_POWER_RAMPING_STEP_DB6; + sib2->rr_config_common_sib.rach_cnfg.preamble_trans_max = LIBLTE_RRC_PREAMBLE_TRANS_MAX_N10; + sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size = LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF10; + sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer = LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF40; + sib2->rr_config_common_sib.rach_cnfg.max_harq_msg3_tx = 4; + + // BCCH + sib2->rr_config_common_sib.bcch_cnfg.modification_period_coeff = LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N16; + + // PCCH + sib2->rr_config_common_sib.pcch_cnfg.default_paging_cycle = LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF128; + sib2->rr_config_common_sib.pcch_cnfg.nB = LIBLTE_RRC_NB_ONE_THIRTY_SECOND_T; + + // PRACH Configuration + sib2->rr_config_common_sib.prach_cnfg.root_sequence_index = 41; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag = false; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index = 4; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset = 2; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config = 11; + + // PDSCH configuration + sib2->rr_config_common_sib.pdsch_cnfg.p_b = 0; + sib2->rr_config_common_sib.pdsch_cnfg.rs_power = -5; + + // PUSCH configuration + sib2->rr_config_common_sib.pusch_cnfg.n_sb = 1; + sib2->rr_config_common_sib.pusch_cnfg.hopping_mode = LIBLTE_RRC_HOPPING_MODE_INTER_SUBFRAME; + sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset = 4; + sib2->rr_config_common_sib.pusch_cnfg.enable_64_qam = false; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift = 0; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch = 0; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_hopping_enabled = false; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.sequence_hopping_enabled = false; + + // PUCCH configuration + sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift = LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2; + sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi = 2; + sib2->rr_config_common_sib.pucch_cnfg.n_cs_an = 0; + sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an = 12; + + // SRS configuration + sib2->rr_config_common_sib.srs_ul_cnfg.present = false; + + // UL power control + sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pusch = -80; + sib2->rr_config_common_sib.ul_pwr_ctrl.alpha = LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_1; + sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pucch = -80; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_0; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_5; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2a = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_preamble_msg3 = 4; + + sib2->rr_config_common_sib.ul_cp_length = LIBLTE_RRC_UL_CP_LENGTH_1; + + sib2->ue_timers_and_constants.t300 = LIBLTE_RRC_T300_MS1000; + sib2->ue_timers_and_constants.t301 = LIBLTE_RRC_T301_MS1000; + sib2->ue_timers_and_constants.n310 = LIBLTE_RRC_N310_N10; + sib2->ue_timers_and_constants.t311 = LIBLTE_RRC_T311_MS1000; + sib2->ue_timers_and_constants.n311 = LIBLTE_RRC_N311_N1; + + sib2->time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + sib2->additional_spectrum_emission = 1; + sib2->arfcn_value_eutra.present = false; + sib2->ul_bw.present = false; + + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = &msg[1].sibs[1].sib.sib3; + + bzero(sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT)); + sib3->q_hyst = LIBLTE_RRC_Q_HYST_DB_2; + sib3->s_non_intra_search = 6; + sib3->s_non_intra_search_present = true; + sib3->thresh_serving_low = 4; + sib3->cell_resel_prio = 6; + sib3->q_rx_lev_min = -122; + sib3->p_max = 23; + sib3->p_max_present = true; + sib3->s_intra_search = 10; + sib3->s_intra_search_present = true; + sib3->presence_ant_port_1 = true; + sib3->neigh_cell_cnfg = 1; + sib3->t_resel_eutra = 1; + + // Genreate payload + LIBLTE_BIT_MSG_STRUCT bitbuffer[2]; + for (int i=0;i<2;i++) { + liblte_rrc_pack_bcch_dlsch_msg(&msg[i], &bitbuffer[i]); + srslte_bit_pack_vector(bitbuffer[i].msg, sib_buffer[i].msg, bitbuffer[i].N_bits); + sib_buffer[i].N_bytes = (bitbuffer[i].N_bits-1)/8+1; + } + + // Fill MAC scheduler configuration + bzero(mac_cfg, sizeof(srsenb::sched_interface::cell_cfg_t)); + memcpy(&mac_cfg->cell, &cell, sizeof(srslte_cell_t)); + mac_cfg->sibs[0].len = sib_buffer[0].N_bytes; + mac_cfg->sibs[0].period_rf = 8; // Fixed to 8 rf + mac_cfg->sibs[1].len = sib_buffer[1].N_bytes; + mac_cfg->sibs[1].period_rf = liblte_rrc_si_periodicity_num[sib1->sched_info[0].si_periodicity]; + mac_cfg->si_window_ms = liblte_rrc_si_window_length_num[sib1->si_window_length]; + + + mac_cfg->prach_rar_window = liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size]; + + // Copy PHY common configuration + bzero(phy_cfg, sizeof(srsenb::phy_cfg_t)); + memcpy(&phy_cfg->cell, &cell, sizeof(srslte_cell_t)); + memcpy(&phy_cfg->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + memcpy(&phy_cfg->pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); +} + +int main(int argc, char *argv[]) +{ + + parse_args(&prog_args, argc, argv); + + logger.init("/tmp/ip_test.log"); + log_phy.init("PHY ", &logger, true); + log_mac.init("MAC ", &logger, true); + log_rlc.init("RLC ", &logger); + log_tester.init("TEST", &logger); + logger.log("\n\n"); + + if (srsapps_verbose == 1) { + log_phy.set_level(srslte::LOG_LEVEL_INFO); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(1000); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log_phy.set_level(srslte::LOG_LEVEL_DEBUG); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(100); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + printf("Log level debug\n"); + } + + // Init Radio and PHY +#ifdef USE_RADIO + my_radio.init(); +#else + my_radio.init(NULL, (char*) "dummy"); +#endif + my_radio.set_tx_freq(prog_args.tx_freq); + my_radio.set_tx_gain(prog_args.tx_gain); + my_radio.set_rx_freq(prog_args.rx_freq); + my_radio.set_rx_gain(prog_args.rx_gain); + //my_radio.set_tx_adv_neg(true); + if (prog_args.time_adv >= 0) { + printf("Setting TA=%d samples\n", prog_args.time_adv); + my_radio.set_tx_adv(prog_args.time_adv); + } + + // Configuure cell + srsenb::phy_cfg_t phy_cfg; + srsenb::sched_interface::cell_cfg_t mac_cfg; + srsenb::mac_args_t mac_args; + srsenb::phy_args_t phy_args; + + mac_args.link_failure_nof_err = 10; + phy_args.equalizer_mode = "mmse"; + phy_args.estimator_fil_w = 0.2; + phy_args.max_prach_offset_us = 50; + phy_args.nof_phy_threads = 1; + phy_args.pusch_max_its = 5; + + generate_cell_configuration(&mac_cfg, &phy_cfg); + + my_phy.init(&phy_args, &phy_cfg, &my_radio, &my_mac, &log_phy); + my_mac.init(&mac_args, &mac_cfg.cell, &my_phy, &my_tester, &my_tester, &log_mac); + my_rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac, 0 /* SRB0 */); + my_tester.init(&my_rlc, &my_mac, &my_phy, &log_tester, prog_args.ip_address); + + if (prog_args.enable_gui) { + sleep(1); + my_phy.start_plot(); + } + + bool running = true; + while(running) { + printf("Main running\n"); + sleep(1); + } + my_phy.stop(); + my_mac.stop(); +} + + + + +/******************* This is copied from srsue gw **********************/ +int setup_if_addr(char *ip_addr) +{ + char *dev = (char*) "tun_srsenb"; + int sock = -1; + + // Construct the TUN device + int tun_fd = open("/dev/net/tun", O_RDWR); + if(0 > tun_fd) + { + perror("open"); + return SRSLTE_ERROR; + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); + if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) + { + perror("ioctl1"); + goto clean_exit; + } + + // Bring up the interface + sock = socket(AF_INET, SOCK_DGRAM, 0); + if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) + { + perror("socket"); + goto clean_exit; + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) + { + perror("ioctl2"); + goto clean_exit; + } + + // Setup the IP address + sock = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = inet_addr(ip_addr); + if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) + { + perror("ioctl"); + goto clean_exit; + } + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) + { + perror("ioctl"); + goto clean_exit; + } + shutdown(sock, SHUT_RDWR); + + return(tun_fd); + +clean_exit: + if (sock != -1) { + shutdown(sock, SHUT_RDWR); + } + if (tun_fd != -1) { + close(tun_fd); + } + return SRSLTE_ERROR; +} diff --git a/srsenb/test/upper/plmn_test.cc b/srsenb/test/upper/plmn_test.cc new file mode 100644 index 0000000..c8e9bdd --- /dev/null +++ b/srsenb/test/upper/plmn_test.cc @@ -0,0 +1,77 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2016 Software Radio Systems Limited + * + * + */ + +#include +#include "srsenb/hdr/upper/common_enb.h" +#include "srslte/asn1/liblte_rrc.h" + +void rrc_plmn_test() +{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_in, plmn_out; + plmn_in.mcc = 0xF123; + plmn_in.mnc = 0xFF45; + + // 2-digit MNC test + uint8_t bit_buf[32]; + uint8_t *ie_ptr = bit_buf; + + liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr); + uint8_t byte_buf[4]; + liblte_pack(bit_buf, 22, byte_buf); + uint8_t ref[3] = {0x89, 0x19, 0x05}; + for(int i=0;i<3;i++) { + assert(ref[i] == byte_buf[i]); + } + ie_ptr = bit_buf; + liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out); + assert(plmn_in.mcc == plmn_out.mcc); + assert(plmn_in.mnc == plmn_out.mnc); + + // 3-digit MNC test + plmn_in.mnc = 0xF456; + ie_ptr = bit_buf; + liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr); + liblte_pack(bit_buf, 26, byte_buf); + uint8_t ref2[4] = {0x89, 0x1D, 0x15, 0x02}; + for(int i=0;i<3;i++) { + assert(ref2[i] == byte_buf[i]); + } + ie_ptr = bit_buf; + liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out); + assert(plmn_in.mcc == plmn_out.mcc); + assert(plmn_in.mnc == plmn_out.mnc); +} + +void s1ap_plmn_test() +{ + uint16_t mcc = 0xF123; + uint16_t mnc = 0xFF45; + uint32_t plmn; + + // 2-digit MNC test + srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn); + assert(plmn == 0x21F354); + srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc); + assert(mcc == 0xF123); + assert(mnc == 0xFF45); + + // 3-digit MNC test + mnc = 0xF456; + srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn); + assert(plmn == 0x216354); + srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc); + assert(mcc == 0xF123); + assert(mnc == 0xF456); +} + +int main(int argc, char **argv) +{ + rrc_plmn_test(); + s1ap_plmn_test(); +} diff --git a/srsepc/CMakeLists.txt b/srsepc/CMakeLists.txt new file mode 100644 index 0000000..0405fd9 --- /dev/null +++ b/srsepc/CMakeLists.txt @@ -0,0 +1,57 @@ +# +# Copyright 2013-2017 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/. +# + +find_package(LibConfig REQUIRED) +find_package(SCTP REQUIRED) + +if(BUILD_STATIC) + set(LIBCONFIGPP_LIBRARIES "${LIBCONFIGPP_STATIC_LIBRARY_PATH}") +endif(BUILD_STATIC) + +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsEPC") +endif() + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR} +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) + +######################################################################## +# Default configuration files +######################################################################## +install(FILES epc.conf.example DESTINATION ${DATA_DIR}) +install(FILES mbms.conf.example DESTINATION ${DATA_DIR}) +install(FILES user_db.csv.example DESTINATION ${DATA_DIR}) +install(PROGRAMS srsepc_if_masq.sh DESTINATION ${RUNTIME_DIR}) \ No newline at end of file diff --git a/srsepc/epc.conf.example b/srsepc/epc.conf.example new file mode 100644 index 0000000..02ef797 --- /dev/null +++ b/srsepc/epc.conf.example @@ -0,0 +1,77 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MME configuration +# +# mme_code: 8-bit MME code identifies the MME within a group. +# mme_group: 16-bit MME group identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# apn: Set Access Point Name (APN) +# mme_bind_addr: IP bind addr to listen for eNB S1-MME connnections +# dns_addr: DNS server address for the UEs +# +##################################################################### +[mme] +mme_code = 0x1a +mme_group = 0x0001 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_bind_addr = 127.0.1.100 +apn = srsapn +dns_addr = 8.8.8.8 + +##################################################################### +# HSS configuration +# +# algo: Authentication algorithm (xor/milenage) +# db_file: Location of .csv file that stores UEs information. +# +##################################################################### +[hss] +auth_algo = xor +db_file = user_db.csv + + +##################################################################### +# SP-GW configuration +# +# gtpu_bind_addr: GTP-U bind adress. +# +##################################################################### + +[spgw] +gtpu_bind_addr=127.0.1.100 +sgi_if_addr=172.16.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: s1ap, gtpc, spgw, hss, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +##################################################################### +[log] +all_level = debug +all_hex_limit = 32 +filename = /tmp/epc.log + +#s1ap_level = debug +#gtpc_level = debug +#spgw_level = debug +#hss_level = debug diff --git a/srsepc/hdr/CMakeLists.txt b/srsepc/hdr/CMakeLists.txt new file mode 100644 index 0000000..093086b --- /dev/null +++ b/srsepc/hdr/CMakeLists.txt @@ -0,0 +1,5 @@ +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in + ${PROJECT_BINARY_DIR}/version.h +) + diff --git a/srsepc/hdr/hss/hss.h b/srsepc/hdr/hss/hss.h new file mode 100644 index 0000000..7160cb5 --- /dev/null +++ b/srsepc/hdr/hss/hss.h @@ -0,0 +1,133 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ + +/****************************************************************************** + * File: hss.h + * Description: Top-level HSS class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef SRSEPC_HSS_H +#define SRSEPC_HSS_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/interfaces/epc_interfaces.h" +#include +#include + +namespace srsepc{ + +typedef struct{ + std::string auth_algo; + std::string db_file; + uint16_t mcc; + uint16_t mnc; +}hss_args_t; + +typedef struct{ + std::string name; + uint64_t imsi; + uint8_t key[16]; + bool op_configured; + uint8_t op[16]; + uint8_t opc[16]; + uint8_t amf[2]; + uint8_t sqn[6]; + uint16_t qci; + uint8_t last_rand[16]; +}hss_ue_ctx_t; + +enum hss_auth_algo { + HSS_ALGO_XOR, + HSS_ALGO_MILENAGE +}; + +class hss : public hss_interface_s1ap +{ +public: + static hss* get_instance(void); + static void cleanup(void); + int init(hss_args_t *hss_args, srslte::log_filter* hss_log); + void stop(void); + + bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + bool gen_update_loc_answer(uint64_t imsi, uint8_t* qci); + + bool resync_sqn(uint64_t imsi, uint8_t *auts); + +private: + + hss(); + virtual ~hss(); + static hss *m_instance; + + srslte::byte_buffer_pool *m_pool; + + std::map m_imsi_to_ue_ctx; + + + void gen_rand(uint8_t rand_[16]); + bool get_k_amf_opc_sqn(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *opc, uint8_t *sqn); + + bool gen_auth_info_answer_milenage(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + bool gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + + bool resync_sqn_milenage(uint64_t imsi, uint8_t *auts); + bool resync_sqn_xor(uint64_t imsi, uint8_t *auts); + + std::vector split_string(const std::string &str, char delimiter); + void get_uint_vec_from_hex_str(const std::string &key_str, uint8_t *key, uint len); + + void increment_ue_sqn(uint64_t imsi); + void increment_sqn(uint8_t *sqn, uint8_t *next_sqn); + void set_sqn(uint64_t imsi, uint8_t *sqn); + + void set_last_rand(uint64_t imsi, uint8_t *rand); + void get_last_rand(uint64_t imsi, uint8_t *rand); + + bool set_auth_algo(std::string auth_algo); + bool read_db_file(std::string db_file); + bool write_db_file(std::string db_file); + bool get_ue_ctx(uint64_t imsi, hss_ue_ctx_t **ue_ctx); + + std::string hex_string(uint8_t *hex, int size); + + enum hss_auth_algo m_auth_algo; + std::string db_file; + /*Logs*/ + srslte::log_filter *m_hss_log; + + uint16_t mcc; + uint16_t mnc; +}; + +} // namespace srsepc + +#endif // SRSEPC_HSS_H diff --git a/srsepc/hdr/mbms-gw/mbms-gw.h b/srsepc/hdr/mbms-gw/mbms-gw.h new file mode 100644 index 0000000..c238c0a --- /dev/null +++ b/srsepc/hdr/mbms-gw/mbms-gw.h @@ -0,0 +1,102 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ + +/****************************************************************************** + * File: mbms-gw.h + * Description: Top-level MBMS-GW class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef MBMS_GW_H +#define MBMS_GW_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/threads.h" +#include "srslte/asn1/gtpc.h" + +namespace srsepc{ + + +const uint16_t GTPU_RX_PORT = 2152; + +typedef struct { + std::string name; + std::string sgi_mb_if_addr; + std::string sgi_mb_if_mask; + std::string m1u_multi_addr; +} mbms_gw_args_t; + +struct pseudo_hdr +{ + uint32_t src_addr; + uint32_t dst_addr; + uint8_t placeholder; + uint8_t protocol; + uint16_t udp_len; +}; + +class mbms_gw: + public thread +{ +public: + static mbms_gw* get_instance(void); + static void cleanup(void); + int init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log); + void stop(); + void run_thread(); + +private: + + /* Methods */ + mbms_gw(); + virtual ~mbms_gw(); + static mbms_gw *m_instance; + + srslte::error_t init_sgi_mb_if(mbms_gw_args_t *args); + srslte::error_t init_m1_u(mbms_gw_args_t *args); + void handle_sgi_md_pdu(srslte::byte_buffer_t *msg); + uint16_t in_cksum(uint16_t *iphdr, int count); + + /* Members */ + bool m_running; + srslte::byte_buffer_pool *m_pool; + srslte::log_filter *m_mbms_gw_log; + + bool m_sgi_mb_up; + int m_sgi_mb_if; + + bool m_m1u_up; + int m_m1u; + struct sockaddr_in m_m1u_multi_addr; +}; + +} // namespace srsepc + +#endif // SGW_H diff --git a/srsepc/hdr/mme/mme.h b/srsepc/hdr/mme/mme.h new file mode 100644 index 0000000..5990592 --- /dev/null +++ b/srsepc/hdr/mme/mme.h @@ -0,0 +1,92 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ + +/****************************************************************************** + * File: mme.h + * Description: Top-level MME class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef SRSEPC_MME_H +#define SRSEPC_MME_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/threads.h" +#include "s1ap.h" + + +namespace srsepc{ + +/* +typedef struct { + std::string s1ap_level; + std::string all_level; + int s1ap_hex_limit; + std::string filename; +}log_args_t; +*/ + +typedef struct{ + s1ap_args_t s1ap_args; + //diameter_args_t diameter_args; + //gtpc_args_t gtpc_args; +} mme_args_t; + + +class mme: + public thread +{ +public: + static mme* get_instance(void); + static void cleanup(void); + int init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_s1ap * hss_); + void stop(); + int get_s1_mme(); + void run_thread(); + +private: + + mme(); + virtual ~mme(); + static mme *m_instance; + s1ap *m_s1ap; + mme_gtpc *m_mme_gtpc; + + bool m_running; + srslte::byte_buffer_pool *m_pool; + + /*Logs*/ + srslte::log_filter *m_s1ap_log; + srslte::log_filter *m_mme_gtpc_log; +}; + +} // namespace srsepc + +#endif // SRSEPC_MME_H diff --git a/srsepc/hdr/mme/mme_gtpc.h b/srsepc/hdr/mme/mme_gtpc.h new file mode 100644 index 0000000..ad2181c --- /dev/null +++ b/srsepc/hdr/mme/mme_gtpc.h @@ -0,0 +1,81 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_MME_GTPC_H +#define SRSEPC_MME_GTPC_H + +#include "srslte/common/log.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/asn1/gtpc.h" +#include "s1ap_common.h" +namespace srsepc +{ + +class spgw; +class s1ap; + +class mme_gtpc +{ +public: + + typedef struct gtpc_ctx{ + srslte::gtp_fteid_t mme_ctr_fteid; + srslte::gtp_fteid_t sgw_ctr_fteid; + }gtpc_ctx_t; + static mme_gtpc* get_instance(void); + static void cleanup(void); + + bool init(srslte::log_filter *mme_gtpc_log); + + uint32_t get_new_ctrl_teid(); + void send_create_session_request(uint64_t imsi); + void handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu); + void send_modify_bearer_request(uint64_t imsi, erab_ctx_t *bearer_ctx); + void handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu); + void send_release_access_bearers_request(uint64_t imsi); + void send_delete_session_request(uint64_t imsi); + +private: + + mme_gtpc(); + virtual ~mme_gtpc(); + static mme_gtpc *m_instance; + + srslte::log_filter *m_mme_gtpc_log; + srslte::byte_buffer_pool *m_pool; + + s1ap* m_s1ap; + spgw* m_spgw; + in_addr_t m_mme_gtpc_ip; + + uint32_t m_next_ctrl_teid; + std::map m_mme_ctr_teid_to_imsi; + std::map m_imsi_to_gtpc_ctx; + +}; + +} +#endif // SRSEPC_MME_GTPC_H diff --git a/srsepc/hdr/mme/s1ap.h b/srsepc/hdr/mme/s1ap.h new file mode 100644 index 0000000..ebedc9f --- /dev/null +++ b/srsepc/hdr/mme/s1ap.h @@ -0,0 +1,139 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_S1AP_H +#define SRSEPC_S1AP_H + +#include "srslte/asn1/gtpc.h" +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/asn1/liblte_mme.h" +#include "srslte/common/common.h" +#include "srslte/common/log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "s1ap_common.h" +#include "s1ap_mngmt_proc.h" +#include "s1ap_nas_transport.h" +#include "s1ap_ctx_mngmt_proc.h" +#include "mme_gtpc.h" +#include "srsepc/hdr/hss/hss.h" + +namespace srsepc{ + +const uint16_t S1MME_PORT = 36412; + +class s1ap +{ +public: + + static s1ap* get_instance(); + static void cleanup(); + + int enb_listen(); + int init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1ap * hss_); + void stop(); + + int get_s1_mme(); + + void delete_enb_ctx(int32_t assoc_id); + + bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu, struct sctp_sndrcvinfo *enb_sri); + bool handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri); + bool handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + + void activate_eps_bearer(uint64_t imsi, uint8_t ebi); + + void print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx); + + uint32_t get_plmn(); + uint32_t get_next_mme_ue_s1ap_id(); + enb_ctx_t* find_enb_ctx(uint16_t enb_id); + void add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo* enb_sri); + void get_enb_ctx(uint16_t sctp_stream); + + bool add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx); + bool add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx); + bool add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id); + + ue_ctx_t* find_ue_ctx_from_imsi(uint64_t imsi); + ue_ctx_t* find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id); + + bool release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id); + void release_ues_ecm_ctx_in_enb(int32_t enb_assoc); + bool delete_ue_ctx(uint64_t imsi); + + uint32_t allocate_m_tmsi(uint64_t imsi); + + s1ap_args_t m_s1ap_args; + srslte::log_filter *m_s1ap_log; + + s1ap_mngmt_proc* m_s1ap_mngmt_proc; + s1ap_nas_transport* m_s1ap_nas_transport; + s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc; + + std::map m_tmsi_to_imsi; + +private: + s1ap(); + virtual ~s1ap(); + + static s1ap *m_instance; + + uint32_t m_plmn; + srslte::byte_buffer_pool *m_pool; + + hss_interface_s1ap *m_hss; + int m_s1mme; + std::map m_active_enbs; + std::map m_sctp_to_enb_id; + std::map > m_enb_assoc_to_ue_ids; + + std::map m_imsi_to_ue_ctx; + std::map m_mme_ue_s1ap_id_to_ue_ctx; + + uint32_t m_next_mme_ue_s1ap_id; + uint32_t m_next_m_tmsi; + + //FIXME the GTP-C should be moved to the MME class, when the packaging of GTP-C messages is done. + mme_gtpc *m_mme_gtpc; +}; + +inline uint32_t +s1ap::get_plmn() +{ + return m_plmn; +} + + +} //namespace srsepc + +#endif // SRSEPC_S1AP_H diff --git a/srsepc/hdr/mme/s1ap_common.h b/srsepc/hdr/mme/s1ap_common.h new file mode 100644 index 0000000..262d034 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_common.h @@ -0,0 +1,167 @@ +/* + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_S1AP_COMMON_H +#define SRSEPC_S1AP_COMMON_H + +#include "srslte/common/security.h" +#include "srslte/asn1/gtpc_ies.h" +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/asn1/liblte_mme.h" +#include + +namespace srsepc{ + +static const uint8_t MAX_TA=255; //Maximum TA supported +static const uint8_t MAX_BPLMN=6; //Maximum broadcasted PLMNs per TAC +static const uint8_t MAX_ERABS_PER_UE = 16; + +// MME EMM states (3GPP 24.301 v10.0.0, section 5.1.3.4) +typedef enum { + EMM_STATE_DEREGISTERED = 0, + EMM_STATE_COMMON_PROCEDURE_INITIATED, + EMM_STATE_REGISTERED, + EMM_STATE_DEREGISTERED_INITIATED, + EMM_STATE_N_ITEMS, +} emm_state_t; +static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"DEREGISTERED", + "COMMON PROCEDURE INITIATED", + "REGISTERED", + "DEREGISTERED INITIATED"}; + +// MME ECM states (3GPP 23.401 v10.0.0, section 4.6.3) +typedef enum { + ECM_STATE_IDLE = 0, + ECM_STATE_CONNECTED, + ECM_STATE_N_ITEMS, +} ecm_state_t; +static const char ecm_state_text[ECM_STATE_N_ITEMS][100] = {"IDLE", + "CONNECTED"}; + +// MME ESM states (3GPP 23.401 v10.0.0, section 4.6.3) +typedef enum { + ESM_BEARER_CONTEXT_INACTIVE = 0, + ESM_BEARER_CONTEXT_ACTIVE_PENDING, + ESM_BEARER_CONTEXT_ACTIVE, + ESM_BEARER_CONTEXT_INACTIVE_PENDING, + ESM_BEARER_CONTEXT_MODIFY_PENDING, + ESM_BEARER_PROCEDURE_TRANSACTION_INACTIVE, + ESM_BEARER_PROCEDURE_TRANSACTION_PENDING, + ESM_STATE_N_ITEMS, +} esm_state_t; +static const char esm_state_text[ESM_STATE_N_ITEMS][100] = {"CONTEXT INACTIVE", + "CONTEXT ACTIVE PENDING", + "CONTEXT ACTIVE", + "CONTEXT_INACTIVE_PENDING", + "CONTEXT_MODIFY_PENDING", + "PROCEDURE_TRANSACTION_INACTIVE" + "PROCEDURE_TRANSACTION_PENDING"}; + +enum erab_state +{ + ERAB_DEACTIVATED, + ERAB_CTX_REQUESTED, + ERAB_CTX_SETUP, + ERAB_ACTIVE +}; + + +typedef struct{ + uint8_t mme_code; + uint16_t mme_group; + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string mme_bind_addr; + std::string mme_name; + std::string dns_addr; + std::string mme_apn; +} s1ap_args_t; + +typedef struct{ + bool enb_name_present; + uint32_t enb_id; + uint8_t enb_name[150]; + uint16_t mcc, mnc; + uint32_t plmn; + uint8_t nof_supported_ta; + uint16_t tac[MAX_TA]; + uint8_t nof_supported_bplmns[MAX_TA]; + uint16_t bplmns[MAX_TA][MAX_BPLMN]; + LIBLTE_S1AP_PAGINGDRX_ENUM drx; + struct sctp_sndrcvinfo sri; +} enb_ctx_t; + +typedef struct{ + uint8_t eksi; + uint8_t k_asme[32]; + uint8_t xres[16]; //minimum 6, maximum 16 + uint32_t dl_nas_count; + uint32_t ul_nas_count; + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + uint8_t k_nas_enc[32]; + uint8_t k_nas_int[32]; + uint8_t k_enb[32]; + LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap; + bool ms_network_cap_present; + LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap; +} eps_sec_ctx_t; + +typedef struct{ + enum erab_state state; + uint8_t erab_id; + uint8_t qci; + srslte::gtpc_f_teid_ie enb_fteid; + srslte::gtpc_f_teid_ie sgw_s1u_fteid; + srslte::gtpc_pdn_address_allocation_ie pdn_addr_alloc; +} erab_ctx_t; + +typedef struct{ + uint64_t imsi; + LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; + eps_sec_ctx_t security_ctxt; + uint8_t procedure_transaction_id; + emm_state_t state; + uint32_t mme_ue_s1ap_id; + uint8_t attach_type; + struct in_addr ue_ip; + srslte::gtpc_f_teid_ie sgw_ctrl_fteid; +} ue_emm_ctx_t; + +typedef struct{ + uint64_t imsi; + uint32_t enb_ue_s1ap_id; + uint32_t mme_ue_s1ap_id; + struct sctp_sndrcvinfo enb_sri; + ecm_state_t state; + erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE]; + bool eit; +} ue_ecm_ctx_t; + + +typedef struct{ + ue_emm_ctx_t emm_ctx; + eps_sec_ctx_t sec_ctx; + ue_ecm_ctx_t ecm_ctx; +} ue_ctx_t; +}//namespace + +#endif // SRSEPC_S1AP_COMMON_H diff --git a/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h new file mode 100644 index 0000000..e618226 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h @@ -0,0 +1,73 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_S1AP_CTX_MNGMT_PROC_H +#define SRSEPC_S1AP_CTX_MNGMT_PROC_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/common.h" +#include "s1ap_common.h" +#include "srslte/common/log_filter.h" +#include "mme_gtpc.h" +#include "srslte/common/buffer_pool.h" + +namespace srsepc{ + +class s1ap; + +class s1ap_ctx_mngmt_proc +{ +public: + + static s1ap_ctx_mngmt_proc *m_instance; + static s1ap_ctx_mngmt_proc* get_instance(void); + static void cleanup(void); + + void init(void); + + //bool send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid); + bool send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, ue_ecm_ctx_t *ecm_ctx, erab_ctx_t *erab_ctx); + bool handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp); + bool handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer); + bool handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp); + +private: + s1ap_ctx_mngmt_proc(); + virtual ~s1ap_ctx_mngmt_proc(); + + s1ap* m_s1ap; + s1ap_nas_transport* m_s1ap_nas_transport; + srslte::log_filter *m_s1ap_log; + + s1ap_args_t m_s1ap_args; + + mme_gtpc* m_mme_gtpc; + srslte::byte_buffer_pool *m_pool; +}; + +} //namespace srsepc + +#endif // SRSEPC_S1AP_CTX_MNGMT_PROC_H diff --git a/srsepc/hdr/mme/s1ap_mngmt_proc.h b/srsepc/hdr/mme/s1ap_mngmt_proc.h new file mode 100644 index 0000000..859a647 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_mngmt_proc.h @@ -0,0 +1,68 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_S1AP_MNGMT_PROC_H +#define SRSEPC_S1AP_MNGMT_PROC_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/common.h" +#include "s1ap_common.h" +#include "srslte/common/log_filter.h" + +namespace srsepc{ + +class s1ap; + +class s1ap_mngmt_proc +{ +public: + + static s1ap_mngmt_proc *m_instance; + + static s1ap_mngmt_proc* get_instance(void); + static void cleanup(void); + void init(void); + + bool handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + + //Packing/unpacking helper functions + bool unpack_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, enb_ctx_t* enb_ctx); + bool pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_ENUM cause, srslte::byte_buffer_t* msg); + bool pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buffer_t* msg); + +private: + s1ap_mngmt_proc(); + virtual ~s1ap_mngmt_proc(); + + s1ap* m_s1ap; + srslte::log_filter *m_s1ap_log; + + int m_s1mme; + s1ap_args_t m_s1ap_args; +}; + +} //namespace srsepc + +#endif // SRSEPC_S1AP_MNGMT_PROC_H diff --git a/srsepc/hdr/mme/s1ap_nas_transport.h b/srsepc/hdr/mme/s1ap_nas_transport.h new file mode 100644 index 0000000..3a11302 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_nas_transport.h @@ -0,0 +1,133 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ +#ifndef SRSEPC_S1AP_NAS_TRANSPORT_H +#define SRSEPC_S1AP_NAS_TRANSPORT_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/buffer_pool.h" +#include "s1ap_common.h" +#include "srslte/asn1/gtpc.h" +#include "srsepc/hdr/hss/hss.h" +#include "mme_gtpc.h" + +namespace srsepc{ + +class s1ap_nas_transport +{ +public: + + static s1ap_nas_transport* m_instance; + static s1ap_nas_transport* get_instance(void); + static void cleanup(void); + void init(hss_interface_s1ap * hss_); + + bool handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + + bool pack_attach_accept(ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer); + +private: + s1ap_nas_transport(); + virtual ~s1ap_nas_transport(); + + srslte::log *m_s1ap_log; + srslte::byte_buffer_pool *m_pool; + + s1ap* m_s1ap; + hss_interface_s1ap* m_hss; + mme_gtpc* m_mme_gtpc; + + //Initial UE messages + bool handle_nas_attach_request( uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + bool handle_nas_imsi_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + bool handle_nas_guti_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + + bool handle_nas_service_request(uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + + bool handle_nas_detach_request(uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + + bool handle_nas_tracking_area_update_request( uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + + bool handle_nas_authentication_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool* reply_flag); + bool handle_nas_security_mode_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_nas_attach_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_esm_information_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + bool handle_identity_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + bool handle_tracking_area_update_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + bool handle_authentication_failure(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_nas_detach_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + + bool integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu); + bool short_integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu); + + bool pack_authentication_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t next_mme_ue_s1ap_id, uint8_t eksi, uint8_t *autn, uint8_t *rand); + bool pack_authentication_reject(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id); + bool unpack_authentication_response(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp); + + bool pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx); + bool pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx); + + bool pack_identity_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id); + + bool pack_emm_information(ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg); + bool pack_service_reject(srslte::byte_buffer_t *reply_msg, uint8_t emm_cause, uint32_t enb_ue_s1ap_id); + + void log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req); + void log_unhandled_pdn_con_request_ies(const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req); + void log_unhandled_initial_ue_message_ies(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue); +}; +} //namespace srsepc +#endif // SRSEPC_S1AP_NAS_TRANSPORT_H diff --git a/srsepc/hdr/spgw/spgw.h b/srsepc/hdr/spgw/spgw.h new file mode 100644 index 0000000..98ad594 --- /dev/null +++ b/srsepc/hdr/spgw/spgw.h @@ -0,0 +1,138 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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/. + * + */ + +/****************************************************************************** + * File: spgw.h + * Description: Top-level SP-GW class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef SRSEPC_SPGW_H +#define SRSEPC_SPGW_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/threads.h" +#include "srslte/asn1/gtpc.h" + +namespace srsepc{ + +class mme_gtpc; + +const uint16_t GTPU_RX_PORT = 2152; + +typedef struct { + std::string gtpu_bind_addr; + std::string sgi_if_addr; +} spgw_args_t; + + +typedef struct spgw_tunnel_ctx { + uint64_t imsi; + in_addr_t ue_ipv4; + uint8_t ebi; + struct srslte::gtpc_f_teid_ie up_ctrl_fteid; + struct srslte::gtpc_f_teid_ie up_user_fteid; + struct srslte::gtpc_f_teid_ie dw_ctrl_fteid; + struct srslte::gtpc_f_teid_ie dw_user_fteid; +} spgw_tunnel_ctx_t; + +class spgw: + public thread +{ +public: + static spgw* get_instance(void); + static void cleanup(void); + int init(spgw_args_t* args, srslte::log_filter *spgw_log); + void stop(); + void run_thread(); + + void handle_create_session_request(struct srslte::gtpc_create_session_request *cs_req, struct srslte::gtpc_pdu *cs_resp_pdu); + void handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct srslte::gtpc_pdu *mb_resp_pdu); + void handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct srslte::gtpc_pdu *del_resp_pdu); + void handle_release_access_bearers_request(struct srslte::gtpc_pdu *rel_req_pdu, struct srslte::gtpc_pdu *rel_resp_pdu); + + void handle_sgi_pdu(srslte::byte_buffer_t *msg); + void handle_s1u_pdu(srslte::byte_buffer_t *msg); + +private: + + spgw(); + virtual ~spgw(); + static spgw *m_instance; + + srslte::error_t init_sgi_if(spgw_args_t *args); + srslte::error_t init_s1u(spgw_args_t *args); + srslte::error_t init_ue_ip(spgw_args_t *args); + + uint64_t get_new_ctrl_teid(); + uint64_t get_new_user_teid(); + in_addr_t get_new_ue_ipv4(); + + spgw_tunnel_ctx_t* create_gtp_ctx(struct srslte::gtpc_create_session_request *cs_req); + bool delete_gtp_ctx(uint32_t ctrl_teid); + + + bool m_running; + srslte::byte_buffer_pool *m_pool; + mme_gtpc *m_mme_gtpc; + + + bool m_sgi_up; + int m_sgi_if; + int m_sgi_sock; + + bool m_s1u_up; + int m_s1u; + + uint64_t m_next_ctrl_teid; + uint64_t m_next_user_teid; + + sockaddr_in m_s1u_addr; + + pthread_mutex_t m_mutex; + + std::map m_imsi_to_ctr_teid; //IMSI to control TEID map. Important to check if UE is previously connected + std::map m_teid_to_tunnel_ctx; //Map control TEID to tunnel ctx. Usefull to get reply ctrl TEID, UE IP, etc. + std::map m_ip_to_teid; //Map IP to User-plane TEID for downlink traffic + + uint32_t m_h_next_ue_ip; + + /*Time*/ + struct timeval m_t_last_dl; + struct timeval m_t_last_ul; + + /*Logs*/ + srslte::log_filter *m_spgw_log; + +}; + +} // namespace srsepc + +#endif // SRSEPC_SPGW_H diff --git a/srsepc/mbms.conf.example b/srsepc/mbms.conf.example new file mode 100644 index 0000000..d923063 --- /dev/null +++ b/srsepc/mbms.conf.example @@ -0,0 +1,40 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MBMS-GW configuration +# +# name: MBMS-GW name +# sgi_mb_if_addr: SGi-mb interface IP address +# m1u_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3) +# +##################################################################### +[mbms_gw] +name = srsmbmsgw01 +sgi_mb_if_addr = 172.16.0.254 +sgi_mb_if_mask = 255.255.255.255 +m1u_multi_addr = 239.255.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: mbms_gw, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +##################################################################### +[log] +all_level = debug +all_hex_limit = 32 +filename = /tmp/mbms.log diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt new file mode 100644 index 0000000..9fb56ce --- /dev/null +++ b/srsepc/src/CMakeLists.txt @@ -0,0 +1,76 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(mme) +add_subdirectory(hss) +add_subdirectory(spgw) +add_subdirectory(mbms-gw) + +# Link libstdc++ and libgcc +if(BUILD_STATIC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") +endif(BUILD_STATIC) + + +if (RPATH) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + + +add_executable(srsepc main.cc ) +target_link_libraries(srsepc srsepc_mme + srsepc_hss + srsepc_sgw + srslte_upper + srslte_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + +add_executable(srsmbms mbms-gw/main.cc ) +target_link_libraries(srsmbms srsepc_mbms_gw + srslte_upper + srslte_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) +if (RPATH) + set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") +endif (RPATH) + +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR}) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILDEPC_CMD} STREQUAL "") + message(STATUS "Added custom post-build-EPC command: ${BUILDENB_CMD}") + add_custom_command(TARGET srsenb POST_BUILD COMMAND ${BUILDENB_CMD}) +else(NOT ${BUILDEPC_CMD} STREQUAL "") + message(STATUS "No post-build-EPC command defined") +endif (NOT ${BUILDEPC_CMD} STREQUAL "") + +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/hss/CMakeLists.txt b/srsepc/src/hss/CMakeLists.txt new file mode 100644 index 0000000..276612c --- /dev/null +++ b/srsepc/src/hss/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_hss STATIC ${SOURCES}) +install(TARGETS srsepc_hss DESTINATION ${LIBRARY_DIR}) + diff --git a/srsepc/src/hss/hss.cc b/srsepc/src/hss/hss.cc new file mode 100644 index 0000000..6bbf860 --- /dev/null +++ b/srsepc/src/hss/hss.cc @@ -0,0 +1,739 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 /* srand, rand */ +#include /* time */ +#include +#include +#include +#include // for printing uint64_t +#include "srsepc/hdr/hss/hss.h" +#include "srslte/common/security.h" + +using namespace srslte; + +namespace srsepc{ + +hss* hss::m_instance = NULL; +pthread_mutex_t hss_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +hss::hss() +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + return; +} + +hss::~hss() +{ + return; +} + +hss* +hss::get_instance(void) +{ + pthread_mutex_lock(&hss_instance_mutex); + if(NULL == m_instance) { + m_instance = new hss(); + } + pthread_mutex_unlock(&hss_instance_mutex); + return(m_instance); +} + +void +hss::cleanup(void) +{ + pthread_mutex_lock(&hss_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&hss_instance_mutex); +} + +int +hss::init(hss_args_t *hss_args, srslte::log_filter *hss_log) +{ + srand(time(NULL)); + /*Init loggers*/ + m_hss_log = hss_log; + + /*Set authentication algorithm*/ + if(set_auth_algo(hss_args->auth_algo) == false) + { + return -1; + } + /*Read user information from DB*/ + if(read_db_file(hss_args->db_file) == false) + { + m_hss_log->console("Error reading user database file %s\n", hss_args->db_file.c_str()); + return -1; + } + + mcc = hss_args->mcc; + mnc = hss_args->mnc; + + db_file = hss_args->db_file; + + m_hss_log->info("HSS Initialized. DB file %s, authentication algorithm %s, MCC: %d, MNC: %d\n", hss_args->db_file.c_str(),hss_args->auth_algo.c_str(), mcc, mnc); + m_hss_log->console("HSS Initialized.\n"); + return 0; +} + +void +hss::stop(void) +{ + write_db_file(db_file); + std::map::iterator it = m_imsi_to_ue_ctx.begin(); + while(it!=m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("Deleting UE context in HSS. IMSI: %015lu\n", it->second->imsi); + m_hss_log->console("Deleting UE context in HSS. IMSI: %015lu\n", it->second->imsi); + delete it->second; + m_imsi_to_ue_ctx.erase(it++); + } + return; +} + + +bool +hss::set_auth_algo(std::string auth_algo) +{ + if(auth_algo != "xor" && auth_algo != "milenage" ) + { + m_hss_log->error("Unrecognized authentication algorithm. auth_algo = %s\n", auth_algo.c_str()); + return false; + } + if(auth_algo == "xor") + { + m_auth_algo = HSS_ALGO_XOR; + } + else + { + m_auth_algo = HSS_ALGO_MILENAGE; + } + return true; +} + +bool +hss::read_db_file(std::string db_filename) +{ + std::ifstream m_db_file; + + m_db_file.open(db_filename.c_str(), std::ifstream::in); + if(!m_db_file.is_open()) + { + return false; + } + m_hss_log->info("Opened DB file: %s\n", db_filename.c_str() ); + + std::string line; + while (std::getline(m_db_file, line)) + { + if(line[0] != '#') + { + uint column_size = 8; + std::vector split = split_string(line,','); + if(split.size() != column_size) + { + m_hss_log->error("Error parsing UE database. Wrong number of columns in .csv\n"); + m_hss_log->error("Columns: %lu, Expected %d.\n",split.size(),column_size); + return false; + } + hss_ue_ctx_t *ue_ctx = new hss_ue_ctx_t; + ue_ctx->name = split[0]; + ue_ctx->imsi = atoll(split[1].c_str()); + get_uint_vec_from_hex_str(split[2],ue_ctx->key,16); + if(split[3] == std::string("op")) + { + ue_ctx->op_configured = true; + get_uint_vec_from_hex_str(split[4],ue_ctx->op,16); + compute_opc(ue_ctx->key,ue_ctx->op,ue_ctx->opc); + } + else if (split[3] == std::string("opc")) + { + ue_ctx->op_configured =false; + get_uint_vec_from_hex_str(split[4],ue_ctx->opc,16); + } + else + { + m_hss_log->error("Neither OP nor OPc configured.\n"); + return false; + } + get_uint_vec_from_hex_str(split[5],ue_ctx->amf,2); + get_uint_vec_from_hex_str(split[6],ue_ctx->sqn,6); + + m_hss_log->debug("Added user from DB, IMSI: %015lu\n", ue_ctx->imsi); + m_hss_log->debug_hex(ue_ctx->key, 16, "User Key : "); + if(ue_ctx->op_configured){ + m_hss_log->debug_hex(ue_ctx->op, 16, "User OP : "); + } + m_hss_log->debug_hex(ue_ctx->opc, 16, "User OPc : "); + m_hss_log->debug_hex(ue_ctx->amf, 2, "AMF : "); + m_hss_log->debug_hex(ue_ctx->sqn, 6, "SQN : "); + ue_ctx->qci = atoi(split[7].c_str()); + m_hss_log->debug("Default Bearer QCI: %d\n",ue_ctx->qci); + m_imsi_to_ue_ctx.insert(std::pair(ue_ctx->imsi,ue_ctx)); + } + } + + if(m_db_file.is_open()) + { + m_db_file.close(); + } + + return true; +} + +bool hss::write_db_file(std::string db_filename) +{ + std::string line; + uint8_t k[16]; + uint8_t amf[2]; + uint8_t op[16]; + uint8_t sqn[6]; + + std::ofstream m_db_file; + + m_db_file.open(db_filename.c_str(), std::ofstream::out); + if(!m_db_file.is_open()) + { + return false; + } + m_hss_log->info("Opened DB file: %s\n", db_filename.c_str() ); + + //Write comment info + m_db_file << "# " << std::endl + << "# .csv to store UE's information in HSS " << std::endl + << "# Kept in the following format: \"Name,IMSI,Key,OP_Type,OP,AMF,SQN,QCI\" " << std::endl + << "# " << std::endl + << "# Name: Human readable name to help distinguish UE's. Ignored by the HSS " << std::endl + << "# IMSI: UE's IMSI value " << std::endl + << "# Key: UE's key, where other keys are derived from. Stored in hexadecimal" << std::endl + << "# OP_Type: Operator's code type, either OP or OPc " << std::endl + << "# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal " << std::endl + << "# AMF: Authentication management field, stored in hexadecimal " << std::endl + << "# SQN: UE's Sequence number for freshness of the authentication " << std::endl + << "# QCI: QoS Class Identifier for the UE's default bearer. " << std::endl + << "# " << std::endl + << "# Note: Lines starting by '#' are ignored and will be overwritten " << std::endl; + + std::map::iterator it = m_imsi_to_ue_ctx.begin(); + while(it!=m_imsi_to_ue_ctx.end()) + { + m_db_file << it->second->name; + m_db_file << ","; + m_db_file << std::setfill('0') << std::setw(15) << it->second->imsi; + m_db_file << ","; + m_db_file << hex_string(it->second->key, 16); + m_db_file << ","; + if(it->second->op_configured){ + m_db_file << "op,"; + m_db_file << hex_string(it->second->op, 16); + } + else{ + m_db_file << "opc,"; + m_db_file << hex_string(it->second->opc, 16); + } + m_db_file << ","; + m_db_file << hex_string(it->second->amf, 2); + m_db_file << ","; + m_db_file << hex_string(it->second->sqn, 6); + m_db_file << ","; + m_db_file << it->second->qci; + m_db_file << std::endl; + it++; + } + if(m_db_file.is_open()) + { + m_db_file.close(); + } + return true; +} + +bool +hss::gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + bool ret = false; + switch (m_auth_algo) + { + case HSS_ALGO_XOR: + ret = gen_auth_info_answer_xor(imsi, k_asme, autn, rand, xres); + break; + case HSS_ALGO_MILENAGE: + ret = gen_auth_info_answer_milenage(imsi, k_asme, autn, rand, xres); + break; + } + increment_ue_sqn(imsi); + return ret; + +} + + + +bool +hss::gen_auth_info_answer_milenage(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + uint8_t k[16]; + uint8_t amf[2]; + uint8_t opc[16]; + uint8_t sqn[6]; + + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + uint8_t mac[8]; + + + if(!get_k_amf_opc_sqn(imsi, k, amf, opc, sqn)) + { + return false; + } + gen_rand(rand); + + security_milenage_f2345( k, + opc, + rand, + xres, + ck, + ik, + ak); + + m_hss_log->debug_hex(k, 16, "User Key : "); + m_hss_log->debug_hex(opc, 16, "User OPc : "); + m_hss_log->debug_hex(rand, 16, "User Rand : "); + m_hss_log->debug_hex(xres, 8, "User XRES: "); + m_hss_log->debug_hex(ck, 16, "User CK: "); + m_hss_log->debug_hex(ik, 16, "User IK: "); + m_hss_log->debug_hex(ak, 6, "User AK: "); + + security_milenage_f1( k, + opc, + rand, + sqn, + amf, + mac); + + m_hss_log->debug_hex(sqn, 6, "User SQN : "); + m_hss_log->debug_hex(mac, 8, "User MAC : "); + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + m_hss_log->debug("User MCC : %x MNC : %x \n", mcc, mnc); + m_hss_log->debug_hex(k_asme, 32, "User k_asme : "); + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + m_hss_log->debug_hex(autn, 16, "User AUTN: "); + + set_last_rand(imsi, rand); + + return true; +} + +bool +hss::gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + uint8_t k[16]; + uint8_t amf[2]; + uint8_t opc[16]; + uint8_t sqn[6]; + + uint8_t xdout[16]; + uint8_t cdout[8]; + + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + uint8_t mac[8]; + + int i = 0; + + if(!get_k_amf_opc_sqn(imsi, k, amf, opc, sqn)) + { + return false; + } + gen_rand(rand); + + // Use RAND and K to compute RES, CK, IK and AK + for(i=0; i<16; i++) { + xdout[i] = k[i]^rand[i]; + } + + for(i=0; i<16; i++) { + xres[i] = xdout[i]; + ck[i] = xdout[(i+1)%16]; + ik[i] = xdout[(i+2)%16]; + } + for(i=0; i<6; i++) { + ak[i] = xdout[i+3]; + } + + m_hss_log->debug_hex(k, 16, "User Key : "); + m_hss_log->debug_hex(opc, 16, "User OPc : "); + m_hss_log->debug_hex(rand, 16, "User Rand : "); + m_hss_log->debug_hex(xres, 8, "User XRES: "); + m_hss_log->debug_hex(ck, 16, "User CK: "); + m_hss_log->debug_hex(ik, 16, "User IK: "); + m_hss_log->debug_hex(ak, 6, "User AK: "); + + // Generate cdout + for(i=0; i<6; i++) { + cdout[i] = sqn[i]; + } + for(i=0; i<2; i++) { + cdout[6+i] = amf[i]; + } + + // Generate MAC + for(i=0;i<8;i++) { + mac[i] = xdout[i] ^ cdout[i]; + } + + m_hss_log->debug_hex(sqn, 6, "User SQN : "); + m_hss_log->debug_hex(mac, 8, "User MAC : "); + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + m_hss_log->debug("User MCC : %x MNC : %x \n", mcc, mnc); + m_hss_log->debug_hex(k_asme, 32, "User k_asme : "); + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + m_hss_log->debug_hex(autn, 8, "User AUTN: "); + + set_last_rand(imsi, rand); + + return true; +} + +bool +hss::gen_update_loc_answer(uint64_t imsi, uint8_t* qci) +{ + std::map::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); + if(ue_ctx_it == m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("User not found. IMSI: %015lu\n",imsi); + m_hss_log->console("User not found. IMSI: %015lu\n",imsi); + return false; + } + hss_ue_ctx_t *ue_ctx = ue_ctx_it->second; + m_hss_log->info("Found User %015lu\n",imsi); + *qci = ue_ctx->qci; + return true; +} + + + +bool +hss::get_k_amf_opc_sqn(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *opc, uint8_t *sqn) +{ + + std::map::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); + if(ue_ctx_it == m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("User not found. IMSI: %015lu\n",imsi); + m_hss_log->console("User not found. IMSI: %015lu\n",imsi); + return false; + } + hss_ue_ctx_t *ue_ctx = ue_ctx_it->second; + m_hss_log->info("Found User %015lu\n",imsi); + memcpy(k, ue_ctx->key, 16); + memcpy(amf, ue_ctx->amf, 2); + memcpy(opc, ue_ctx->opc, 16); + memcpy(sqn, ue_ctx->sqn, 6); + + return true; +} + +bool +hss::resync_sqn(uint64_t imsi, uint8_t *auts) +{ + bool ret = false; + switch (m_auth_algo) + { + case HSS_ALGO_XOR: + ret = resync_sqn_xor(imsi, auts); + break; + case HSS_ALGO_MILENAGE: + ret = resync_sqn_milenage(imsi, auts); + break; + } + increment_ue_sqn(imsi); + return ret; +} + +bool +hss::resync_sqn_xor(uint64_t imsi, uint8_t *auts) +{ + m_hss_log->error("XOR SQN synchronization not supported yet\n"); + m_hss_log->console("XOR SQNs synchronization not supported yet\n"); + return false; +} + + +bool +hss::resync_sqn_milenage(uint64_t imsi, uint8_t *auts) +{ + uint8_t last_rand[16]; + uint8_t ak[6]; + uint8_t mac_s[8]; + uint8_t sqn_ms_xor_ak[6]; + + uint8_t k[16]; + uint8_t amf[2]; + uint8_t opc[16]; + uint8_t sqn[6]; + + if(!get_k_amf_opc_sqn(imsi, k, amf, opc, sqn)) + { + return false; + } + + get_last_rand(imsi, last_rand); + + for(int i=0; i<6; i++){ + sqn_ms_xor_ak[i] = auts[i]; + } + + for(int i=0; i<8; i++){ + mac_s[i] = auts[i+6]; + } + + m_hss_log->debug_hex(k, 16, "User Key : "); + m_hss_log->debug_hex(opc, 16, "User OPc : "); + m_hss_log->debug_hex(last_rand, 16, "User Last Rand : "); + m_hss_log->debug_hex(auts, 16, "AUTS : "); + m_hss_log->debug_hex(sqn_ms_xor_ak, 6, "SQN xor AK : "); + m_hss_log->debug_hex(mac_s, 8, "MAC : "); + + security_milenage_f5_star(k, opc, last_rand, ak); + m_hss_log->debug_hex(ak, 6, "Resynch AK : "); + + uint8_t sqn_ms[6]; + for(int i=0; i<6; i++){ + sqn_ms[i] = sqn_ms_xor_ak[i] ^ ak[i]; + } + m_hss_log->debug_hex(sqn_ms, 6, "SQN MS : "); + m_hss_log->debug_hex(sqn , 6, "SQN HE : "); + + m_hss_log->debug_hex(amf, 2, "AMF : "); + + uint8_t mac_s_tmp[8]; + + security_milenage_f1_star(k, opc, last_rand, sqn_ms, amf, mac_s_tmp); + + m_hss_log->debug_hex(mac_s_tmp, 8, "MAC calc : "); + + set_sqn(imsi, sqn_ms); + + return true; +} + +void +hss::increment_ue_sqn(uint64_t imsi) +{ + hss_ue_ctx_t *ue_ctx = NULL; + bool ret = get_ue_ctx(imsi, &ue_ctx); + if(ret == false) + { + return; + } + + increment_sqn(ue_ctx->sqn,ue_ctx->sqn); + m_hss_log->debug("Incremented SQN (IMSI: %" PRIu64 ")" PRIu64 "\n", imsi); + m_hss_log->debug_hex(ue_ctx->sqn, 6, "SQN: "); +} + +void +hss::increment_sqn(uint8_t *sqn, uint8_t *next_sqn) +{ + // Awkward 48 bit sqn and doing arithmetic + uint64_t tmp_sqn = 0; + uint8_t *p = (uint8_t *)&tmp_sqn; + + for(int i = 0; i < 6; i++) { + p[5-i] = sqn[i]; + } + + tmp_sqn++; + for(int i = 0; i < 6; i++){ + next_sqn[i] = p[5-i]; + } + return; +} + +void +hss::set_sqn(uint64_t imsi, uint8_t *sqn) +{ + hss_ue_ctx_t *ue_ctx = NULL; + bool ret = get_ue_ctx(imsi, &ue_ctx); + if(ret == false) + { + return; + } + memcpy(ue_ctx->sqn, sqn, 6); +} + +void +hss::set_last_rand(uint64_t imsi, uint8_t *rand) +{ + hss_ue_ctx_t *ue_ctx = NULL; + bool ret = get_ue_ctx(imsi, &ue_ctx); + if(ret == false) + { + return; + } + memcpy(ue_ctx->last_rand, rand, 16); + +} + +void +hss::get_last_rand(uint64_t imsi, uint8_t *rand) +{ + hss_ue_ctx_t *ue_ctx = NULL; + bool ret = get_ue_ctx(imsi, &ue_ctx); + if(ret == false) + { + return; + } + memcpy(rand, ue_ctx->last_rand, 16); +} + +void +hss::gen_rand(uint8_t rand_[16]) +{ + for(int i=0;i<16;i++) + { + rand_[i]=rand()%256; //Pulls on byte at a time. It's slow, but does not depend on RAND_MAX. + } + return; +} + +bool hss::get_ue_ctx(uint64_t imsi, hss_ue_ctx_t **ue_ctx) +{ + std::map::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); + if(ue_ctx_it == m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("User not found. IMSI: %015lu\n",imsi); + return false; + } + + *ue_ctx = ue_ctx_it->second; + return true; +} + +/* Helper functions*/ +std::vector +hss::split_string(const std::string &str, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(str); + + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; +} + +void +hss::get_uint_vec_from_hex_str(const std::string &key_str, uint8_t *key, uint len) +{ + const char *pos = key_str.c_str(); + + for (uint count = 0; count < len; count++) { + sscanf(pos, "%2hhx", &key[count]); + pos += 2; + } + + return; +} + + +std::string +hss::hex_string(uint8_t *hex, int size) +{ + std::stringstream ss; + + ss << std::hex << std::setfill('0'); + for(int i=0;i(hex[i]); + } + return ss.str(); +} +} //namespace srsepc diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc new file mode 100644 index 0000000..beed677 --- /dev/null +++ b/srsepc/src/main.cc @@ -0,0 +1,358 @@ +/** + * + * \section COPYRIGHT + * + * \section LICENSE + * + * 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 +#include +#include +#include +#include +#include +#include "srslte/common/bcd_helpers.h" +#include "srslte/common/config_file.h" +#include "srsepc/hdr/mme/mme.h" +#include "srsepc/hdr/hss/hss.h" +#include "srsepc/hdr/spgw/spgw.h" + +using namespace std; +using namespace srsepc; +namespace bpo = boost::program_options; + +bool running = true; + +void +sig_int_handler(int signo){ + running = false; +} + +typedef struct { + std::string s1ap_level; + int s1ap_hex_limit; + std::string gtpc_level; + int gtpc_hex_limit; + std::string spgw_level; + int spgw_hex_limit; + std::string hss_level; + int hss_hex_limit; + std::string all_level; + int all_hex_limit; + std::string filename; +}log_args_t; + + +typedef struct{ + mme_args_t mme_args; + hss_args_t hss_args; + spgw_args_t spgw_args; + log_args_t log_args; +}all_args_t; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void +parse_args(all_args_t *args, int argc, char* argv[]) { + + string mme_name; + string mme_code; + string mme_group; + string tac; + string mcc; + string mnc; + string mme_bind_addr; + string mme_apn; + string spgw_bind_addr; + string sgi_if_addr; + string dns_addr; + string hss_db_file; + string hss_auth_algo; + string log_filename; + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + + ("mme.mme_code", bpo::value(&mme_code)->default_value("0x01"), "MME Code") + ("mme.name", bpo::value(&mme_name)->default_value("srsmme01"), "MME Name") + ("mme.mme_group", bpo::value(&mme_group)->default_value("0x01"), "Cell ID") + ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") + ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") + ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") + ("hss.auth_algo", bpo::value(&hss_auth_algo)->default_value("milenage"),"HSS uthentication algorithm.") + ("spgw.gtpu_bind_addr", bpo::value(&spgw_bind_addr)->default_value("127.0.0.1"),"IP address of SP-GW for the S1-U connection") + ("spgw.sgi_if_addr", bpo::value(&sgi_if_addr)->default_value("176.16.0.1"),"IP address of TUN interface for the SGi connection") + + ("log.s1ap_level", bpo::value(&args->log_args.s1ap_level), "MME S1AP log level") + ("log.s1ap_hex_limit", bpo::value(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit") + ("log.gtpc_level", bpo::value(&args->log_args.gtpc_level), "MME GTPC log level") + ("log.gtpc_hex_limit", bpo::value(&args->log_args.gtpc_hex_limit), "MME GTPC log hex dump limit") + ("log.spgw_level", bpo::value(&args->log_args.spgw_level), "SPGW log level") + ("log.spgw_hex_limit", bpo::value(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit") + //("log.gtpu_level", bpo::value(&args->log.gtpu_level), "GTPU log level") + ("log.hss_level", bpo::value(&args->log_args.hss_level), "HSS log level") + ("log.hss_hex_limit", bpo::value(&args->log_args.hss_hex_limit), "HSS log hex dump limit") + //("log.gtpu_hex_limit",bpo::value(&args->log.gtpu_hex_limit), "GTPU log hex dump limit") + + ("log.all_level", bpo::value(&args->log_args.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log_args.filename)->default_value("/tmp/epc.log"),"Log filename") + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "MME configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + // if no config file given, check users home path + if (!vm.count("config_file")) { + if (!config_exists(config_file, "epc.conf")) { + cout << "Failed to read ePC configuration file " << config_file << " - exiting" << endl; + exit(1); + } + } + + //Parsing Config File + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + + //Concert hex strings + { + std::stringstream sstr; + sstr << std::hex << vm["mme.mme_group"].as(); + sstr >> args->mme_args.s1ap_args.mme_group; + } + { + std::stringstream sstr; + sstr << std::hex << vm["mme.mme_code"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + args->mme_args.s1ap_args.mme_code = tmp; + } + { + std::stringstream sstr; + sstr << std::hex << vm["mme.tac"].as(); + sstr >> args->mme_args.s1ap_args.tac; + } + // Convert MCC/MNC strings + if(!srslte::string_to_mcc(mcc, &args->mme_args.s1ap_args.mcc)) { + cout << "Error parsing mme.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if(!srslte::string_to_mnc(mnc, &args->mme_args.s1ap_args.mnc)) { + cout << "Error parsing mme.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } + + // Convert MCC/MNC strings + if(!srslte::string_to_mcc(mcc, &args->hss_args.mcc)) { + cout << "Error parsing mme.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if(!srslte::string_to_mnc(mnc, &args->hss_args.mnc)) { + cout << "Error parsing mme.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } + + args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; + args->mme_args.s1ap_args.mme_name = mme_name; + args->mme_args.s1ap_args.dns_addr = dns_addr; + args->mme_args.s1ap_args.mme_apn = mme_apn; + args->spgw_args.gtpu_bind_addr = spgw_bind_addr; + args->spgw_args.sgi_if_addr = sgi_if_addr; + args->hss_args.db_file = hss_db_file; + args->hss_args.auth_algo = hss_auth_algo; + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.s1ap_level")) { + args->log_args.s1ap_level = args->log_args.all_level; + } + if(!vm.count("log.gtpc_level")) { + args->log_args.gtpc_level = args->log_args.all_level; + } + if(!vm.count("log.spgw_level")) { + args->log_args.spgw_level = args->log_args.all_level; + } + if(!vm.count("log.hss_level")) { + args->log_args.hss_level = args->log_args.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.s1ap_hex_limit")) { + args->log_args.s1ap_hex_limit = args->log_args.all_hex_limit; + } + if(!vm.count("log.gtpc_hex_limit")) { + args->log_args.gtpc_hex_limit = args->log_args.all_hex_limit; + } + if(!vm.count("log.spgw_hex_limit")) { + args->log_args.spgw_hex_limit = args->log_args.all_hex_limit; + } + if(!vm.count("log.hss_hex_limit")) { + args->log_args.hss_hex_limit = args->log_args.all_hex_limit; + } + } + + // Check user database + if (!config_exists(args->hss_args.db_file, "user_db.csv")) { + cout << "Failed to read HSS user database file " << args->hss_args.db_file << " - exiting" << endl; + exit(1); + } + + return; +} + +srslte::LOG_LEVEL_ENUM +level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +int +main (int argc,char * argv[] ) +{ + cout << endl <<"--- Software Radio Systems EPC ---" << endl << endl; + signal(SIGINT, sig_int_handler); + signal(SIGTERM, sig_int_handler); + signal(SIGKILL, sig_int_handler); + + all_args_t args; + parse_args(&args, argc, argv); + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + + /*Init logger*/ + if (!args.log_args.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args.log_args.filename); + logger_file.log("\n--- Software Radio Systems EPC log ---\n\n"); + logger = &logger_file; + } + + srslte::log_filter s1ap_log; + s1ap_log.init("S1AP",logger); + s1ap_log.set_level(level(args.log_args.s1ap_level)); + s1ap_log.set_hex_limit(args.log_args.s1ap_hex_limit); + + srslte::log_filter mme_gtpc_log; + mme_gtpc_log.init("GTPC",logger); + mme_gtpc_log.set_level(level(args.log_args.gtpc_level)); + mme_gtpc_log.set_hex_limit(args.log_args.gtpc_hex_limit); + + srslte::log_filter hss_log; + hss_log.init("HSS ",logger); + hss_log.set_level(level(args.log_args.hss_level)); + hss_log.set_hex_limit(args.log_args.hss_hex_limit); + + srslte::log_filter spgw_log; + spgw_log.init("SPGW",logger); + spgw_log.set_level(level(args.log_args.spgw_level)); + spgw_log.set_hex_limit(args.log_args.spgw_hex_limit); + + + hss *hss = hss::get_instance(); + if (hss->init(&args.hss_args,&hss_log)) { + cout << "Error initializing HSS" << endl; + exit(1); + } + + mme *mme = mme::get_instance(); + if (mme->init(&args.mme_args, &s1ap_log, &mme_gtpc_log, hss)) { + cout << "Error initializing MME" << endl; + exit(1); + } + + spgw *spgw = spgw::get_instance(); + if (spgw->init(&args.spgw_args,&spgw_log)) { + cout << "Error initializing SP-GW" << endl; + exit(1); + } + + mme->start(); + spgw->start(); + while(running) { + sleep(1); + } + + mme->stop(); + mme->cleanup(); + spgw->stop(); + spgw->cleanup(); + hss->stop(); + hss->cleanup(); + + cout << std::endl <<"--- exiting ---" << endl; + return 0; +} diff --git a/srsepc/src/mbms-gw/CMakeLists.txt b/srsepc/src/mbms-gw/CMakeLists.txt new file mode 100644 index 0000000..412753b --- /dev/null +++ b/srsepc/src/mbms-gw/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_mbms_gw STATIC ${SOURCES}) +install(TARGETS srsepc_mbms_gw DESTINATION ${LIBRARY_DIR}) diff --git a/srsepc/src/mbms-gw/main.cc b/srsepc/src/mbms-gw/main.cc new file mode 100644 index 0000000..f993560 --- /dev/null +++ b/srsepc/src/mbms-gw/main.cc @@ -0,0 +1,223 @@ +/** + * + * \section COPYRIGHT + * + * \section LICENSE + * + * 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 +#include +#include +#include +#include +#include +#include "srslte/common/config_file.h" +#include "srsepc/hdr/mbms-gw/mbms-gw.h" + +using namespace std; +using namespace srsepc; +namespace bpo = boost::program_options; + +bool running = true; + +void +sig_int_handler(int signo){ + running = false; +} + +typedef struct { + std::string mbms_gw_level; + int mbms_gw_hex_limit; + std::string all_level; + int all_hex_limit; + std::string filename; +}log_args_t; + +typedef struct { + mbms_gw_args_t mbms_gw_args; + log_args_t log_args; +}all_args_t; + +srslte::LOG_LEVEL_ENUM +level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void +parse_args(all_args_t *args, int argc, char* argv[]) { + + string mbms_gw_name; + string mbms_gw_sgi_mb_if_addr; + string mbms_gw_sgi_mb_if_mask; + string mbms_gw_m1u_multi_addr; + + string log_filename; + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + + ("mbms_gw.name", bpo::value(&mbms_gw_name)->default_value("srsmbmsgw01"), "MBMS-GW Name") + ("mbms_gw.sgi_mb_if_addr", bpo::value(&mbms_gw_sgi_mb_if_addr)->default_value("172.16.1.1"), "SGi-mb TUN interface Address.") + ("mbms_gw.sgi_mb_if_mask", bpo::value(&mbms_gw_sgi_mb_if_mask)->default_value("255.255.255.255"), "SGi-mb TUN interface mask.") + ("mbms_gw.m1u_multi_addr", bpo::value(&mbms_gw_m1u_multi_addr)->default_value("239.255.0.1"), "M1-u GTPu destination multicast address.") + + ("log.all_level", bpo::value(&args->log_args.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log_args.filename)->default_value("/tmp/mbms.log"),"Log filename") + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "MBMS-GW configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + // if no config file given, check users home path + if (!vm.count("config_file")) { + if (!config_exists(config_file, "mbms.conf")) { + cout << "Failed to read MBMS-GW configuration file " << config_file << " - exiting" << endl; + exit(1); + } + } + + //Parsing Config File + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + + args->mbms_gw_args.name = mbms_gw_name; + args->mbms_gw_args.sgi_mb_if_addr = mbms_gw_sgi_mb_if_addr; + args->mbms_gw_args.sgi_mb_if_mask = mbms_gw_sgi_mb_if_mask; + args->mbms_gw_args.m1u_multi_addr = mbms_gw_m1u_multi_addr; + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.mbms_gw_level")) { + args->log_args.mbms_gw_level = args->log_args.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.mbms_gw_hex_limit")) { + args->log_args.mbms_gw_hex_limit = args->log_args.all_hex_limit; + } + } + return; +} + + + +int +main (int argc,char * argv[] ) +{ + cout << endl <<"--- Software Radio Systems MBMS ---" << endl << endl; + signal(SIGINT, sig_int_handler); + signal(SIGTERM, sig_int_handler); + signal(SIGKILL, sig_int_handler); + + all_args_t args; + parse_args(&args, argc, argv); + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + /*Init logger*/ + if (!args.log_args.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args.log_args.filename); + logger_file.log("\n--- Software Radio Systems MBMS log ---\n\n"); + logger = &logger_file; + } + + srslte::log_filter mbms_gw_log; + mbms_gw_log.init("MBMS",logger); + mbms_gw_log.set_level(level(args.log_args.mbms_gw_level)); + mbms_gw_log.set_hex_limit(args.log_args.mbms_gw_hex_limit); + + mbms_gw *mbms_gw = mbms_gw::get_instance(); + if(mbms_gw->init(&args.mbms_gw_args,&mbms_gw_log)) { + cout << "Error initializing MBMS-GW" << endl; + exit(1); + } + + mbms_gw->start(); + while(running) { + sleep(1); + } + + mbms_gw->stop(); + mbms_gw->cleanup(); + + cout << std::endl <<"--- exiting ---" << endl; + return 0; +} diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc new file mode 100644 index 0000000..b373df4 --- /dev/null +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -0,0 +1,326 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "srsepc/hdr/mbms-gw/mbms-gw.h" +#include "srslte/upper/gtpu.h" + +namespace srsepc{ + +mbms_gw* mbms_gw::m_instance = NULL; +pthread_mutex_t mbms_gw_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +const uint16_t MBMS_GW_BUFFER_SIZE = 2500; + +mbms_gw::mbms_gw(): + m_running(false), + m_sgi_mb_up(false) +{ + return; +} + +mbms_gw::~mbms_gw() +{ + return; +} + +mbms_gw* +mbms_gw::get_instance(void) +{ + pthread_mutex_lock(&mbms_gw_instance_mutex); + if(NULL == m_instance) { + m_instance = new mbms_gw(); + } + pthread_mutex_unlock(&mbms_gw_instance_mutex); + return(m_instance); +} + +void +mbms_gw::cleanup(void) +{ + pthread_mutex_lock(&mbms_gw_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&mbms_gw_instance_mutex); +} + +int +mbms_gw::init(mbms_gw_args_t* args, srslte::log_filter *mbms_gw_log) +{ + srslte::error_t err; + m_pool = srslte::byte_buffer_pool::get_instance(); + + //Init log + m_mbms_gw_log = mbms_gw_log; + + err = init_sgi_mb_if(args); + if (err != srslte::ERROR_NONE) + { + m_mbms_gw_log->console("Error initializing SGi-MB.\n"); + m_mbms_gw_log->error("Error initializing SGi-MB.\n"); + return -1; + } + err = init_m1_u(args); + if (err != srslte::ERROR_NONE) + { + m_mbms_gw_log->console("Error initializing SGi-MB.\n"); + m_mbms_gw_log->error("Error initializing SGi-MB.\n"); + return -1; + } + m_mbms_gw_log->info("MBMS GW Initiated\n"); + m_mbms_gw_log->console("MBMS GW Initiated\n"); + return 0; +} + +void +mbms_gw::stop() +{ + if(m_running) + { + if(m_sgi_mb_up) + { + close(m_sgi_mb_if); + m_mbms_gw_log->info("Closed SGi-MB interface\n"); + } + m_running = false; + thread_cancel(); + wait_thread_finish(); + } + return; +} + +srslte::error_t +mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args) +{ + char dev[IFNAMSIZ] = "sgi_mb"; + struct ifreq ifr; + + if(m_sgi_mb_up) + { + return(srslte::ERROR_ALREADY_STARTED); + } + + + // Construct the TUN device + m_sgi_mb_if = open("/dev/net/tun", O_RDWR); + m_mbms_gw_log->info("TUN file descriptor = %d\n", m_sgi_mb_if); + if(m_sgi_mb_if < 0) + { + m_mbms_gw_log->error("Failed to open TUN device: %s\n", strerror(errno)); + return(srslte::ERROR_CANT_START); + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); + ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0'; + + if(ioctl(m_sgi_mb_if, TUNSETIFF, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + // Bring up the interface + int sgi_mb_sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sgi_mb_sock<0) + { + m_mbms_gw_log->error("Failed to bring up socket: %s\n", strerror(errno)); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + if(ioctl(sgi_mb_sock, SIOCGIFFLAGS, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to bring up interface: %s\n", strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return(srslte::ERROR_CANT_START); + } + + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(ioctl(sgi_mb_sock, SIOCSIFFLAGS, &ifr) < 0) + { + m_mbms_gw_log->error("Failed to set socket flags: %s\n", strerror(errno)); + close(sgi_mb_sock); + close(m_sgi_mb_if); + return(srslte::ERROR_CANT_START); + } + + //Set IP of the interface + struct sockaddr_in *addr = (struct sockaddr_in*)&ifr.ifr_addr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(args->sgi_mb_if_addr.c_str()); + addr->sin_port = 0; + + if (ioctl(sgi_mb_sock, SIOCSIFADDR, &ifr) < 0) { + m_mbms_gw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_mb_if_addr.c_str(), strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return srslte::ERROR_CANT_START; + } + + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(args->sgi_mb_if_mask.c_str()); + if (ioctl(sgi_mb_sock, SIOCSIFNETMASK, &ifr) < 0) { + m_mbms_gw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno)); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return srslte::ERROR_CANT_START; + } + + m_sgi_mb_up = true; + close(sgi_mb_sock); + return(srslte::ERROR_NONE); +} + +srslte::error_t +mbms_gw::init_m1_u(mbms_gw_args_t *args) +{ + int addrlen; + struct sockaddr_in addr; + m_m1u = socket(AF_INET, SOCK_DGRAM, 0); + if(m_m1u<0) + { + m_mbms_gw_log->error("Failed to open socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_m1u_up = true; + + /* set no loopback */ + char loopch = 0; + if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loopch, sizeof(char))<0){ + m_mbms_gw_log->error("Failed to disable loopback: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } else { + m_mbms_gw_log->debug("Loopback disabled\n"); + } + + /* Set local interface for outbound multicast packets*/ + /* The IP must be associated with a local multicast capable interface */ + struct in_addr local_if; + local_if.s_addr = inet_addr("127.0.1.200"); + if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr))<0){ + perror("Error setting multicast interface.\n"); + } else { + printf("Multicast interface specified.\n"); + } + + bzero(&m_m1u_multi_addr,sizeof(m_m1u_multi_addr)); + m_m1u_multi_addr.sin_family = AF_INET; + m_m1u_multi_addr.sin_port = htons(GTPU_RX_PORT+1); + m_m1u_multi_addr.sin_addr.s_addr = inet_addr(args->m1u_multi_addr.c_str()); + m_mbms_gw_log->info("Initialized M1-U\n"); + + return srslte::ERROR_NONE; +} + +void +mbms_gw::run_thread() +{ + //Mark the thread as running + m_running=true; + srslte::byte_buffer_t *msg; + msg = m_pool->allocate(); + + + uint8_t seq = 0; + while(m_running) + { + msg->reset(); + int n; + do{ + n = read(m_sgi_mb_if, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); + }while(n == -1 && errno == EAGAIN); + + if(n<0){ + m_mbms_gw_log->error("Error reading from TUN interface. Error: %s\n", strerror(errno)); + } + else{ + msg->N_bytes = n; + handle_sgi_md_pdu(msg); + } + } + m_pool->deallocate(msg); + return; +} + +void +mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) +{ + uint8_t version; + srslte::gtpu_header_t header; + + //Setup GTP-U header + header.flags = 0x30; + header.message_type = 0xFF; + header.length = msg->N_bytes; + header.teid = 0xAAAA; //FIXME Harcoded TEID for now + + //Sanity Check IP packet + if(msg->N_bytes < 20) + { + m_mbms_gw_log->error("IPv4 min len: %d, drop msg len %d\n", 20, msg->N_bytes); + return; + } + + //IP Headers + struct iphdr *iph = (struct iphdr *) msg->msg; + if(iph->version != 4) + { + m_mbms_gw_log->warning("IPv6 not supported yet.\n"); + return; + } + + //Write GTP-U header into packet + if(!srslte::gtpu_write_header(&header, msg, m_mbms_gw_log)) + { + m_mbms_gw_log->console("Error writing GTP-U header on PDU\n"); + } + + int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, + (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); + if(n<0){ + m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + } + else{ + m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); + } +} + +} //namespace srsepc diff --git a/srsepc/src/mme/CMakeLists.txt b/srsepc/src/mme/CMakeLists.txt new file mode 100644 index 0000000..cd19585 --- /dev/null +++ b/srsepc/src/mme/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_mme STATIC ${SOURCES}) +install(TARGETS srsepc_mme DESTINATION ${LIBRARY_DIR}) + diff --git a/srsepc/src/mme/mme.cc b/srsepc/src/mme/mme.cc new file mode 100644 index 0000000..21554bc --- /dev/null +++ b/srsepc/src/mme/mme.cc @@ -0,0 +1,169 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 //TODO Remove +#include +#include +#include +#include +#include "srsepc/hdr/mme/mme.h" + +namespace srsepc{ + +mme* mme::m_instance = NULL; +pthread_mutex_t mme_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +mme::mme(): + m_running(false) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + return; +} + +mme::~mme() +{ + return; +} + +mme* +mme::get_instance(void) +{ + pthread_mutex_lock(&mme_instance_mutex); + if(NULL == m_instance) { + m_instance = new mme(); + } + pthread_mutex_unlock(&mme_instance_mutex); + return(m_instance); +} + +void +mme::cleanup(void) +{ + pthread_mutex_lock(&mme_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&mme_instance_mutex); +} + +int +mme::init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_s1ap * hss_) +{ + + /*Init logger*/ + m_s1ap_log = s1ap_log; + m_mme_gtpc_log = mme_gtpc_log; + /*Init S1AP*/ + m_s1ap = s1ap::get_instance(); + if(m_s1ap->init(args->s1ap_args, s1ap_log, hss_)){ + m_s1ap_log->error("Error initializing MME S1APP\n"); + exit(-1); + } + + /*Init GTP-C*/ + m_mme_gtpc = mme_gtpc::get_instance(); + if(!m_mme_gtpc->init(m_mme_gtpc_log)) + { + m_s1ap_log->console("Error initializing GTP-C\n"); + exit(-1); + } + + /*Log successful initialization*/ + m_s1ap_log->info("MME Initialized. MCC: %d, MNC: %d\n",args->s1ap_args.mcc, args->s1ap_args.mnc); + m_s1ap_log->console("MME Initialized. \n"); + return 0; +} + +void +mme::stop() +{ + if(m_running) + { + m_s1ap->stop(); + m_s1ap->cleanup(); + m_running = false; + thread_cancel(); + wait_thread_finish(); + } + return; +} + +void +mme::run_thread() +{ + srslte::byte_buffer_t *pdu = m_pool->allocate("mme::run_thread"); + uint32_t sz = SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET; + + struct sockaddr_in enb_addr; + struct sctp_sndrcvinfo sri; + socklen_t fromlen = sizeof(enb_addr); + bzero(&enb_addr, sizeof(enb_addr)); + int rd_sz; + int msg_flags=0; + + //Mark the thread as running + m_running=true; + + //Get S1-MME socket + int s1mme = m_s1ap->get_s1_mme(); + while(m_running) + { + m_s1ap_log->debug("Waiting for SCTP Msg\n"); + pdu->reset(); + rd_sz = sctp_recvmsg(s1mme, pdu->msg, sz,(struct sockaddr*) &enb_addr, &fromlen, &sri, &msg_flags); + if (rd_sz == -1 && errno != EAGAIN){ + m_s1ap_log->error("Error reading from SCTP socket: %s", strerror(errno)); + } + else if (rd_sz == -1 && errno == EAGAIN){ + m_s1ap_log->debug("Socket timeout reached"); + } + else{ + if(msg_flags & MSG_NOTIFICATION) + { + //Received notification + union sctp_notification *notification = (union sctp_notification*)pdu->msg; + m_s1ap_log->debug("SCTP Notification %d\n", notification->sn_header.sn_type); + if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) + { + m_s1ap_log->info("SCTP Association Shutdown. Association: %d\n",sri.sinfo_assoc_id); + m_s1ap_log->console("SCTP Association Shutdown. Association: %d\n",sri.sinfo_assoc_id); + m_s1ap->delete_enb_ctx(sri.sinfo_assoc_id); + } + } + else + { + //Received data + pdu->N_bytes = rd_sz; + m_s1ap_log->info("Received S1AP msg. Size: %d\n", pdu->N_bytes); + m_s1ap->handle_s1ap_rx_pdu(pdu,&sri); + } + } + } + return; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc new file mode 100644 index 0000000..6ef0e2c --- /dev/null +++ b/srsepc/src/mme/mme_gtpc.cc @@ -0,0 +1,388 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 +#include // for printing uint64_t +#include "srslte/asn1/gtpc.h" +#include "srsepc/hdr/mme/mme_gtpc.h" +#include "srsepc/hdr/mme/s1ap.h" +#include "srsepc/hdr/spgw/spgw.h" + +namespace srsepc{ + +mme_gtpc* mme_gtpc::m_instance = NULL; +pthread_mutex_t mme_gtpc_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + + +mme_gtpc::mme_gtpc() +{ +} + +mme_gtpc::~mme_gtpc() +{ +} + +mme_gtpc* +mme_gtpc::get_instance(void) +{ + pthread_mutex_lock(&mme_gtpc_instance_mutex); + if(NULL == m_instance) { + m_instance = new mme_gtpc(); + } + pthread_mutex_unlock(&mme_gtpc_instance_mutex); + return(m_instance); +} + +void +mme_gtpc::cleanup(void) +{ + pthread_mutex_lock(&mme_gtpc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&mme_gtpc_instance_mutex); +} + + +bool +mme_gtpc::init(srslte::log_filter *mme_gtpc_log) +{ + + /*Init log*/ + m_mme_gtpc_log = mme_gtpc_log; + + m_next_ctrl_teid = 1; + + m_s1ap = s1ap::get_instance(); + m_mme_gtpc_ip = inet_addr("127.0.0.1");//FIXME At the moment, the GTP-C messages are not sent over the wire. So this parameter is not used. + m_spgw = spgw::get_instance(); + + m_mme_gtpc_log->info("MME GTP-C Initialized\n"); + m_mme_gtpc_log->console("MME GTP-C Initialized\n"); + return true; +} + +uint32_t +mme_gtpc::get_new_ctrl_teid() +{ + return m_next_ctrl_teid++; //FIXME Use a Id pool? +} +void +mme_gtpc::send_create_session_request(uint64_t imsi) +{ + m_mme_gtpc_log->info("Sending Create Session Request.\n"); + m_mme_gtpc_log->console("Sending Create Session Request.\n"); + struct srslte::gtpc_pdu cs_req_pdu; + struct srslte::gtpc_create_session_request *cs_req = &cs_req_pdu.choice.create_session_request; + + struct srslte::gtpc_pdu cs_resp_pdu; + + //Initialize GTP-C message to zero + bzero(&cs_req_pdu, sizeof(struct srslte::gtpc_pdu)); + + //Setup GTP-C Header. FIXME: Length, sequence and other fields need to be added. + cs_req_pdu.header.piggyback = false; + cs_req_pdu.header.teid_present = true; + cs_req_pdu.header.teid = 0; //Send create session request to the butler TEID + cs_req_pdu.header.type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_REQUEST; + + //Setup GTP-C Create Session Request IEs + cs_req->imsi = imsi; + // Control TEID allocated + cs_req->sender_f_teid.teid = get_new_ctrl_teid(); + cs_req->sender_f_teid.ipv4 = m_mme_gtpc_ip; + + m_mme_gtpc_log->info("Next MME control TEID: %d\n", m_next_ctrl_teid); + m_mme_gtpc_log->info("Allocated MME control TEID: %d\n", cs_req->sender_f_teid.teid); + m_mme_gtpc_log->console("Creating Session Response -- IMSI: %" PRIu64 "\n", imsi); + m_mme_gtpc_log->console("Creating Session Response -- MME control TEID: %d\n", cs_req->sender_f_teid.teid); + + // APN + strncpy(cs_req->apn, m_s1ap->m_s1ap_args.mme_apn.c_str(), sizeof(cs_req->apn)-1); + cs_req->apn[sizeof(cs_req->apn)-1] = 0; + + // RAT Type + //cs_req->rat_type = srslte::GTPC_RAT_TYPE::EUTRAN; + + //Bearer QoS + cs_req->eps_bearer_context_created.ebi = 5; + + //Check whether this UE is already registed + std::map::iterator it = m_imsi_to_gtpc_ctx.find(imsi); + if(it != m_imsi_to_gtpc_ctx.end()) + { + m_mme_gtpc_log->warning("Create Session Request being called for an UE with an active GTP-C connection.\n"); + m_mme_gtpc_log->warning("Deleting previous GTP-C connection.\n"); + std::map::iterator jt = m_mme_ctr_teid_to_imsi.find(it->second.mme_ctr_fteid.teid); + if(jt == m_mme_ctr_teid_to_imsi.end()) + { + m_mme_gtpc_log->error("Could not find IMSI from MME Ctrl TEID. MME Ctr TEID: %d\n", it->second.mme_ctr_fteid.teid); + } + else + { + m_mme_ctr_teid_to_imsi.erase(jt); + } + m_imsi_to_gtpc_ctx.erase(it); + //No need to send delete session request to the SPGW. + //The create session request will be interpreted as a new request and SPGW will delete locally in existing context. + } + + //Save RX Control TEID + m_mme_ctr_teid_to_imsi.insert(std::pair(cs_req->sender_f_teid.teid, imsi)); + + //Save GTP-C context + gtpc_ctx_t gtpc_ctx; + bzero(>pc_ctx,sizeof(gtpc_ctx_t)); + gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid; + m_imsi_to_gtpc_ctx.insert(std::pair(imsi,gtpc_ctx)); + m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu); + +} + +void +mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) +{ + struct srslte::gtpc_create_session_response *cs_resp = & cs_resp_pdu->choice.create_session_response; + m_mme_gtpc_log->info("Received Create Session Response\n"); + m_mme_gtpc_log->console("Received Create Session Response\n"); + if (cs_resp_pdu->header.type != srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE) + { + m_mme_gtpc_log->warning("Could not create GTPC session. Not a create session response\n"); + //TODO Handle error + return; + } + if (cs_resp->cause.cause_value != srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED){ + m_mme_gtpc_log->warning("Could not create GTPC session. Create Session Request not accepted\n"); + //TODO Handle error + return; + } + + //Get IMSI from the control TEID + std::map::iterator id_it = m_mme_ctr_teid_to_imsi.find(cs_resp_pdu->header.teid); + if(id_it == m_mme_ctr_teid_to_imsi.end()) + { + m_mme_gtpc_log->warning("Could not find IMSI from Ctrl TEID.\n"); + return; + } + uint64_t imsi = id_it->second; + + m_mme_gtpc_log->info("MME GTPC Ctrl TEID %" PRIu64 ", IMSI %" PRIu64 "\n", cs_resp_pdu->header.teid, imsi); + + //Get S-GW Control F-TEID + srslte::gtp_fteid_t sgw_ctr_fteid; + sgw_ctr_fteid.teid = cs_resp_pdu->header.teid; + sgw_ctr_fteid.ipv4 = 0; //FIXME This is not used for now. In the future it will be obtained from the socket addr_info + + //Get S-GW S1-u F-TEID + if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){ + m_mme_gtpc_log->error("Did not receive SGW S1-U F-TEID in create session response\n"); + return; + } + m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid); + m_mme_gtpc_log->info("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid); + in_addr s1u_addr; + s1u_addr.s_addr = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4; + m_mme_gtpc_log->console("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr)); + m_mme_gtpc_log->info("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr)); + + //Check UE Ipv4 address was allocated + if(cs_resp->paa_present != true) + { + m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + return; + } + if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) + { + m_mme_gtpc_log->error("IPv6 not supported yet\n"); + return; + } + + //Save create session response info to E-RAB context + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(imsi); + if(ue_ctx == NULL){ + m_mme_gtpc_log->error("Could not find UE context. IMSI %015lu\n", imsi); + return; + } + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + //Save UE IP to nas ctxt + emm_ctx->ue_ip.s_addr = cs_resp->paa.ipv4; + m_mme_gtpc_log->console("SPGW Allocated IP %s to ISMI %015lu\n",inet_ntoa(emm_ctx->ue_ip),emm_ctx->imsi); + //Save SGW ctrl F-TEID in GTP-C context + std::map::iterator it_g = m_imsi_to_gtpc_ctx.find(imsi); + if(it_g == m_imsi_to_gtpc_ctx.end()) + { + //Could not find GTP-C Context + m_mme_gtpc_log->error("Could not find GTP-C context\n"); + return; + } + gtpc_ctx_t *gtpc_ctx = &it_g->second; + gtpc_ctx->sgw_ctr_fteid = sgw_ctr_fteid; + + //Set EPS bearer context + //FIXME default EPS bearer is hard-coded + int default_bearer=5; + erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[default_bearer]; + erab_ctx->pdn_addr_alloc= cs_resp->paa; + erab_ctx->sgw_s1u_fteid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid; + m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(emm_ctx, ecm_ctx, erab_ctx); + return; +} + + +void +mme_gtpc::send_modify_bearer_request(uint64_t imsi, erab_ctx_t *erab_ctx) +{ + m_mme_gtpc_log->info("Sending GTP-C Modify bearer request\n"); + srslte::gtpc_pdu mb_req_pdu; + srslte::gtp_fteid_t *enb_fteid = &erab_ctx->enb_fteid; + + std::map::iterator it = m_imsi_to_gtpc_ctx.find(imsi); + if(it == m_imsi_to_gtpc_ctx.end()) + { + m_mme_gtpc_log->error("Modify bearer request for UE without GTP-C connection\n"); + return; + } + srslte::gtp_fteid_t sgw_ctr_fteid = it->second.sgw_ctr_fteid; + + srslte::gtpc_header *header = &mb_req_pdu.header; + header->teid_present = true; + header->teid = sgw_ctr_fteid.teid; + header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST; + + srslte::gtpc_modify_bearer_request *mb_req = &mb_req_pdu.choice.modify_bearer_request; + mb_req->eps_bearer_context_to_modify.ebi = erab_ctx->erab_id; + mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4 = enb_fteid->ipv4; + mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid = enb_fteid->teid; + + m_mme_gtpc_log->info("GTP-C Modify bearer request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid ); + struct in_addr addr; + addr.s_addr = enb_fteid->ipv4; + m_mme_gtpc_log->info("GTP-C Modify bearer request -- S1-U TEID 0x%x, IP %s\n", enb_fteid->teid, inet_ntoa(addr) ); + + // + srslte::gtpc_pdu mb_resp_pdu; + m_spgw->handle_modify_bearer_request(&mb_req_pdu,&mb_resp_pdu); + handle_modify_bearer_response(&mb_resp_pdu); + return; +} + +void +mme_gtpc::handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu) +{ + uint32_t mme_ctrl_teid = mb_resp_pdu->header.teid; + std::map::iterator imsi_it = m_mme_ctr_teid_to_imsi.find(mme_ctrl_teid); + if(imsi_it == m_mme_ctr_teid_to_imsi.end()) + { + m_mme_gtpc_log->error("Could not find IMSI from control TEID\n"); + return; + } + + uint8_t ebi = mb_resp_pdu->choice.modify_bearer_response.eps_bearer_context_modified.ebi; + m_mme_gtpc_log->debug("Activating EPS bearer with id %d\n", ebi); + m_s1ap->activate_eps_bearer(imsi_it->second,ebi); + + return; +} + +void +mme_gtpc::send_delete_session_request(uint64_t imsi) +{ + m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request. IMSI %" PRIu64 "\n",imsi); + srslte::gtpc_pdu del_req_pdu; + srslte::gtp_fteid_t sgw_ctr_fteid; + srslte::gtp_fteid_t mme_ctr_fteid; + //Get S-GW Ctr TEID + std::map::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi); + if(it_ctx == m_imsi_to_gtpc_ctx.end()) + { + m_mme_gtpc_log->error("Could not find GTP-C context to remove\n"); + return; + } + sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid; + mme_ctr_fteid = it_ctx->second.mme_ctr_fteid; + srslte::gtpc_header *header = &del_req_pdu.header; + header->teid_present = true; + header->teid = sgw_ctr_fteid.teid; + header->type = srslte::GTPC_MSG_TYPE_DELETE_SESSION_REQUEST; + + srslte::gtpc_delete_session_request *del_req = &del_req_pdu.choice.delete_session_request; + del_req->cause.cause_value = srslte::GTPC_CAUSE_VALUE_ISR_DEACTIVATION; + m_mme_gtpc_log->info("GTP-C Delete Session Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid ); + + srslte::gtpc_pdu del_resp_pdu; + m_spgw->handle_delete_session_request(&del_req_pdu, &del_resp_pdu); + + //TODO Handle delete session response + + //Delete GTP-C context + std::map::iterator it_imsi = m_mme_ctr_teid_to_imsi.find(mme_ctr_fteid.teid); + if(it_imsi == m_mme_ctr_teid_to_imsi.end()) + { + m_mme_gtpc_log->error("Could not find IMSI from MME ctr TEID"); + } + else + { + m_mme_ctr_teid_to_imsi.erase(it_imsi); + } + m_imsi_to_gtpc_ctx.erase(it_ctx); + return; +} + +void +mme_gtpc::send_release_access_bearers_request(uint64_t imsi) +{ + m_mme_gtpc_log->info("Sending GTP-C Delete Access Bearers Request\n"); + srslte::gtpc_pdu rel_req_pdu; + srslte::gtp_fteid_t sgw_ctr_fteid; + + //Get S-GW Ctr TEID + std::map::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi); + if(it_ctx == m_imsi_to_gtpc_ctx.end()) + { + m_mme_gtpc_log->error("Could not find GTP-C context to remove\n"); + return; + } + sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid; + + //Set GTP-C header + srslte::gtpc_header *header = &rel_req_pdu.header; + header->teid_present = true; + header->teid = sgw_ctr_fteid.teid; + header->type = srslte::GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST; + + srslte::gtpc_release_access_bearers_request *rel_req = &rel_req_pdu.choice.release_access_bearers_request; + m_mme_gtpc_log->info("GTP-C Release Access Berarers Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid ); + + srslte::gtpc_pdu rel_resp_pdu; + m_spgw->handle_release_access_bearers_request(&rel_req_pdu, &rel_resp_pdu); + + //The GTP-C connection will not be torn down, just the user plane bearers. + return; +} +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap.cc b/srsepc/src/mme/s1ap.cc new file mode 100644 index 0000000..180859f --- /dev/null +++ b/srsepc/src/mme/s1ap.cc @@ -0,0 +1,620 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 +#include +#include // for printing uint64_t +#include "srslte/common/bcd_helpers.h" +#include "srsepc/hdr/mme/s1ap.h" +#include "srslte/asn1/gtpc.h" +#include "srslte/common/liblte_security.h" + +namespace srsepc{ + +s1ap* s1ap::m_instance = NULL; +pthread_mutex_t s1ap_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +s1ap::s1ap(): + m_s1mme(-1), + m_next_mme_ue_s1ap_id(1), + m_mme_gtpc(NULL), + m_pool(NULL) +{ +} + +s1ap::~s1ap() +{ +} + +s1ap* +s1ap::get_instance(void) +{ + + pthread_mutex_lock(&s1ap_instance_mutex); + if(m_instance == NULL) { + m_instance = new s1ap(); + } + pthread_mutex_unlock(&s1ap_instance_mutex); + return(m_instance); +} + +void +s1ap::cleanup(void) +{ + pthread_mutex_lock(&s1ap_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&s1ap_instance_mutex); +} + +int +s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1ap * hss_) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + + m_s1ap_args = s1ap_args; + srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn); + m_next_m_tmsi = rand(); + //Init log + m_s1ap_log = s1ap_log; + + //Get pointer to the HSS + m_hss = hss_; + + //Init message handlers + m_s1ap_mngmt_proc = s1ap_mngmt_proc::get_instance(); //Managment procedures + m_s1ap_mngmt_proc->init(); + m_s1ap_nas_transport = s1ap_nas_transport::get_instance(); //NAS Transport procedures + m_s1ap_nas_transport->init(m_hss); + m_s1ap_ctx_mngmt_proc = s1ap_ctx_mngmt_proc::get_instance(); //Context Management Procedures + m_s1ap_ctx_mngmt_proc->init(); + + //Get pointer to GTP-C class + m_mme_gtpc = mme_gtpc::get_instance(); + //Initialize S1-MME + m_s1mme = enb_listen(); + + m_s1ap_log->info("S1AP Initialized\n"); + return 0; +} + +void +s1ap::stop() +{ + if (m_s1mme != -1){ + close(m_s1mme); + } + std::map::iterator enb_it = m_active_enbs.begin(); + while(enb_it!=m_active_enbs.end()) + { + m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id); + m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id); + delete enb_it->second; + m_active_enbs.erase(enb_it++); + } + + std::map::iterator ue_it = m_imsi_to_ue_ctx.begin(); + while(ue_it!=m_imsi_to_ue_ctx.end()) + { + m_s1ap_log->info("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first); + m_s1ap_log->console("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first); + delete ue_it->second; + m_imsi_to_ue_ctx.erase(ue_it++); + } + //Cleanup message handlers + s1ap_mngmt_proc::cleanup(); + s1ap_nas_transport::cleanup(); + s1ap_ctx_mngmt_proc::cleanup(); + return; +} + +int +s1ap::get_s1_mme() +{ + return m_s1mme; +} + +uint32_t +s1ap::get_next_mme_ue_s1ap_id() +{ + return m_next_mme_ue_s1ap_id++; +} + + +int +s1ap::enb_listen() +{ + /*This function sets up the SCTP socket for eNBs to connect to*/ + int sock_fd, err; + struct sockaddr_in s1mme_addr; + struct sctp_event_subscribe evnts; + + m_s1ap_log->info("S1-MME Initializing\n"); + sock_fd = socket (AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + if (sock_fd == -1){ + m_s1ap_log->console("Could not create SCTP socket\n"); + return -1; + } + + //Sets the data_io_event to be able to use sendrecv_info + //Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown + bzero (&evnts, sizeof (evnts)) ; + evnts.sctp_data_io_event = 1; + evnts.sctp_shutdown_event=1; + if(setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof (evnts))){ + close(sock_fd); + m_s1ap_log->console("Subscribing to sctp_data_io_events failed\n"); + return -1; + } + + //S1-MME bind + bzero(&s1mme_addr, sizeof(s1mme_addr)); + s1mme_addr.sin_family = AF_INET; + inet_pton(AF_INET, m_s1ap_args.mme_bind_addr.c_str(), &(s1mme_addr.sin_addr) ); + s1mme_addr.sin_port = htons(S1MME_PORT); + err = bind(sock_fd, (struct sockaddr*) &s1mme_addr, sizeof (s1mme_addr)); + if (err != 0){ + close(sock_fd); + m_s1ap_log->error("Error binding SCTP socket\n"); + m_s1ap_log->console("Error binding SCTP socket\n"); + return -1; + } + + //Listen for connections + err = listen(sock_fd,SOMAXCONN); + if (err != 0){ + close(sock_fd); + m_s1ap_log->error("Error in SCTP socket listen\n"); + m_s1ap_log->console("Error in SCTP socket listen\n"); + return -1; + } + + return sock_fd; +} + + +bool +s1ap::handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu, struct sctp_sndrcvinfo *enb_sri) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT rx_pdu; + + if(liblte_s1ap_unpack_s1ap_pdu((LIBLTE_BYTE_MSG_STRUCT*)pdu, &rx_pdu) != LIBLTE_SUCCESS) { + m_s1ap_log->error("Failed to unpack received PDU\n"); + return false; + } + + switch(rx_pdu.choice_type) { + case LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE: + m_s1ap_log->info("Received initiating PDU\n"); + return handle_initiating_message(&rx_pdu.choice.initiatingMessage, enb_sri); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME: + m_s1ap_log->info("Received Succeseful Outcome PDU\n"); + return handle_successful_outcome(&rx_pdu.choice.successfulOutcome); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME: + m_s1ap_log->info("Received Unsucceseful Outcome PDU\n"); + return true;//TODO handle_unsuccessfuloutcome(&rx_pdu.choice.unsuccessfulOutcome); + break; + default: + m_s1ap_log->error("Unhandled PDU type %d\n", rx_pdu.choice_type); + return false; + } + + return true; + +} + +bool +s1ap::handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri) +{ + bool reply_flag = false; + srslte::byte_buffer_t * reply_buffer = m_pool->allocate(); + + switch(msg->choice_type) { + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST: + m_s1ap_log->info("Received S1 Setup Request.\n"); + m_s1ap_mngmt_proc->handle_s1_setup_request(&msg->choice.S1SetupRequest, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE: + m_s1ap_log->info("Received Initial UE Message.\n"); + m_s1ap_nas_transport->handle_initial_ue_message(&msg->choice.InitialUEMessage, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT: + m_s1ap_log->info("Received Uplink NAS Transport Message.\n"); + m_s1ap_nas_transport->handle_uplink_nas_transport(&msg->choice.UplinkNASTransport, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST: + m_s1ap_log->info("Received UE Context Release Request Message.\n"); + m_s1ap_ctx_mngmt_proc->handle_ue_context_release_request(&msg->choice.UEContextReleaseRequest, enb_sri, reply_buffer, &reply_flag); + break; + default: + m_s1ap_log->error("Unhandled S1AP intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); + m_s1ap_log->console("Unhandled S1APintiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); + } + //Send Reply to eNB + if(reply_flag == true) + { + ssize_t n_sent = sctp_send(m_s1mme,reply_buffer->msg, reply_buffer->N_bytes, enb_sri, 0); + if(n_sent == -1) + { + m_s1ap_log->console("Failed to send S1AP Initiating Reply.\n"); + m_s1ap_log->error("Failed to send S1AP Initiating Reply. \n"); + m_pool->deallocate(reply_buffer); + return false; + } + } + m_pool->deallocate(reply_buffer); + return true; +} + +bool +s1ap::handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE: + m_s1ap_log->info("Received Initial Context Setup Response.\n"); + return m_s1ap_ctx_mngmt_proc->handle_initial_context_setup_response(&msg->choice.InitialContextSetupResponse); + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE: + m_s1ap_log->info("Received UE Context Release Complete\n"); + return m_s1ap_ctx_mngmt_proc->handle_ue_context_release_complete(&msg->choice.UEContextReleaseComplete); + default: + m_s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + +//eNB Context Managment +void +s1ap::add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo *enb_sri) +{ + m_s1ap_log->info("Adding new eNB context. eNB ID %d\n", enb_ctx.enb_id); + std::set ue_set; + enb_ctx_t *enb_ptr = new enb_ctx_t; + memcpy(enb_ptr,&enb_ctx,sizeof(enb_ctx_t)); + m_active_enbs.insert(std::pair(enb_ptr->enb_id,enb_ptr)); + m_sctp_to_enb_id.insert(std::pair(enb_sri->sinfo_assoc_id, enb_ptr->enb_id)); + m_enb_assoc_to_ue_ids.insert(std::pair >(enb_sri->sinfo_assoc_id,ue_set)); + + return; +} + +enb_ctx_t* +s1ap::find_enb_ctx(uint16_t enb_id) +{ + std::map::iterator it = m_active_enbs.find(enb_id); + if(it == m_active_enbs.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +void +s1ap::delete_enb_ctx(int32_t assoc_id) +{ + std::map::iterator it_assoc = m_sctp_to_enb_id.find(assoc_id); + uint16_t enb_id = it_assoc->second; + + std::map::iterator it_ctx = m_active_enbs.find(enb_id); + if(it_ctx == m_active_enbs.end() || it_assoc == m_sctp_to_enb_id.end()) + { + m_s1ap_log->error("Could not find eNB to delete. Association: %d\n",assoc_id); + return; + } + + m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_id); + m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_id); + + //Delete connected UEs ctx + release_ues_ecm_ctx_in_enb(assoc_id); + + //Delete eNB + delete it_ctx->second; + m_active_enbs.erase(it_ctx); + m_sctp_to_enb_id.erase(it_assoc); + return; +} + + +//UE Context Management +bool +s1ap::add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx) +{ + std::map::iterator ctx_it = m_imsi_to_ue_ctx.find(ue_ctx->emm_ctx.imsi); + if(ctx_it != m_imsi_to_ue_ctx.end()) + { + m_s1ap_log->error("UE Context already exists. IMSI %015lu",ue_ctx->emm_ctx.imsi); + return false; + } + if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0) + { + std::map::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); + if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx) + { + m_s1ap_log->error("Context identified with IMSI does not match context identified by MME UE S1AP Id.\n"); + return false; + } + } + m_imsi_to_ue_ctx.insert(std::pair(ue_ctx->emm_ctx.imsi, ue_ctx)); + m_s1ap_log->debug("Saved UE context corresponding to IMSI %015lu\n",ue_ctx->emm_ctx.imsi); + return true; +} + +bool +s1ap::add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx) +{ + if(ue_ctx->ecm_ctx.mme_ue_s1ap_id == 0) + { + m_s1ap_log->error("Could not add UE context to MME UE S1AP map. MME UE S1AP ID 0 is not valid."); + return false; + } + std::map::iterator ctx_it = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); + if(ctx_it != m_mme_ue_s1ap_id_to_ue_ctx.end()) + { + m_s1ap_log->error("UE Context already exists. MME UE S1AP Id %015lu",ue_ctx->emm_ctx.imsi); + return false; + } + if(ue_ctx->ecm_ctx.imsi != 0) + { + std::map::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); + if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx) + { + m_s1ap_log->error("Context identified with MME UE S1AP Id does not match context identified by IMSI.\n"); + return false; + } + } + m_mme_ue_s1ap_id_to_ue_ctx.insert(std::pair(ue_ctx->ecm_ctx.mme_ue_s1ap_id, ue_ctx)); + m_s1ap_log->debug("Saved UE context corresponding to MME UE S1AP Id %d\n",ue_ctx->ecm_ctx.mme_ue_s1ap_id); + return true; +} + +bool +s1ap::add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id) +{ + + std::map >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc); + if(ues_in_enb == m_enb_assoc_to_ue_ids.end()) + { + m_s1ap_log->error("Could not find eNB from eNB SCTP association %d",enb_assoc); + return false; + } + std::set::iterator ue_id = ues_in_enb->second.find(mme_ue_s1ap_id); + if(ue_id != ues_in_enb->second.end()) + { + m_s1ap_log->error("UE with MME UE S1AP Id already exists %d",mme_ue_s1ap_id); + return false; + } + ues_in_enb->second.insert(mme_ue_s1ap_id); + m_s1ap_log->debug("Added UE with MME-UE S1AP Id %d to eNB with association %d\n", mme_ue_s1ap_id, enb_assoc); + return true; +} + +ue_ctx_t* +s1ap::find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id) +{ + std::map::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id); + if(it == m_mme_ue_s1ap_id_to_ue_ctx.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +ue_ctx_t* +s1ap::find_ue_ctx_from_imsi(uint64_t imsi) +{ + std::map::iterator it = m_imsi_to_ue_ctx.find(imsi); + if(it == m_imsi_to_ue_ctx.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +void +s1ap::release_ues_ecm_ctx_in_enb(int32_t enb_assoc) +{ + m_s1ap_log->console("Releasing UEs context\n"); + //delete UEs ctx + std::map >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc); + std::set::iterator ue_id = ues_in_enb->second.begin(); + if(ue_id == ues_in_enb->second.end()) + { + m_s1ap_log->console("No UEs to be released\n"); + } else { + while(ue_id != ues_in_enb->second.end() ) + { + std::map::iterator ue_ctx = m_mme_ue_s1ap_id_to_ue_ctx.find(*ue_id); + ue_emm_ctx_t *emm_ctx = &ue_ctx->second->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->second->ecm_ctx; + + m_s1ap_log->info("Releasing UE context. IMSI: %015lu, UE-MME S1AP Id: %d\n", emm_ctx->imsi, ecm_ctx->mme_ue_s1ap_id); + if(emm_ctx->state == EMM_STATE_REGISTERED) + { + m_mme_gtpc->send_delete_session_request(emm_ctx->imsi); + emm_ctx->state = EMM_STATE_DEREGISTERED; + } + m_s1ap_log->console("Releasing UE ECM context. UE-MME S1AP Id: %d\n", ecm_ctx->mme_ue_s1ap_id); + ecm_ctx->state = ECM_STATE_IDLE; + ecm_ctx->mme_ue_s1ap_id = 0; + ecm_ctx->enb_ue_s1ap_id = 0; + ues_in_enb->second.erase(ue_id++); + } + } +} + +bool +s1ap::release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id) +{ + ue_ctx_t *ue_ctx = find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->error("Cannot release UE ECM context, UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + return false; + } + ue_ecm_ctx_t* ecm_ctx = &ue_ctx->ecm_ctx; + + //Delete UE within eNB UE set + std::map::iterator it = m_sctp_to_enb_id.find(ecm_ctx->enb_sri.sinfo_assoc_id); + if(it == m_sctp_to_enb_id.end() ) + { + m_s1ap_log->error("Could not find eNB for UE release request.\n"); + return false; + } + uint16_t enb_id = it->second; + std::map >::iterator ue_set = m_enb_assoc_to_ue_ids.find(ecm_ctx->enb_sri.sinfo_assoc_id); + if(ue_set == m_enb_assoc_to_ue_ids.end()) + { + m_s1ap_log->error("Could not find the eNB's UEs.\n"); + return false; + } + ue_set->second.erase(mme_ue_s1ap_id); + + //Release UE ECM context + m_mme_ue_s1ap_id_to_ue_ctx.erase(mme_ue_s1ap_id); + ecm_ctx->state = ECM_STATE_IDLE; + ecm_ctx->mme_ue_s1ap_id = 0; + ecm_ctx->enb_ue_s1ap_id = 0; + + m_s1ap_log->info("Released UE ECM Context.\n"); + return true; +} + +bool +s1ap::delete_ue_ctx(uint64_t imsi) +{ + ue_ctx_t *ue_ctx = find_ue_ctx_from_imsi(imsi); + if(ue_ctx == NULL) + { + m_s1ap_log->info("Cannot delete UE context, UE not found. IMSI: %" PRIu64 "\n", imsi); + return false; + } + + //Make sure to release ECM ctx + if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0) + { + release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id); + } + + //Delete UE context + m_imsi_to_ue_ctx.erase(imsi); + delete ue_ctx; + m_s1ap_log->info("Deleted UE Context.\n"); + return true; +} + + + + +//UE Bearer Managment +void +s1ap::activate_eps_bearer(uint64_t imsi, uint8_t ebi) +{ + std::map::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); + if(ue_ctx_it == m_imsi_to_ue_ctx.end()) + { + m_s1ap_log->error("Could not activate EPS bearer: Could not find UE context\n"); + return; + } + //Make sure NAS is active + uint32_t mme_ue_s1ap_id = ue_ctx_it->second->ecm_ctx.mme_ue_s1ap_id; + std::map::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id); + if(it == m_mme_ue_s1ap_id_to_ue_ctx.end()) + { + m_s1ap_log->error("Could not activate EPS bearer: ECM context seems to be missing\n"); + return; + } + + ue_ecm_ctx_t * ecm_ctx = &ue_ctx_it->second->ecm_ctx; + if (ecm_ctx->erabs_ctx[ebi].state != ERAB_CTX_SETUP) + { + m_s1ap_log->error("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, ecm_ctx->erabs_ctx[ebi].state); + m_s1ap_log->console("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, ecm_ctx->erabs_ctx[ebi].state); + return; + } + + ecm_ctx->erabs_ctx[ebi].state = ERAB_ACTIVE; + ecm_ctx->state = ECM_STATE_CONNECTED; + m_s1ap_log->info("Activated EPS Bearer: Bearer id %d\n",ebi); + return; +} + +uint32_t +s1ap::allocate_m_tmsi(uint64_t imsi) +{ + uint32_t m_tmsi = m_next_m_tmsi; + m_next_m_tmsi = (m_next_m_tmsi + 1) % UINT32_MAX; + + m_tmsi_to_imsi.insert(std::pair(m_tmsi,imsi)); + m_s1ap_log->debug("Allocated M-TMSI 0x%x to IMSI %015lu,\n",m_tmsi,imsi); + return m_tmsi; +} + +void +s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx) +{ + std::string mnc_str, mcc_str; + + if(enb_ctx.enb_name_present) + { + m_s1ap_log->console("%s - eNB Name: %s, eNB id: 0x%x\n",prefix.c_str(), enb_ctx.enb_name, enb_ctx.enb_id); + m_s1ap_log->info("%s - eNB Name: %s, eNB id: 0x%x\n", prefix.c_str(), enb_ctx.enb_name, enb_ctx.enb_id); + } + else + { + m_s1ap_log->console("%s - eNB Id 0x%x\n",prefix.c_str(), enb_ctx.enb_id); + m_s1ap_log->info("%s - eNB Id 0x%x\n", prefix.c_str(), enb_ctx.enb_id); + } + srslte::mcc_to_string(enb_ctx.mcc, &mcc_str); + srslte::mnc_to_string(enb_ctx.mnc, &mnc_str); + m_s1ap_log->info("%s - MCC:%s, MNC:%s, PLMN: %d\n", prefix.c_str(), mcc_str.c_str(), mnc_str.c_str(), enb_ctx.plmn); + m_s1ap_log->console("%s - MCC:%s, MNC:%s, PLMN: %d\n", prefix.c_str(), mcc_str.c_str(), mnc_str.c_str(), enb_ctx.plmn); + for(int i=0;iinfo("%s - TAC %d, B-PLMN %d\n",prefix.c_str(), enb_ctx.tac[i],enb_ctx.bplmns[i][j]); + m_s1ap_log->console("%s - TAC %d, B-PLMN %d\n",prefix.c_str(), enb_ctx.tac[i],enb_ctx.bplmns[i][j]); + } + } + m_s1ap_log->console("%s - Paging DRX %d\n",prefix.c_str(),enb_ctx.drx); + return; +} + +} //namespace srsepc + diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc new file mode 100644 index 0000000..b9d599d --- /dev/null +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -0,0 +1,417 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 "srslte/upper/s1ap_common.h" +#include "srslte/common/bcd_helpers.h" +#include "srsepc/hdr/mme/s1ap.h" +#include "srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h" +#include "srslte/common/liblte_security.h" + + +namespace srsepc{ + +s1ap_ctx_mngmt_proc* s1ap_ctx_mngmt_proc::m_instance = NULL; +pthread_mutex_t s1ap_ctx_mngmt_proc_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + + +s1ap_ctx_mngmt_proc::s1ap_ctx_mngmt_proc() +{ +} + +s1ap_ctx_mngmt_proc::~s1ap_ctx_mngmt_proc() +{ +} + +s1ap_ctx_mngmt_proc* +s1ap_ctx_mngmt_proc::get_instance(void) +{ + pthread_mutex_lock(&s1ap_ctx_mngmt_proc_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_ctx_mngmt_proc(); + } + pthread_mutex_unlock(&s1ap_ctx_mngmt_proc_instance_mutex); + return(m_instance); +} + +void +s1ap_ctx_mngmt_proc::cleanup(void) +{ + pthread_mutex_lock(&s1ap_ctx_mngmt_proc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&s1ap_ctx_mngmt_proc_instance_mutex); +} + +void +s1ap_ctx_mngmt_proc::init(void) +{ + m_s1ap = s1ap::get_instance(); + m_mme_gtpc = mme_gtpc::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_s1ap_args = m_s1ap->m_s1ap_args; + m_pool = srslte::byte_buffer_pool::get_instance(); + m_s1ap_nas_transport = s1ap_nas_transport::get_instance(); +} + +bool +s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, + ue_ecm_ctx_t *ecm_ctx, + erab_ctx_t *erab_ctx) +{ + + int s1mme = m_s1ap->get_s1_mme(); + + //Prepare reply PDU + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST; + + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *in_ctxt_req = &init->choice.InitialContextSetupRequest; + + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctx_req = &in_ctxt_req->E_RABToBeSetupListCtxtSUReq.buffer[0]; //FIXME support more than one erab + srslte::byte_buffer_t *reply_buffer = m_pool->allocate(); + + m_s1ap_log->info("Preparing to send Initial Context Setup request\n"); + + //Add MME and eNB S1AP Ids + in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id; + in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id; + + //Set UE-AMBR + in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000; + in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL.BitRate=1000000000;//FIXME Get UE-AMBR from HSS + + //Setup eRAB context + in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1; + erab_ctx_req->e_RAB_ID.E_RAB_ID = erab_ctx->erab_id; + //Setup E-RAB QoS parameters + erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = erab_ctx->qci; + erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest + erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION; + erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE; + erab_ctx_req->e_RABlevelQoSParameters.gbrQosInformation_present=false; + + + //Set E-RAB S-GW F-TEID + //if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){ + // m_s1ap_log->error("Did not receive S1-U TEID in create session response\n"); + // return false; + //} + erab_ctx_req->transportLayerAddress.n_bits = 32; //IPv4 + uint32_t sgw_s1u_ip = htonl(erab_ctx->sgw_s1u_fteid.ipv4); + //uint32_t sgw_s1u_ip = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4; + uint8_t *tmp_ptr = erab_ctx_req->transportLayerAddress.buffer; + liblte_value_2_bits(sgw_s1u_ip, &tmp_ptr, 32);//FIXME consider ipv6 + + uint32_t sgw_s1u_teid = erab_ctx->sgw_s1u_fteid.teid; + memcpy(erab_ctx_req->gTP_TEID.buffer, &sgw_s1u_teid, sizeof(uint32_t)); + + //Set UE security capabilities and k_enb + bzero(in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer,sizeof(uint8_t)*16); + bzero(in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer,sizeof(uint8_t)*16); + for(int i = 0; i<3; i++) + { + if(emm_ctx->security_ctxt.ue_network_cap.eea[i+1] == true) + { + in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 1; //EEA supported + } + else + { + in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 0; //EEA not supported + } + if(emm_ctx->security_ctxt.ue_network_cap.eia[i+1] == true) + { + in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 1; //EEA supported + } + else + { + in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 0; //EEA not supported + } + } + //Get K eNB + liblte_unpack(emm_ctx->security_ctxt.k_enb, 32, in_ctxt_req->SecurityKey.buffer); + m_s1ap_log->info_hex(emm_ctx->security_ctxt.k_enb, 32, "Initial Context Setup Request -- Key eNB (k_enb)\n"); + + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + if(emm_ctx->state == EMM_STATE_DEREGISTERED) + { + //Attach procedure initiated from an attach request + m_s1ap_log->console("Adding attach accept to Initial Context Setup Request\n"); + m_s1ap_log->info("Adding attach accept to Initial Context Setup Request\n"); + m_s1ap_nas_transport->pack_attach_accept(emm_ctx, ecm_ctx, erab_ctx_req, &erab_ctx->pdn_addr_alloc, nas_buffer); + } + + + LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n"); + return false; + } + + //Send Reply to eNB + ssize_t n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ecm_ctx->enb_sri, 0); + if(n_sent == -1) + { + m_s1ap_log->error("Failed to send Initial Context Setup Request\n"); + return false; + } + + //Change E-RAB state to Context Setup Requested and save S-GW control F-TEID + ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED; + + struct in_addr addr; + addr.s_addr = htonl(sgw_s1u_ip); + m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); + m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); + m_s1ap_log->console("Initial Context Setup Request -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); + m_s1ap_log->console("Initial Context Setup Request -- QCI %d \n", erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI); + + m_pool->deallocate(reply_buffer); + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp) +{ + uint32_t mme_ue_s1ap_id = in_ctxt_resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); + if (ue_ctx == NULL) + { + m_s1ap_log->error("Could not find UE's context in active UE's map\n"); + return false; + } + ue_emm_ctx_t * emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t * ecm_ctx = &ue_ctx->ecm_ctx; + + m_s1ap_log->console("Received Initial Context Setup Response\n"); + //Setup E-RABs + for(uint32_t i=0; iE_RABSetupListCtxtSURes.len;i++) + { + uint8_t erab_id = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].e_RAB_ID.E_RAB_ID; + erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[erab_id]; + if (erab_ctx->state != ERAB_CTX_REQUESTED) + { + m_s1ap_log->error("E-RAB requested was not previously requested %d\n",erab_id); + return false; + } + //Mark E-RAB with context setup + erab_ctx->state = ERAB_CTX_SETUP; + + //Set the GTP information + uint8_t *bit_ptr = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer; + erab_ctx->enb_fteid.ipv4 = htonl(liblte_bits_2_value(&bit_ptr,32)); + memcpy(&erab_ctx->enb_fteid.teid, in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].gTP_TEID.buffer, 4); + erab_ctx->enb_fteid.teid = ntohl(erab_ctx->enb_fteid.teid); + + char enb_addr_str[INET_ADDRSTRLEN+1]; + const char *err = inet_ntop(AF_INET, &erab_ctx->enb_fteid.ipv4,enb_addr_str,sizeof(enb_addr_str)); + if(err == NULL) + { + m_s1ap_log->error("Error converting IP to string\n"); + } + + m_s1ap_log->info("E-RAB Context Setup. E-RAB id %d\n",erab_ctx->erab_id); + m_s1ap_log->info("E-RAB Context -- eNB TEID 0x%x, eNB Address %s\n", erab_ctx->enb_fteid.teid, enb_addr_str); + m_s1ap_log->console("E-RAB Context Setup. E-RAB id %d\n",erab_ctx->erab_id); + m_s1ap_log->console("E-RAB Context -- eNB TEID 0x%x; eNB GTP-U Address %s\n", erab_ctx->enb_fteid.teid, enb_addr_str); + + } + if(emm_ctx->state == EMM_STATE_REGISTERED) + { + m_s1ap_log->console("Initial Context Setup Response triggered from Service Request.\n"); + m_s1ap_log->console("Sending Modify Bearer Request.\n"); + m_mme_gtpc->send_modify_bearer_request(emm_ctx->imsi, &ecm_ctx->erabs_ctx[5]); + } + return true; +} + +bool +s1ap_ctx_mngmt_proc::handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT ue_rel_req; + + uint32_t mme_ue_s1ap_id = ue_rel->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + m_s1ap_log->info("Received UE Context Release Request. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("Received UE Context Release Request. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); + + ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + return false; + } + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + //Delete user plane context at the SPGW (but keep GTP-C connection). + if (ecm_ctx->state == ECM_STATE_CONNECTED) + { + //There are active E-RABs, send release access mearers request + m_s1ap_log->console("There are active E-RABs, send release access mearers request\n"); + m_s1ap_log->info("There are active E-RABs, send release access mearers request\n"); + + //The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED + //It will release the UEs downstream S1-u and keep the upstream S1-U connection active. + m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi); + + //Send release context command to enb, so that it can release it's bearers + send_ue_context_release_command(ecm_ctx,reply_buffer); + } + else + { + //No ECM Context to release + m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); + //Make sure E-RABS are merked as DEACTIVATED. + for(int i=0;ierabs_ctx[i].state = ERAB_DEACTIVATED; + } + } + + //Delete UE context + ecm_ctx->state = ECM_STATE_IDLE; + ecm_ctx->enb_ue_s1ap_id = 0; + ecm_ctx->mme_ue_s1ap_id = 0; + m_s1ap_log->info("UE is ECM IDLE.\n"); + m_s1ap_log->console("UE is ECM IDLE.\n"); + return true; +} + +bool +s1ap_ctx_mngmt_proc::send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer) +{ + + int s1mme = m_s1ap->get_s1_mme(); + + //Prepare reply PDU + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND; + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ctx_rel_cmd = &init->choice.UEContextReleaseCommand; + + ctx_rel_cmd->UE_S1AP_IDs.choice_type = LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR; + ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.mME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id; + ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id; + + ctx_rel_cmd->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_NAS; + ctx_rel_cmd->Cause.choice.nas.ext = false; + ctx_rel_cmd->Cause.choice.nas.e = LIBLTE_S1AP_CAUSENAS_NORMAL_RELEASE; + + LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n"); + return false; + } + //Send Reply to eNB + int n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ecm_ctx->enb_sri, 0); + if(n_sent == -1) + { + m_s1ap_log->error("Failed to send Initial Context Setup Request\n"); + return false; + } + + + return true; +} + +bool +s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp) +{ + /* + typedef struct{ + bool ext; + LIBLTE_S1AP_MME_UE_S1AP_ID_STRUCT MME_UE_S1AP_ID; + LIBLTE_S1AP_ENB_UE_S1AP_ID_STRUCT eNB_UE_S1AP_ID; + LIBLTE_S1AP_CRITICALITYDIAGNOSTICS_STRUCT CriticalityDiagnostics; + bool CriticalityDiagnostics_present; + LIBLTE_S1AP_USERLOCATIONINFORMATION_STRUCT UserLocationInformation; + bool UserLocationInformation_present; + }LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT; + */ + + uint32_t mme_ue_s1ap_id = rel_comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + m_s1ap_log->info("Received UE Context Release Complete. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("Received UE Context Release Complete. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); + + ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + return false; + } + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + //Delete user plane context at the SPGW (but keep GTP-C connection). + if (ecm_ctx->state == ECM_STATE_CONNECTED) + { + //There are active E-RABs, send release access mearers request + m_s1ap_log->console("There are active E-RABs, send release access mearers request"); + m_s1ap_log->info("There are active E-RABs, send release access mearers request"); + m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi); + //The handle_releease_access_bearers_response function will make sure to mark E-RABS DEACTIVATED + //It will release the UEs downstream S1-u and keep the upstream S1-U connection active. + } + else + { + //No ECM Context to release + m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); + //Make sure E-RABS are merked as DEACTIVATED. + for(int i=0;ierabs_ctx[i].state = ERAB_DEACTIVATED; + } + } + + //Delete UE context + m_s1ap->release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id); + m_s1ap_log->info("UE Context Release Completed.\n"); + m_s1ap_log->console("UE Context Release Completed.\n"); + return true; +} + + +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc new file mode 100644 index 0000000..079eca1 --- /dev/null +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -0,0 +1,271 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 "srslte/upper/s1ap_common.h" +#include "srslte/common/bcd_helpers.h" +#include "srsepc/hdr/mme/s1ap.h" +#include "srsepc/hdr/mme/s1ap_mngmt_proc.h" + +namespace srsepc{ + +s1ap_mngmt_proc* s1ap_mngmt_proc::m_instance = NULL; +pthread_mutex_t s1ap_mngmt_proc_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + + +s1ap_mngmt_proc::s1ap_mngmt_proc() +{ +} + +s1ap_mngmt_proc::~s1ap_mngmt_proc() +{ +} + +s1ap_mngmt_proc* +s1ap_mngmt_proc::get_instance(void) +{ + pthread_mutex_lock(&s1ap_mngmt_proc_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_mngmt_proc(); + } + pthread_mutex_unlock(&s1ap_mngmt_proc_instance_mutex); + return(m_instance); +} + +void +s1ap_mngmt_proc::cleanup(void) +{ + pthread_mutex_lock(&s1ap_mngmt_proc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&s1ap_mngmt_proc_instance_mutex); +} + +void +s1ap_mngmt_proc::init(void) +{ + m_s1ap = s1ap::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_s1mme = m_s1ap->get_s1_mme(); + m_s1ap_args = m_s1ap->m_s1ap_args; +} + +bool +s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + enb_ctx_t enb_ctx; + LIBLTE_S1AP_S1AP_PDU_STRUCT reply_pdu; + + if(!unpack_s1_setup_request(msg, &enb_ctx)) + { + m_s1ap_log->error("Malformed S1 Setup Request\n"); + return false; + } + + //Log S1 Setup Request Info + m_s1ap_log->console("Received S1 Setup Request.\n"); + m_s1ap->print_enb_ctx_info(std::string("S1 Setup Request"),enb_ctx); + + //Check matching PLMNs + if(enb_ctx.plmn!=m_s1ap->get_plmn()){ + + m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); + } + else{ + enb_ctx_t *enb_ptr = m_s1ap->find_enb_ctx(enb_ctx.enb_id); + if(enb_ptr != NULL) + { + //eNB already registered + //TODO replace enb_ctx + m_s1ap_log->warning("eNB Already registered\n"); + } + else + { + //new eNB + m_s1ap->add_new_enb_ctx(enb_ctx,enb_sri); + } + + pack_s1_setup_response(m_s1ap_args, reply_buffer); + m_s1ap_log->console("Sending S1 Setup Response\n"); + m_s1ap_log->info("Sending S1 Setup Response\n"); + } + + *reply_flag = true; + return true; +} + + +/* + * Packing/Unpacking helper functions. + */ +bool +s1ap_mngmt_proc::unpack_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, enb_ctx_t* enb_ctx) +{ + + uint8_t enb_id_bits[32]; + uint32_t plmn = 0; + uint16_t tac, bplmn; + + uint32_t tmp32=0; + //eNB Name + enb_ctx->enb_name_present=msg->eNBname_present; + if(msg->eNBname_present) + { + bzero(enb_ctx->enb_name,sizeof(enb_ctx->enb_name)); + memcpy(enb_ctx->enb_name,&msg->eNBname.buffer,msg->eNBname.n_octets); + } + //eNB Id + bzero(enb_id_bits,sizeof(enb_id_bits)); + memcpy(&enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], msg->Global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer, LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + liblte_pack(enb_id_bits, 32, (uint8_t*) &tmp32); + enb_ctx->enb_id=ntohl(tmp32); + + //PLMN Id + ((uint8_t*)&plmn)[1] = msg->Global_ENB_ID.pLMNidentity.buffer[0]; + ((uint8_t*)&plmn)[2] = msg->Global_ENB_ID.pLMNidentity.buffer[1]; + ((uint8_t*)&plmn)[3] = msg->Global_ENB_ID.pLMNidentity.buffer[2]; + + enb_ctx->plmn = ntohl(plmn); + srslte::s1ap_plmn_to_mccmnc(enb_ctx->plmn, &enb_ctx->mcc, &enb_ctx->mnc); + + //SupportedTAs + enb_ctx->nof_supported_ta=msg->SupportedTAs.len; + for(uint16_t i=0; iSupportedTAs.len; i++) + { + //TAC + ((uint8_t*)&enb_ctx->tac[i])[0] = msg->SupportedTAs.buffer[i].tAC.buffer[0]; + ((uint8_t*)&enb_ctx->tac[i])[1] = msg->SupportedTAs.buffer[i].tAC.buffer[1]; + enb_ctx->tac[i]=ntohs(enb_ctx->tac[i]); + enb_ctx->nof_supported_bplmns[i]=msg->SupportedTAs.buffer[i].broadcastPLMNs.len; + for (uint16_t j=0; jSupportedTAs.buffer[i].broadcastPLMNs.len; j++) + { + //BPLMNs + ((uint8_t*)&enb_ctx->bplmns[i][j])[1] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[0]; + ((uint8_t*)&enb_ctx->bplmns[i][j])[2] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[1]; + ((uint8_t*)&enb_ctx->bplmns[i][j])[3] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[2]; + + enb_ctx->bplmns[i][j] = ntohl(enb_ctx->bplmns[i][j]); + } + } + + //Default Paging DRX + enb_ctx->drx = msg->DefaultPagingDRX.e; + + return true; +} + +bool +s1ap_mngmt_proc::pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_ENUM cause, srslte::byte_buffer_t *msg) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME; + + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *unsucc = &pdu.choice.unsuccessfulOutcome; + unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + unsucc->criticality = LIBLTE_S1AP_CRITICALITY_REJECT; + unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE; + + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT* s1_fail=(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT*)&unsucc->choice; + + s1_fail->TimeToWait_present=false; + s1_fail->CriticalityDiagnostics_present=false; + s1_fail->Cause.ext=false; + s1_fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_MISC; + s1_fail->Cause.choice.misc.ext=false; + s1_fail->Cause.choice.misc.e=cause; + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)msg); + return true; + } + + +bool +s1ap_mngmt_proc::pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buffer_t *msg) +{ + + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + succ->criticality = LIBLTE_S1AP_CRITICALITY_IGNORE; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE; + + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT* s1_resp=(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT*)&succ->choice; + + s1_resp->ext=false; + + //MME Name + s1_resp->MMEname_present=true; + s1_resp->MMEname.ext=false; + s1_resp->MMEname.n_octets=s1ap_args.mme_name.length(); + memcpy(s1_resp->MMEname.buffer,s1ap_args.mme_name.c_str(),s1ap_args.mme_name.length()); + + //Served GUMEIs + s1_resp->ServedGUMMEIs.len=1;//TODO Only one served GUMMEI supported + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *serv_gummei = &s1_resp->ServedGUMMEIs.buffer[0]; + + serv_gummei->ext=false; + serv_gummei->iE_Extensions_present = false; + + uint32_t plmn=0; + srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &plmn); + plmn=htonl(plmn); + serv_gummei->servedPLMNs.len = 1; //Only one PLMN supported + serv_gummei->servedPLMNs.buffer[0].buffer[0]=((uint8_t*)&plmn)[1]; + serv_gummei->servedPLMNs.buffer[0].buffer[1]=((uint8_t*)&plmn)[2]; + serv_gummei->servedPLMNs.buffer[0].buffer[2]=((uint8_t*)&plmn)[3]; + + serv_gummei->servedGroupIDs.len=1; //LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT + uint16_t tmp=htons(s1ap_args.mme_group); + serv_gummei->servedGroupIDs.buffer[0].buffer[0]=((uint8_t*)&tmp)[0]; + serv_gummei->servedGroupIDs.buffer[0].buffer[1]=((uint8_t*)&tmp)[1]; + + serv_gummei->servedMMECs.len=1; //Only one MMEC served + serv_gummei->servedMMECs.buffer[0].buffer[0]=s1ap_args.mme_code; + + //Relative MME Capacity + s1_resp->RelativeMMECapacity.RelativeMMECapacity=255; + + //Relay Unsupported + s1_resp->MMERelaySupportIndicator_present=false; + + s1_resp->CriticalityDiagnostics_present = false; + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)msg); + + return true; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc new file mode 100644 index 0000000..1bb871f --- /dev/null +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -0,0 +1,2202 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 +#include +#include // for printing uint64_t +#include "srsepc/hdr/mme/s1ap.h" +#include "srsepc/hdr/mme/s1ap_nas_transport.h" +#include "srslte/common/security.h" +#include "srslte/common/liblte_security.h" + +namespace srsepc{ + +s1ap_nas_transport* s1ap_nas_transport::m_instance = NULL; +pthread_mutex_t s1ap_nas_transport_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +s1ap_nas_transport::s1ap_nas_transport() +{ + return; +} + +s1ap_nas_transport::~s1ap_nas_transport() +{ + return; +} + +s1ap_nas_transport* +s1ap_nas_transport::get_instance(void) +{ + pthread_mutex_lock(&s1ap_nas_transport_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_nas_transport(); + } + pthread_mutex_unlock(&s1ap_nas_transport_instance_mutex); + return(m_instance); +} + +void +s1ap_nas_transport::cleanup(void) +{ + pthread_mutex_lock(&s1ap_nas_transport_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&s1ap_nas_transport_instance_mutex); +} + +void +s1ap_nas_transport::init(hss_interface_s1ap * hss_) +{ + m_s1ap = s1ap::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_pool = srslte::byte_buffer_pool::get_instance(); + + m_hss = hss_; + m_mme_gtpc = mme_gtpc::get_instance(); +} + + +bool +s1ap_nas_transport::handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + //Get info from initial UE message + uint32_t enb_ue_s1ap_id = init_ue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + + //Log unhandled Initial UE message IEs + log_unhandled_initial_ue_message_ies(init_ue); + + /*Check whether NAS Attach Request or Service Request*/ + bool mac_valid = false; + uint8_t pd, msg_type, sec_hdr_type; + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + memcpy(nas_msg->msg, &init_ue->NAS_PDU.buffer, init_ue->NAS_PDU.n_octets); + nas_msg->N_bytes = init_ue->NAS_PDU.n_octets; + + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + if(msg_type == LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST) + { + m_s1ap_log->info("Received Attach Request \n"); + m_s1ap_log->console("Received Attach Request \n"); + handle_nas_attach_request(enb_ue_s1ap_id, nas_msg, reply_buffer,reply_flag, enb_sri); + } + else if(msg_type == LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST) + { + m_s1ap_log->info("Received Service Request \n"); + m_s1ap_log->console("Received Service Request \n"); + if(!init_ue->S_TMSI_present) + { + m_s1ap_log->error("Service request -- S-TMSI not present\n"); + m_s1ap_log->console("Service request -- S-TMSI not present\n" ); + return false; + } + uint32_t *m_tmsi = (uint32_t*) &init_ue->S_TMSI.m_TMSI.buffer; + uint32_t enb_ue_s1ap_id = init_ue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + m_s1ap_log->info("Service request -- S-TMSI 0x%x\n", ntohl(*m_tmsi)); + m_s1ap_log->console("Service request -- S-TMSI 0x%x\n", ntohl(*m_tmsi) ); + m_s1ap_log->info("Service request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + m_s1ap_log->console("Service request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + handle_nas_service_request(ntohl(*m_tmsi), enb_ue_s1ap_id, nas_msg, reply_buffer,reply_flag, enb_sri); + return true; + } + else if(msg_type == LIBLTE_MME_MSG_TYPE_DETACH_REQUEST) + { + m_s1ap_log->console("Received Initial UE message -- Detach Request\n"); + m_s1ap_log->info("Received Initial UE message -- Detach Request\n"); + if(!init_ue->S_TMSI_present) + { + m_s1ap_log->error("Detach request -- S-TMSI not present\n"); + m_s1ap_log->console("Detach request -- S-TMSI not present\n" ); + return false; + } + uint32_t *m_tmsi = (uint32_t*) &init_ue->S_TMSI.m_TMSI.buffer; + uint32_t enb_ue_s1ap_id = init_ue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + m_s1ap_log->info("Detach Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi)); + m_s1ap_log->console("Detach Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi) ); + m_s1ap_log->info("Detach Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + m_s1ap_log->console("Detach Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + + handle_nas_detach_request(ntohl(*m_tmsi), enb_ue_s1ap_id, nas_msg, reply_buffer,reply_flag, enb_sri); + return true; + } + else if(msg_type == LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST) + { + m_s1ap_log->console("Received Initial UE message -- Tracking Area Update Request\n"); + m_s1ap_log->info("Received Initial UE message -- Tracking Area Update Request\n"); + if(!init_ue->S_TMSI_present) + { + m_s1ap_log->error("Tracking Area Update Request -- S-TMSI not present\n"); + m_s1ap_log->console("Tracking Area Update Request -- S-TMSI not present\n" ); + return false; + } + uint32_t *m_tmsi = (uint32_t*) &init_ue->S_TMSI.m_TMSI.buffer; + uint32_t enb_ue_s1ap_id = init_ue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + m_s1ap_log->info("Tracking Area Update Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi)); + m_s1ap_log->console("Tracking Area Update Request -- S-TMSI 0x%x\n", ntohl(*m_tmsi) ); + m_s1ap_log->info("Tracking Area Update Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + m_s1ap_log->console("Tracking Area Update Request -- eNB UE S1AP Id %d\n", enb_ue_s1ap_id); + + handle_nas_tracking_area_update_request(ntohl(*m_tmsi), enb_ue_s1ap_id, nas_msg, reply_buffer,reply_flag, enb_sri); + return true; + } + else + { + m_s1ap_log->info("Unhandled Initial UE Message 0x%x\n",msg_type); + m_s1ap_log->console("Unhandled Initial UE Message 0x%x \n", msg_type); + } + m_pool->deallocate(nas_msg); + + return true; +} + +bool +s1ap_nas_transport::handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + uint8_t pd, msg_type, sec_hdr_type; + uint32_t enb_ue_s1ap_id = ul_xport->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + uint32_t mme_ue_s1ap_id = ul_xport->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + bool mac_valid = false; + + //Get UE ECM context + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->warning("Received uplink NAS, but could not find UE ECM context. MME-UE S1AP id: %d\n",mme_ue_s1ap_id); + return false; + } + + m_s1ap_log->debug("Received uplink NAS and found UE ECM context. MME-UE S1AP id: %d\n",mme_ue_s1ap_id); + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + //Parse NAS message header + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + memcpy(nas_msg->msg, &ul_xport->NAS_PDU.buffer, ul_xport->NAS_PDU.n_octets); + nas_msg->N_bytes = ul_xport->NAS_PDU.n_octets; + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + + // Parse the message security header + liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg, &pd, &sec_hdr_type); + + //Find UE EMM context if message is security protected. + if(sec_hdr_type != LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS) + { + //Make sure EMM context is set-up, to do integrity check/de-chiphering + if(emm_ctx->imsi == 0) + { + //No EMM context found. + //Perhaps a temporary context is being created? + //This can happen with integrity protected identity reponse messages + if( !(msg_type == LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE && sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY)) + { + m_s1ap_log->warning("Uplink NAS: could not find security context for integrity protected message. MME-UE S1AP id: %d\n",mme_ue_s1ap_id); + m_pool->deallocate(nas_msg); + return false; + } + } + } + + if( sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS || + (msg_type == LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE && sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY) || + (msg_type == LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE && sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY) || + (msg_type == LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE && sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY)) + { + //Only identity response and authentication response are valid as plain NAS. + //Sometimes authentication response/failure and identity response are sent as integrity protected, + //but these messages are sent when the securty context is not setup yet, so we cannot integrity check it. + switch(msg_type) + { + case LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE: + m_s1ap_log->info("Uplink NAS: Received Identity Response\n"); + m_s1ap_log->console("Uplink NAS: Received Identity Response\n"); + handle_identity_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE: + m_s1ap_log->info("Uplink NAS: Received Authentication Response\n"); + m_s1ap_log->console("Uplink NAS: Received Authentication Response\n"); + handle_nas_authentication_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + // Authentication failure with the option sync failure can be sent not integrity protected + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE: + m_s1ap_log->info("Plain UL NAS: Authentication Failure\n"); + m_s1ap_log->console("Plain UL NAS: Authentication Failure\n"); + handle_authentication_failure(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + // Detach request can be sent not integrity protected when "power off" option is used + case LIBLTE_MME_MSG_TYPE_DETACH_REQUEST: + m_s1ap_log->info("Plain Protected UL NAS: Detach Request\n"); + m_s1ap_log->console("Plain Protected UL NAS: Detach Request\n"); + handle_nas_detach_request(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + default: + m_s1ap_log->warning("Unhandled Plain NAS message 0x%x\n", msg_type ); + m_s1ap_log->console("Unhandled Plain NAS message 0x%x\n", msg_type ); + m_pool->deallocate(nas_msg); + return false; + } + //Increment UL NAS count. + emm_ctx->security_ctxt.ul_nas_count++; + } + else if(sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT || sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT) + { + switch (msg_type) { + case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE: + m_s1ap_log->info("Uplink NAS: Received Security Mode Complete\n"); + m_s1ap_log->console("Uplink NAS: Received Security Mode Complete\n"); + emm_ctx->security_ctxt.ul_nas_count = 0; + emm_ctx->security_ctxt.dl_nas_count = 0; + mac_valid = integrity_check(emm_ctx,nas_msg); + if(mac_valid){ + handle_nas_security_mode_complete(nas_msg, ue_ctx, reply_buffer, reply_flag); + } else { + m_s1ap_log->warning("Invalid MAC in Security Mode Command Complete message.\n" ); + } + break; + default: + m_s1ap_log->warning("Unhandled NAS message with new EPS security context 0x%x\n", msg_type ); + m_s1ap_log->warning("Unhandled NAS message with new EPS security context 0x%x\n", msg_type ); + } + } + else if(sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED) + { + //Integrity protected NAS message, possibly ciphered. + emm_ctx->security_ctxt.ul_nas_count++; + mac_valid = integrity_check(emm_ctx,nas_msg); + if(!mac_valid){ + m_s1ap_log->warning("Invalid MAC in NAS message type 0x%x.\n", msg_type); + m_pool->deallocate(nas_msg); + return false; + } + switch (msg_type) { + case LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE: + m_s1ap_log->info("Integrity Protected UL NAS: Received Attach Complete\n"); + m_s1ap_log->console("Integrity Protected UL NAS: Received Attach Complete\n"); + handle_nas_attach_complete(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE: + m_s1ap_log->info("Integrity Protected UL NAS: Received ESM Information Response\n"); + m_s1ap_log->console("Integrity Protected UL NAS: Received ESM Information Response\n"); + handle_esm_information_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST: + m_s1ap_log->info("Integrity Protected UL NAS: Tracking Area Update Request\n"); + m_s1ap_log->console("Integrity Protected UL NAS: Tracking Area Update Request\n"); + handle_tracking_area_update_request(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE: + m_s1ap_log->info("Integrity Protected UL NAS: Authentication Failure\n"); + m_s1ap_log->console("Integrity Protected UL NAS: Authentication Failure\n"); + handle_authentication_failure(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_DETACH_REQUEST: + m_s1ap_log->info("Integrity Protected UL NAS: Detach Request\n"); + m_s1ap_log->console("Integrity Protected UL NAS: Detach Request\n"); + handle_nas_detach_request(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + default: + m_s1ap_log->warning("Unhandled NAS integrity protected message 0x%x\n", msg_type ); + m_s1ap_log->console("Unhandled NAS integrity protected message 0x%x\n", msg_type ); + m_pool->deallocate(nas_msg); + return false; + } + } + else + { + m_s1ap_log->error("Unhandled security header type in Uplink NAS Transport: %d\n", sec_hdr_type); + m_pool->deallocate(nas_msg); + return false; + } + + + if(*reply_flag == true) + { + m_s1ap_log->console("DL NAS: Sent Downlink NAS Message. DL NAS Count=%d, UL NAS count=%d\n",emm_ctx->security_ctxt.dl_nas_count,emm_ctx->security_ctxt.ul_nas_count ); + m_s1ap_log->info("DL NAS: Sent Downlink NAS message. DL NAS Count=%d, UL NAS count=%d\n",emm_ctx->security_ctxt.dl_nas_count, emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->info("DL NAS: MME UE S1AP id %d\n",ecm_ctx->mme_ue_s1ap_id); + m_s1ap_log->console("DL NAS: MME UE S1AP id %d\n",ecm_ctx->mme_ue_s1ap_id); + } + m_pool->deallocate(nas_msg); + return true; +} + +bool +s1ap_nas_transport::handle_nas_attach_request(uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req; + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT pdn_con_req; + + //Get NAS Attach Request and PDN connectivity request messages + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_attach_request_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &attach_req); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS attach request. Error: %s\n", liblte_error_text[err]); + m_pool->deallocate(nas_msg); + return false; + } + /*Get PDN Connectivity Request*/ + err = liblte_mme_unpack_pdn_connectivity_request_msg(&attach_req.esm_msg, &pdn_con_req); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS PDN Connectivity Request. Error: %s\n", liblte_error_text[err]); + m_pool->deallocate(nas_msg); + return false; + } + + //Get attach type from attach request + if(attach_req.eps_mobile_id.type_of_id == LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI) + { + m_s1ap_log->console("Attach Request -- IMSI-style attach request\n"); + m_s1ap_log->info("Attach Request -- IMSI-style attach request\n"); + handle_nas_imsi_attach_request(enb_ue_s1ap_id, attach_req, pdn_con_req, reply_buffer, reply_flag, enb_sri); + } + else if(attach_req.eps_mobile_id.type_of_id == LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI) + { + m_s1ap_log->console("Attach Request -- GUTI-style attach request\n"); + m_s1ap_log->info("Attach Request -- GUTI-style attach request\n"); + handle_nas_guti_attach_request(enb_ue_s1ap_id, attach_req, pdn_con_req, nas_msg, reply_buffer, reply_flag, enb_sri); + } + else + { + m_s1ap_log->error("Unhandle Mobile Id type in attach request\n"); + return false; + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_imsi_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + uint8_t k_asme[32]; + uint8_t autn[16]; + uint8_t rand[16]; + uint8_t xres[8]; + + ue_ctx_t ue_ctx; + ue_emm_ctx_t *emm_ctx = &ue_ctx.emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx.ecm_ctx; + + //Set UE's EMM context + uint64_t imsi = 0; + for(int i=0;i<=14;i++){ + imsi += attach_req.eps_mobile_id.imsi[i]*std::pow(10,14-i); + } + + //Check if UE is + ue_ctx_t *old_ctx = m_s1ap->find_ue_ctx_from_imsi(imsi); + if(old_ctx!=NULL) + { + m_s1ap_log->console("Attach Request -- UE is already attached."); + m_s1ap_log->info("Attach Request -- UE is already attached."); + //Detaching previoulsy attached UE. + m_mme_gtpc->send_delete_session_request(imsi); + if(old_ctx->ecm_ctx.mme_ue_s1ap_id!=0) + { + m_s1ap->m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(&old_ctx->ecm_ctx, reply_buffer); + } + m_s1ap->delete_ue_ctx(imsi); + } + + emm_ctx->imsi = imsi; + emm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + emm_ctx->state = EMM_STATE_DEREGISTERED; + //Save UE network capabilities + memcpy(&emm_ctx->security_ctxt.ue_network_cap, &attach_req.ue_network_cap, sizeof(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT)); + emm_ctx->security_ctxt.ms_network_cap_present = attach_req.ms_network_cap_present; + if(attach_req.ms_network_cap_present) + { + memcpy(&emm_ctx->security_ctxt.ms_network_cap, &attach_req.ms_network_cap, sizeof(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT)); + } + uint8_t eps_bearer_id = pdn_con_req.eps_bearer_id; //TODO: Unused + emm_ctx->procedure_transaction_id = pdn_con_req.proc_transaction_id; + + //Initialize NAS count + emm_ctx->security_ctxt.ul_nas_count = 0; + emm_ctx->security_ctxt.dl_nas_count = 0; + + //Set UE ECM context + ecm_ctx->imsi = emm_ctx->imsi; + ecm_ctx->mme_ue_s1ap_id = emm_ctx->mme_ue_s1ap_id; + + //Set eNB information + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + memcpy(&ecm_ctx->enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + + //Save whether secure ESM information transfer is necessary + ecm_ctx->eit = pdn_con_req.esm_info_transfer_flag_present; + + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; + ecm_ctx->erabs_ctx[i].erab_id = i; + } + + //Log Attach Request information + m_s1ap_log->console("Attach request -- IMSI: %015lu\n", emm_ctx->imsi); + m_s1ap_log->info("Attach request -- IMSI: %015lu\n", emm_ctx->imsi); + m_s1ap_log->console("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + m_s1ap_log->info("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + m_s1ap_log->console("Attach request -- Attach type: %d\n", attach_req.eps_attach_type); + m_s1ap_log->info("Attach request -- Attach type: %d\n", attach_req.eps_attach_type); + + m_s1ap_log->console("Attach Request -- UE Network Capabilities EEA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eea[0], + attach_req.ue_network_cap.eea[1], + attach_req.ue_network_cap.eea[2], + attach_req.ue_network_cap.eea[3], + attach_req.ue_network_cap.eea[4], + attach_req.ue_network_cap.eea[5], + attach_req.ue_network_cap.eea[6], + attach_req.ue_network_cap.eea[7]); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EIA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eia[0], + attach_req.ue_network_cap.eia[1], + attach_req.ue_network_cap.eia[2], + attach_req.ue_network_cap.eia[3], + attach_req.ue_network_cap.eia[4], + attach_req.ue_network_cap.eia[5], + attach_req.ue_network_cap.eia[6], + attach_req.ue_network_cap.eia[7]); + m_s1ap_log->console("Attach Request -- MS Network Capabilities Present: %s\n", attach_req.ms_network_cap_present ? "true" : "false"); + m_s1ap_log->console("PDN Connectivity Request -- EPS Bearer Identity requested: %d\n", pdn_con_req.eps_bearer_id); + m_s1ap_log->console("PDN Connectivity Request -- Procedure Transaction Id: %d\n", pdn_con_req.proc_transaction_id); + m_s1ap_log->console("PDN Connectivity Request -- ESM Information Transfer requested: %s\n", pdn_con_req.esm_info_transfer_flag_present ? "true" : "false"); + + //Save attach request type + emm_ctx->attach_type = attach_req.eps_attach_type; + + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(emm_ctx->imsi, emm_ctx->security_ctxt.k_asme, autn, rand, emm_ctx->security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n",emm_ctx->imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n",emm_ctx->imsi); + return false; + } + //Allocate eKSI for this authentication vector + //Here we assume a new security context thus a new eKSI + emm_ctx->security_ctxt.eksi=0; + + //Save the UE context + ue_ctx_t *new_ctx = new ue_ctx_t; + memcpy(new_ctx,&ue_ctx,sizeof(ue_ctx_t)); + m_s1ap->add_ue_ctx_to_imsi_map(new_ctx); + m_s1ap->add_ue_ctx_to_mme_ue_s1ap_id_map(new_ctx); + m_s1ap->add_ue_to_enb_set(enb_sri->sinfo_assoc_id,ecm_ctx->mme_ue_s1ap_id); + + //Pack NAS Authentication Request in Downlink NAS Transport msg + pack_authentication_request(reply_buffer, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id, emm_ctx->security_ctxt.eksi, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sending Authentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sending Authentication Request\n"); + return true; +} + +bool +s1ap_nas_transport::handle_nas_guti_attach_request( uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + //Parse the message security header + uint8 pd = 0; + uint8 sec_hdr_type = 0; + liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg, &pd, &sec_hdr_type); + + bool integrity_valid = false; + if(sec_hdr_type != LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY) + { + m_s1ap_log->info("Attach request -- GUTI-stlye attach request is not integrity protected\n"); + m_s1ap_log->console("Attach request -- GUTI-stlye attach request is not integrity protected\n"); + } + else{ + m_s1ap_log->info("Attach request -- GUTI-stlye attach request is integrity protected\n"); + m_s1ap_log->console("Attach request -- GUTI-stlye attach request is integrity protected\n"); + } + + + //GUTI style attach + uint32_t m_tmsi = attach_req.eps_mobile_id.guti.m_tmsi; + std::map::iterator it = m_s1ap->m_tmsi_to_imsi.find(m_tmsi); + if(it == m_s1ap->m_tmsi_to_imsi.end()) + { + + m_s1ap_log->console("Attach Request -- Could not find M-TMSI 0x%x\n", m_tmsi); + m_s1ap_log->info("Attach Request -- Could not find M-TMSI 0x%x\n", m_tmsi); + + //Could not find IMSI from M-TMSI, send Id request + ue_ctx_t ue_ctx; + ue_emm_ctx_t *emm_ctx = &ue_ctx.emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx.ecm_ctx; + + //We do not know the IMSI of the UE yet + //The IMSI will be set when the identity response is received + //Set EMM ctx + emm_ctx->imsi = 0; + emm_ctx->state = EMM_STATE_DEREGISTERED; + emm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + + //Save UE network capabilities + memcpy(&emm_ctx->security_ctxt.ue_network_cap, &attach_req.ue_network_cap, sizeof(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT)); + emm_ctx->security_ctxt.ms_network_cap_present = attach_req.ms_network_cap_present; + if(attach_req.ms_network_cap_present) + { + memcpy(&emm_ctx->security_ctxt.ms_network_cap, &attach_req.ms_network_cap, sizeof(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT)); + } + //Initialize NAS count + emm_ctx->security_ctxt.ul_nas_count = 0; + emm_ctx->security_ctxt.dl_nas_count = 0; + emm_ctx->procedure_transaction_id = pdn_con_req.proc_transaction_id; + + //Set ECM context + ecm_ctx->imsi = 0; + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + ecm_ctx->mme_ue_s1ap_id = emm_ctx->mme_ue_s1ap_id; + + uint8_t eps_bearer_id = pdn_con_req.eps_bearer_id; //TODO: Unused + + //Save attach request type + emm_ctx->attach_type = attach_req.eps_attach_type; + + //Save whether ESM information transfer is necessary + ecm_ctx->eit = pdn_con_req.esm_info_transfer_flag_present; + + //Add eNB info to UE ctxt + memcpy(&ecm_ctx->enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; + ecm_ctx->erabs_ctx[i].erab_id = i; + } + m_s1ap_log->console("Attach request -- IMSI: %015lu\n", ecm_ctx->imsi); + m_s1ap_log->info("Attach request -- IMSI: %015lu\n", ecm_ctx->imsi); + m_s1ap_log->console("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EEA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eea[0], attach_req.ue_network_cap.eea[1], attach_req.ue_network_cap.eea[2], attach_req.ue_network_cap.eea[3], + attach_req.ue_network_cap.eea[4], attach_req.ue_network_cap.eea[5], attach_req.ue_network_cap.eea[6], attach_req.ue_network_cap.eea[7]); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EIA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eia[0], attach_req.ue_network_cap.eia[1], attach_req.ue_network_cap.eia[2], attach_req.ue_network_cap.eia[3], + attach_req.ue_network_cap.eia[4], attach_req.ue_network_cap.eia[5], attach_req.ue_network_cap.eia[6], attach_req.ue_network_cap.eia[7]); + m_s1ap_log->console("Attach Request -- MS Network Capabilities Present: %s\n", attach_req.ms_network_cap_present ? "true" : "false"); + m_s1ap_log->console("PDN Connectivity Request -- EPS Bearer Identity requested: %d\n", pdn_con_req.eps_bearer_id); + m_s1ap_log->console("PDN Connectivity Request -- Procedure Transaction Id: %d\n", pdn_con_req.proc_transaction_id); + m_s1ap_log->console("PDN Connectivity Request -- ESM Information Transfer requested: %s\n", pdn_con_req.esm_info_transfer_flag_present ? "true" : "false"); + + m_s1ap_log->console("Could not find M-TMSI=0x%x. Sending ID request\n",m_tmsi); + m_s1ap_log->info("Could not find M-TMSI=0x%x. Sending Id Request\n", m_tmsi); + + //Store temporary ue context + ue_ctx_t *new_ctx = new ue_ctx_t; + memcpy(new_ctx,&ue_ctx,sizeof(ue_ctx_t)); + m_s1ap->add_ue_ctx_to_mme_ue_s1ap_id_map(new_ctx); + m_s1ap->add_ue_to_enb_set(enb_sri->sinfo_assoc_id,ecm_ctx->mme_ue_s1ap_id); + + pack_identity_request(reply_buffer, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + *reply_flag = true; + return true; + } + else{ + + m_s1ap_log->console("Attach Request -- Found M-TMSI: %d\n",m_tmsi); + m_s1ap_log->console("Attach Request -- IMSI: %015lu\n",it->second); + //Get UE EMM context + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(it->second); + if(ue_ctx!=NULL) + { + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + m_s1ap_log->console("Found UE context. IMSI: %015lu, old eNB UE S1ap Id %d, old MME UE S1AP Id %d\n",emm_ctx->imsi, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + //Check NAS integrity + bool msg_valid = false; + emm_ctx->security_ctxt.ul_nas_count++; + msg_valid = integrity_check(emm_ctx,nas_msg); + if(msg_valid == true && emm_ctx->state == EMM_STATE_DEREGISTERED) + { + m_s1ap_log->console("GUTI Attach Integrity valid. UL count %d, DL count %d\n",emm_ctx->security_ctxt.ul_nas_count, emm_ctx->security_ctxt.dl_nas_count); + + //Create new MME UE S1AP Identity + emm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + ecm_ctx->mme_ue_s1ap_id = emm_ctx->mme_ue_s1ap_id; + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + ecm_ctx->imsi = ecm_ctx->imsi; + + emm_ctx->procedure_transaction_id = pdn_con_req.proc_transaction_id; + //Save Attach type + emm_ctx->attach_type = attach_req.eps_attach_type; + + //Set eNB information + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + memcpy(&ecm_ctx->enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + //Save whether secure ESM information transfer is necessary + ecm_ctx->eit = pdn_con_req.esm_info_transfer_flag_present; + + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; + ecm_ctx->erabs_ctx[i].erab_id = i; + } + + //Store context based on MME UE S1AP id + m_s1ap->add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx); + m_s1ap->add_ue_to_enb_set(enb_sri->sinfo_assoc_id,ecm_ctx->mme_ue_s1ap_id); + + //Re-generate K_eNB + srslte::security_generate_k_enb(emm_ctx->security_ctxt.k_asme, emm_ctx->security_ctxt.ul_nas_count, emm_ctx->security_ctxt.k_enb); + m_s1ap_log->info("Generating KeNB with UL NAS COUNT: %d\n",emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->console("Generating KeNB with UL NAS COUNT: %d\n",emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->info_hex(emm_ctx->security_ctxt.k_enb, 32, "Key eNodeB (k_enb)\n"); + + m_s1ap_log->console("Attach request -- IMSI: %015lu\n", ecm_ctx->imsi); + m_s1ap_log->info("Attach request -- IMSI: %015lu\n", ecm_ctx->imsi); + m_s1ap_log->console("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EEA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eea[0], attach_req.ue_network_cap.eea[1], attach_req.ue_network_cap.eea[2], attach_req.ue_network_cap.eea[3], + attach_req.ue_network_cap.eea[4], attach_req.ue_network_cap.eea[5], attach_req.ue_network_cap.eea[6], attach_req.ue_network_cap.eea[7]); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EIA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eia[0], attach_req.ue_network_cap.eia[1], attach_req.ue_network_cap.eia[2], attach_req.ue_network_cap.eia[3], + attach_req.ue_network_cap.eia[4], attach_req.ue_network_cap.eia[5], attach_req.ue_network_cap.eia[6], attach_req.ue_network_cap.eia[7]); + m_s1ap_log->console("Attach Request -- MS Network Capabilities Present: %s\n", attach_req.ms_network_cap_present ? "true" : "false"); + m_s1ap_log->console("PDN Connectivity Request -- EPS Bearer Identity requested: %d\n", pdn_con_req.eps_bearer_id); + m_s1ap_log->console("PDN Connectivity Request -- Procedure Transaction Id: %d\n", pdn_con_req.proc_transaction_id); + m_s1ap_log->console("PDN Connectivity Request -- ESM Information Transfer requested: %s\n", pdn_con_req.esm_info_transfer_flag_present ? "true" : "false"); + + //Create session request + m_s1ap_log->console("GUTI Attach -- NAS Integrity OK.\n"); + if(ecm_ctx->eit) + { + m_s1ap_log->console("Secure ESM information transfer requested.\n"); + m_s1ap_log->info("Secure ESM information transfer requested.\n"); + pack_esm_information_request(reply_buffer, emm_ctx, ecm_ctx); + *reply_flag = true; + } + else + { + //Get subscriber info from HSS + uint8_t default_bearer=5; + m_hss->gen_update_loc_answer(emm_ctx->imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_mme_gtpc->send_create_session_request(emm_ctx->imsi); + *reply_flag = false; //No reply needed + } + return true; + } + else + { + if(emm_ctx->state != EMM_STATE_DEREGISTERED) + { + m_s1ap_log->error("Received GUTI-Attach Request from attached user.\n"); + m_s1ap_log->console("Received GUTI-Attach Request from attached user.\n"); + + //Delete previous Ctx, restart authentication + //Detaching previoulsy attached UE. + m_mme_gtpc->send_delete_session_request(emm_ctx->imsi); + if(ecm_ctx->mme_ue_s1ap_id!=0) + { + m_s1ap->m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(ecm_ctx, reply_buffer); + } + } + emm_ctx->security_ctxt.ul_nas_count = 0; + emm_ctx->security_ctxt.dl_nas_count = 0; + + //Create new MME UE S1AP Identity + uint32_t new_mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + + //Make sure context from previous NAS connections is not present + if(ecm_ctx->mme_ue_s1ap_id!=0) + { + m_s1ap->release_ue_ecm_ctx(ecm_ctx->mme_ue_s1ap_id); + } + emm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + ecm_ctx->mme_ue_s1ap_id = emm_ctx->mme_ue_s1ap_id; + //Set EMM as de-registered + emm_ctx->state = EMM_STATE_DEREGISTERED; + //Save Attach type + emm_ctx->attach_type = attach_req.eps_attach_type; + + //Set UE ECM context + ecm_ctx->imsi = ecm_ctx->imsi; + ecm_ctx->mme_ue_s1ap_id = ecm_ctx->mme_ue_s1ap_id; + + //Set eNB information + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + memcpy(&ecm_ctx->enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + //Save whether secure ESM information transfer is necessary + ecm_ctx->eit = pdn_con_req.esm_info_transfer_flag_present; + + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; + ecm_ctx->erabs_ctx[i].erab_id = i; + } + //Store context based on MME UE S1AP id + m_s1ap->add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx); + m_s1ap->add_ue_to_enb_set(enb_sri->sinfo_assoc_id,ecm_ctx->mme_ue_s1ap_id); + + //NAS integrity failed. Re-start authentication process. + m_s1ap_log->console("GUTI Attach request NAS integrity failed.\n"); + m_s1ap_log->console("RE-starting authentication procedure.\n"); + uint8_t autn[16]; + uint8_t rand[16]; + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(emm_ctx->imsi, emm_ctx->security_ctxt.k_asme, autn, rand, emm_ctx->security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n",emm_ctx->imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n",emm_ctx->imsi); + return false; + } + //Restarting security context. Reseting eKSI to 0. + emm_ctx->security_ctxt.eksi=0; + pack_authentication_request(reply_buffer, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id, emm_ctx->security_ctxt.eksi, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sent Authentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sent Authentication Request\n"); + return true; + } + } + else + { + m_s1ap_log->error("Found M-TMSI but could not find UE context\n"); + m_s1ap_log->console("Error: Found M-TMSI but could not find UE context\n"); + return false; + } + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_service_request(uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + + bool mac_valid = false; + LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT service_req; + + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_service_request_msg((LIBLTE_BYTE_MSG_STRUCT*) nas_msg, &service_req); + if(err !=LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not unpack service request\n"); + return false; + } + + std::map::iterator it = m_s1ap->m_tmsi_to_imsi.find(m_tmsi); + if(it == m_s1ap->m_tmsi_to_imsi.end()) + { + m_s1ap_log->console("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + m_s1ap_log->error("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + pack_service_reject(reply_buffer, LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED, enb_ue_s1ap_id); + *reply_flag = true; + return true; + } + + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(it->second); + if(ue_ctx == NULL || ue_ctx->emm_ctx.state != EMM_STATE_REGISTERED) + { + m_s1ap_log->console("UE is not EMM-Registered.\n"); + m_s1ap_log->error("UE is not EMM-Registered.\n"); + pack_service_reject(reply_buffer, LIBLTE_MME_EMM_CAUSE_IMPLICITLY_DETACHED, enb_ue_s1ap_id); + *reply_flag = true; + return true; + } + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + emm_ctx->security_ctxt.ul_nas_count++; + mac_valid = short_integrity_check(emm_ctx,nas_msg); + if(mac_valid) + { + m_s1ap_log->console("Service Request -- Short MAC valid\n"); + m_s1ap_log->info("Service Request -- Short MAC valid\n"); + if(ecm_ctx->state == ECM_STATE_CONNECTED) + { + m_s1ap_log->error("Service Request -- User is ECM CONNECTED\n"); + + //Release previous context + m_s1ap_log->info("Service Request -- Releasing previouse ECM context. eNB S1AP Id %d, MME UE S1AP Id %d\n", ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + m_s1ap->m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(ecm_ctx,reply_buffer); + m_s1ap->release_ue_ecm_ctx(ecm_ctx->mme_ue_s1ap_id); + } + + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + + //UE not connect. Connect normally. + m_s1ap_log->console("Service Request -- User is ECM DISCONNECTED\n"); + m_s1ap_log->info("Service Request -- User is ECM DISCONNECTED\n"); + //Create ECM context + ecm_ctx->imsi = emm_ctx->imsi; + ecm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + emm_ctx->mme_ue_s1ap_id = ecm_ctx->mme_ue_s1ap_id; + //Set eNB information + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + memcpy(&ecm_ctx->enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + + //Save whether secure ESM information transfer is necessary + ecm_ctx->eit = false; + + //Get UE IP, and uplink F-TEID + if(emm_ctx->ue_ip.s_addr == 0 ) + { + m_s1ap_log->error("UE has no valid IP assigned upon reception of service request"); + } + + m_s1ap_log->console("UE previously assigned IP: %s",inet_ntoa(emm_ctx->ue_ip)); + + //Re-generate K_eNB + srslte::security_generate_k_enb(emm_ctx->security_ctxt.k_asme, emm_ctx->security_ctxt.ul_nas_count, emm_ctx->security_ctxt.k_enb); + m_s1ap_log->info("Generating KeNB with UL NAS COUNT: %d\n",emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->console("Generating KeNB with UL NAS COUNT: %d\n",emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->info_hex(emm_ctx->security_ctxt.k_enb, 32, "Key eNodeB (k_enb)\n"); + m_s1ap_log->console("UE Ctr TEID %d\n", emm_ctx->sgw_ctrl_fteid.teid); + + //Save UE ctx to MME UE S1AP id + m_s1ap->add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx); + m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(emm_ctx, ecm_ctx,&ecm_ctx->erabs_ctx[5]); + } + else + { + m_s1ap_log->console("Service Request -- Short MAC invalid. Ignoring service request\n"); + m_s1ap_log->console("Service Request -- Short MAC invalid. Ignoring service request\n"); + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_detach_request(uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + bool mac_valid = false; + LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT detach_req; + + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_detach_request_msg((LIBLTE_BYTE_MSG_STRUCT*) nas_msg, &detach_req); + if(err !=LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not unpack detach request\n"); + return false; + } + + std::map::iterator it = m_s1ap->m_tmsi_to_imsi.find(m_tmsi); + if(it == m_s1ap->m_tmsi_to_imsi.end()) + { + m_s1ap_log->console("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + m_s1ap_log->error("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + return true; + } + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(it->second); + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + m_mme_gtpc->send_delete_session_request(emm_ctx->imsi); + emm_ctx->state = EMM_STATE_DEREGISTERED; + emm_ctx->security_ctxt.ul_nas_count++; + + m_s1ap_log->console("Received. M-TMSI 0x%x\n", m_tmsi); + //Received detach request as an initial UE message + //eNB created new ECM context to send the detach request; this needs to be cleared. + ecm_ctx->mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + ecm_ctx->enb_ue_s1ap_id = enb_ue_s1ap_id; + m_s1ap->m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(ecm_ctx, reply_buffer); + return true; +} + +//FIXME re-factor to reduce code duplication +bool +s1ap_nas_transport::handle_nas_detach_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + + m_s1ap_log->console("Detach request -- IMSI %015lu\n", ue_ctx->emm_ctx.imsi); + m_s1ap_log->info("Detach request -- IMSI %015lu\n", ue_ctx->emm_ctx.imsi); + LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT detach_req; + + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_detach_request_msg((LIBLTE_BYTE_MSG_STRUCT*) nas_msg, &detach_req); + if(err !=LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not unpack detach request\n"); + return false; + } + + m_mme_gtpc->send_delete_session_request(ue_ctx->emm_ctx.imsi); + ue_ctx->emm_ctx.state = EMM_STATE_DEREGISTERED; + if(ue_ctx->ecm_ctx.mme_ue_s1ap_id!=0) + { + m_s1ap->m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(&ue_ctx->ecm_ctx, reply_msg); + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_tracking_area_update_request(uint32_t m_tmsi, + uint32_t enb_ue_s1ap_id, + srslte::byte_buffer_t *nas_msg, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + m_s1ap_log->console("Warning: Tracking area update requests are not handled yet.\n"); + m_s1ap_log->warning("Tracking area update requests are not handled yet.\n"); + + std::map::iterator it = m_s1ap->m_tmsi_to_imsi.find(m_tmsi); + if(it == m_s1ap->m_tmsi_to_imsi.end()) + { + m_s1ap_log->console("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + m_s1ap_log->error("Could not find IMSI from M-TMSI. M-TMSI 0x%x\n", m_tmsi); + return true; + } + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(it->second); + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + emm_ctx->security_ctxt.ul_nas_count++;//Increment the NAS count, not to break the security ctx + return true; +} +bool +s1ap_nas_transport::handle_nas_authentication_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool* reply_flag) +{ + + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp; + bool ue_valid=true; + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + m_s1ap_log->console("Authentication Response -- IMSI %015lu\n", emm_ctx->imsi); + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &auth_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + m_s1ap_log->console("Authentication Response -- RES 0x%x%x%x%x%x%x%x%x\n", + auth_resp.res[0], auth_resp.res[1], auth_resp.res[2], auth_resp.res[3], + auth_resp.res[4], auth_resp.res[5], auth_resp.res[6], auth_resp.res[7]); + m_s1ap_log->info("Authentication Response -- RES 0x%x%x%x%x%x%x%x%x\n", + auth_resp.res[0], auth_resp.res[1], auth_resp.res[2], auth_resp.res[3], + auth_resp.res[4], auth_resp.res[5], auth_resp.res[6], auth_resp.res[7]); + + for(int i=0; i<8;i++) + { + if(auth_resp.res[i] != emm_ctx->security_ctxt.xres[i]) + { + ue_valid = false; + } + } + if(!ue_valid) + { + m_s1ap_log->info_hex(emm_ctx->security_ctxt.xres,8, "XRES"); + m_s1ap_log->console("UE Authentication Rejected.\n"); + m_s1ap_log->warning("UE Authentication Rejected.\n"); + + //Send back Athentication Reject + pack_authentication_reject(reply_buffer, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id); + *reply_flag = true; + m_s1ap_log->console("Downlink NAS: Sending Authentication Reject.\n"); + return false; + } + else + { + m_s1ap_log->console("UE Authentication Accepted.\n"); + m_s1ap_log->info("UE Authentication Accepted.\n"); + + //Send Security Mode Command + emm_ctx->security_ctxt.ul_nas_count = 0; // Reset the NAS uplink counter for the right key k_enb derivation + pack_security_mode_command(reply_buffer, emm_ctx, ecm_ctx); + *reply_flag = true; + m_s1ap_log->console("Downlink NAS: Sending NAS Security Mode Command.\n"); + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_security_mode_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sm_comp; + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_security_mode_complete_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &sm_comp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + //TODO Handle imeisv + if(sm_comp.imeisv_present) + { + m_s1ap_log->warning("IMEI-SV present but not handled"); + } + + m_s1ap_log->info("Security Mode Command Complete -- IMSI: %lu\n", emm_ctx->imsi); + m_s1ap_log->console("Security Mode Command Complete -- IMSI: %lu\n", emm_ctx->imsi); + if(ecm_ctx->eit == true) + { + pack_esm_information_request(reply_buffer, emm_ctx, ecm_ctx); + m_s1ap_log->console("Sending ESM information request\n"); + m_s1ap_log->info_hex(reply_buffer->msg, reply_buffer->N_bytes, "Sending ESM information request\n"); + *reply_flag = true; + } + else + { + //Get subscriber info from HSS + uint8_t default_bearer=5; + m_hss->gen_update_loc_answer(emm_ctx->imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + //FIXME The packging of GTP-C messages is not ready. + //This means that GTP-U tunnels are created with function calls, as opposed to GTP-C. + m_mme_gtpc->send_create_session_request(emm_ctx->imsi); + *reply_flag = false; //No reply needed + } + return true; +} + + +bool +s1ap_nas_transport::handle_nas_attach_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + + LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT attach_comp; + uint8_t pd, msg_type; + srslte::byte_buffer_t *esm_msg = m_pool->allocate(); + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT act_bearer; + + m_s1ap_log->info_hex(nas_msg->msg, nas_msg->N_bytes, "NAS Attach complete"); + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_attach_complete_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &attach_comp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + err = liblte_mme_unpack_activate_default_eps_bearer_context_accept_msg( (LIBLTE_BYTE_MSG_STRUCT *) &attach_comp.esm_msg, &act_bearer); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking Activate EPS Bearer Context Accept Msg. Error: %s\n", liblte_error_text[err]); + return false; + } + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + m_s1ap_log->console("Unpacked Attached Complete Message. IMSI %" PRIu64 "\n", emm_ctx->imsi); + m_s1ap_log->console("Unpacked Activate Default EPS Bearer message. EPS Bearer id %d\n",act_bearer.eps_bearer_id); + //ue_ctx->erabs_ctx[act_bearer->eps_bearer_id].enb_fteid; + if(act_bearer.eps_bearer_id < 5 || act_bearer.eps_bearer_id > 15) + { + m_s1ap_log->error("EPS Bearer ID out of range\n"); + return false; + } + if(emm_ctx->state == EMM_STATE_DEREGISTERED) + { + //Attach requested from attach request + m_mme_gtpc->send_modify_bearer_request(emm_ctx->imsi, &ecm_ctx->erabs_ctx[act_bearer.eps_bearer_id]); + //Send reply to eNB + m_s1ap_log->console("Packing EMM Information\n"); + *reply_flag = pack_emm_information(ue_ctx, reply_msg); + m_s1ap_log->console("Sending EMM Information, bytes %d\n",reply_msg->N_bytes); + m_s1ap_log->info("Sending EMM Information\n"); + } + emm_ctx->state = EMM_STATE_REGISTERED; + return true; +} + +bool +s1ap_nas_transport::handle_esm_information_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT esm_info_resp; + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = srslte_mme_unpack_esm_information_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &esm_info_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + m_s1ap_log->info("ESM Info: EPS bearer id %d\n",esm_info_resp.eps_bearer_id); + if(esm_info_resp.apn_present) + { + m_s1ap_log->info("ESM Info: APN %s\n",esm_info_resp.apn.apn); + m_s1ap_log->console("ESM Info: APN %s\n",esm_info_resp.apn.apn); + } + if(esm_info_resp.protocol_cnfg_opts_present) + { + m_s1ap_log->info("ESM Info: %d Protocol Configuration Options\n",esm_info_resp.protocol_cnfg_opts.N_opts); + m_s1ap_log->console("ESM Info: %d Protocol Configuration Options\n",esm_info_resp.protocol_cnfg_opts.N_opts); + } + + //Get subscriber info from HSS + uint8_t default_bearer=5; + m_hss->gen_update_loc_answer(ue_ctx->emm_ctx.imsi,&ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->debug("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + m_s1ap_log->console("Getting subscription information -- QCI %d\n", ue_ctx->ecm_ctx.erabs_ctx[default_bearer].qci); + + //FIXME The packging of GTP-C messages is not ready. + //This means that GTP-U tunnels are created with function calls, as opposed to GTP-C. + m_mme_gtpc->send_create_session_request(ue_ctx->emm_ctx.imsi); + return true; +} + +bool +s1ap_nas_transport::handle_identity_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + uint8_t autn[16]; + uint8_t rand[16]; + uint8_t xres[8]; + + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT id_resp; + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_identity_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &id_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS identity response. Error: %s\n", liblte_error_text[err]); + return false; + } + + uint64_t imsi = 0; + for(int i=0;i<=14;i++){ + imsi += id_resp.mobile_id.imsi[i]*std::pow(10,14-i); + } + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + m_s1ap_log->info("ID response -- IMSI: %015lu\n", imsi); + m_s1ap_log->console("ID Response -- IMSI: %015lu\n", imsi); + + //Set UE's context IMSI + emm_ctx->imsi=imsi; + ecm_ctx->imsi = imsi; + + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(imsi, emm_ctx->security_ctxt.k_asme, autn, rand, emm_ctx->security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n",imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n",imsi); + return false; + } + //Identity reponse from unknown GUTI atach. Assigning new eKSI. + emm_ctx->security_ctxt.eksi=0; + + //Store UE context im IMSI map + m_s1ap->add_ue_ctx_to_imsi_map(ue_ctx); + + //Pack NAS Authentication Request in Downlink NAS Transport msg + pack_authentication_request(reply_msg, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id, emm_ctx->security_ctxt.eksi, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sent Authentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sent Authentication Request\n"); + //TODO Start T3460 Timer! + + return true; +} + + +bool +s1ap_nas_transport::handle_tracking_area_update_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + + m_s1ap_log->console("Warning: Tracking Area Update Request messages not handled yet.\n"); + m_s1ap_log->warning("Warning: Tracking Area Update Request messages not handled yet.\n"); + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->ecm_ctx.mme_ue_s1ap_id; + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->ecm_ctx.enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + //m_s1ap_log->console("Tracking area accept to MME-UE S1AP Id %d\n", ue_ctx->mme_ue_s1ap_id); + LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT tau_acc; + + //Get decimal MCC and MNC + uint32_t mcc = 0; + mcc += 0x000F & m_s1ap->m_s1ap_args.mcc; + mcc += 10*( (0x00F0 & m_s1ap->m_s1ap_args.mcc) >> 4); + mcc += 100*( (0x0F00 & m_s1ap->m_s1ap_args.mcc) >> 8); + + uint32_t mnc = 0; + if( 0xFF00 == (m_s1ap->m_s1ap_args.mnc & 0xFF00 )) + { + //Two digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + } + else + { + //Three digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + mnc += 100*((0x0F00 & m_s1ap->m_s1ap_args.mnc) >> 8); + } + + //T3412 Timer + tau_acc.t3412_present = true; + tau_acc.t3412.unit = LIBLTE_MME_GPRS_TIMER_UNIT_1_MINUTE; // GPRS 1 minute unit + tau_acc.t3412.value = 30; // 30 minute periodic timer + + //GUTI + tau_acc.guti_present=true; + tau_acc.guti.type_of_id = 6; //110 -> GUTI + tau_acc.guti.guti.mcc = mcc; + tau_acc.guti.guti.mnc = mnc; + tau_acc.guti.guti.mme_group_id = m_s1ap->m_s1ap_args.mme_group; + tau_acc.guti.guti.mme_code = m_s1ap->m_s1ap_args.mme_code; + tau_acc.guti.guti.m_tmsi = 0xF000; + m_s1ap_log->debug("Allocated GUTI: MCC %d, MNC %d, MME Group Id %d, MME Code 0x%x, M-TMSI 0x%x\n", + tau_acc.guti.guti.mcc, + tau_acc.guti.guti.mnc, + tau_acc.guti.guti.mme_group_id, + tau_acc.guti.guti.mme_code, + tau_acc.guti.guti.m_tmsi); + + //Unused Options + tau_acc.t3402_present = false; + tau_acc.t3423_present = false; + tau_acc.equivalent_plmns_present = false; + tau_acc.emerg_num_list_present = false; + tau_acc.eps_network_feature_support_present = false; + tau_acc.additional_update_result_present = false; + tau_acc.t3412_ext_present = false; + + return true; +} + +bool +s1ap_nas_transport::short_integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu) +{ + uint8_t exp_mac[4]; + uint8_t *mac = &pdu->msg[2]; + int i; + + srslte::security_128_eia1(&emm_ctx->security_ctxt.k_nas_int[16], + emm_ctx->security_ctxt.ul_nas_count, + 0, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[0], + 2, + &exp_mac[0]); + + // Check if expected mac equals the sent mac + for(i=0; i<2; i++){ + if(exp_mac[i+2] != mac[i]){ + m_s1ap_log->warning("Short integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " + "Received: count=%d, [%02x %02x]\n", + emm_ctx->security_ctxt.ul_nas_count, exp_mac[0], exp_mac[1], exp_mac[2], exp_mac[3], + pdu->msg[1] & 0x1F, mac[0], mac[1]); + return false; + } + } + m_s1ap_log->info("Integrity check ok. Local: count=%d, Received: count=%d\n", + emm_ctx->security_ctxt.ul_nas_count, pdu->msg[1] & 0x1F); + return true; +} + + +bool +s1ap_nas_transport::integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu) +{ + uint8_t exp_mac[4]; + uint8_t *mac = &pdu->msg[1]; + int i; + + srslte::security_128_eia1(&emm_ctx->security_ctxt.k_nas_int[16], + emm_ctx->security_ctxt.ul_nas_count, + 0, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes-5, + &exp_mac[0]); + + // Check if expected mac equals the sent mac + for(i=0; i<4; i++){ + if(exp_mac[i] != mac[i]){ + m_s1ap_log->warning("Integrity check failure. UL Local: count=%d, [%02x %02x %02x %02x], " + "Received: UL count=%d, [%02x %02x %02x %02x]\n", + emm_ctx->security_ctxt.ul_nas_count, exp_mac[0], exp_mac[1], exp_mac[2], exp_mac[3], + pdu->msg[5], mac[0], mac[1], mac[2], mac[3]); + return false; + } + } + m_s1ap_log->info("Integrity check ok. Local: count=%d, Received: count=%d\n", + emm_ctx->security_ctxt.ul_nas_count, pdu->msg[5]); + return true; +} + + +bool +s1ap_nas_transport::handle_authentication_failure(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + uint8_t autn[16]; + uint8_t rand[16]; + uint8_t xres[8]; + + LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT auth_fail; + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_failure_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &auth_fail); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication failure. Error: %s\n", liblte_error_text[err]); + return false; + } + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + + switch(auth_fail.emm_cause){ + case 20: + m_s1ap_log->console("MAC code failure\n"); + m_s1ap_log->info("MAC code failure\n"); + break; + case 26: + m_s1ap_log->console("Non-EPS authentication unacceptable\n"); + m_s1ap_log->info("Non-EPS authentication unacceptable\n"); + break; + case 21: + m_s1ap_log->console("Authentication Failure -- Synchronization Failure\n"); + m_s1ap_log->info("Authentication Failure -- Synchronization Failure\n"); + if(auth_fail.auth_fail_param_present == false){ + m_s1ap_log->error("Missing fail parameter\n"); + return false; + } + if(!m_hss->resync_sqn(emm_ctx->imsi, auth_fail.auth_fail_param)) + { + m_s1ap_log->console("Resynchronization failed. IMSI %015lu\n", emm_ctx->imsi); + m_s1ap_log->info("Resynchronization failed. IMSI %015lu\n", emm_ctx->imsi); + return false; + } + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(emm_ctx->imsi, emm_ctx->security_ctxt.k_asme, autn, rand, emm_ctx->security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n", emm_ctx->imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n", emm_ctx->imsi); + return false; + } + //Making sure eKSI is different from previous eKSI. + emm_ctx->security_ctxt.eksi = (emm_ctx->security_ctxt.eksi+1)%6; + + //Pack NAS Authentication Request in Downlink NAS Transport msg + pack_authentication_request(reply_msg, ecm_ctx->enb_ue_s1ap_id, ecm_ctx->mme_ue_s1ap_id, emm_ctx->security_ctxt.eksi, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sent Authentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sent Authentication Request\n"); + //TODO Start T3460 Timer! + + break; + } + return true; +} + +/*Packing/Unpacking helper functions*/ +bool +s1ap_nas_transport::pack_authentication_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t next_mme_ue_s1ap_id, uint8_t eksi, uint8_t *autn, uint8_t *rand) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = next_mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + //Pack NAS PDU + LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req; + memcpy(auth_req.autn , autn, 16); + memcpy(auth_req.rand, rand, 16); + auth_req.nas_ksi.tsc_flag=LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + auth_req.nas_ksi.nas_ksi = eksi; + + LIBLTE_ERROR_ENUM err = liblte_mme_pack_authentication_request_msg(&auth_req, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Authentication Request\n"); + m_s1ap_log->console("Error packing Authentication Request\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Authentication Request\n"); + m_s1ap_log->console("Error packing Authentication Request\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + + return true; +} + +bool +s1ap_nas_transport::pack_authentication_reject(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT auth_rej; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_authentication_reject_msg(&auth_rej, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Authentication Reject\n"); + m_s1ap_log->console("Error packing Authentication Reject\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Authentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Authentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::unpack_authentication_response(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp ) +{ + + /*Get NAS Authentiation Response Message*/ + uint8_t pd, msg_type; + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + + memcpy(nas_msg->msg, &ul_xport->NAS_PDU.buffer, ul_xport->NAS_PDU.n_octets); + nas_msg->N_bytes = ul_xport->NAS_PDU.n_octets; + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + + if(msg_type!=LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE){ + m_s1ap_log->error("Error unpacking NAS authentication response\n"); + return false; + } + + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, auth_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + m_pool->deallocate(nas_msg); + return true; +} + +bool +s1ap_nas_transport::pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ecm_ctx->mme_ue_s1ap_id; + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ecm_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + //Pack NAS PDU + LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sm_cmd; + + sm_cmd.selected_nas_sec_algs.type_of_eea = LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA0; + sm_cmd.selected_nas_sec_algs.type_of_eia = LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_128_EIA1; + + sm_cmd.nas_ksi.tsc_flag=LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + sm_cmd.nas_ksi.nas_ksi=ue_emm_ctx->security_ctxt.eksi; + + //Replay UE security cap + memcpy(sm_cmd.ue_security_cap.eea,ue_emm_ctx->security_ctxt.ue_network_cap.eea,8*sizeof(bool)); + memcpy(sm_cmd.ue_security_cap.eia,ue_emm_ctx->security_ctxt.ue_network_cap.eia,8*sizeof(bool)); + sm_cmd.ue_security_cap.uea_present = ue_emm_ctx->security_ctxt.ue_network_cap.uea_present; + memcpy(sm_cmd.ue_security_cap.uea,ue_emm_ctx->security_ctxt.ue_network_cap.uea,8*sizeof(bool)); + sm_cmd.ue_security_cap.uia_present = ue_emm_ctx->security_ctxt.ue_network_cap.uia_present; + memcpy(sm_cmd.ue_security_cap.uia,ue_emm_ctx->security_ctxt.ue_network_cap.uia,8*sizeof(bool)); + sm_cmd.ue_security_cap.gea_present = ue_emm_ctx->security_ctxt.ms_network_cap_present; + memcpy(sm_cmd.ue_security_cap.gea,ue_emm_ctx->security_ctxt.ms_network_cap.gea,8*sizeof(bool)); + + sm_cmd.imeisv_req_present=false; + sm_cmd.nonce_ue_present=false; + sm_cmd.nonce_mme_present=false; + + uint8_t sec_hdr_type=3; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_security_mode_command_msg(&sm_cmd,sec_hdr_type, ue_emm_ctx->security_ctxt.dl_nas_count,(LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->console("Error packing Authentication Request\n"); + return false; + } + + //Generate EPS security context + uint8_t mac[4]; + srslte::security_generate_k_nas( ue_emm_ctx->security_ctxt.k_asme, + srslte::CIPHERING_ALGORITHM_ID_EEA0, + srslte::INTEGRITY_ALGORITHM_ID_128_EIA1, + ue_emm_ctx->security_ctxt.k_nas_enc, + ue_emm_ctx->security_ctxt.k_nas_int + ); + + m_s1ap_log->info_hex(ue_emm_ctx->security_ctxt.k_nas_enc, 32, "Key NAS Encryption (k_nas_enc)\n"); + m_s1ap_log->info_hex(ue_emm_ctx->security_ctxt.k_nas_int, 32, "Key NAS Integrity (k_nas_int)\n"); + + uint8_t key_enb[32]; + srslte::security_generate_k_enb(ue_emm_ctx->security_ctxt.k_asme, ue_emm_ctx->security_ctxt.ul_nas_count, ue_emm_ctx->security_ctxt.k_enb); + m_s1ap_log->info("Generating KeNB with UL NAS COUNT: %d\n", ue_emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->console("Generating KeNB with UL NAS COUNT: %d\n", ue_emm_ctx->security_ctxt.ul_nas_count); + m_s1ap_log->info_hex(ue_emm_ctx->security_ctxt.k_enb, 32, "Key eNodeB (k_enb)\n"); + //Generate MAC for integrity protection + //FIXME Write wrapper to support EIA1, EIA2, etc. + srslte::security_128_eia1 (&ue_emm_ctx->security_ctxt.k_nas_int[16], + ue_emm_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->console("Error packing Authentication Request\n"); + return false; + } + m_s1ap_log->debug_hex(reply_msg->msg, reply_msg->N_bytes, "Security Mode Command: "); + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ecm_ctx->mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ecm_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT esm_info_req; + esm_info_req.eps_bearer_id = 0; + esm_info_req.proc_transaction_id = ue_emm_ctx->procedure_transaction_id; + + uint8_t sec_hdr_type = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED; + + ue_emm_ctx->security_ctxt.dl_nas_count++; + LIBLTE_ERROR_ENUM err = srslte_mme_pack_esm_information_request_msg(&esm_info_req, sec_hdr_type,ue_emm_ctx->security_ctxt.dl_nas_count,(LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing ESM information request\n"); + m_s1ap_log->console("Error packing ESM information request\n"); + return false; + } + + uint8_t mac[4]; + srslte::security_128_eia1 (&ue_emm_ctx->security_ctxt.k_nas_int[16], + ue_emm_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Authentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Authentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_attach_accept(ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer) { + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept; + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req; + //bzero(&act_def_eps_bearer_context_req,sizeof(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT)); + + m_s1ap_log->info("Packing Attach Accept\n"); + + //Get decimal MCC and MNC + uint32_t mcc = 0; + mcc += 0x000F & m_s1ap->m_s1ap_args.mcc; + mcc += 10*( (0x00F0 & m_s1ap->m_s1ap_args.mcc) >> 4); + mcc += 100*( (0x0F00 & m_s1ap->m_s1ap_args.mcc) >> 8); + + uint32_t mnc = 0; + if( 0xFF00 == (m_s1ap->m_s1ap_args.mnc & 0xFF00 )) + { + //Two digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + } + else + { + //Three digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + mnc += 100*((0x0F00 & m_s1ap->m_s1ap_args.mnc) >> 8); + } + + //Attach accept + attach_accept.eps_attach_result = ue_emm_ctx->attach_type; + + //FIXME: Set t3412 from config + attach_accept.t3412.unit = LIBLTE_MME_GPRS_TIMER_UNIT_1_MINUTE; // GPRS 1 minute unit + attach_accept.t3412.value = 30; // 30 minute periodic timer + //FIXME: Set tai_list from config + attach_accept.tai_list.N_tais = 1; + attach_accept.tai_list.tai[0].mcc = mcc; + attach_accept.tai_list.tai[0].mnc = mnc; + attach_accept.tai_list.tai[0].tac = m_s1ap->m_s1ap_args.tac; + + //Allocate a GUTI ot the UE + attach_accept.guti_present=true; + attach_accept.guti.type_of_id = 6; //110 -> GUTI + attach_accept.guti.guti.mcc = mcc; + attach_accept.guti.guti.mnc = mnc; + attach_accept.guti.guti.mme_group_id = m_s1ap->m_s1ap_args.mme_group; + attach_accept.guti.guti.mme_code = m_s1ap->m_s1ap_args.mme_code; + attach_accept.guti.guti.m_tmsi = m_s1ap->allocate_m_tmsi(ue_emm_ctx->imsi); + m_s1ap_log->debug("Allocated GUTI: MCC %d, MNC %d, MME Group Id %d, MME Code 0x%x, M-TMSI 0x%x\n", + attach_accept.guti.guti.mcc, + attach_accept.guti.guti.mnc, + attach_accept.guti.guti.mme_group_id, + attach_accept.guti.guti.mme_code, + attach_accept.guti.guti.m_tmsi); + + //Set EMM cause to no CS available + attach_accept.emm_cause_present=false; + //attach_accept.emm_cause_present=true; + //attach_accept.emm_cause=18; + + //Set up LAI for combined EPS/IMSI attach + //attach_accept.lai_present=false; + attach_accept.lai_present=true; + attach_accept.lai.mcc = mcc; + attach_accept.lai.mnc = mnc; + attach_accept.lai.lac = 001; + + attach_accept.ms_id_present=true; + attach_accept.ms_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_TMSI; + attach_accept.ms_id.tmsi = attach_accept.guti.guti.m_tmsi; + + //Make sure all unused options are set to false + attach_accept.t3402_present=false; + attach_accept.t3423_present=false; + attach_accept.equivalent_plmns_present=false; + attach_accept.emerg_num_list_present=false; + attach_accept.eps_network_feature_support_present=false; + attach_accept.additional_update_result_present=false; + attach_accept.t3412_ext_present=false; + + //Set activate default eps bearer (esm_ms) + //Set pdn_addr + act_def_eps_bearer_context_req.pdn_addr.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4; + memcpy(act_def_eps_bearer_context_req.pdn_addr.addr, &paa->ipv4, 4); + //Set eps bearer id + act_def_eps_bearer_context_req.eps_bearer_id = erab_ctxt->e_RAB_ID.E_RAB_ID; + act_def_eps_bearer_context_req.transaction_id_present = false; + //set eps_qos + act_def_eps_bearer_context_req.eps_qos.qci = erab_ctxt->e_RABlevelQoSParameters.qCI.QCI; + act_def_eps_bearer_context_req.eps_qos.mbr_ul = 254; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_dl = 254; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_ul_ext = 250; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_dl_ext = 250; //FIXME check + + //set apn + strncpy(act_def_eps_bearer_context_req.apn.apn, m_s1ap->m_s1ap_args.mme_apn.c_str(), LIBLTE_STRING_LEN); + act_def_eps_bearer_context_req.proc_transaction_id = ue_emm_ctx->procedure_transaction_id; //FIXME + + //Set DNS server + act_def_eps_bearer_context_req.protocol_cnfg_opts_present = true; + act_def_eps_bearer_context_req.protocol_cnfg_opts.N_opts = 1; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].id = 0x0d; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].len = 4; + + struct sockaddr_in dns_addr; + inet_pton(AF_INET, m_s1ap->m_s1ap_args.dns_addr.c_str(), &(dns_addr.sin_addr)); + memcpy(act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents,&dns_addr.sin_addr.s_addr, 4); + + //Make sure all unused options are set to false + act_def_eps_bearer_context_req.negotiated_qos_present = false; + act_def_eps_bearer_context_req.llc_sapi_present = false; + act_def_eps_bearer_context_req.radio_prio_present = false; + act_def_eps_bearer_context_req.packet_flow_id_present = false; + act_def_eps_bearer_context_req.apn_ambr_present = false; + act_def_eps_bearer_context_req.esm_cause_present = false; + act_def_eps_bearer_context_req.connectivity_type_present = false; + + uint8_t sec_hdr_type =2; + ue_emm_ctx->security_ctxt.dl_nas_count++; + liblte_mme_pack_activate_default_eps_bearer_context_request_msg(&act_def_eps_bearer_context_req, &attach_accept.esm_msg); + liblte_mme_pack_attach_accept_msg(&attach_accept, sec_hdr_type, ue_emm_ctx->security_ctxt.dl_nas_count, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + //Integrity protect NAS message + uint8_t mac[4]; + srslte::security_128_eia1 (&ue_emm_ctx->security_ctxt.k_nas_int[16], + ue_emm_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + m_s1ap_log->info("Packed Attach Complete\n"); + + //Add nas message to context setup request + erab_ctxt->nAS_PDU_present = true; + memcpy(erab_ctxt->nAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + erab_ctxt->nAS_PDU.n_octets = nas_buffer->N_bytes; + + return true; +} + +bool +s1ap_nas_transport::pack_identity_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_s1ap_id; + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_ID_REQUEST_MSG_STRUCT id_req; + id_req.id_type = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_identity_request_msg(&id_req, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Identity Request\n"); + m_s1ap_log->console("Error packing Identity REquest\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Authentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Authentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_emm_information( ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_msg) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; + ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT emm_info; + emm_info.full_net_name_present = true; + strncpy(emm_info.full_net_name.name, "Software Radio Systems LTE", LIBLTE_STRING_LEN); + emm_info.full_net_name.add_ci = LIBLTE_MME_ADD_CI_DONT_ADD; + emm_info.short_net_name_present = true; + strncpy(emm_info.short_net_name.name, "srsLTE", LIBLTE_STRING_LEN); + emm_info.short_net_name.add_ci = LIBLTE_MME_ADD_CI_DONT_ADD; + + emm_info.local_time_zone_present = false; + emm_info.utc_and_local_time_zone_present = false; + emm_info.net_dst_present = false; + + uint8_t sec_hdr_type =2; + emm_ctx->security_ctxt.dl_nas_count++; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_emm_information_msg(&emm_info, sec_hdr_type, emm_ctx->security_ctxt.dl_nas_count, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing EMM Information\n"); + m_s1ap_log->console("Error packing EMM Information\n"); + return false; + } + + uint8_t mac[4]; + srslte::security_128_eia1 (&emm_ctx->security_ctxt.k_nas_int[16], + emm_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: EMM Info\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: EMM Info\n"); + return false; + } + + m_s1ap_log->info("Packed UE EMM information\n"); + return true; +} + +bool +s1ap_nas_transport::pack_service_reject(srslte::byte_buffer_t *reply_msg, uint8_t emm_cause, uint32_t enb_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = m_s1ap->get_next_mme_ue_s1ap_id(); + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT service_rej; + service_rej.t3442_present = true; + service_rej.t3442.unit = LIBLTE_MME_GPRS_TIMER_DEACTIVATED; + service_rej.t3442.value = 0; + service_rej.t3446_present = true; + service_rej.t3446 = 0; + service_rej.emm_cause = emm_cause; + + LIBLTE_ERROR_ENUM err = liblte_mme_pack_service_reject_msg(&service_rej, LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS, 0, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Service Reject\n"); + m_s1ap_log->console("Error packing Service Reject\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Service Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Service Reject\n"); + return false; + } + return true; +} +/*Helper functions*/ +void +s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req) +{ + if(attach_req->old_p_tmsi_signature_present) + { + m_s1ap_log->warning("NAS attach request: Old P-TMSI signature present, but not handled.\n"); + } + if(attach_req->additional_guti_present) + { + m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + } + if(attach_req->last_visited_registered_tai_present) + { + m_s1ap_log->warning("NAS attach request: Last visited registered TAI present, but not handled.\n"); + } + if(attach_req->drx_param_present) + { + m_s1ap_log->warning("NAS attach request: DRX Param present, but not handled.\n"); + } + if(attach_req->ms_network_cap_present) + { + m_s1ap_log->warning("NAS attach request: MS network cap present, but not handled.\n"); + } + if(attach_req->old_lai_present) + { + m_s1ap_log->warning("NAS attach request: Old LAI present, but not handled.\n"); + } + if(attach_req->tmsi_status_present) + { + m_s1ap_log->warning("NAS attach request: TSMI status present, but not handled.\n"); + } + if(attach_req->ms_cm2_present) + { + m_s1ap_log->warning("NAS attach request: MS CM2 present, but not handled.\n"); + } + if(attach_req->ms_cm3_present) + { + m_s1ap_log->warning("NAS attach request: MS CM3 present, but not handled.\n"); + } + if(attach_req->supported_codecs_present) + { + m_s1ap_log->warning("NAS attach request: Supported CODECs present, but not handled.\n"); + } + if(attach_req->additional_update_type_present) + { + m_s1ap_log->warning("NAS attach request: Additional Update Type present, but not handled.\n"); + } + if(attach_req->voice_domain_pref_and_ue_usage_setting_present) + { + m_s1ap_log->warning("NAS attach request: Voice domain preference and UE usage setting present, but not handled.\n"); + } + if(attach_req->device_properties_present) + { + m_s1ap_log->warning("NAS attach request: Device properties present, but not handled.\n"); + } + if(attach_req->old_guti_type_present) + { + m_s1ap_log->warning("NAS attach request: Old GUTI type present, but not handled.\n"); + } + return; +} + +void +s1ap_nas_transport::log_unhandled_pdn_con_request_ies(const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req) +{ + //Handle the optional flags + if(pdn_con_req->esm_info_transfer_flag_present) + { + m_s1ap_log->warning("PDN Connectivity request: ESM info transfer flag properties present, but not handled.\n"); + } + if(pdn_con_req->apn_present) + { + m_s1ap_log->warning("PDN Connectivity request: APN present, but not handled.\n"); + } + if(pdn_con_req->protocol_cnfg_opts_present) + { + m_s1ap_log->warning("PDN Connectivity request: Protocol Cnfg options present, but not handled.\n"); + } + if(pdn_con_req->device_properties_present) + { + m_s1ap_log->warning("PDN Connectivity request: Device properties present, but not handled.\n"); + } +} + + +void +s1ap_nas_transport::log_unhandled_initial_ue_message_ies(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue) +{ + if(init_ue->S_TMSI_present){ + m_s1ap_log->warning("S-TMSI present, but not handled.\n"); + } + if(init_ue->CSG_Id_present){ + m_s1ap_log->warning("S-TMSI present, but not handled.\n"); + } + if(init_ue->GUMMEI_ID_present){ + m_s1ap_log->warning("GUMMEI ID present, but not handled.\n"); + } + if(init_ue->CellAccessMode_present){ + m_s1ap_log->warning("Cell Access Mode present, but not handled.\n"); + } + if(init_ue->GW_TransportLayerAddress_present){ + m_s1ap_log->warning("GW Transport Layer present, but not handled.\n"); + } + if(init_ue->GW_TransportLayerAddress_present){ + m_s1ap_log->warning("GW Transport Layer present, but not handled.\n"); + } + if(init_ue->RelayNode_Indicator_present){ + m_s1ap_log->warning("Relay Node Indicator present, but not handled.\n"); + } + if(init_ue->GUMMEIType_present){ + m_s1ap_log->warning("GUMMEI Type present, but not handled.\n"); + } + if(init_ue->Tunnel_Information_for_BBF_present){ + m_s1ap_log->warning("Tunnel Information for BBF present, but not handled.\n"); + } + if(init_ue->SIPTO_L_GW_TransportLayerAddress_present){ + m_s1ap_log->warning("SIPTO GW Transport Layer Address present, but not handled.\n"); + } + if(init_ue->LHN_ID_present){ + m_s1ap_log->warning("LHN Id present, but not handled.\n"); + } + return; +} + + +} //namespace srsepc diff --git a/srsepc/src/spgw/CMakeLists.txt b/srsepc/src/spgw/CMakeLists.txt new file mode 100644 index 0000000..ba50e48 --- /dev/null +++ b/srsepc/src/spgw/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_sgw STATIC ${SOURCES}) +install(TARGETS srsepc_sgw DESTINATION ${LIBRARY_DIR}) diff --git a/srsepc/src/spgw/spgw.cc b/srsepc/src/spgw/spgw.cc new file mode 100644 index 0000000..20311cc --- /dev/null +++ b/srsepc/src/spgw/spgw.cc @@ -0,0 +1,666 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include // for printing uint64_t +#include "srsepc/hdr/spgw/spgw.h" +#include "srsepc/hdr/mme/mme_gtpc.h" +#include "srslte/upper/gtpu.h" + +namespace srsepc{ + +spgw* spgw::m_instance = NULL; +pthread_mutex_t spgw_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +const uint16_t SPGW_BUFFER_SIZE = 2500; + +spgw::spgw(): + m_running(false), + m_sgi_up(false), + m_s1u_up(false), + m_next_ctrl_teid(1), + m_next_user_teid(1) +{ + return; +} + +spgw::~spgw() +{ + return; +} + +spgw* +spgw::get_instance(void) +{ + pthread_mutex_lock(&spgw_instance_mutex); + if(NULL == m_instance) { + m_instance = new spgw(); + } + pthread_mutex_unlock(&spgw_instance_mutex); + return(m_instance); +} + +void +spgw::cleanup(void) +{ + pthread_mutex_lock(&spgw_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } + pthread_mutex_unlock(&spgw_instance_mutex); +} + +int +spgw::init(spgw_args_t* args, srslte::log_filter *spgw_log) +{ + srslte::error_t err; + m_pool = srslte::byte_buffer_pool::get_instance(); + + //Init log + m_spgw_log = spgw_log; + m_mme_gtpc = mme_gtpc::get_instance(); + + //Init SGi interface + err = init_sgi_if(args); + if (err != srslte::ERROR_NONE) + { + m_spgw_log->console("Could not initialize the SGi interface.\n"); + return -1; + } + + //Init S1-U + err = init_s1u(args); + if (err != srslte::ERROR_NONE) + { + m_spgw_log->console("Could not initialize the S1-U interface.\n"); + return -1; + } + //Initialize UE ip pool + err = init_ue_ip(args); + if (err != srslte::ERROR_NONE) + { + m_spgw_log->console("Could not initialize the S1-U interface.\n"); + return -1; + } + + //Init mutex + pthread_mutex_init(&m_mutex,NULL); + m_spgw_log->info("SP-GW Initialized.\n"); + m_spgw_log->console("SP-GW Initialized.\n"); + return 0; +} + +void +spgw::stop() +{ + if(m_running) + { + m_running = false; + thread_cancel(); + wait_thread_finish(); + + //Clean up SGi interface + if(m_sgi_up) + { + close(m_sgi_if); + close(m_sgi_sock); + } + //Clean up S1-U socket + if(m_s1u_up) + { + close(m_s1u); + } + } + std::map::iterator it = m_teid_to_tunnel_ctx.begin(); //Map control TEID to tunnel ctx. Usefull to get reply ctrl TEID, UE IP, etc. + while(it!=m_teid_to_tunnel_ctx.end()) + { + m_spgw_log->info("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); + m_spgw_log->console("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); + delete it->second; + m_teid_to_tunnel_ctx.erase(it++); + } + return; +} + +srslte::error_t +spgw::init_sgi_if(spgw_args_t *args) +{ + char dev[IFNAMSIZ] = "srs_spgw_sgi"; + struct ifreq ifr; + + if(m_sgi_up) + { + return(srslte::ERROR_ALREADY_STARTED); + } + + + // Construct the TUN device + m_sgi_if = open("/dev/net/tun", O_RDWR); + m_spgw_log->info("TUN file descriptor = %d\n", m_sgi_if); + if(m_sgi_if < 0) + { + m_spgw_log->error("Failed to open TUN device: %s\n", strerror(errno)); + return(srslte::ERROR_CANT_START); + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); + ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0'; + + if(ioctl(m_sgi_if, TUNSETIFF, &ifr) < 0) + { + m_spgw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); + close(m_sgi_if); + return(srslte::ERROR_CANT_START); + } + + // Bring up the interface + m_sgi_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if(ioctl(m_sgi_sock, SIOCGIFFLAGS, &ifr) < 0) + { + m_spgw_log->error("Failed to bring up socket: %s\n", strerror(errno)); + close(m_sgi_if); + return(srslte::ERROR_CANT_START); + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(ioctl(m_sgi_sock, SIOCSIFFLAGS, &ifr) < 0) + { + m_spgw_log->error("Failed to set socket flags: %s\n", strerror(errno)); + close(m_sgi_if); + return(srslte::ERROR_CANT_START); + } + + //Set IP of the interface + struct sockaddr_in *addr = (struct sockaddr_in*)&ifr.ifr_addr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str()); + addr->sin_port = 0; + + if (ioctl(m_sgi_sock, SIOCSIFADDR, &ifr) < 0) { + m_spgw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_if_addr.c_str(), strerror(errno)); + close(m_sgi_if); + close(m_sgi_sock); + return srslte::ERROR_CANT_START; + } + + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if (ioctl(m_sgi_sock, SIOCSIFNETMASK, &ifr) < 0) { + m_spgw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno)); + close(m_sgi_if); + close(m_sgi_sock); + return srslte::ERROR_CANT_START; + } + + //Set initial time of setup + gettimeofday(&m_t_last_dl, NULL); + + m_sgi_up = true; + return(srslte::ERROR_NONE); +} + +srslte::error_t +spgw::init_s1u(spgw_args_t *args) +{ + //Open S1-U socket + m_s1u = socket(AF_INET,SOCK_DGRAM,0); + if (m_s1u == -1) + { + m_spgw_log->error("Failed to open socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_s1u_up = true; + + //Bind the socket + m_s1u_addr.sin_family = AF_INET; + m_s1u_addr.sin_addr.s_addr=inet_addr(args->gtpu_bind_addr.c_str()); + m_s1u_addr.sin_port=htons(GTPU_RX_PORT); + + if (bind(m_s1u,(struct sockaddr *)&m_s1u_addr,sizeof(struct sockaddr_in))) { + m_spgw_log->error("Failed to bind socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_spgw_log->info("S1-U socket = %d\n", m_s1u); + m_spgw_log->info("S1-U IP = %s, Port = %d \n", inet_ntoa(m_s1u_addr.sin_addr),ntohs(m_s1u_addr.sin_port)); + + return srslte::ERROR_NONE; +} + +srslte::error_t +spgw::init_ue_ip(spgw_args_t *args) +{ + m_h_next_ue_ip = ntohl(inet_addr(args->sgi_if_addr.c_str())); + return srslte::ERROR_NONE; +} + +void +spgw::run_thread() +{ + //Mark the thread as running + m_running=true; + srslte::byte_buffer_t *msg; + msg = m_pool->allocate(); + + struct sockaddr src_addr; + socklen_t addrlen; + struct iphdr *ip_pkt; + int sgi = m_sgi_if; + + fd_set set; + //struct timeval to; + int max_fd = std::max(m_s1u,sgi); + while (m_running) + { + msg->reset(); + FD_ZERO(&set); + FD_SET(m_s1u, &set); + FD_SET(sgi, &set); + + //m_spgw_log->info("Waiting for S1-U or SGi packets.\n"); + int n = select(max_fd+1, &set, NULL, NULL, NULL); + if (n == -1) + { + m_spgw_log->error("Error from select\n"); + } + else if (n) + { + //m_spgw_log->info("Data is available now.\n"); + if (FD_ISSET(m_s1u, &set)) + { + msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen ); + handle_s1u_pdu(msg); + } + if (FD_ISSET(m_sgi_if, &set)) + { + msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); + handle_sgi_pdu(msg); + } + } + else + { + m_spgw_log->debug("No data from select.\n"); + } + } + m_pool->deallocate(msg); + return; +} + +void +spgw::handle_sgi_pdu(srslte::byte_buffer_t *msg) +{ + uint8_t version=0; + uint32_t dest_ip; + struct in_addr dest_addr; + std::map::iterator gtp_fteid_it; + bool ip_found = false; + srslte::gtpc_f_teid_ie enb_fteid; + + struct iphdr *iph = (struct iphdr *) msg->msg; + if(iph->version != 4) + { + m_spgw_log->warning("IPv6 not supported yet.\n"); + return; + } + if(iph->tot_len < 20) + { + m_spgw_log->warning("Invalid IP header length.\n"); + return; + } + + pthread_mutex_lock(&m_mutex); + gtp_fteid_it = m_ip_to_teid.find(iph->daddr); + if(gtp_fteid_it != m_ip_to_teid.end()) + { + ip_found = true; + enb_fteid = gtp_fteid_it->second; + } + pthread_mutex_unlock(&m_mutex); + + if(ip_found == false) + { + //m_spgw_log->console("IP Packet is not for any UE\n"); + return; + } + struct sockaddr_in enb_addr; + enb_addr.sin_family = AF_INET; + enb_addr.sin_port = htons(GTPU_RX_PORT); + enb_addr.sin_addr.s_addr = enb_fteid.ipv4; + //m_spgw_log->console("UE F-TEID found, TEID 0x%x, eNB IP %s\n", enb_fteid.teid, inet_ntoa(enb_addr.sin_addr)); + + //Setup GTP-U header + srslte::gtpu_header_t header; + header.flags = 0x30; + header.message_type = 0xFF; + header.length = msg->N_bytes; + header.teid = enb_fteid.teid; + + //Write header into packet + if(!srslte::gtpu_write_header(&header, msg, m_spgw_log)) + { + m_spgw_log->console("Error writing GTP-U header on PDU\n"); + } + + + //Send packet to destination + int n = sendto(m_s1u,msg->msg,msg->N_bytes,0,(struct sockaddr*) &enb_addr,sizeof(enb_addr)); + if(n<0) + { + m_spgw_log->error("Error sending packet to eNB\n"); + return; + } + else if((unsigned int) n!=msg->N_bytes) + { + m_spgw_log->error("Mis-match between packet bytes and sent bytes: Sent: %d, Packet: %d \n",n,msg->N_bytes); + } + return; +} + + +void +spgw::handle_s1u_pdu(srslte::byte_buffer_t *msg) +{ + //m_spgw_log->console("Received PDU from S1-U. Bytes=%d\n",msg->N_bytes); + srslte::gtpu_header_t header; + srslte::gtpu_read_header(msg, &header, m_spgw_log); + + //m_spgw_log->console("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes); + int n = write(m_sgi_if, msg->msg, msg->N_bytes); + if(n<0) + { + m_spgw_log->error("Could not write to TUN interface.\n"); + } + else + { + //m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes); + } + return; +} + +/* + * Helper Functions + */ +uint64_t +spgw::get_new_ctrl_teid() +{ + return m_next_ctrl_teid++; +} + +uint64_t +spgw::get_new_user_teid() +{ + return m_next_user_teid++; +} + +in_addr_t +spgw::get_new_ue_ipv4() +{ + m_h_next_ue_ip++; + return ntohl(m_h_next_ue_ip);//FIXME Tmp hack +} + +spgw_tunnel_ctx_t* +spgw::create_gtp_ctx(struct srslte::gtpc_create_session_request *cs_req) +{ + //Setup uplink control TEID + uint64_t spgw_uplink_ctrl_teid = get_new_ctrl_teid(); + //Setup uplink user TEID + uint64_t spgw_uplink_user_teid = get_new_user_teid(); + //Allocate UE IP + in_addr_t ue_ip = get_new_ue_ipv4(); + //in_addr_t ue_ip = inet_addr("172.16.0.2"); + uint8_t default_bearer_id = 5; + + m_spgw_log->console("SPGW: Allocated Ctrl TEID %" PRIu64 "\n", spgw_uplink_ctrl_teid); + m_spgw_log->console("SPGW: Allocated User TEID %" PRIu64 "\n", spgw_uplink_user_teid); + struct in_addr ue_ip_; + ue_ip_.s_addr=ue_ip; + m_spgw_log->console("SPGW: Allocate UE IP %s\n", inet_ntoa(ue_ip_)); + + + //Save the UE IP to User TEID map + spgw_tunnel_ctx_t *tunnel_ctx = new spgw_tunnel_ctx_t; + bzero(tunnel_ctx,sizeof(spgw_tunnel_ctx_t)); + + tunnel_ctx->imsi = cs_req->imsi; + tunnel_ctx->ebi = default_bearer_id; + tunnel_ctx->up_user_fteid.teid = spgw_uplink_user_teid; + tunnel_ctx->up_user_fteid.ipv4 = m_s1u_addr.sin_addr.s_addr; + tunnel_ctx->dw_ctrl_fteid.teid = cs_req->sender_f_teid.teid; + tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req->sender_f_teid.ipv4; + + tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid; + tunnel_ctx->ue_ipv4 = ue_ip; + m_teid_to_tunnel_ctx.insert(std::pair(spgw_uplink_ctrl_teid,tunnel_ctx)); + m_imsi_to_ctr_teid.insert(std::pair(cs_req->imsi,spgw_uplink_ctrl_teid)); + return tunnel_ctx; +} + +bool +spgw::delete_gtp_ctx(uint32_t ctrl_teid) +{ + spgw_tunnel_ctx_t *tunnel_ctx; + if(!m_teid_to_tunnel_ctx.count(ctrl_teid)){ + m_spgw_log->error("Could not find GTP context to delete.\n"); + return false; + } + tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid]; + + //Remove GTP-U connections, if any. + if(m_ip_to_teid.count(tunnel_ctx->ue_ipv4)) + { + pthread_mutex_lock(&m_mutex); + m_ip_to_teid.erase(tunnel_ctx->ue_ipv4); + pthread_mutex_unlock(&m_mutex); + } + //Remove Ctrl TEID from IMSI to control TEID map + m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi); + + //Remove GTP context from control TEID mapping + m_teid_to_tunnel_ctx.erase(ctrl_teid); + delete tunnel_ctx; + return true; +} + +void +spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *cs_req, struct srslte::gtpc_pdu *cs_resp_pdu) +{ + m_spgw_log->info("Received Create Session Request\n"); + spgw_tunnel_ctx_t *tunnel_ctx; + int default_bearer_id = 5; + //Check if IMSI has active GTP-C and/or GTP-U + bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req->imsi); + if(gtpc_present) + { + m_spgw_log->console("SPGW: GTP-C context for IMSI %015lu already exists.\n", cs_req->imsi); + delete_gtp_ctx(m_imsi_to_ctr_teid[cs_req->imsi]); + m_spgw_log->console("SPGW: Deleted previous context.\n"); + } + + m_spgw_log->info("Creating new GTP-C context\n"); + tunnel_ctx = create_gtp_ctx(cs_req); + + //Create session response message + srslte::gtpc_header *header = &cs_resp_pdu->header; + srslte::gtpc_create_session_response *cs_resp = &cs_resp_pdu->choice.create_session_response; + + //Setup GTP-C header + header->piggyback = false; + header->teid_present = true; + header->teid = tunnel_ctx->dw_ctrl_fteid.teid; //Send create session requesponse to the UE's MME Ctrl TEID + header->type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE; + + //Initialize to zero + bzero(cs_resp,sizeof(struct srslte::gtpc_create_session_response)); + //Setup Cause + cs_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + //Setup sender F-TEID (ctrl) + cs_resp->sender_f_teid.ipv4_present = true; + cs_resp->sender_f_teid = tunnel_ctx->up_ctrl_fteid; + + //Bearer context created + cs_resp->eps_bearer_context_created.ebi = default_bearer_id; + cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present=true; + cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid; + + //Fill in the PAA + cs_resp->paa_present = true; + cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4; + cs_resp->paa.ipv4_present = true; + cs_resp->paa.ipv4 = tunnel_ctx->ue_ipv4; + m_spgw_log->info("Sending Create Session Response\n"); + m_mme_gtpc->handle_create_session_response(cs_resp_pdu); + return; +} + + + + +void +spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct srslte::gtpc_pdu *mb_resp_pdu) +{ + m_spgw_log->info("Received Modified Bearer Request\n"); + + //Get control tunnel info from mb_req PDU + uint32_t ctrl_teid = mb_req_pdu->header.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if(tunnel_it == m_teid_to_tunnel_ctx.end()) + { + m_spgw_log->warning("Could not find TEID %d to modify\n",ctrl_teid); + return; + } + spgw_tunnel_ctx_t *tunnel_ctx = tunnel_it->second; + + //Store user DW link TEID + srslte::gtpc_modify_bearer_request *mb_req = &mb_req_pdu->choice.modify_bearer_request; + tunnel_ctx->dw_user_fteid.teid = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid; + tunnel_ctx->dw_user_fteid.ipv4 = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4; + //Set up actual tunnel + m_spgw_log->info("Setting Up GTP-U tunnel. Tunnel info: \n"); + struct in_addr addr; + addr.s_addr = tunnel_ctx->ue_ipv4; + m_spgw_log->info("IMSI: %lu, UE IP, %s \n",tunnel_ctx->imsi, inet_ntoa(addr)); + m_spgw_log->info("S-GW Rx Ctrl TEID 0x%x, MME Rx Ctrl TEID 0x%x\n", tunnel_ctx->up_ctrl_fteid.teid, tunnel_ctx->dw_ctrl_fteid.teid); + m_spgw_log->info("S-GW Rx Ctrl IP (NA), MME Rx Ctrl IP (NA)\n"); + + struct in_addr addr2; + addr2.s_addr = tunnel_ctx->up_user_fteid.ipv4; + m_spgw_log->info("S-GW Rx User TEID 0x%x, S-GW Rx User IP %s\n", tunnel_ctx->up_user_fteid.teid, inet_ntoa(addr2)); + + struct in_addr addr3; + addr3.s_addr = tunnel_ctx->dw_user_fteid.ipv4; + m_spgw_log->info("eNB Rx User TEID 0x%x, eNB Rx User IP %s\n", tunnel_ctx->dw_user_fteid.teid, inet_ntoa(addr3)); + + //Setup IP to F-TEID map + //bool ret = false; + pthread_mutex_lock(&m_mutex); + m_ip_to_teid[tunnel_ctx->ue_ipv4]=tunnel_ctx->dw_user_fteid; + //ret = m_ip_to_teid.insert(std::pair(tunnel_ctx->ue_ipv4, tunnel_ctx->dw_user_fteid)); + pthread_mutex_unlock(&m_mutex); + + //Setting up Modify bearer response PDU + //Header + srslte::gtpc_header *header = &mb_resp_pdu->header; + header->piggyback = false; + header->teid_present = true; + header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // + header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE; + + //PDU + srslte::gtpc_modify_bearer_response *mb_resp = &mb_resp_pdu->choice.modify_bearer_response; + mb_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi; + //printf("%d %d\n",mb_resp->eps_bearer_context_modified.ebi, tunnel_ctx->ebi); + mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; +} + +void +spgw::handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct srslte::gtpc_pdu *del_resp_pdu) +{ + //Find tunel ctxt + uint32_t ctrl_teid = del_req_pdu->header.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if(tunnel_it == m_teid_to_tunnel_ctx.end()) + { + m_spgw_log->warning("Could not find TEID %d to delete\n",ctrl_teid); + return; + } + spgw_tunnel_ctx_t *tunnel_ctx = tunnel_it->second; + in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; + + //Delete data tunnel + pthread_mutex_lock(&m_mutex); + std::map::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4); + if(data_it != m_ip_to_teid.end()) + { + m_ip_to_teid.erase(data_it); + } + pthread_mutex_unlock(&m_mutex); + m_teid_to_tunnel_ctx.erase(tunnel_it); + + delete tunnel_ctx; + return; +} + +void +spgw::handle_release_access_bearers_request(struct srslte::gtpc_pdu *rel_req_pdu, struct srslte::gtpc_pdu *rel_resp_pdu) +{ + //Find tunel ctxt + uint32_t ctrl_teid = rel_req_pdu->header.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if(tunnel_it == m_teid_to_tunnel_ctx.end()) + { + m_spgw_log->warning("Could not find TEID %d to release bearers from\n",ctrl_teid); + return; + } + spgw_tunnel_ctx_t *tunnel_ctx = tunnel_it->second; + in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; + + //Delete data tunnel + pthread_mutex_lock(&m_mutex); + std::map::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4); + if(data_it != m_ip_to_teid.end()) + { + m_ip_to_teid.erase(data_it); + } + pthread_mutex_unlock(&m_mutex); + + //Do NOT delete control tunnel + return; +} + +} //namespace srsepc diff --git a/srsepc/srsepc_if_masq.sh b/srsepc/srsepc_if_masq.sh new file mode 100755 index 0000000..88d7f37 --- /dev/null +++ b/srsepc/srsepc_if_masq.sh @@ -0,0 +1,37 @@ +#/bin/bash + +################################################################### +# +# 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/. +# +################################################################### + +#Check for sudo rights +sudo -v || exit + +#Check if outbound interface was specified +if [ ! $# -eq 1 ] + then + echo "Usage :'sudo ./if_masq.sh ' " + exit +fi + +echo "Masquerading Interface "$1 + +echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward 1>/dev/null +sudo iptables -t nat -A POSTROUTING -o $1 -j MASQUERADE + diff --git a/srsepc/user_db.csv.example b/srsepc/user_db.csv.example new file mode 100644 index 0000000..54d61eb --- /dev/null +++ b/srsepc/user_db.csv.example @@ -0,0 +1,16 @@ +# +# .csv to store UE's information in HSS +# Kept in the following format: "Name,IMSI,Key,OP_Type,OP,AMF,SQN,QCI" +# +# Name: Human readable name to help distinguish UE's. Ignored by the HSS +# IMSI: UE's IMSI value +# Key: UE's key, where other keys are derived from. Stored in hexadecimal +# OP_Type: Operator's code type, either OP or OPc +# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal +# AMF: Authentication management field, stored in hexadecimal +# SQN: UE's Sequence number for freshness of the authentication +# QCI: QoS Class Identifier for the UE's default bearer. +# +# Note: Lines starting by '#' are ignored and will be overwritten +ue1,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001234,7 +ue2,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7 diff --git a/srsue/CMakeLists.txt b/srsue/CMakeLists.txt new file mode 100644 index 0000000..2f91068 --- /dev/null +++ b/srsue/CMakeLists.txt @@ -0,0 +1,51 @@ +# +# Copyright 2013-2017 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/. +# + +######################################################################## +# Boost is required +######################################################################## +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsUE and ") +endif() + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR} +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +add_subdirectory(test) + +######################################################################## +# Default configuration files +######################################################################## +install(FILES ue.conf.example DESTINATION ${DATA_DIR}) \ No newline at end of file diff --git a/srsue/hdr/mac/demux.h b/srsue/hdr/mac/demux.h new file mode 100644 index 0000000..c20b8eb --- /dev/null +++ b/srsue/hdr/mac/demux.h @@ -0,0 +1,97 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_DEMUX_H +#define SRSUE_DEMUX_H + +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/pdu_queue.h" +#include "srslte/common/log.h" +#include "srslte/common/timers.h" +#include "srslte/common/pdu.h" + +/* Logical Channel Demultiplexing and MAC CE dissassemble */ + + +namespace srsue { + +class demux : public srslte::pdu_queue::process_callback +{ +public: + demux(); + void init(phy_interface_mac_common* phy_h_, rlc_interface_mac *rlc, srslte::log* log_h_, srslte::timers::timer* time_alignment_timer); + + bool process_pdus(); + uint8_t* request_buffer(uint32_t len); + uint8_t* request_buffer_bcch(uint32_t len); + void deallocate(uint8_t* payload_buffer_ptr); + + void push_pdu(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp); + void push_pdu_bcch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp); + void push_pdu_mch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp); + void push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes); + + void set_uecrid_callback(bool (*callback)(void*, uint64_t), void *arg); + bool get_uecrid_successful(); + + void process_pdu(uint8_t *pdu, uint32_t nof_bytes, srslte::pdu_queue::channel_t channel, uint32_t tstamp); + void mch_start_rx(uint32_t lcid); +private: + const static int MAX_PDU_LEN = 150*1024/8; // ~ 150 Mbps + const static int NOF_BUFFER_PDUS = 64; // Number of PDU buffers per HARQ pid + const static int MAX_BCCH_PDU_LEN = 1024; + uint8_t bcch_buffer[MAX_BCCH_PDU_LEN]; // BCCH PID has a dedicated buffer + + bool (*uecrid_callback) (void*, uint64_t); + void *uecrid_callback_arg; + + srslte::sch_pdu mac_msg; + srslte::mch_pdu mch_mac_msg; + srslte::sch_pdu pending_mac_msg; + uint8_t mch_lcids[SRSLTE_N_MCH_LCIDS]; + void process_sch_pdu(srslte::sch_pdu *pdu); + void process_mch_pdu(srslte::mch_pdu *pdu); + + + bool process_ce(srslte::sch_subh *subheader); + + bool is_uecrid_successful; + + phy_interface_mac_common *phy_h; + srslte::log *log_h; + srslte::timers::timer *time_alignment_timer; + rlc_interface_mac *rlc; + + // Buffer of PDUs + srslte::pdu_queue pdus; +}; + +} // namespace srsue + +#endif // SRSUE_DEMUX_H + + + diff --git a/srsue/hdr/mac/dl_harq.h b/srsue/hdr/mac/dl_harq.h new file mode 100644 index 0000000..f9dd394 --- /dev/null +++ b/srsue/hdr/mac/dl_harq.h @@ -0,0 +1,458 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_DL_HARQ_H +#define SRSUE_DL_HARQ_H + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srslte/common/log.h" +#include "srslte/common/timers.h" +#include "demux.h" +#include "dl_sps.h" +#include "srslte/common/mac_pcap.h" + +#include "srslte/interfaces/ue_interfaces.h" + +/* Downlink HARQ entity as defined in 5.3.2 of 36.321 */ + + +namespace srsue { + +template +class dl_harq_entity +{ +public: + + const static uint32_t HARQ_BCCH_PID = N; + + dl_harq_entity() : proc(N+1) + { + pcap = NULL; + } + + bool init(srslte::log *log_h_, srslte::timers::timer *timer_aligment_timer_, demux *demux_unit_) + { + timer_aligment_timer = timer_aligment_timer_; + demux_unit = demux_unit_; + si_window_start = 0; + log_h = log_h_; + for (uint32_t i=0;iMAC interface for DL processes **************************/ + void new_grant_dl(Tgrant grant, Taction *action) + { + if (grant.rnti_type != SRSLTE_RNTI_SPS) { + uint32_t harq_pid; + // Set BCCH PID for SI RNTI + if (grant.rnti_type == SRSLTE_RNTI_SI) { + harq_pid = HARQ_BCCH_PID; + } else { + harq_pid = grant.pid%N; + } + if (grant.rnti_type == SRSLTE_RNTI_TEMP && last_temporal_crnti != grant.rnti) { + grant.ndi[0] = true; + Info("Set NDI=1 for Temp-RNTI DL grant\n"); + last_temporal_crnti = grant.rnti; + } + if (grant.rnti_type == SRSLTE_RNTI_USER && proc[harq_pid].is_sps()) { + grant.ndi[0] = true; + Info("Set NDI=1 for C-RNTI DL grant\n"); + } + proc[harq_pid].new_grant_dl(grant, action); + } else { + /* This is for SPS scheduling */ + uint32_t harq_pid = get_harq_sps_pid(grant.tti)%N; + if (grant.ndi[0]) { + grant.ndi[0] = false; + proc[harq_pid].new_grant_dl(grant, action); + } else { + if (grant.is_sps_release) { + dl_sps_assig.clear(); + if (timer_aligment_timer->is_running()) { + //phy_h->send_sps_ack(); + Warning("PHY Send SPS ACK not implemented\n"); + } + } else { + Error("SPS not implemented\n"); + //dl_sps_assig.reset(grant.tti, grant); + //grant.ndi = true; + //procs[harq_pid].save_grant(); + } + } + } + } + + + void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) + { + if (rnti_type == SRSLTE_RNTI_SI) { + proc[N].tb_decoded(ack, 0); + } else { + proc[harq_pid%N].tb_decoded(ack, tb_idx); + } + } + + + void reset() + { + for (uint32_t i=0;igenerate_ack = true; + action->rnti = grant.rnti; + + /* For each subprocess... */ + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + action->default_ack[tb] = false; + action->decode_enabled[tb] = false; + action->phy_grant.dl.tb_en[tb] = grant.tb_en[tb]; + if (grant.tb_en[tb]) { + subproc[tb].new_grant_dl(grant, action); + } + } + } + + int get_current_tbs(uint32_t tb_idx) { return subproc[tb_idx].get_current_tbs(); } + + bool is_sps() { return false; } + + void tb_decoded(bool ack_, uint32_t tb_idx) { + subproc[tb_idx].tb_decoded(ack_); + } + + private: + + const static int RESET_DUPLICATE_TIMEOUT = 8*6; + + class dl_tb_process { + public: + dl_tb_process(void) { + is_initiated = false; + ack = false; + bzero(&cur_grant, sizeof(Tgrant)); + payload_buffer_ptr = NULL; + pthread_mutex_init(&mutex, NULL); + } + + ~dl_tb_process() { + if (is_initiated) { + srslte_softbuffer_rx_free(&softbuffer); + } + } + + bool init(uint32_t pid_, dl_harq_entity *parent, uint32_t tb_idx) { + tid = tb_idx; + if (srslte_softbuffer_rx_init(&softbuffer, 110)) { + Error("Error initiating soft buffer\n"); + return false; + } else { + pid = pid_; + is_first_tb = true; + is_initiated = true; + harq_entity = parent; + log_h = harq_entity->log_h; + return true; + } + } + + void reset(bool lock = true) { + if (lock) { + pthread_mutex_lock(&mutex); + } + is_first_tb = true; + ack = false; + n_retx = 0; + if (payload_buffer_ptr) { + if (pid != HARQ_BCCH_PID) { + harq_entity->demux_unit->deallocate(payload_buffer_ptr); + } + payload_buffer_ptr = NULL; + } + bzero(&cur_grant, sizeof(Tgrant)); + if (is_initiated && lock) { + srslte_softbuffer_rx_reset(&softbuffer); + } + if (lock) { + pthread_mutex_unlock(&mutex); + } + } + + void new_grant_dl(Tgrant grant, Taction *action) { + + pthread_mutex_lock(&mutex); + + // Compute RV for BCCH when not specified in PDCCH format + if (pid == HARQ_BCCH_PID && grant.rv[tid] == -1) { + uint32_t k; + if ((grant.tti / 10) % 2 == 0 && grant.tti % 10 == 5) { // This is SIB1, k is different + k = (grant.tti / 20) % 4; + grant.rv[tid] = ((uint32_t) ceilf((float) 1.5 * k)) % 4; + } else if (grant.rv[tid] == -1) { + k = (grant.tti - harq_entity->si_window_start) % 4; + grant.rv[tid] = ((uint32_t) ceilf((float) 1.5 * k)) % 4; + } + } + calc_is_new_transmission(grant); + // If this is a new transmission or the size of the TB has changed + if (is_new_transmission || (cur_grant.n_bytes[tid] != grant.n_bytes[tid])) { + if (!is_new_transmission) { + Warning("DL PID %d: Size of grant changed during a retransmission %d!=%d\n", pid, + cur_grant.n_bytes[tid], grant.n_bytes[tid]); + } + ack = false; + srslte_softbuffer_rx_reset_tbs(&softbuffer, cur_grant.n_bytes[tid] * 8); + n_retx = 0; + } + + // If data has not yet been successfully decoded + if (!ack) { + + // Save grant + grant.last_ndi[tid] = cur_grant.ndi[tid]; + grant.last_tti = cur_grant.tti; + memcpy(&cur_grant, &grant, sizeof(Tgrant)); + + if (payload_buffer_ptr) { + Warning("DL PID %d: Allocating buffer already allocated. Deallocating.\n", pid); + if (pid != HARQ_BCCH_PID) { + harq_entity->demux_unit->deallocate(payload_buffer_ptr); + } + } + + // Instruct the PHY To combine the received data and attempt to decode it + if (pid == HARQ_BCCH_PID) { + payload_buffer_ptr = harq_entity->demux_unit->request_buffer_bcch(cur_grant.n_bytes[tid]); + } else { + payload_buffer_ptr = harq_entity->demux_unit->request_buffer(cur_grant.n_bytes[tid]); + } + action->payload_ptr[tid] = payload_buffer_ptr; + if (!action->payload_ptr[tid]) { + action->decode_enabled[tid] = false; + Error("Can't get a buffer for TBS=%d\n", cur_grant.n_bytes[tid]); + pthread_mutex_unlock(&mutex); + return; + } + action->decode_enabled[tid] = true; + action->rv[tid] = cur_grant.rv[tid]; + action->softbuffers[tid] = &softbuffer; + memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(Tphygrant)); + n_retx++; + + } else { + action->default_ack[tid] = true; + uint32_t interval = srslte_tti_interval(grant.tti, cur_grant.tti); + Warning("DL PID %d: Received duplicate TB. Discarting and retransmitting ACK (grant_tti=%d, ndi=%d, sz=%d, reset=%s)\n", + pid, cur_grant.tti, cur_grant.ndi[tid], cur_grant.n_bytes[tid], interval>RESET_DUPLICATE_TIMEOUT?"yes":"no"); + if (interval > RESET_DUPLICATE_TIMEOUT) { + reset(false); + } + } + + if (pid == HARQ_BCCH_PID || harq_entity->timer_aligment_timer->is_expired()) { + // Do not generate ACK + Debug("Not generating ACK\n"); + action->generate_ack = false; + } else { + if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP && !ack) { + // Postpone ACK after contention resolution is resolved + action->generate_ack_callback = harq_entity->generate_ack_callback; + action->generate_ack_callback_arg = harq_entity->demux_unit; + Debug("ACK pending contention resolution\n"); + } else { + Debug("Generating ACK\n"); + } + } + + if (!action->decode_enabled[tid]) { + pthread_mutex_unlock(&mutex); + } + + } + + void tb_decoded(bool ack_) { + ack = ack_; + if (ack) { + if (pid == HARQ_BCCH_PID) { + if (harq_entity->pcap) { + harq_entity->pcap->write_dl_sirnti(payload_buffer_ptr, cur_grant.n_bytes[tid], ack, cur_grant.tti); + } + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH)\n", cur_grant.n_bytes[tid]); + harq_entity->demux_unit->push_pdu_bcch(payload_buffer_ptr, cur_grant.n_bytes[tid], cur_grant.tti); + } else { + if (harq_entity->pcap) { + harq_entity->pcap->write_dl_crnti(payload_buffer_ptr, cur_grant.n_bytes[tid], cur_grant.rnti, ack, + cur_grant.tti); + } + if (cur_grant.rnti_type == SRSLTE_RNTI_TEMP) { + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI)\n", + cur_grant.n_bytes[tid]); + harq_entity->demux_unit->push_pdu_temp_crnti(payload_buffer_ptr, cur_grant.n_bytes[tid]); + } else { + Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit\n", cur_grant.n_bytes[tid]); + harq_entity->demux_unit->push_pdu(payload_buffer_ptr, cur_grant.n_bytes[tid], cur_grant.tti); + + // Compute average number of retransmissions per packet + harq_entity->average_retx = SRSLTE_VEC_CMA((float) n_retx, harq_entity->average_retx, + harq_entity->nof_pkts++); + } + } + } else if (pid != HARQ_BCCH_PID) { + harq_entity->demux_unit->deallocate(payload_buffer_ptr); + } + + payload_buffer_ptr = NULL; + + Info("DL %d (TB %d): %s tbs=%d, rv=%d, ack=%s, ndi=%d (%d), tti=%d (%d)\n", + pid, tid, is_new_transmission ? "newTX" : "reTX ", + cur_grant.n_bytes[tid], cur_grant.rv[tid], ack ? "OK" : "KO", + cur_grant.ndi[tid], cur_grant.last_ndi[tid], cur_grant.tti, cur_grant.last_tti); + + pthread_mutex_unlock(&mutex); + + if (ack && pid == HARQ_BCCH_PID) { + reset(); + } + } + + int get_current_tbs(void) { return cur_grant.n_bytes[tid] * 8; } + + private: + // Determine if it's a new transmission 5.3.2.2 + bool calc_is_new_transmission(Tgrant grant) { + + if (grant.phy_grant.dl.mcs[tid].idx <= 28 && // mcs 29,30,31 always retx regardless of rest + ((grant.ndi[tid] != cur_grant.ndi[tid]) || // 1st condition (NDI has changed) + (pid == HARQ_BCCH_PID && grant.rv[tid] == 0) || // 2nd condition (Broadcast and 1st transmission) + is_first_tb)) + { + is_first_tb = false; + is_new_transmission = true; + Debug("Set HARQ for new transmission\n"); + } else { + is_new_transmission = false; + Debug("Set HARQ for retransmission\n"); + } + + return is_new_transmission; + } + + pthread_mutex_t mutex; + + bool is_initiated; + dl_harq_entity *harq_entity; + srslte::log *log_h; + + bool is_first_tb; + bool is_new_transmission; + + uint32_t pid; /* HARQ Proccess ID */ + uint32_t tid; /* Transport block ID */ + uint8_t *payload_buffer_ptr; + bool ack; + + uint32_t n_retx; + + Tgrant cur_grant; + srslte_softbuffer_rx_t softbuffer; + }; + + /* Transport blocks */ + std::vector subproc; + }; + // Private members of dl_harq_entity + + static bool generate_ack_callback(void *arg) + { + demux *demux_unit = (demux*) arg; + return demux_unit->get_uecrid_successful(); + } + + uint32_t get_harq_sps_pid(uint32_t tti) { return 0; } + + dl_sps dl_sps_assig; + + + std::vector proc; + srslte::timers::timer *timer_aligment_timer; + demux *demux_unit; + srslte::log *log_h; + srslte::mac_pcap *pcap; + uint16_t last_temporal_crnti; + int si_window_start; + + float average_retx; + uint64_t nof_pkts; +}; + +} // namespace srsue + +#endif // SRSUE_DL_HARQ_H diff --git a/srsue/hdr/mac/dl_sps.h b/srsue/hdr/mac/dl_sps.h new file mode 100644 index 0000000..dd6328f --- /dev/null +++ b/srsue/hdr/mac/dl_sps.h @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_DL_SPS_H +#define SRSUE_DL_SPS_H + +#include "srslte/common/log.h" +#include "srslte/common/timers.h" + +/* Downlink Semi-Persistent schedulign (Section 5.10.1) */ + + +namespace srsue { + +class dl_sps +{ +public: + + void clear() {} + void reset() {} + bool get_pending_grant(uint32_t tti, mac_interface_phy::mac_grant_t *grant) { + return false; + } +private: + +}; + +} // namespace srsue + +#endif // SRSUE_DL_SPS_H diff --git a/srsue/hdr/mac/mac.h b/srsue/hdr/mac/mac.h new file mode 100644 index 0000000..f2800f3 --- /dev/null +++ b/srsue/hdr/mac/mac.h @@ -0,0 +1,197 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_MAC_H +#define SRSUE_MAC_H + +#include "srslte/common/log.h" +#include "dl_harq.h" +#include "ul_harq.h" +#include "srslte/common/timers.h" +#include "mac_metrics.h" +#include "proc_ra.h" +#include "proc_sr.h" +#include "proc_bsr.h" +#include "proc_phr.h" +#include "mux.h" +#include "demux.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/common/threads.h" + +namespace srsue { + +class mac + :public mac_interface_phy + ,public mac_interface_rrc + ,public srslte::timer_callback + ,public srslte::mac_interface_timers + ,public periodic_thread +{ +public: + mac(); + bool init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac* rrc, srslte::log *log_h); + void stop(); + + void get_metrics(mac_metrics_t &m); + + /******** Interface from PHY (PHY -> MAC) ****************/ + /* see mac_interface.h for comments */ + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action); + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action); + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action); + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action); + void new_mch_dl(srslte_ra_dl_grant_t phy_grant, tb_action_dl_t *action); + void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid); + void bch_decoded_ok(uint8_t *payload, uint32_t len); + + void pch_decoded_ok(uint32_t len); + void mch_decoded_ok(uint32_t len); + void process_mch_pdu(uint32_t len); + + void set_mbsfn_config(uint32_t nof_mbsfn_services); + + /******** Interface from RRC (RRC -> MAC) ****************/ + void bcch_start_rx(); + void bcch_start_rx(int si_window_start, int si_window_length); + void pcch_start_rx(); + void clear_rntis(); + void setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD); + void mch_start_rx(uint32_t lcid); + void reconfiguration(); + void reset(); + void wait_uplink(); + + /******** set/get MAC configuration ****************/ + void set_config(mac_cfg_t *mac_cfg); + void get_config(mac_cfg_t *mac_cfg); + void set_config_main(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *main_cfg); + void set_config_rach(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT *rach_cfg, uint32_t prach_config_index); + void set_config_sr(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT *sr_cfg); + void set_contention_id(uint64_t uecri); + + void start_noncont_ho(uint32_t preamble_index, uint32_t prach_mask); + void start_cont_ho(); + + void get_rntis(ue_rnti_t *rntis); + void set_ho_rnti(uint16_t crnti, uint16_t target_pci); + + void start_pcap(srslte::mac_pcap* pcap); + + // Timer callback interface + void timer_expired(uint32_t timer_id); + + uint32_t get_current_tti(); + + // Interface for upper-layer timers + srslte::timers::timer* timer_get(uint32_t timer_id); + void timer_release_id(uint32_t timer_id); + uint32_t timer_get_unique_id(); + + +private: + void run_period(); + + static const int MAC_MAIN_THREAD_PRIO = -1; // Use default high-priority below UHD + static const int MAC_PDU_THREAD_PRIO = DEFAULT_PRIORITY-5; + static const int MAC_NOF_HARQ_PROC = 2*HARQ_DELAY_MS; + + // Interaction with PHY + phy_interface_mac *phy_h; + rlc_interface_mac *rlc_h; + rrc_interface_mac *rrc_h; + srslte::log *log_h; + mac_interface_phy::mac_phy_cfg_mbsfn_t phy_mbsfn_cfg; + + // MAC configuration + mac_cfg_t config; + + // UE-specific RNTIs + ue_rnti_t uernti; + + uint32_t tti; + + /* Multiplexing/Demultiplexing Units */ + mux mux_unit; + demux demux_unit; + + /* DL/UL HARQ */ + dl_harq_entity dl_harq; + ul_harq_entity ul_harq; + + /* MAC Uplink-related Procedures */ + ra_proc ra_procedure; + sr_proc sr_procedure; + bsr_proc bsr_procedure; + phr_proc phr_procedure; + + /* Buffers for PCH reception (not included in DL HARQ) */ + const static uint32_t pch_payload_buffer_sz = 8*1024; + srslte_softbuffer_rx_t pch_softbuffer; + uint8_t pch_payload_buffer[pch_payload_buffer_sz]; + + /* Buffers for MCH reception (not included in DL HARQ) */ + const static uint32_t mch_payload_buffer_sz = SRSLTE_MAX_BUFFER_SIZE_BYTES; + srslte_softbuffer_rx_t mch_softbuffer; + uint8_t mch_payload_buffer[mch_payload_buffer_sz]; + srslte::mch_pdu mch_msg; + + + + /* Functions for MAC Timers */ + uint32_t timer_alignment; + uint32_t contention_resolution_timer; + void setup_timers(); + void timer_alignment_expire(); + srslte::timers timers; + + // pointer to MAC PCAP object + srslte::mac_pcap* pcap; + bool is_first_ul_grant; + + mac_metrics_t metrics; + + /* Class to process MAC PDUs from DEMUX unit */ + class pdu_process : public thread { + public: + pdu_process(demux *demux_unit); + void notify(); + void stop(); + private: + void run_thread(); + bool running; + bool have_data; + pthread_mutex_t mutex; + pthread_cond_t cvar; + demux* demux_unit; + }; + pdu_process pdu_process_thread; +}; + +} // namespace srsue + +#endif // SRSUE_MAC_H diff --git a/srsue/hdr/mac/mac_metrics.h b/srsue/hdr/mac/mac_metrics.h new file mode 100644 index 0000000..a201d2a --- /dev/null +++ b/srsue/hdr/mac/mac_metrics.h @@ -0,0 +1,46 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_MAC_METRICS_H +#define SRSUE_MAC_METRICS_H + + +namespace srsue { + +struct mac_metrics_t +{ + int tx_pkts; + int tx_errors; + int tx_brate; + int rx_pkts; + int rx_errors; + int rx_brate; + int ul_buffer; +}; + +} // namespace srsue + +#endif // SRSUE_MAC_METRICS_H diff --git a/srsue/hdr/mac/mux.h b/srsue/hdr/mac/mux.h new file mode 100644 index 0000000..2e62136 --- /dev/null +++ b/srsue/hdr/mac/mux.h @@ -0,0 +1,118 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_MUX_H +#define SRSUE_MUX_H + +#include + +#include + +#include "srslte/common/log.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/pdu.h" +#include "proc_bsr.h" +#include "proc_phr.h" + +/* Logical Channel Multiplexing and Prioritization + Msg3 Buffer */ + + +typedef struct { + uint32_t id; + int Bj; + int PBR; // -1 sets to infinity + uint32_t BSD; + uint32_t priority; + int sched_len; + int buffer_len; +} lchid_t; + +namespace srsue { + +class mux +{ +public: + mux(uint8_t nof_harq_proc_); + void reset(); + void init(rlc_interface_mac *rlc, srslte::log *log_h, bsr_interface_mux *bsr_procedure, phr_proc *phr_procedure_); + + bool is_pending_any_sdu(); + bool is_pending_sdu(uint32_t lcid); + + uint8_t* pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32_t pid); + uint8_t* msg3_get(uint8_t* payload, uint32_t pdu_sz); + + void msg3_flush(); + bool msg3_is_transmitted(); + + void msg3_prepare(); + bool msg3_is_pending(); + + void append_crnti_ce_next_tx(uint16_t crnti); + + void set_priority(uint32_t lcid, uint32_t priority, int PBR_x_tti, uint32_t BSD); + void clear_lch(uint32_t lch_id); + void pusch_retx(uint32_t tx_tti, uint32_t pid); + +private: + int find_lchid(uint32_t lch_id); + bool pdu_move_to_msg3(uint32_t pdu_sz); + bool allocate_sdu(uint32_t lcid, srslte::sch_pdu *pdu, int max_sdu_sz); + bool sched_sdu(lchid_t *ch, int *sdu_space, int max_sdu_sz); + + const static int MIN_RLC_SDU_LEN = 0; + const static int MAX_NOF_SUBHEADERS = 20; + + std::vector lch; + + // Keep track of the PIDs that transmitted BSR reports + std::vector pid_has_bsr; + + // Mutex for exclusive access + pthread_mutex_t mutex; + + srslte::log *log_h; + rlc_interface_mac *rlc; + bsr_interface_mux *bsr_procedure; + phr_proc *phr_procedure; + uint16_t pending_crnti_ce; + uint8_t nof_harq_proc; + + /* Msg3 Buffer */ + static const uint32_t MSG3_BUFF_SZ = 1024; + uint8_t msg3_buff[MSG3_BUFF_SZ]; + uint8_t *msg3_buff_start_pdu; + + /* PDU Buffer */ + srslte::sch_pdu pdu_msg; + bool msg3_has_been_transmitted; + bool msg3_pending; +}; + +} // namespace srsue + +#endif // SRSUE_MUX_H + diff --git a/srsue/hdr/mac/proc.h b/srsue/hdr/mac/proc.h new file mode 100644 index 0000000..f360138 --- /dev/null +++ b/srsue/hdr/mac/proc.h @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PROC_H +#define SRSUE_PROC_H + +#include + +/* Interface for a MAC procedure */ + + +namespace srsue { + +class proc +{ +public: + proc() { + running = false; + } + void run() { + running = true; + } + void stop() { + running = false; + } + bool is_running() { + return running; + } + virtual void step(uint32_t tti) = 0; +private: + bool running; +}; + +} // namespace srsue + +#endif // SRSUE_PROC_H diff --git a/srsue/hdr/mac/proc_bsr.h b/srsue/hdr/mac/proc_bsr.h new file mode 100644 index 0000000..9592df6 --- /dev/null +++ b/srsue/hdr/mac/proc_bsr.h @@ -0,0 +1,91 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PROC_BSR_H +#define SRSUE_PROC_BSR_H + +#include + +#include "srslte/common/log.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/timers.h" + +/* Buffer status report procedure */ + +namespace srsue { + +class bsr_proc : public srslte::timer_callback, public bsr_interface_mux +{ +public: + bsr_proc(); + void init(rlc_interface_mac *rlc, srslte::log *log_h, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_db); + void step(uint32_t tti); + void reset(); + void setup_lcg(uint32_t lcid, uint32_t new_lcg); + void set_priority(uint32_t lcid, uint32_t priority); + void timer_expired(uint32_t timer_id); + uint32_t get_buffer_state(); + bool need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr); + bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr); + bool need_to_send_sr(uint32_t tti); + bool need_to_reset_sr(); + void set_tx_tti(uint32_t tti); + +private: + + const static int QUEUE_STATUS_PERIOD_MS = 500; + + bool reset_sr; + mac_interface_rrc::mac_cfg_t *mac_cfg; + srslte::timers *timers_db; + srslte::log *log_h; + rlc_interface_mac *rlc; + bool initiated; + const static int MAX_LCID = 6; + int lcg[MAX_LCID]; + uint32_t last_pending_data[MAX_LCID]; + int priorities[MAX_LCID]; + uint32_t find_max_priority_lcid(); + typedef enum {NONE, REGULAR, PADDING, PERIODIC} triggered_bsr_type_t; + triggered_bsr_type_t triggered_bsr_type; + + bool sr_is_sent; + uint32_t last_print; + uint32_t next_tx_tti; + void update_pending_data(); + bool check_highest_channel(); + bool check_single_channel(); + bool generate_bsr(bsr_t *bsr, uint32_t nof_padding_bytes); + char* bsr_type_tostring(triggered_bsr_type_t type); + char* bsr_format_tostring(bsr_format_t format); + + uint32_t timer_periodic_id; + uint32_t timer_retx_id; +}; + +} // namespace srsue + +#endif // SRSUE_PROC_BSR_H diff --git a/srsue/hdr/mac/proc_phr.h b/srsue/hdr/mac/proc_phr.h new file mode 100644 index 0000000..b122802 --- /dev/null +++ b/srsue/hdr/mac/proc_phr.h @@ -0,0 +1,77 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PROC_PHR_H +#define SRSUE_PROC_PHR_H + +#include +#include "srslte/common/timers.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/log.h" + + +/* Power headroom report procedure */ + + +namespace srsue { + +class phr_proc : public srslte::timer_callback +{ +public: + phr_proc(); + void init(phy_interface_mac* phy_h, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg, srslte::timers *timers_db_); + + void step(uint32_t tti); + void reset(); + + bool generate_phr_on_ul_grant(float *phr); + void timer_expired(uint32_t timer_id); + + void start_timer(); + +private: + + bool pathloss_changed(); + + srslte::log* log_h; + mac_interface_rrc::mac_cfg_t *mac_cfg; + phy_interface_mac* phy_h; + srslte::timers* timers_db; + bool initiated; + int timer_prohibit_value; + int timer_periodic_value; + int dl_pathloss_change; + int last_pathloss_db; + bool phr_is_triggered; + + uint32_t timer_periodic_id; + uint32_t timer_prohibit_id; + +}; + +} // namespace srsue + +#endif // SRSUE_PROC_PHR_H diff --git a/srsue/hdr/mac/proc_ra.h b/srsue/hdr/mac/proc_ra.h new file mode 100644 index 0000000..833173b --- /dev/null +++ b/srsue/hdr/mac/proc_ra.h @@ -0,0 +1,219 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PROC_RA_H +#define SRSUE_PROC_RA_H + +#include + +#include "srslte/common/log.h" +#include "srslte/common/timers.h" +#include "mux.h" +#include "demux.h" +#include "srslte/common/pdu.h" +#include "srslte/common/mac_pcap.h" + +/* Random access procedure as specified in Section 5.1 of 36.321 */ + + +namespace srsue { + +class ra_proc : public srslte::timer_callback +{ +public: + ra_proc() : rar_pdu_msg(20) { + bzero(&softbuffer_rar, sizeof(srslte_softbuffer_rx_t)); + pcap = NULL; + backoff_interval_start = 0; + backoff_inteval = 0; + received_target_power_dbm = 0; + ra_rnti = 0; + current_ta = 0; + state = IDLE; + last_msg3_group = RA_GROUP_A; + msg3_transmitted = false; + first_rar_received = false; + phy_h = NULL; + log_h = NULL; + mac_cfg = NULL; + mux_unit = NULL; + demux_unit = NULL; + rrc = NULL; + transmitted_contention_id = 0; + transmitted_crnti = 0; + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + started_by_pdcch = false; + rar_grant_nbytes = 0; + rar_grant_tti = 0; + msg3_flushed = false; + + noncontention_enabled = false; + next_preamble_idx = 0; + next_prach_mask = 0; + + time_alignment_timer = NULL; + contention_resolution_timer = NULL; + }; + + ~ra_proc(); + + void init(phy_interface_mac *phy_h, + rrc_interface_mac *rrc_, + srslte::log *log_h, + mac_interface_rrc::ue_rnti_t *rntis, + mac_interface_rrc::mac_cfg_t *mac_cfg, + srslte::timers::timer* time_alignment_timer_, + srslte::timers::timer* contention_resolution_timer_, + mux *mux_unit, + demux *demux_unit); + void reset(); + void start_pdcch_order(); + void start_mac_order(uint32_t msg_len_bits = 56, bool is_ho = false); + void step(uint32_t tti); + bool is_successful(); + bool is_response_error(); + bool is_contention_resolution(); + void harq_retx(); + bool is_error(); + bool in_progress(); + void pdcch_to_crnti(bool contains_uplink_grant); + void timer_expired(uint32_t timer_id); + + void new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action); + void tb_decoded_ok(); + + void start_noncont(uint32_t preamble_index, uint32_t prach_mask); + + void start_pcap(srslte::mac_pcap* pcap); +private: + static bool uecrid_callback(void *arg, uint64_t uecri); + + bool contention_resolution_id_received(uint64_t uecri); + void process_timeadv_cmd(uint32_t ta_cmd); + void step_initialization(); + void step_resource_selection(); + void step_preamble_transmission(); + void step_pdcch_setup(); + void step_response_reception(); + void step_response_error(); + void step_backoff_wait(); + void step_contention_resolution(); + void step_completition(); + + // Buffer to receive RAR PDU + static const uint32_t MAX_RAR_PDU_LEN = 2048; + uint8_t rar_pdu_buffer[MAX_RAR_PDU_LEN]; + srslte::rar_pdu rar_pdu_msg; + + // Random Access parameters provided by higher layers defined in 5.1.1 + uint32_t configIndex; + uint32_t nof_preambles; + uint32_t nof_groupA_preambles; + uint32_t nof_groupB_preambles; + uint32_t messagePowerOffsetGroupB; + uint32_t messageSizeGroupA; + uint32_t responseWindowSize; + uint32_t powerRampingStep; + uint32_t preambleTransMax; + uint32_t iniReceivedTargetPower; + int delta_preamble_db; + uint32_t contentionResolutionTimer; + uint32_t maskIndex; + int preambleIndex; + uint32_t new_ra_msg_len; + + bool noncontention_enabled; + uint32_t next_preamble_idx; + uint32_t next_prach_mask; + + // Internal variables + uint32_t preambleTransmissionCounter; + uint32_t backoff_param_ms; + uint32_t sel_maskIndex; + uint32_t sel_preamble; + uint32_t backoff_interval_start; + uint32_t backoff_inteval; + int received_target_power_dbm; + uint32_t ra_rnti; + uint32_t current_ta; + + srslte_softbuffer_rx_t softbuffer_rar; + + enum { + IDLE = 0, + INITIALIZATION, // Section 5.1.1 + RESOURCE_SELECTION, // Section 5.1.2 + PREAMBLE_TRANSMISSION, // Section 5.1.3 + PDCCH_SETUP, + RESPONSE_RECEPTION, // Section 5.1.4 + RESPONSE_ERROR, + BACKOFF_WAIT, + CONTENTION_RESOLUTION, // Section 5.1.5 + COMPLETION, // Section 5.1.6 + COMPLETION_DONE, + RA_PROBLEM // Section 5.1.5 last part + } state; + + typedef enum {RA_GROUP_A, RA_GROUP_B} ra_group_t; + + ra_group_t last_msg3_group; + bool msg3_transmitted; + bool first_rar_received; + void read_params(); + + phy_interface_mac *phy_h; + srslte::log *log_h; + mux *mux_unit; + demux *demux_unit; + srslte::mac_pcap *pcap; + rrc_interface_mac *rrc; + + srslte::timers::timer *time_alignment_timer; + srslte::timers::timer *contention_resolution_timer; + + mac_interface_rrc::ue_rnti_t *rntis; + mac_interface_rrc::mac_cfg_t *mac_cfg; + + uint64_t transmitted_contention_id; + uint16_t transmitted_crnti; + + enum { + PDCCH_CRNTI_NOT_RECEIVED = 0, + PDCCH_CRNTI_UL_GRANT, + PDCCH_CRNTI_DL_GRANT + } pdcch_to_crnti_received; + + bool ra_is_ho; + bool started_by_pdcch; + uint32_t rar_grant_nbytes; + uint32_t rar_grant_tti; + bool msg3_flushed; + bool rar_received; +}; + +} // namespace srsue + +#endif // SRSUE_PROC_RA_H diff --git a/srsue/hdr/mac/proc_sr.h b/srsue/hdr/mac/proc_sr.h new file mode 100644 index 0000000..878f92f --- /dev/null +++ b/srsue/hdr/mac/proc_sr.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PROC_SR_H +#define SRSUE_PROC_SR_H + +#include +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/log.h" + +/* Scheduling Request procedure as defined in 5.4.4 of 36.321 */ + + +namespace srsue { + +class sr_proc +{ +public: + sr_proc(); + void init(phy_interface_mac *phy_h, rrc_interface_mac *rrc, srslte::log *log_h, mac_interface_rrc::mac_cfg_t *mac_cfg); + void step(uint32_t tti); + void reset(); + void start(); + bool need_random_access(); + +private: + bool need_tx(uint32_t tti); + + uint32_t sr_counter; + uint32_t dsr_transmax; + bool is_pending_sr; + mac_interface_rrc::mac_cfg_t *mac_cfg; + + rrc_interface_mac *rrc; + phy_interface_mac *phy_h; + srslte::log *log_h; + + bool initiated; + bool do_ra; +}; + +} // namespace srsue + +#endif // SRSUE_PROC_SR_H diff --git a/srsue/hdr/mac/ul_harq.h b/srsue/hdr/mac/ul_harq.h new file mode 100644 index 0000000..d62a94a --- /dev/null +++ b/srsue/hdr/mac/ul_harq.h @@ -0,0 +1,432 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_UL_HARQ_H +#define SRSUE_UL_HARQ_H + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/log.h" +#include "mux.h" +#include "ul_sps.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/common/timers.h" +#include "srslte/common/interfaces_common.h" + +/* Uplink HARQ entity as defined in 5.4.2 of 36.321 */ +namespace srsue { + +template +class ul_harq_entity { +public: + static uint32_t pidof(uint32_t tti) { + return (uint32_t) tti % N; + } + + ul_harq_entity() : proc(N) { + contention_timer = NULL; + + pcap = NULL; + mux_unit = NULL; + log_h = NULL; + params = NULL; + rntis = NULL; + average_retx = 0; + nof_pkts = 0; + } + + bool init(srslte::log *log_h_, + mac_interface_rrc_common::ue_rnti_t *rntis_, + mac_interface_rrc_common::ul_harq_params_t *params_, + srslte::timers::timer *contention_timer_, + mux *mux_unit_) { + log_h = log_h_; + mux_unit = mux_unit_; + params = params_; + rntis = rntis_; + contention_timer = contention_timer_; + for (uint32_t i = 0; i < N; i++) { + if (!proc[i].init(i, this)) { + return false; + } + } + return true; + } + + void reset() { + for (uint32_t i = 0; i < N; i++) { + proc[i].reset(); + } + ul_sps_assig.clear(); + } + + void reset_ndi() { + for (uint32_t i = 0; i < N; i++) { + proc[i].reset_ndi(); + } + } + + void start_pcap(srslte::mac_pcap *pcap_) { + pcap = pcap_; + } + + + /***************** PHY->MAC interface for UL processes **************************/ + void new_grant_ul(Tgrant grant, Taction *action) { + new_grant_ul_ack(grant, NULL, action); + } + void new_grant_ul_ack(Tgrant grant, bool *ack, Taction *action) + { + if (grant.rnti_type == SRSLTE_RNTI_USER || + grant.rnti_type == SRSLTE_RNTI_TEMP || + grant.rnti_type == SRSLTE_RNTI_RAR) + { + if (grant.rnti_type == SRSLTE_RNTI_USER && proc[pidof(grant.tti)].is_sps()) { + grant.ndi[0] = true; + } + run_tti(grant.tti, &grant, ack, action); + } else if (grant.rnti_type == SRSLTE_RNTI_SPS) { + if (grant.ndi[0]) { + grant.ndi[0] = proc[pidof(grant.tti)].get_ndi(); + run_tti(grant.tti, &grant, ack, action); + } else { + Info("Not implemented\n"); + } + } + } + + void harq_recv(uint32_t tti, bool ack, Taction *action) + { + run_tti(tti, NULL, &ack, action); + } + + int get_current_tbs(uint32_t tti) + { + int tti_harq = (int) tti-4; + if (tti_harq < 0) { + tti_harq += 10240; + } + uint32_t pid_harq = pidof(tti_harq); + return proc[pid_harq].get_current_tbs(); + } + + float get_average_retx() + { + return average_retx; + } + +private: + class ul_harq_process { + public: + ul_harq_process() { + pid = 0; + harq_feedback = false; + log_h = NULL; + bzero(&softbuffer, sizeof(srslte_softbuffer_tx_t)); + is_msg3 = false; + pdu_ptr = NULL; + current_tx_nb = 0; + current_irv = 0; + is_initiated = false; + is_grant_configured = false; + tti_last_tx = 0; + payload_buffer = NULL; + bzero(&cur_grant, sizeof(Tgrant)); + } + + ~ul_harq_process() + { + if (is_initiated) { + if (payload_buffer) { + free(payload_buffer); + } + srslte_softbuffer_tx_free(&softbuffer); + } + } + + bool init(uint32_t pid_, ul_harq_entity *parent) + { + if (srslte_softbuffer_tx_init(&softbuffer, 110)) { + fprintf(stderr, "Error initiating soft buffer\n"); + return false; + } else { + is_initiated = true; + harq_entity = parent; + log_h = harq_entity->log_h; + pid = pid_; + payload_buffer = (uint8_t*) srslte_vec_malloc(payload_buffer_len*sizeof(uint8_t)); + if (!payload_buffer) { + Error("Allocating memory\n"); + return false; + } + pdu_ptr = payload_buffer; + return true; + } + } + + void reset() + { + current_tx_nb = 0; + current_irv = 0; + tti_last_tx = 0; + is_grant_configured = false; + bzero(&cur_grant, sizeof(Tgrant)); + } + + void reset_ndi() { cur_grant.ndi[0] = false; } + + void run_tti(uint32_t tti_tx, Tgrant *grant, bool *ack, Taction* action) + { + if (ack) { + if (grant) { + if (grant->ndi[0] == get_ndi() && grant->phy_grant.ul.mcs.tbs != 0) { + *ack = false; + } + } + harq_feedback = *ack; + } + + // Reset HARQ process if TB has changed + if (harq_feedback && has_grant() && grant) { + if (grant->n_bytes[0] != cur_grant.n_bytes[0] && cur_grant.n_bytes[0] > 0 && grant->n_bytes[0] > 0) { + Debug("UL %d: Reset due to change of grant size last_grant=%d, new_grant=%d\n", + pid, cur_grant.n_bytes[0], grant->n_bytes[0]); + reset(); + } + } + + // Receive and route HARQ feedbacks + if (grant) { + if (grant->has_cqi_request && grant->phy_grant.ul.mcs.tbs == 0) { + /* Only CQI reporting (without SCH) */ + memcpy(&action->phy_grant.ul, &grant->phy_grant.ul, sizeof(srslte_ra_ul_grant_t)); + //memcpy(&cur_grant, grant, sizeof(Tgrant)); + action->tx_enabled = true; + action->rnti = grant->rnti; + } else if ((!(grant->rnti_type == SRSLTE_RNTI_TEMP) && grant->ndi[0] != get_ndi() && harq_feedback) || + (grant->rnti_type == SRSLTE_RNTI_USER && !has_grant()) || + grant->is_from_rar) + { + // New transmission + reset(); + + // Uplink grant in a RAR and there is a PDU in the Msg3 buffer + if (grant->is_from_rar && harq_entity->mux_unit->msg3_is_pending()) { + Debug("Getting Msg3 buffer payload, grant size=%d bytes\n", grant->n_bytes[0]); + pdu_ptr = harq_entity->mux_unit->msg3_get(payload_buffer, grant->n_bytes[0]); + if (pdu_ptr) { + generate_new_tx(tti_tx, true, grant, action); + } else { + Warning("UL RAR grant available but no Msg3 on buffer\n"); + } + + // Normal UL grant + } else { + if (grant->is_from_rar) { + grant->rnti = harq_entity->rntis->crnti; + } + // Request a MAC PDU from the Multiplexing & Assemble Unit + pdu_ptr = harq_entity->mux_unit->pdu_get(payload_buffer, grant->n_bytes[0], tti_tx, pid); + if (pdu_ptr) { + generate_new_tx(tti_tx, false, grant, action); + } else { + Warning("Uplink grant but no MAC PDU in Multiplex Unit buffer\n"); + } + } + } else if (has_grant()) { + // Adaptive Re-TX + generate_retx(tti_tx, grant, action); + } else { + Warning("UL %d: Received retransmission but no previous grant available for this PID.\n", pid); + } + } else if (has_grant()) { + // Non-Adaptive Re-Tx + generate_retx(tti_tx, action); + } + if (harq_entity->pcap && grant) { + if (grant->is_from_rar && harq_entity->rntis->temp_rnti) { + grant->rnti = harq_entity->rntis->temp_rnti; + } + harq_entity->pcap->write_ul_crnti(pdu_ptr, grant->n_bytes[0], grant->rnti, get_nof_retx(), tti_tx); + } + } + + uint32_t get_rv() + { + int rv_of_irv[4] = {0, 2, 3, 1}; + return rv_of_irv[current_irv%4]; + } + + bool has_grant() { return is_grant_configured; } + bool get_ndi() { return cur_grant.ndi[0]; } + bool is_sps() { return false; } + uint32_t get_nof_retx() { return current_tx_nb; } + int get_current_tbs() { return cur_grant.n_bytes[0]*8; } + + private: + Tgrant cur_grant; + + uint32_t pid; + uint32_t current_tx_nb; + uint32_t current_irv; + bool harq_feedback; + srslte::log *log_h; + ul_harq_entity *harq_entity; + bool is_grant_configured; + srslte_softbuffer_tx_t softbuffer; + bool is_msg3; + bool is_initiated; + uint32_t tti_last_tx; + + + const static int payload_buffer_len = 128*1024; + uint8_t *payload_buffer; + uint8_t *pdu_ptr; + + void generate_retx(uint32_t tti_tx, Taction *action) + { + generate_retx(tti_tx, NULL, action); + } + + // Retransmission with or w/o grant (Section 5.4.2.2) + void generate_retx(uint32_t tti_tx, Tgrant *grant, + Taction *action) + { + uint32_t max_retx; + if (is_msg3) { + max_retx = harq_entity->params->max_harq_msg3_tx; + } else { + max_retx = harq_entity->params->max_harq_tx; + } + + if (current_tx_nb >= max_retx) { + Info("UL %d: Maximum number of ReTX reached (%d). Discarting TB.\n", pid, max_retx); + reset(); + action->expect_ack = false; + return; + } + + int irv_of_rv[4] = {0, 3, 1, 2}; + + // HARQ entity requests an adaptive transmission + if (grant) { + if (grant->rv[0]) { + current_irv = irv_of_rv[grant->rv[0]%4]; + } + + Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d, HI=%s, ndi=%d, prev_ndi=%d\n", + pid, current_tx_nb, get_rv(), grant->n_bytes[0], harq_feedback?"ACK":"NACK", grant->ndi[0], cur_grant.ndi[0]); + + memcpy(&cur_grant, grant, sizeof(Tgrant)); + harq_feedback = false; + + generate_tx(tti_tx, action); + + // HARQ entity requests a non-adaptive transmission + } else if (!harq_feedback) { + // Non-adaptive retx are only sent if HI=NACK. If HI=ACK but no grant was received do not reset PID + Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d, HI=%s\n", + pid, current_tx_nb, get_rv(), cur_grant.n_bytes[0], harq_feedback?"ACK":"NACK"); + + generate_tx(tti_tx, action); + } + + // On every Msg3 retransmission, restart mac-ContentionResolutionTimer as defined in Section 5.1.5 + if (is_msg3) { + harq_entity->contention_timer->reset(); + } + + harq_entity->mux_unit->pusch_retx(tti_tx, pid); + } + + // New transmission (Section 5.4.2.2) + void generate_new_tx(uint32_t tti_tx, bool is_msg3_, Tgrant *grant, Taction *action) + { + if (grant) { + // Compute average number of retransmissions per packet considering previous packet + harq_entity->average_retx = SRSLTE_VEC_CMA((float) current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++); + memcpy(&cur_grant, grant, sizeof(Tgrant)); + harq_feedback = false; + is_grant_configured = true; + current_tx_nb = 0; + current_irv = 0; + is_msg3 = is_msg3_; + + Info("UL %d: New TX%s, RV=%d, TBS=%d, RNTI=%d\n", + pid, is_msg3?" for Msg3":"", get_rv(), cur_grant.n_bytes[0], + is_msg3?harq_entity->rntis->temp_rnti:cur_grant.rnti); + generate_tx(tti_tx, action); + } + } + + // Transmission of pending frame (Section 5.4.2.2) + void generate_tx(uint32_t tti_tx, Taction *action) + { + action->current_tx_nb = current_tx_nb; + current_tx_nb++; + action->expect_ack = true; + action->rnti = is_msg3?harq_entity->rntis->temp_rnti:cur_grant.rnti; + action->rv[0] = cur_grant.rv[0]>0?cur_grant.rv[0]:get_rv(); + action->softbuffers = &softbuffer; + action->tx_enabled = true; + action->payload_ptr[0] = pdu_ptr; + memcpy(&action->phy_grant, &cur_grant.phy_grant, sizeof(Tphygrant)); + + current_irv = (current_irv+1)%4; + tti_last_tx = tti_tx; + } + }; + + // Implements Section 5.4.2.1 + // Called with UL grant + void run_tti(uint32_t tti, Tgrant *grant, bool *ack, Taction* action) + { + uint32_t tti_tx = (tti+action->tti_offset)%10240; + proc[pidof(tti_tx)].run_tti(tti_tx, grant, ack, action); + } + + ul_sps ul_sps_assig; + + srslte::timers::timer *contention_timer; + mux *mux_unit; + std::vector proc; + srslte::log *log_h; + srslte::mac_pcap *pcap; + + mac_interface_rrc_common::ue_rnti_t *rntis; + mac_interface_rrc_common::ul_harq_params_t *params; + + float average_retx; + uint64_t nof_pkts; +}; + +} // namespace srsue + +#endif // SRSUE_UL_HARQ_H diff --git a/srsue/hdr/mac/ul_sps.h b/srsue/hdr/mac/ul_sps.h new file mode 100644 index 0000000..985b847 --- /dev/null +++ b/srsue/hdr/mac/ul_sps.h @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_UL_SPS_H +#define SRSUE_UL_SPS_H + +#include "srslte/common/log.h" +#include "srslte/common/timers.h" + +/* Uplink Semi-Persistent schedulign (Section 5.10.2) */ + + +namespace srsue { + +typedef _Complex float cf_t; + +class ul_sps +{ +public: + + void clear() {} + void reset(uint32_t tti) {} + bool get_pending_grant(uint32_t tti, mac_interface_phy::mac_grant_t *grant) { return false; } +private: + +}; + +} // namespace srsue + +#endif // SRSUE_UL_SPS_H diff --git a/srsue/hdr/metrics_csv.h b/srsue/hdr/metrics_csv.h new file mode 100644 index 0000000..f07b0c1 --- /dev/null +++ b/srsue/hdr/metrics_csv.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: metrics_csv.h + * Description: Metrics class writing to CSV file. + *****************************************************************************/ + +#ifndef SRSUE_METRICS_CSV_H +#define SRSUE_METRICS_CSV_H + +#include +#include +#include +#include +#include + +#include "srslte/common/metrics_hub.h" +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_csv : public srslte::metrics_listener +{ +public: + metrics_csv(std::string filename); + ~metrics_csv(); + + void set_metrics(ue_metrics_t &m, const uint32_t period_usec); + void set_ue_handle(ue_metrics_interface *ue_); + void stop(); + +private: + std::string float_to_string(float f, int digits, bool add_semicolon = true); + + float metrics_report_period; + std::ofstream file; + ue_metrics_interface* ue; + uint32_t n_reports; +}; + +} // namespace srsue + +#endif // SRSUE_METRICS_CSV_H diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h new file mode 100644 index 0000000..2b0df9c --- /dev/null +++ b/srsue/hdr/metrics_stdout.h @@ -0,0 +1,67 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: metrics_stdout.h + * Description: Metrics class printing to stdout. + *****************************************************************************/ + +#ifndef SRSUE_METRICS_STDOUT_H +#define SRSUE_METRICS_STDOUT_H + +#include +#include +#include + +#include "srslte/common/metrics_hub.h" +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_stdout : public srslte::metrics_listener +{ +public: + metrics_stdout(); + + void set_periodicity(float metrics_report_period_sec); + void toggle_print(bool b); + void set_metrics(ue_metrics_t &m, const uint32_t period_usec); + void set_ue_handle(ue_metrics_interface *ue_); + void stop() {}; + +private: + std::string float_to_string(float f, int digits); + std::string float_to_eng_string(float f, int digits); + std::string int_to_eng_string(int f, int digits); + + bool do_print; + uint8_t n_reports; + ue_metrics_interface* ue; +}; + +} // namespace srsue + +#endif // SRSUE_METRICS_STDOUT_H diff --git a/srsue/hdr/phy/phch_common.h b/srsue/hdr/phy/phch_common.h new file mode 100644 index 0000000..90379c8 --- /dev/null +++ b/srsue/hdr/phy/phch_common.h @@ -0,0 +1,239 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PHCH_COMMON_H +#define SRSUE_PHCH_COMMON_H + +#define TX_MODE_CONTINUOUS 1 + + +#include +#include +#include +#include "srslte/srslte.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/radio/radio.h" +#include "srslte/common/log.h" +#include "srslte/common/gen_mch_tables.h" +#include "phy_metrics.h" + + +namespace srsue { + +class chest_feedback_itf +{ +public: + virtual void in_sync() = 0; + virtual void out_of_sync() = 0; + virtual void set_cfo(float cfo) = 0; +}; + + +typedef enum{ + SUBFRAME_TYPE_REGULAR = 0, + SUBFRAME_TYPE_MBSFN, + SUBFRAME_TYPE_N_ITEMS, +} subframe_type_t; +static const char subframe_type_text[SUBFRAME_TYPE_N_ITEMS][20] = {"Regular", "MBSFN"}; + +/* Subframe config */ + +typedef struct { + subframe_type_t sf_type; + uint8_t mbsfn_area_id; + uint8_t non_mbsfn_region_length; + uint8_t mbsfn_mcs; + bool mbsfn_decode; + bool is_mcch; +} subframe_cfg_t; + + +/* Subclass that manages variables common to all workers */ + class phch_common { + public: + + /* Common variables used by all phy workers */ + phy_interface_rrc::phy_cfg_t *config; + phy_args_t *args; + rrc_interface_phy *rrc; + mac_interface_phy *mac; + srslte_ue_ul_t ue_ul; + + /* Power control variables */ + float pathloss; + float cur_pathloss; + float p0_preamble; + float cur_radio_power; + float cur_pusch_power; + float avg_rsrp; + float avg_rsrp_cqi; + float avg_rsrp_dbm; + float avg_rsrp_sync_dbm; + float avg_rsrq_db; + float avg_rssi_dbm; + float last_radio_rssi; + float rx_gain_offset; + float avg_snr_db_cqi; + float avg_snr_db_sync; + + float avg_noise; + bool pcell_meas_enabled; + + uint32_t pcell_report_period; + bool pcell_first_measurement; + + // Save last TBS for mcs>28 cases + int last_dl_tbs[2*HARQ_DELAY_MS][SRSLTE_MAX_CODEWORDS]; + uint32_t last_dl_tti[2*HARQ_DELAY_MS]; + + int last_ul_tbs[2*HARQ_DELAY_MS]; + uint32_t last_ul_tti[2*HARQ_DELAY_MS]; + srslte_mod_t last_ul_mod[2*HARQ_DELAY_MS]; + uint32_t last_ul_idx[2*HARQ_DELAY_MS]; + uint8_t last_ri; + uint8_t last_pmi; + + phch_common(uint32_t max_mutex = 3); + void init(phy_interface_rrc::phy_cfg_t *config, + phy_args_t *args, + srslte::log *_log, + srslte::radio *_radio, + rrc_interface_phy *rrc, + mac_interface_phy *_mac); + + /* For RNTI searches, -1 means now or forever */ + void set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_ul_rnti(uint32_t tti); + srslte_rnti_type_t get_ul_rnti_type(); + + void set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start = -1, int tti_end = -1); + uint16_t get_dl_rnti(uint32_t tti); + srslte_rnti_type_t get_dl_rnti_type(); + + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + bool get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant = NULL); + + void reset_pending_ack(uint32_t tti); + void set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs); + bool get_pending_ack(uint32_t tti); + bool get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs); + bool is_any_pending_ack(); + + void worker_end(uint32_t tti, bool tx_enable, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + + void set_nof_mutex(uint32_t nof_mutex); + + bool sr_enabled; + int sr_last_tx_tti; + + + srslte::radio* get_radio(); + + void set_cell(const srslte_cell_t &c); + uint32_t get_nof_prb(); + void set_dl_metrics(const dl_metrics_t &m); + void get_dl_metrics(dl_metrics_t &m); + void set_ul_metrics(const ul_metrics_t &m); + void get_ul_metrics(ul_metrics_t &m); + void set_sync_metrics(const sync_metrics_t &m); + void get_sync_metrics(sync_metrics_t &m); + + void reset_ul(); + void reset(); + + // MBSFN helpers + void build_mch_table(); + void build_mcch_table(); + void set_mcch(); + void get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti); + void set_mch_period_stop(uint32_t stop); + + private: + + bool have_mtch_stop; + pthread_mutex_t mtch_mutex; + pthread_cond_t mtch_cvar; + + + + std::vector tx_mutex; + + bool is_first_of_burst; + srslte::radio *radio_h; + float cfo; + srslte::log *log_h; + + + bool ul_rnti_active(uint32_t tti); + bool dl_rnti_active(uint32_t tti); + uint16_t ul_rnti, dl_rnti; + srslte_rnti_type_t ul_rnti_type, dl_rnti_type; + int ul_rnti_start, ul_rnti_end, dl_rnti_start, dl_rnti_end; + + float time_adv_sec; + + srslte_dci_rar_grant_t rar_grant; + bool rar_grant_pending; + uint32_t rar_grant_tti; + + typedef struct { + bool enabled; + uint32_t I_lowest; + uint32_t n_dmrs; + } pending_ack_t; + pending_ack_t pending_ack[TTIMOD_SZ]; + + bool is_first_tx; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + + srslte_cell_t cell; + + dl_metrics_t dl_metrics; + uint32_t dl_metrics_count; + bool dl_metrics_read; + ul_metrics_t ul_metrics; + uint32_t ul_metrics_count; + bool ul_metrics_read; + sync_metrics_t sync_metrics; + uint32_t sync_metrics_count; + bool sync_metrics_read; + + // MBSFN + bool sib13_configured; + bool mcch_configured; + uint32_t mch_period_stop; + uint8_t mch_table[40]; + uint8_t mcch_table[10]; + + bool is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + bool is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti); + }; +} // namespace srsue + +#endif // SRSUE_PDCH_COMMON_H diff --git a/srsue/hdr/phy/phch_recv.h b/srsue/hdr/phy/phch_recv.h new file mode 100644 index 0000000..78559d3 --- /dev/null +++ b/srsue/hdr/phy/phch_recv.h @@ -0,0 +1,446 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PHCH_RECV_H +#define SRSUE_PHCH_RECV_H + +#include +#include + +#include "srslte/srslte.h" +#include "srslte/common/log.h" +#include "srslte/common/threads.h" +#include "srslte/common/thread_pool.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/radio/radio_multi.h" +#include "prach.h" +#include "phch_worker.h" +#include "phch_common.h" +#include "srslte/interfaces/ue_interfaces.h" + +namespace srsue { + +typedef _Complex float cf_t; + + +class phch_recv : public thread, public chest_feedback_itf +{ +public: + phch_recv(); + ~phch_recv(); + + void init(srslte::radio_multi* radio_handler, mac_interface_phy *mac,rrc_interface_phy *rrc, + prach *prach_buffer, srslte::thread_pool *_workers_pool, + phch_common *_worker_com, srslte::log* _log_h, srslte::log *_log_phy_lib_h, uint32_t nof_rx_antennas, uint32_t prio, int sync_cpu_affinity = -1); + void stop(); + void radio_overflow(); + + // RRC interface for controling the SYNC state + phy_interface_rrc::cell_search_ret_t cell_search(phy_interface_rrc::phy_cell_t *cell); + bool cell_select(phy_interface_rrc::phy_cell_t *cell); + bool cell_is_camping(); + + // RRC interface for controlling the neighbour cell measurement + void meas_reset(); + int meas_start(uint32_t earfcn, int pci); + int meas_stop(uint32_t earfcn, int pci); + + // from chest_feedback_itf + void in_sync(); + void out_of_sync(); + void set_cfo(float cfo); + + void set_time_adv_sec(float time_adv_sec); + void get_current_cell(srslte_cell_t *cell, uint32_t *earfcn = NULL); + uint32_t get_current_tti(); + + // From UE configuration + void set_agc_enable(bool enable); + void set_earfcn(std::vector earfcn); + void force_freq(float dl_freq, float ul_freq); + + // Other functions + const static int MUTEX_X_WORKER = 4; + double set_rx_gain(double gain); + int radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time); + int scell_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time); + +private: + + // Class to run cell search + class search { + public: + typedef enum {CELL_NOT_FOUND, CELL_FOUND, ERROR, TIMEOUT} ret_code; + + ~search(); + void init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, phch_recv *parent); + void reset(); + float get_last_cfo(); + void set_agc_enable(bool enable); + ret_code run(srslte_cell_t *cell); + + private: + phch_recv *p; + srslte::log *log_h; + cf_t *buffer[SRSLTE_MAX_PORTS]; + srslte_ue_cellsearch_t cs; + srslte_ue_mib_sync_t ue_mib_sync; + int force_N_id_2; + }; + + // Class to synchronize system frame number + class sfn_sync { + public: + typedef enum {IDLE, SFN_FOUND, SFX0_FOUND, SFN_NOFOUND, ERROR} ret_code; + + ~sfn_sync(); + void init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_subframes = SFN_SYNC_NOF_SUBFRAMES); + void reset(); + bool set_cell(srslte_cell_t cell); + ret_code run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only = false); + + private: + const static int SFN_SYNC_NOF_SUBFRAMES = 100; + + uint32_t cnt; + uint32_t timeout; + srslte::log *log_h; + srslte_ue_sync_t *ue_sync; + cf_t *buffer[SRSLTE_MAX_PORTS]; + srslte_ue_mib_t ue_mib; + }; + + // Class to perform cell measurements + class measure { + + // TODO: This class could early stop once the variance between the last N measurements is below 3GPP requirements + + public: + typedef enum {IDLE, MEASURE_OK, ERROR} ret_code; + + ~measure(); + void init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, + uint32_t nof_rx_antennas, uint32_t nof_subframes = RSRP_MEASURE_NOF_FRAMES); + void reset(); + void set_cell(srslte_cell_t cell); + ret_code run_subframe(uint32_t sf_idx); + ret_code run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx); + ret_code run_multiple_subframes(cf_t *buffer, uint32_t offset, uint32_t sf_idx, uint32_t nof_sf); + float rssi(); + float rsrp(); + float rsrq(); + float snr(); + uint32_t frame_st_idx(); + void set_rx_gain_offset(float rx_gain_offset); + private: + srslte::log *log_h; + srslte_ue_dl_t ue_dl; + cf_t *buffer[SRSLTE_MAX_PORTS]; + uint32_t cnt; + uint32_t nof_subframes; + uint32_t current_prb; + float rx_gain_offset; + float mean_rsrp, mean_rsrq, mean_snr, mean_rssi; + uint32_t final_offset; + const static int RSRP_MEASURE_NOF_FRAMES = 5; + }; + + // Class to receive secondary cell + class scell_recv { + public: + const static int MAX_CELLS = 8; + typedef struct { + uint32_t pci; + float rsrp; + float rsrq; + uint32_t offset; + } cell_info_t; + void init(srslte::log *log_h, bool sic_pss_enabled, uint32_t max_sf_window); + void deinit(); + void reset(); + int find_cells(cf_t *input_buffer, float rx_gain_offset, srslte_cell_t current_cell, uint32_t nof_sf, cell_info_t found_cells[MAX_CELLS]); + private: + + cf_t *sf_buffer[SRSLTE_MAX_PORTS]; + srslte::log *log_h; + srslte_sync_t sync_find; + + bool sic_pss_enabled; + uint32_t current_fft_sz; + measure measure_p; + }; + + /* TODO: Intra-freq measurements can be improved by capturing 200 ms length signal and run cell search + + * measurements offline using sync object and finding multiple cells for each N_id_2 + */ + + // Class to perform intra-frequency measurements + class intra_measure : public thread { + public: + ~intra_measure(); + void init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h); + void stop(); + void add_cell(int pci); + void rem_cell(int pci); + void set_primay_cell(uint32_t earfcn, srslte_cell_t cell); + void clear_cells(); + int get_offset(uint32_t pci); + void write(uint32_t tti, cf_t *data, uint32_t nsamples); + private: + void run_thread(); + const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; + + scell_recv scell; + rrc_interface_phy *rrc; + srslte::log *log_h; + phch_common *common; + uint32_t current_earfcn; + uint32_t current_sflen; + srslte_cell_t primary_cell; + std::vector active_pci; + + srslte::tti_sync_cv tti_sync; + + cf_t *search_buffer; + + scell_recv::cell_info_t info[scell_recv::MAX_CELLS]; + + bool running; + bool receive_enabled; + bool receiving; + uint32_t measure_tti; + uint32_t receive_cnt; + srslte_ringbuffer_t ring_buffer; + }; + + // 36.133 9.1.2.1 for band 7 + const static float ABSOLUTE_RSRP_THRESHOLD_DBM = -125; + + std::vector earfcn; + + void reset(); + void radio_error(); + void set_ue_sync_opts(srslte_ue_sync_t *q, float cfo); + void run_thread(); + float get_tx_cfo(); + + void set_sampling_rate(); + bool set_frequency(); + bool set_cell(); + + uint32_t new_earfcn; + srslte_cell_t new_cell; + + bool radio_is_overflow; + bool radio_overflow_return; + bool running; + + // Objects for internal use + search search_p; + sfn_sync sfn_p; + intra_measure intra_freq_meas; + + uint32_t current_sflen; + int next_offset; + uint32_t nof_rx_antennas; + + // Pointers to other classes + mac_interface_phy *mac; + rrc_interface_phy *rrc; + srslte::log *log_h; + srslte::log *log_phy_lib_h; + srslte::thread_pool *workers_pool; + srslte::radio_multi *radio_h; + phch_common *worker_com; + prach *prach_buffer; + + // Object for synchronization of the primary cell + srslte_ue_sync_t ue_sync; + + // Buffer for primary cell samples + cf_t *sf_buffer[SRSLTE_MAX_PORTS]; + + // Sync metrics + sync_metrics_t metrics; + + // in-sync / out-of-sync counters + uint32_t out_of_sync_cnt; + uint32_t in_sync_cnt; + + const static uint32_t NOF_OUT_OF_SYNC_SF = 200; + const static uint32_t NOF_IN_SYNC_SF = 100; + + // State machine for SYNC thread + class sync_state { + public: + typedef enum { + IDLE = 0, + CELL_SEARCH, + SFN_SYNC, + CAMPING, + } state_t; + + /* Run_state is called by the main thread at the start of each loop. It updates the state + * and returns the current state + */ + state_t run_state() { + pthread_mutex_lock(&inside); + cur_state = next_state; + pthread_cond_broadcast(&cvar); + pthread_mutex_unlock(&inside); + return cur_state; + } + + // Called by the main thread at the end of each state to indicate it has finished. + void state_exit(bool exit_ok = true) { + pthread_mutex_lock(&inside); + if (cur_state == SFN_SYNC && exit_ok == true) { + next_state = CAMPING; + } else { + next_state = IDLE; + } + pthread_mutex_unlock(&inside); + } + void force_sfn_sync() { + pthread_mutex_lock(&inside); + next_state = SFN_SYNC; + pthread_mutex_unlock(&inside); + } + + /* Functions to be called from outside the STM thread to instruct the STM to switch state. + * The functions change the state and wait until it has changed it. + * + * These functions are mutexed and only 1 can be called at a time + */ + void go_idle() { + pthread_mutex_lock(&outside); + go_state(IDLE); + pthread_mutex_unlock(&outside); + } + void run_cell_search() { + pthread_mutex_lock(&outside); + go_state(CELL_SEARCH); + wait_state_change(CELL_SEARCH); + pthread_mutex_unlock(&outside); + } + void run_sfn_sync() { + pthread_mutex_lock(&outside); + go_state(SFN_SYNC); + wait_state_change(SFN_SYNC); + pthread_mutex_unlock(&outside); + } + + + /* Helpers below this */ + bool is_idle() { + return cur_state == IDLE; + } + bool is_camping() { + return cur_state == CAMPING; + } + + const char *to_string() { + switch(cur_state) { + case IDLE: + return "IDLE"; + case CELL_SEARCH: + return "SEARCH"; + case SFN_SYNC: + return "SYNC"; + case CAMPING: + return "CAMPING"; + default: + return "UNKNOWN"; + } + } + + sync_state() { + pthread_mutex_init(&inside, NULL); + pthread_mutex_init(&outside, NULL); + pthread_cond_init(&cvar, NULL); + cur_state = IDLE; + next_state = IDLE; + } + private: + + void go_state(state_t s) { + pthread_mutex_lock(&inside); + next_state = s; + while(cur_state != s) { + pthread_cond_wait(&cvar, &inside); + } + pthread_mutex_unlock(&inside); + } + + /* Waits until there is a call to set_state() and then run_state(). Returns when run_state() returns */ + void wait_state_change(state_t prev_state) { + pthread_mutex_lock(&inside); + while(cur_state == prev_state) { + pthread_cond_wait(&cvar, &inside); + } + pthread_mutex_unlock(&inside); + } + + state_t cur_state, next_state; + pthread_mutex_t inside, outside; + pthread_cond_t cvar; + + }; + + pthread_mutex_t rrc_mutex; + + sync_state phy_state; + + search::ret_code cell_search_ret; + + // Sampling rate mode (find is 1.96 MHz, camp is the full cell BW) + enum { + SRATE_NONE=0, SRATE_FIND, SRATE_CAMP + } srate_mode; + float current_srate; + + // This is the primary cell + srslte_cell_t cell; + bool started; + float time_adv_sec, next_time_adv_sec; + uint32_t tti; + bool do_agc; + + uint32_t nof_tx_mutex; + uint32_t tx_mutex_cnt; + + float ul_dl_factor; + int current_earfcn; + uint32_t cellsearch_earfcn_index; + + float dl_freq; + float ul_freq; + +}; + +} // namespace srsue + +#endif // SRSUE_PHCH_RECV_H diff --git a/srsue/hdr/phy/phch_worker.h b/srsue/hdr/phy/phch_worker.h new file mode 100644 index 0000000..2446579 --- /dev/null +++ b/srsue/hdr/phy/phch_worker.h @@ -0,0 +1,200 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PHCH_WORKER_H +#define SRSUE_PHCH_WORKER_H + +#include +#include "srslte/srslte.h" +#include "srslte/common/thread_pool.h" +#include "srslte/common/trace.h" +#include "phch_common.h" + +#define LOG_EXECTIME + +namespace srsue { + +class phch_worker : public srslte::thread_pool::worker +{ +public: + + phch_worker(); + ~phch_worker(); + void reset(); + void set_common(phch_common *phy); + void enable_pdsch_coworker(); + bool init(uint32_t max_prb, srslte::log *log, srslte::log *log_phy_lib_h, chest_feedback_itf *chest_loop); + + bool set_cell(srslte_cell_t cell); + + /* Functions used by main PHY thread */ + cf_t* get_buffer(uint32_t antenna_idx); + void set_tti(uint32_t tti, uint32_t tx_tti); + void set_tx_time(srslte_timestamp_t tx_time, uint32_t next_offset); + void set_prach(cf_t *prach_ptr, float prach_power); + void set_cfo(float cfo); + + void set_ul_params(bool pregen_disabled = false); + void set_crnti(uint16_t rnti); + void enable_pregen_signals(bool enabled); + + void start_trace(); + void write_trace(std::string filename); + + int read_ce_abs(float *ce_abs, uint32_t tx_antenna, uint32_t rx_antenna); + uint32_t get_cell_nof_ports() { + if (cell_initiated) { + return cell.nof_ports; + } else { + return 1; + } + }; + uint32_t get_rx_nof_antennas() { + return ue_dl.nof_rx_antennas; + }; + int read_pdsch_d(cf_t *pdsch_d); + void start_plot(); + + float get_ref_cfo(); + float get_snr(); + float get_rsrp(); + float get_noise(); + float get_cfo(); + +private: + /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ + void work_imp(); + + + /* Internal methods */ + + void compute_ri(uint8_t *ri, uint8_t *pmi, float *sinr); + bool extract_fft_and_pdcch_llr(subframe_cfg_t sf_cfg); + + /* ... for DL */ + bool decode_pdcch_ul(mac_interface_phy::mac_grant_t *grant); + bool decode_pdcch_dl(mac_interface_phy::mac_grant_t *grant); + bool decode_phich(bool *ack); + + int decode_pdsch(srslte_ra_dl_grant_t *grant, + uint8_t *payload[SRSLTE_MAX_CODEWORDS], + srslte_softbuffer_rx_t *softbuffers[SRSLTE_MAX_CODEWORDS], + int rv[SRSLTE_MAX_CODEWORDS], + uint16_t rnti, + uint32_t pid, + bool acks[SRSLTE_MAX_CODEWORDS]); + + bool decode_pmch(srslte_ra_dl_grant_t *grant, + uint8_t *payload, + srslte_softbuffer_rx_t* softbuffer, + uint16_t mbsfn_area_id); + + /* ... for UL */ + void encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, uint32_t current_tx_nb, srslte_softbuffer_tx_t *softbuffer, + uint32_t rv, uint16_t rnti, bool is_from_rar); + void encode_pucch(); + void encode_srs(); + void reset_uci(); + void set_uci_sr(); + void set_uci_periodic_cqi(); + void set_uci_aperiodic_cqi(); + void set_uci_ack(bool ack[SRSLTE_MAX_CODEWORDS], bool tb_en[SRSLTE_MAX_CODEWORDS]); + bool srs_is_ready_to_send(); + float set_power(float tx_power); + void setup_tx_gain(); + + void update_measurements(); + + void tr_log_start(); + void tr_log_end(); + struct timeval tr_time[3]; + srslte::trace tr_exec; + bool trace_enabled; + + pthread_mutex_t mutex; + + /* Common objects */ + phch_common *phy; + srslte::log *log_h; + srslte::log *log_phy_lib_h; + chest_feedback_itf *chest_loop; + srslte_cell_t cell; + bool mem_initiated; + bool cell_initiated; + cf_t *signal_buffer[SRSLTE_MAX_PORTS]; + uint32_t tti; + uint32_t tx_tti; + bool pregen_enabled; + uint32_t last_dl_pdcch_ncce; + bool rnti_is_set; + + uint32_t next_offset; + + /* Objects for DL */ + srslte_ue_dl_t ue_dl; + uint32_t cfi; + uint16_t dl_rnti; + + /* Objects for UL */ + srslte_ue_ul_t ue_ul; + srslte_timestamp_t tx_time; + srslte_uci_data_t uci_data; + srslte_cqi_value_t cqi_report; + uint16_t ul_rnti; + + // UL configuration parameters + srslte_refsignal_srs_cfg_t srs_cfg; + srslte_pucch_cfg_t pucch_cfg; + srslte_refsignal_dmrs_pusch_cfg_t dmrs_cfg; + srslte_pusch_hopping_cfg_t pusch_hopping; + srslte_pucch_sched_t pucch_sched; + srslte_uci_cfg_t uci_cfg; + srslte_cqi_periodic_cfg_t period_cqi; + srslte_ue_ul_powerctrl_t power_ctrl; + uint32_t I_sr; + bool sr_configured; + float cfo; + bool rar_cqi_request; + cf_t *prach_ptr; + float prach_power; + + uint32_t rssi_read_cnt; + + // Metrics + dl_metrics_t dl_metrics; + ul_metrics_t ul_metrics; + +#ifdef LOG_EXECTIME + struct timeval logtime_start[3]; + bool chest_done; +#endif + +}; + +} // namespace srsue + +#endif // SRSUE_PHCH_WORKER_H + diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h new file mode 100644 index 0000000..fa3ed2f --- /dev/null +++ b/srsue/hdr/phy/phy.h @@ -0,0 +1,194 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PHY_H +#define SRSUE_PHY_H + +#include "srslte/srslte.h" +#include "srslte/common/log_filter.h" +#include "phy_metrics.h" +#include "phch_recv.h" +#include "prach.h" +#include "phch_worker.h" +#include "phch_common.h" +#include "srslte/radio/radio.h" +#include "srslte/common/task_dispatcher.h" +#include "srslte/common/trace.h" +#include "srslte/interfaces/ue_interfaces.h" + +namespace srsue { + +typedef _Complex float cf_t; + +class phy + : public phy_interface_mac + , public phy_interface_rrc + , public thread +{ +public: + phy(); + bool init(srslte::radio_multi *radio_handler, + mac_interface_phy *mac, + rrc_interface_phy *rrc, + std::vector log_vec, + phy_args_t *args = NULL); + + void stop(); + + void wait_initialize(); + bool is_initiated(); + + void set_agc_enable(bool enabled); + + void get_metrics(phy_metrics_t &m); + void srslte_phy_logger(phy_logger_level_t log_level, char *str); + + + static uint32_t tti_to_SFN(uint32_t tti); + static uint32_t tti_to_subf(uint32_t tti); + + void enable_pregen_signals(bool enable); + + void start_trace(); + void write_trace(std::string filename); + + void set_earfcn(std::vector earfcns); + void force_freq(float dl_freq, float ul_freq); + + void radio_overflow(); + + /********** RRC INTERFACE ********************/ + void reset(); + void configure_ul_params(bool pregen_disabled = false); + cell_search_ret_t cell_search(phy_cell_t *cell); + bool cell_select(phy_cell_t *cell); + + void meas_reset(); + int meas_start(uint32_t earfcn, int pci); + int meas_stop(uint32_t earfcn, int pci); + + // also MAC interface + bool cell_is_camping(); + + /********** MAC INTERFACE ********************/ + /* Sets a C-RNTI allowing the PHY to pregenerate signals if necessary */ + void set_crnti(uint16_t rnti); + + /* Instructs the PHY to configure using the parameters written by set_param() */ + void configure_prach_params(); + + /* Transmits PRACH in the next opportunity */ + void prach_send(uint32_t preamble_idx, int allowed_subframe = -1, float target_power_dbm = 0.0); + int prach_tx_tti(); + + /* Indicates the transmission of a SR signal in the next opportunity */ + void sr_send(); + int sr_last_tx_tti(); + + // Time advance commands + void set_timeadv_rar(uint32_t ta_cmd); + void set_timeadv(uint32_t ta_cmd); + + /* Sets RAR grant payload */ + void set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]); + + /* Instruct the PHY to decode PDCCH with the CRC scrambled with given RNTI */ + void pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start = -1, int tti_end = -1); + void pdcch_ul_search_reset(); + void pdcch_dl_search_reset(); + + /* Get/Set PHY parameters interface from RRC */ + void get_config(phy_cfg_t *phy_cfg); + void set_config(phy_cfg_t *phy_cfg); + void set_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated); + void set_config_common(phy_cfg_common_t *common); + void set_config_tdd(LIBLTE_RRC_TDD_CONFIG_STRUCT *tdd); + void set_config_64qam_en(bool enable); + void set_config_mbsfn_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2); + void set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + void set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch); + + /*Set MAC->PHY MCH period stopping point*/ + void set_mch_period_stop(uint32_t stop); + + + float get_phr(); + float get_pathloss_db(); + + uint32_t get_current_tti(); + + void get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn = NULL); + uint32_t get_current_earfcn(); + uint32_t get_current_pci(); + + void start_plot(); + +private: + + void run_thread(); + + bool initiated; + uint32_t nof_workers; + uint32_t nof_coworkers; + + const static int MAX_WORKERS = 3; + const static int DEFAULT_WORKERS = 2; + + const static int SF_RECV_THREAD_PRIO = 1; + const static int WORKERS_THREAD_PRIO = 0; + + srslte::radio_multi *radio_handler; + std::vector log_vec; + srslte::log *log_h; + srslte::log *log_phy_lib_h; + srsue::mac_interface_phy *mac; + srsue::rrc_interface_phy *rrc; + + srslte::thread_pool workers_pool; + std::vector workers; + phch_common workers_common; + phch_recv sf_recv; + prach prach_buffer; + + srslte_cell_t cell; + + phy_cfg_t config; + phy_args_t *args; + phy_args_t default_args; + + /* Current time advance */ + uint32_t n_ta; + + bool init_(srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log *log_h, bool do_agc, uint32_t nof_workers); + void set_default_args(phy_args_t *args); + bool check_args(phy_args_t *args); + +}; + +} // namespace srsue + +#endif // SRSUE_PHY_H diff --git a/srsue/hdr/phy/phy_metrics.h b/srsue/hdr/phy/phy_metrics.h new file mode 100644 index 0000000..eb2f560 --- /dev/null +++ b/srsue/hdr/phy/phy_metrics.h @@ -0,0 +1,69 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PHY_METRICS_H +#define SRSUE_PHY_METRICS_H + + +namespace srsue { + +struct sync_metrics_t +{ + float ta_us; + float cfo; + float sfo; +}; + +struct dl_metrics_t +{ + float n; + float sinr; + float rsrp; + float rsrq; + float rssi; + float turbo_iters; + float mcs; + float pathloss; + float mabr_mbps; +}; + +struct ul_metrics_t +{ + float mcs; + float power; + float mabr_mbps; +}; + +struct phy_metrics_t +{ + sync_metrics_t sync; + dl_metrics_t dl; + ul_metrics_t ul; +}; + +} // namespace srsue + +#endif // SRSUE_PHY_METRICS_H diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h new file mode 100644 index 0000000..06d8f61 --- /dev/null +++ b/srsue/hdr/phy/prach.h @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PRACH_H +#define SRSUE_PRACH_H + +#include + +#include "srslte/srslte.h" +#include "srslte/radio/radio.h" +#include "srslte/common/log.h" +#include "srslte/interfaces/ue_interfaces.h" + +namespace srsue { + + class prach { + public: + prach() { + bzero(&prach_obj, sizeof(srslte_prach_t)); + bzero(&cell, sizeof(srslte_cell_t)); + bzero(&cfo_h, sizeof(srslte_cfo_t)); + + args = NULL; + config = NULL; + transmitted_tti = 0; + target_power_dbm = 0; + mem_initiated = false; + cell_initiated = false; + signal_buffer = NULL; + } + ~prach(); + void init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config, uint32_t max_prb, phy_args_t *args, srslte::log *log_h); + bool set_cell(srslte_cell_t cell); + bool prepare_to_send(uint32_t preamble_idx, int allowed_subframe = -1, float target_power_dbm = -1); + bool is_ready_to_send(uint32_t current_tti); + bool is_pending(); + int tx_tti(); + cf_t* generate(float cfo, uint32_t *nof_sf, float *target_power = NULL); + + private: + + const static int MAX_LEN_SF = 3; + + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config; + phy_args_t *args; + + srslte::log *log_h; + int preamble_idx; + int allowed_subframe; + bool mem_initiated; + bool cell_initiated; + uint32_t len; + cf_t *buffer[64]; + srslte_prach_t prach_obj; + int transmitted_tti; + srslte_cell_t cell; + cf_t *signal_buffer; + srslte_cfo_t cfo_h; + float target_power_dbm; + + }; + +} // namespace srsue + +#endif // SRSUE_PRACH_H diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h new file mode 100644 index 0000000..b45c079 --- /dev/null +++ b/srsue/hdr/ue.h @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: ue.h + * Description: Top-level UE class. Creates and links all + * layers and helpers. + *****************************************************************************/ + +#ifndef SRSUE_UE_H +#define SRSUE_UE_H + +#include +#include +#include + +#include "ue_base.h" +#include "srslte/radio/radio_multi.h" +#include "phy/phy.h" +#include "mac/mac.h" +#include "srslte/upper/rlc.h" +#include "srslte/upper/pdcp.h" +#include "upper/rrc.h" +#include "upper/nas.h" +#include "upper/gw.h" +#include "upper/usim.h" + +#include "srslte/common/buffer_pool.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" + +#include "ue_metrics_interface.h" + +namespace srsue { + +/******************************************************************************* + Main UE class +*******************************************************************************/ + +class ue + :public ue_base +{ +public: + ue(); + + bool init(all_args_t *args_); + void stop(); + bool attach(); + bool deattach(); + bool is_attached(); + void start_plot(); + void print_mbms(); + bool mbms_service_start(uint32_t serv, uint32_t port); + + void print_pool(); + + static void rf_msg(srslte_rf_error_t error); + + // UE metrics interface + bool get_metrics(ue_metrics_t &m); + + void pregenerate_signals(bool enable); + + void radio_overflow(); + +private: + virtual ~ue(); + + srslte::radio_multi radio; + srsue::phy phy; + srsue::mac mac; + srslte::mac_pcap mac_pcap; + srslte::nas_pcap nas_pcap; + srslte::rlc rlc; + srslte::pdcp pdcp; + srsue::rrc rrc; + srsue::nas nas; + srsue::gw gw; + srsue::usim_base* usim; + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + // rf_log is on ue_base + std::vector phy_log; + srslte::log_filter mac_log; + srslte::log_filter rlc_log; + srslte::log_filter pdcp_log; + srslte::log_filter rrc_log; + srslte::log_filter nas_log; + srslte::log_filter gw_log; + srslte::log_filter usim_log; + srslte::log_filter pool_log; + + all_args_t *args; + bool started; + + bool check_srslte_version(); +}; + +} // namespace srsue + +#endif // SRSUE_UE_H + diff --git a/srsue/hdr/ue_base.h b/srsue/hdr/ue_base.h new file mode 100644 index 0000000..c41a951 --- /dev/null +++ b/srsue/hdr/ue_base.h @@ -0,0 +1,196 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +/****************************************************************************** + * File: ue_base.h + * Description: Base class for UEs. + *****************************************************************************/ + +#ifndef SRSUE_UE_BASE_H +#define SRSUE_UE_BASE_H + +#include +#include +#include +#include "srslte/radio/radio_multi.h" +#include "phy/phy.h" +#include "upper/usim.h" +#include "upper/rrc.h" +#include "upper/nas.h" +#include "srslte/interfaces/ue_interfaces.h" + +#include "srslte/common/logger.h" +#include "srslte/common/log_filter.h" + +#include "ue_metrics_interface.h" + +namespace srsue { + +/******************************************************************************* + UE Parameters +*******************************************************************************/ + +typedef struct { + uint32_t dl_earfcn; + float dl_freq; + float ul_freq; + float freq_offset; + float rx_gain; + float tx_gain; + uint32_t nof_rx_ant; + std::string device_name; + std::string device_args; + std::string time_adv_nsamples; + std::string burst_preamble; + std::string continuous_tx; +}rf_args_t; + +typedef struct { + bool enable; + std::string filename; + bool nas_enable; + std::string nas_filename; +}pcap_args_t; + +typedef struct { + bool enable; + std::string phy_filename; + std::string radio_filename; +}trace_args_t; + +typedef struct { + std::string phy_level; + std::string phy_lib_level; + std::string mac_level; + std::string rlc_level; + std::string pdcp_level; + std::string rrc_level; + std::string gw_level; + std::string nas_level; + std::string usim_level; + std::string all_level; + int phy_hex_limit; + int mac_hex_limit; + int rlc_hex_limit; + int pdcp_hex_limit; + int rrc_hex_limit; + int gw_hex_limit; + int nas_hex_limit; + int usim_hex_limit; + int all_hex_limit; + int file_max_size; + std::string filename; +}log_args_t; + +typedef struct { + bool enable; +}gui_args_t; + +typedef struct { + std::string ip_netmask; + phy_args_t phy; + float metrics_period_secs; + bool pregenerate_signals; + bool print_buffer_state; + bool metrics_csv_enable; + std::string metrics_csv_filename; + int mbms_service; +}expert_args_t; + +typedef struct { + rf_args_t rf; + rf_cal_t rf_cal; + pcap_args_t pcap; + trace_args_t trace; + log_args_t log; + gui_args_t gui; + usim_args_t usim; + rrc_args_t rrc; + std::string ue_category_str; + nas_args_t nas; + expert_args_t expert; +}all_args_t; + +typedef enum { + LTE = 0, + SRSUE_INSTANCE_TYPE_NITEMS +} srsue_instance_type_t; +static const char srsue_instance_type_text[SRSUE_INSTANCE_TYPE_NITEMS][10] = { "LTE" }; + + +/******************************************************************************* + Main UE class +*******************************************************************************/ + +class ue_base + :public ue_interface + ,public ue_metrics_interface +{ +public: + ue_base(); + virtual ~ue_base(); + + static ue_base* get_instance(srsue_instance_type_t type); + + void cleanup(void); + + virtual bool init(all_args_t *args_) = 0; + virtual void stop() = 0; + virtual bool attach() = 0; + virtual bool deattach() = 0; + virtual bool is_attached() = 0; + virtual void start_plot() = 0; + + virtual void print_pool() = 0; + + virtual void radio_overflow() = 0; + + virtual void print_mbms() = 0; + virtual bool mbms_service_start(uint32_t serv, uint32_t port) = 0; + + void handle_rf_msg(srslte_rf_error_t error); + + // UE metrics interface + virtual bool get_metrics(ue_metrics_t &m) = 0; + + virtual void pregenerate_signals(bool enable) = 0; + + srslte::log_filter rf_log; + rf_metrics_t rf_metrics; + srslte::LOG_LEVEL_ENUM level(std::string l); + + std::string get_build_mode(); + std::string get_build_info(); + std::string get_build_string(); + +private: + srslte::byte_buffer_pool *pool; +}; + +} // namespace srsue + +#endif // SRSUE_UE_BASE_H + diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h new file mode 100644 index 0000000..968b0de --- /dev/null +++ b/srsue/hdr/ue_metrics_interface.h @@ -0,0 +1,65 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_UE_METRICS_INTERFACE_H +#define SRSUE_UE_METRICS_INTERFACE_H + +#include + +#include "srslte/common/metrics_hub.h" +#include "upper/gw_metrics.h" +#include "srslte/upper/rlc_metrics.h" +#include "mac/mac_metrics.h" +#include "phy/phy_metrics.h" + +namespace srsue { + +typedef struct { + uint32_t rf_o; + uint32_t rf_u; + uint32_t rf_l; + bool rf_error; +}rf_metrics_t; + +typedef struct { + rf_metrics_t rf; + phy_metrics_t phy; + mac_metrics_t mac; + srslte::rlc_metrics_t rlc; + gw_metrics_t gw; +}ue_metrics_t; + +// UE interface +class ue_metrics_interface : public srslte::metrics_interface +{ +public: + virtual bool get_metrics(ue_metrics_t &m) = 0; + virtual bool is_attached() = 0; +}; + +} // namespace srsue + +#endif // SRSUE_UE_METRICS_INTERFACE_H diff --git a/srsue/hdr/upper/gw.h b/srsue/hdr/upper/gw.h new file mode 100644 index 0000000..46059b5 --- /dev/null +++ b/srsue/hdr/upper/gw.h @@ -0,0 +1,107 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_GW_H +#define SRSUE_GW_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/common/interfaces_common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/threads.h" +#include "gw_metrics.h" + +#include + +namespace srsue { + +class gw + :public gw_interface_pdcp + ,public gw_interface_nas + ,public gw_interface_rrc + ,public thread +{ +public: + gw(); + void init(pdcp_interface_gw *pdcp_, nas_interface_gw *nas_, srslte::log *gw_log_, srslte::srslte_gw_config_t); + void stop(); + + void get_metrics(gw_metrics_t &m); + void set_netmask(std::string netmask); + + // PDCP interface + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu); + + // NAS interface + srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str); + + // RRC interface + void add_mch_port(uint32_t lcid, uint32_t port); + +private: + + bool default_netmask; + std::string netmask; + + static const int GW_THREAD_PRIO = 7; + + pdcp_interface_gw *pdcp; + nas_interface_gw *nas; + + srslte::byte_buffer_pool *pool; + srslte::log *gw_log; + + srslte::srslte_gw_config_t cfg; + + bool running; + bool run_enable; + int32 tun_fd; + struct ifreq ifr; + int32 sock; + bool if_up; + + uint32_t current_ip_addr; + + long ul_tput_bytes; + long dl_tput_bytes; + struct timeval metrics_time[3]; + + void run_thread(); + srslte::error_t init_if(char *err_str); + + // MBSFN + int mbsfn_sock_fd; // Sink UDP socket file descriptor + struct sockaddr_in mbsfn_sock_addr; // Target address + uint32_t mbsfn_ports[SRSLTE_N_MCH_LCIDS]; // Target ports for MBSFN data + +}; + +} // namespace srsue + + +#endif // SRSUE_GW_H diff --git a/srsue/hdr/upper/gw_metrics.h b/srsue/hdr/upper/gw_metrics.h new file mode 100644 index 0000000..cce3af7 --- /dev/null +++ b/srsue/hdr/upper/gw_metrics.h @@ -0,0 +1,41 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_GW_METRICS_H +#define SRSUE_GW_METRICS_H + + +namespace srsue { + +struct gw_metrics_t +{ + double dl_tput_mbps; + double ul_tput_mbps; +}; + +} // namespace srsue + +#endif // SRSUE_GW_METRICS_H diff --git a/srsue/hdr/upper/nas.h b/srsue/hdr/upper/nas.h new file mode 100644 index 0000000..85c7a2f --- /dev/null +++ b/srsue/hdr/upper/nas.h @@ -0,0 +1,231 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_NAS_H +#define SRSUE_NAS_H + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" +#include "srslte/asn1/liblte_mme.h" +#include "srslte/common/nas_pcap.h" + +using srslte::byte_buffer_t; + +namespace srsue { + +typedef struct { + std::string apn_name; + std::string apn_user; + std::string apn_pass; + bool force_imsi_attach; +} nas_args_t; + +// EMM states (3GPP 24.302 v10.0.0) +typedef enum { + EMM_STATE_NULL = 0, + EMM_STATE_DEREGISTERED, + EMM_STATE_REGISTERED, + EMM_STATE_DEREGISTERED_INITIATED, + EMM_STATE_TAU_INITIATED, + EMM_STATE_N_ITEMS, +} emm_state_t; +static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"NULL", + "DEREGISTERED", + "REGISTERED", + "DEREGISTERED INITIATED", + "TRACKING AREA UPDATE INITIATED"}; + +static const bool eia_caps[8] = {false, true, true, false, false, false, false, false}; +static const bool eea_caps[8] = {true, true, true, false, false, false, false, false}; + +class nas + : public nas_interface_rrc, + public nas_interface_ue, + public nas_interface_gw +{ +public: + nas(); + void init(usim_interface_nas *usim_, + rrc_interface_nas *rrc_, + gw_interface_nas *gw_, + srslte::log *nas_log_, + srslte::srslte_nas_config_t cfg_); + void stop(); + + emm_state_t get_state(); + + // RRC interface + void paging(LIBLTE_RRC_S_TMSI_STRUCT *ue_identiy); + void set_barring(barring_t barring); + void write_pdu(uint32_t lcid, byte_buffer_t *pdu); + uint32_t get_ul_count(); + bool is_attached(); + bool get_k_asme(uint8_t *k_asme_, uint32_t n); + + // UE interface + bool attach_request(); + bool deattach_request(); + + // PCAP + void start_pcap(srslte::nas_pcap *pcap_); + +private: + srslte::byte_buffer_pool *pool; + srslte::log *nas_log; + rrc_interface_nas *rrc; + usim_interface_nas *usim; + gw_interface_nas *gw; + + srslte::srslte_nas_config_t cfg; + + emm_state_t state; + + nas_interface_rrc::barring_t current_barring; + + bool plmn_is_selected; + LIBLTE_RRC_PLMN_IDENTITY_STRUCT current_plmn; + LIBLTE_RRC_PLMN_IDENTITY_STRUCT home_plmn; + + std::vector known_plmns; + + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT emm_info; + + // Security context + struct nas_sec_ctxt{ + uint8_t ksi; + uint8_t k_asme[32]; + uint32_t tx_count; + uint32_t rx_count; + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; + }; + + bool have_guti; + bool have_ctxt; + nas_sec_ctxt ctxt; + + uint32_t ip_addr; + uint8_t eps_bearer_id; + + uint8_t chap_id; + + uint8_t transaction_id; + + // Security + uint8_t k_nas_enc[32]; + uint8_t k_nas_int[32]; + + // PCAP + srslte::nas_pcap *pcap = NULL; + + bool running; + + bool rrc_connect(); + + void integrity_generate(uint8_t *key_128, + uint32_t count, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac); + bool integrity_check(byte_buffer_t *pdu); + void cipher_encrypt(byte_buffer_t *pdu); + void cipher_decrypt(byte_buffer_t *pdu); + + bool check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *caps); + + void select_plmn(); + + // Parsers + void parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu); + void parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu); + void parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu); + void parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu); + void parse_identity_request(uint32_t lcid, byte_buffer_t *pdu); + void parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu); + void parse_service_reject(uint32_t lcid, byte_buffer_t *pdu); + void parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu); + void parse_emm_information(uint32_t lcid, byte_buffer_t *pdu); + + // Packet generators + void gen_attach_request(byte_buffer_t *msg); + void gen_service_request(byte_buffer_t *msg); + + // Senders + void send_identity_response(); + void send_service_request(); + void send_esm_information_response(const uint8 proc_transaction_id); + void send_authentication_response(const uint8_t* res, const size_t res_len); + void send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param); + void gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg); + void send_security_mode_reject(uint8_t cause); + + // security context persistence file + bool read_ctxt_file(nas_sec_ctxt *ctxt); + bool write_ctxt_file(nas_sec_ctxt ctxt); + + // ctxt file helpers + std::string hex_to_string(uint8_t *hex, int size); + bool string_to_hex(std::string hex_str, uint8_t *hex, uint32_t len); + std::string emm_info_str(LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT *info); + + template + bool readvar(std::istream &file, const char *key, T *var) + { + std::string line; + size_t len = strlen(key); + std::getline(file, line); + if(line.substr(0,len).compare(key)) { + return false; + } + *var = (T)atoi(line.substr(len).c_str()); + return true; + } + + bool readvar(std::istream &file, const char *key, uint8_t *var, int varlen) + { + std::string line; + size_t len = strlen(key); + std::getline(file, line); + if(line.substr(0,len).compare(key)) { + return false; + } + std::string tmp = line.substr(len); + if(!string_to_hex(tmp, var, varlen)) { + return false; + } + return true; + } +}; + +} // namespace srsue + + +#endif // SRSUE_NAS_H diff --git a/srsue/hdr/upper/pcsc_usim.h b/srsue/hdr/upper/pcsc_usim.h new file mode 100644 index 0000000..862b740 --- /dev/null +++ b/srsue/hdr/upper/pcsc_usim.h @@ -0,0 +1,270 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_PCSC_USIM_H +#define SRSUE_PCSC_USIM_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" +#include "srsue/hdr/upper/usim.h" +#include + +namespace srsue { + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define MAC_LEN 8 +#define IK_LEN 16 +#define CK_LEN 16 +#define AK_LEN 6 +#define SQN_LEN 6 + +#define KEY_LEN 32 + +typedef enum { + SCARD_GSM_SIM, + SCARD_USIM +} sim_types_t; + +static inline uint16_t to_uint16(const uint8_t *a) +{ + return (a[0] << 8) | a[1]; +} + +class pcsc_usim + :public usim_base +{ +public: + pcsc_usim(); + ~pcsc_usim(); + int init(usim_args_t *args, srslte::log *usim_log_); + void stop(); + + // NAS interface + std::string get_imsi_str(); + std::string get_imei_str(); + + bool get_imsi_vec(uint8_t* imsi_, uint32_t n); + bool get_imei_vec(uint8_t* imei_, uint32_t n); + bool get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id); + + auth_result_t generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); + + void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + // RRC interface + void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + +private: + srslte::log *log; + + // User data + uint8_t amf[2]; // 3GPP 33.102 v10.0.0 Annex H + uint8_t op[16]; + uint64_t imsi; + uint64_t imei; + uint8_t k[16]; + + std::string imsi_str; + std::string imei_str; + + uint32_t mnc_length; + + // Security variables + uint8_t rand[AKA_RAND_LEN]; + uint8_t ck[CK_LEN]; + uint8_t ik[IK_LEN]; + uint8_t ak[AK_LEN]; + uint8_t mac[MAC_LEN]; + uint8_t autn[AKA_AUTN_LEN]; + uint8_t k_asme[KEY_LEN]; + uint8_t nh[KEY_LEN]; + uint8_t k_enb[KEY_LEN]; + uint8_t k_enb_star[KEY_LEN]; + uint8_t auts[AKA_AUTS_LEN]; + + uint32_t current_ncc; + + bool initiated; + + // Smartcard sub-class which is a port of the PC/SC smartcard implementation + // of WPA Supplicant written by Jouni Malinen and licensed under BSD + // Source: https://w1.fi/cvs.html + class scard { + public: + scard() : log(NULL) {}; + ~scard() {}; + + int init(usim_args_t *args, srslte::log *log_); + void deinit(); + + int select_file(unsigned short file_id,unsigned char *buf, size_t *buf_len); + int _select_file(unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types_t sim_type, unsigned char *aid, size_t aidlen); + + long transmit(unsigned char *_send, size_t send_len, unsigned char *_recv, size_t *recv_len); + + int get_aid(unsigned char *aid, size_t maxlen); + int get_record_len(unsigned char recnum, unsigned char mode); + int read_record(unsigned char *data, size_t len, unsigned char recnum, unsigned char mode); + int get_imsi(char *imsi, size_t *len); + int parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len); + int read_file(unsigned char *data, size_t len); + int get_mnc_len(); + int umts_auth(const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, int *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts); + int pin_needed(unsigned char *hdr, size_t hlen); + int verify_pin(const char *pin); + int get_pin_retry_counter(); + + private: + /* See ETSI GSM 11.11 and ETSI TS 102 221 for details. + * SIM commands: + * Command APDU: CLA INS P1 P2 P3 Data + * CLA (class of instruction): A0 for GSM, 00 for USIM + * INS (instruction) + * P1 P2 P3 (parameters, P3 = length of Data) + * Response APDU: Data SW1 SW2 + * SW1 SW2 (Status words) + * Commands (INS P1 P2 P3): + * SELECT: A4 00 00 02 + * GET RESPONSE: C0 00 00 + * RUN GSM ALG: 88 00 00 00 + * RUN UMTS ALG: 88 00 81 data: 0x10 | RAND | 0x10 | AUTN + * P1 = ID of alg in card + * P2 = ID of secret key + * READ BINARY: B0 + * READ RECORD: B2 + * P2 (mode) = '02' (next record), '03' (previous record), + * '04' (absolute mode) + * VERIFY CHV: 20 00 08 + * CHANGE CHV: 24 00 10 + * DISABLE CHV: 26 00 01 08 + * ENABLE CHV: 28 00 01 08 + * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 + * SLEEP: FA 00 00 00 + */ + + /* GSM SIM commands */ + #define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 + #define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 + #define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 + #define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 + #define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 + #define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 + + /* USIM commands */ + #define USIM_CLA 0x00 + #define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 + #define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 + + #define SIM_RECORD_MODE_ABSOLUTE 0x04 + + #define USIM_FSP_TEMPL_TAG 0x62 + + #define USIM_TLV_FILE_DESC 0x82 + #define USIM_TLV_FILE_ID 0x83 + #define USIM_TLV_DF_NAME 0x84 + #define USIM_TLV_PROPR_INFO 0xA5 + #define USIM_TLV_LIFE_CYCLE_STATUS 0x8A + #define USIM_TLV_FILE_SIZE 0x80 + #define USIM_TLV_TOTAL_FILE_SIZE 0x81 + #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 + #define USIM_TLV_SHORT_FILE_ID 0x88 + #define USIM_TLV_SECURITY_ATTR_8B 0x8B + #define USIM_TLV_SECURITY_ATTR_8C 0x8C + #define USIM_TLV_SECURITY_ATTR_AB 0xAB + + #define USIM_PS_DO_TAG 0x90 + + /* GSM files + * File type in first octet: + * 3F = Master File + * 7F = Dedicated File + * 2F = Elementary File under the Master File + * 6F = Elementary File under a Dedicated File + */ + #define SCARD_FILE_MF 0x3F00 + #define SCARD_FILE_GSM_DF 0x7F20 + #define SCARD_FILE_UMTS_DF 0x7F50 + #define SCARD_FILE_GSM_EF_IMSI 0x6F07 + #define SCARD_FILE_GSM_EF_AD 0x6FAD + #define SCARD_FILE_EF_DIR 0x2F00 + #define SCARD_FILE_EF_ICCID 0x2FE2 + #define SCARD_FILE_EF_CK 0x6FE1 + #define SCARD_FILE_EF_IK 0x6FE2 + + #define SCARD_CHV1_OFFSET 13 + #define SCARD_CHV1_FLAG 0x80 + + SCARDCONTEXT scard_context; + SCARDHANDLE scard_handle; + long unsigned scard_protocol; + sim_types_t sim_type; + bool pin1_needed; + srslte::log *log; + }; + + scard sc; +}; + +} // namespace srsue + +#endif // SRSUE_PCSC_USIM_H diff --git a/srsue/hdr/upper/rrc.h b/srsue/hdr/upper/rrc.h new file mode 100644 index 0000000..dfef5e7 --- /dev/null +++ b/srsue/hdr/upper/rrc.h @@ -0,0 +1,653 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_RRC_H +#define SRSUE_RRC_H + +#include "pthread.h" + +#include "rrc_common.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" +#include "srslte/common/threads.h" +#include "srslte/common/block_queue.h" + +#include +#include +#include + +typedef struct { + uint32_t ue_category; + uint32_t feature_group; + uint8_t supported_bands[LIBLTE_RRC_BAND_N_ITEMS]; + uint32_t nof_supported_bands; +}rrc_args_t; + +using srslte::byte_buffer_t; + +namespace srsue { + +class cell_t +{ + public: + bool is_valid() { + return phy_cell.earfcn != 0 && srslte_cell_isvalid(&phy_cell.cell); + } + bool equals(cell_t *x) { + return equals(x->phy_cell.earfcn, x->phy_cell.cell.id); + } + bool equals(uint32_t earfcn, uint32_t pci) { + return earfcn == this->phy_cell.earfcn && pci == phy_cell.cell.id; + } + // NaN means an RSRP value has not yet been obtained. Keep then in the list and clean them if never updated + bool greater(cell_t *x) { + return rsrp > x->rsrp || isnan(rsrp); + } + bool plmn_equals(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) { + if (has_valid_sib1) { + for (uint32_t i = 0; i < sib1.N_plmn_ids; i++) { + if (plmn_id.mcc == sib1.plmn_id[i].id.mcc && plmn_id.mnc == sib1.plmn_id[i].id.mnc) { + return true; + } + } + } + return false; + } + + uint32_t nof_plmns() { + if (has_valid_sib1) { + return sib1.N_plmn_ids; + } else { + return 0; + } + } + + LIBLTE_RRC_PLMN_IDENTITY_STRUCT get_plmn(uint32_t idx) { + if (idx < sib1.N_plmn_ids && has_valid_sib1) { + return sib1.plmn_id[idx].id; + } else { + LIBLTE_RRC_PLMN_IDENTITY_STRUCT null; + null.mnc = 0; + null.mcc = 0; + return null; + } + } + + uint16_t get_tac() { + if (has_valid_sib1) { + return sib1.tracking_area_code; + } else { + return 0; + } + } + + cell_t() { + phy_interface_rrc::phy_cell_t tmp; + ZERO_OBJECT(tmp); + cell_t(tmp, 0); + } + cell_t(phy_interface_rrc::phy_cell_t phy_cell, float rsrp) { + gettimeofday(&last_update, NULL); + this->has_valid_sib1 = false; + this->has_valid_sib2 = false; + this->has_valid_sib3 = false; + this->has_valid_sib13 = false; + this->phy_cell = phy_cell; + this->rsrp = rsrp; + in_sync = true; + bzero(&sib1, sizeof(sib1)); + bzero(&sib2, sizeof(sib2)); + bzero(&sib3, sizeof(sib3)); + bzero(&sib13, sizeof(sib13)); + } + + uint32_t get_earfcn() { + return phy_cell.earfcn; + } + + uint32_t get_pci() { + return phy_cell.cell.id; + } + + void set_rsrp(float rsrp) { + if (!isnan(rsrp)) { + this->rsrp = rsrp; + } + in_sync = true; + gettimeofday(&last_update, NULL); + } + + float get_rsrp() { + return rsrp; + } + + void set_sib1(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1) { + memcpy(&this->sib1, sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + has_valid_sib1 = true; + } + void set_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) { + memcpy(&this->sib2, sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + has_valid_sib2 = true; + } + void set_sib3(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3) { + memcpy(&this->sib3, sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT)); + has_valid_sib3 = true; + } + void set_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) { + memcpy(&this->sib13, sib13, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT)); + has_valid_sib13 = true; + } + + uint32_t timeout_secs(struct timeval now) { + struct timeval t[3]; + memcpy(&t[2], &now, sizeof(struct timeval)); + memcpy(&t[1], &last_update, sizeof(struct timeval)); + get_time_interval(t); + return t[0].tv_sec; + } + + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1ptr() { + return &sib1; + } + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2ptr() { + return &sib2; + } + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3ptr() { + return &sib3; + } + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13ptr() { + return &sib13; + } + + uint32_t get_cell_id() { + return sib1.cell_id; + } + + bool has_sib1() { + return has_valid_sib1; + } + bool has_sib2() { + return has_valid_sib2; + } + bool has_sib3() { + return has_valid_sib3; + } + bool has_sib13() { + return has_valid_sib13; + } + + bool has_sib(uint32_t index) { + switch(index) { + case 0: + return has_sib1(); + case 1: + return has_sib2(); + case 2: + return has_sib3(); + case 12: + return has_sib13(); + } + return false; + } + + uint16_t get_mcc() { + if (has_valid_sib1) { + if (sib1.N_plmn_ids > 0) { + return sib1.plmn_id[0].id.mcc; + } + } + return 0; + } + + uint16_t get_mnc() { + if (has_valid_sib1) { + if (sib1.N_plmn_ids > 0) { + return sib1.plmn_id[0].id.mnc; + } + } + return 0; + } + + phy_interface_rrc::phy_cell_t phy_cell; + bool in_sync; + bool has_mcch; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT sib3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT sib13; + LIBLTE_RRC_MCCH_MSG_STRUCT mcch; + +private: + float rsrp; + + struct timeval last_update; + + bool has_valid_sib1; + bool has_valid_sib2; + bool has_valid_sib3; + bool has_valid_sib13; +}; + +class rrc + :public rrc_interface_nas + ,public rrc_interface_phy + ,public rrc_interface_mac + ,public rrc_interface_pdcp + ,public rrc_interface_rlc + ,public srslte::timer_callback + ,public thread +{ +public: + rrc(); + ~rrc(); + + void init(phy_interface_rrc *phy_, + mac_interface_rrc *mac_, + rlc_interface_rrc *rlc_, + pdcp_interface_rrc *pdcp_, + nas_interface_rrc *nas_, + usim_interface_rrc *usim_, + gw_interface_rrc *gw_, + srslte::mac_interface_timers *mac_timers_, + srslte::log *rrc_log_); + + void stop(); + + rrc_state_t get_state(); + void set_args(rrc_args_t *args); + + // Timeout callback interface + void timer_expired(uint32_t timeout_id); + void liblte_rrc_log(char *str); + + void print_mbms(); + bool mbms_service_start(uint32_t serv, uint32_t port); + + // NAS interface + void write_sdu(uint32_t lcid, byte_buffer_t *sdu); + void enable_capabilities(); + uint16_t get_mcc(); + uint16_t get_mnc(); + int plmn_search(found_plmn_t found_plmns[MAX_FOUND_PLMNS]); + void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id); + bool connection_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause, + srslte::byte_buffer_t *dedicatedInfoNAS); + void set_ue_idenity(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi); + + // PHY interface + void in_sync(); + void out_of_sync(); + void new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn, int pci); + + // MAC interface + void ho_ra_completed(bool ra_successful); + void release_pucch_srs(); + void run_tti(uint32_t tti); + void ra_problem(); + + // GW interface + bool is_connected(); // this is also NAS interface + bool have_drb(); + + // PDCP interface + void write_pdu(uint32_t lcid, byte_buffer_t *pdu); + void write_pdu_bcch_bch(byte_buffer_t *pdu); + void write_pdu_bcch_dlsch(byte_buffer_t *pdu); + void write_pdu_pcch(byte_buffer_t *pdu); + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu); + +private: + + typedef struct { + enum { + PCCH, + STOP + } command; + byte_buffer_t *pdu; + } cmd_msg_t; + + bool running; + srslte::block_queue cmd_q; + void run_thread(); + + void process_pcch(byte_buffer_t *pdu); + + srslte::byte_buffer_pool *pool; + srslte::log *rrc_log; + phy_interface_rrc *phy; + mac_interface_rrc *mac; + rlc_interface_rrc *rlc; + pdcp_interface_rrc *pdcp; + nas_interface_rrc *nas; + usim_interface_rrc *usim; + gw_interface_rrc *gw; + + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + + byte_buffer_t *dedicatedInfoNAS; + + byte_buffer_t* byte_align_and_pack(); + void send_ul_ccch_msg(); + void send_ul_dcch_msg(); + srslte::bit_buffer_t bit_buf; + + pthread_mutex_t mutex; + + rrc_state_t state; + uint8_t transaction_id; + LIBLTE_RRC_S_TMSI_STRUCT ueIdentity; + bool ueIdentity_configured; + + bool drb_up; + + rrc_args_t args; + + uint32_t cell_clean_cnt; + + uint16_t ho_src_rnti; + cell_t ho_src_cell; + phy_interface_rrc::phy_cfg_t previous_phy_cfg; + mac_interface_rrc::mac_cfg_t previous_mac_cfg; + bool pending_mob_reconf; + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT mob_reconf; + + uint8_t k_rrc_enc[32]; + uint8_t k_rrc_int[32]; + uint8_t k_up_enc[32]; + uint8_t k_up_int[32]; // Not used: only for relay nodes (3GPP 33.401 Annex A.7) + + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + + std::map srbs; + std::map drbs; + + // RRC constants and timers + srslte::mac_interface_timers *mac_timers; + uint32_t n310_cnt, N310; + uint32_t n311_cnt, N311; + uint32_t t300, t301, t302, t310, t311, t304; + + // Radio bearers + typedef enum{ + RB_ID_SRB0 = 0, + RB_ID_SRB1, + RB_ID_SRB2, + RB_ID_DRB1, + RB_ID_DRB2, + RB_ID_DRB3, + RB_ID_DRB4, + RB_ID_DRB5, + RB_ID_DRB6, + RB_ID_DRB7, + RB_ID_DRB8, + RB_ID_MAX + } rb_id_t; + + static const std::string rb_id_str[]; + + std::string get_rb_name(uint32_t lcid) + { + if (lcid < RB_ID_MAX) { + return rb_id_str[lcid]; + } else { + return "INVALID_RB"; + } + } + + // List of strongest neighbour cell + const static int NEIGHBOUR_TIMEOUT = 5; + const static int NOF_NEIGHBOUR_CELLS = 8; + std::vector neighbour_cells; + cell_t *serving_cell; + void set_serving_cell(uint32_t cell_idx); + void set_serving_cell(phy_interface_rrc::phy_cell_t phy_cell); + + int find_neighbour_cell(uint32_t earfcn, uint32_t pci); + bool add_neighbour_cell(uint32_t earfcn, uint32_t pci, float rsrp); + bool add_neighbour_cell(phy_interface_rrc::phy_cell_t phy_cell, float rsrp); + bool add_neighbour_cell(cell_t *cell); + void sort_neighbour_cells(); + void clean_neighbours(); + std::vector::iterator delete_neighbour(std::vector::iterator it); + void delete_neighbour(uint32_t cell_idx); + + bool configure_serving_cell(); + + bool si_acquire(uint32_t index); + uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf); + const static int SIB_SEARCH_TIMEOUT_MS = 1000; + + bool initiated; + bool ho_start; + bool go_idle; + bool go_rlf; + + // Measurements sub-class + class rrc_meas { + public: + void init(rrc *parent); + void reset(); + bool parse_meas_config(LIBLTE_RRC_MEAS_CONFIG_STRUCT *meas_config); + void new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti); + void run_tti(uint32_t tti); + bool timer_expired(uint32_t timer_id); + void ho_finish(); + void delete_report(uint32_t earfcn, uint32_t pci); + private: + + const static int NOF_MEASUREMENTS = 3; + + typedef enum {RSRP = 0, RSRQ = 1, BOTH = 2} quantity_t; + + typedef struct { + uint32_t pci; + float q_offset; + } meas_cell_t; + + typedef struct { + uint32_t earfcn; + float q_offset; + std::map cells; + } meas_obj_t; + + typedef struct { + uint32_t interval; + uint32_t max_cell; + uint32_t amount; + quantity_t trigger_quantity; + quantity_t report_quantity; + LIBLTE_RRC_EVENT_EUTRA_STRUCT event; + enum {EVENT, PERIODIC} trigger_type; + } report_cfg_t; + + typedef struct { + float ms[NOF_MEASUREMENTS]; + bool triggered; + bool timer_enter_triggered; + bool timer_exit_triggered; + uint32_t enter_tti; + uint32_t exit_tti; + } meas_value_t; + + typedef struct { + uint32_t nof_reports_sent; + uint32_t report_id; + uint32_t object_id; + bool triggered; + uint32_t periodic_timer; + std::map cell_values; // Value for each PCI in this object + } meas_t; + + std::map objects; + std::map reports_cfg; + std::map active; + + rrc *parent; + srslte::log *log_h; + phy_interface_rrc *phy; + srslte::mac_interface_timers *mac_timers; + + uint32_t filter_k_rsrp, filter_k_rsrq; + float filter_a[NOF_MEASUREMENTS]; + + meas_value_t pcell_measurement; + + bool s_measure_enabled; + float s_measure_value; + + void stop_reports(meas_t *m); + void stop_reports_object(uint32_t object_id); + void remove_meas_object(uint32_t object_id); + void remove_meas_report(uint32_t report_id); + void remove_meas_id(uint32_t measId); + void remove_meas_id(std::map::iterator it); + void calculate_triggers(uint32_t tti); + void update_phy(); + void L3_filter(meas_value_t *value, float rsrp[NOF_MEASUREMENTS]); + bool find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t **object, int *cell_idx); + float range_to_value(quantity_t quant, uint8_t range); + uint8_t value_to_range(quantity_t quant, float value); + bool process_event(LIBLTE_RRC_EVENT_EUTRA_STRUCT *event, uint32_t tti, + bool enter_condition, bool exit_condition, + meas_t *m, meas_value_t *cell); + + void generate_report(uint32_t meas_id); + }; + + rrc_meas measurements; + + // Measurement object from phy + typedef struct { + float rsrp; + float rsrq; + uint32_t tti; + uint32_t earfcn; + uint32_t pci; + } phy_meas_t; + + void process_phy_meas(); + void process_new_phy_meas(phy_meas_t meas); + srslte::block_queue phy_meas_q; + + // Cell selection/reselection functions/variables + typedef struct { + float Qrxlevmin; + float Qrxlevminoffset; + float Qqualmin; + float Qqualminoffset; + float s_intrasearchP; + float q_hyst; + float threshservinglow; + } cell_resel_cfg_t; + + cell_resel_cfg_t cell_resel_cfg; + + float get_srxlev(float Qrxlevmeas); + float get_squal(float Qqualmeas); + + typedef enum { + CHANGED_CELL = 0, + SAME_CELL = 1, + NO_CELL = 2 + } cs_ret_t; + + cs_ret_t cell_selection(); + bool cell_selection_criteria(float rsrp, float rsrq = 0); + void cell_reselection(float rsrp, float rsrq); + + phy_interface_rrc::cell_search_ret_t cell_search(); + + LIBLTE_RRC_PLMN_IDENTITY_STRUCT selected_plmn_id; + bool plmn_is_selected; + + bool security_is_activated; + + // RLC interface + void max_retx_attempted(); + + // Senders + void send_con_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause); + void send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM cause); + void send_con_restablish_complete(); + void send_con_setup_complete(byte_buffer_t *nas_msg); + void send_ul_info_transfer(byte_buffer_t *nas_msg); + void send_security_mode_complete(); + void send_rrc_con_reconfig_complete(); + void send_rrc_ue_cap_info(); + + // Parsers + void parse_dl_ccch(byte_buffer_t *pdu); + void parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu); + void parse_dl_info_transfer(uint32_t lcid, byte_buffer_t *pdu); + + // Helpers + bool con_reconfig(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig); + void con_reconfig_failed(); + bool con_reconfig_ho(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig); + bool ho_prepare(); + void ho_failed(); + void rrc_connection_release(); + void radio_link_failure(); + void leave_connected(); + + void apply_rr_config_common_dl(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *config); + void apply_rr_config_common_ul(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *config); + void handle_sib1(); + void handle_sib2(); + void handle_sib3(); + void handle_sib13(); + + void apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2); + void apply_sib13_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13); + void handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup); + void handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup); + void handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig); + void add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg); + void add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg); + void release_drb(uint8_t lcid); + void add_mrb(uint32_t lcid, uint32_t port); + bool apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg); + void apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults); + void apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg, bool apply_defaults); + + // Helpers for setting default values + void set_phy_default_pucch_srs(); + void set_phy_default(); + void set_mac_default(); + void set_rrc_default(); +}; + +} // namespace srsue + + +#endif // SRSUE_RRC_H diff --git a/srsue/hdr/upper/rrc_common.h b/srsue/hdr/upper/rrc_common.h new file mode 100644 index 0000000..f79a98b --- /dev/null +++ b/srsue/hdr/upper/rrc_common.h @@ -0,0 +1,45 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_RRC_COMMON_H +#define SRSUE_RRC_COMMON_H + +namespace srsue { + + +// RRC states (3GPP 36.331 v10.0.0) +typedef enum { + RRC_STATE_IDLE = 0, + RRC_STATE_CONNECTED, + RRC_STATE_N_ITEMS, +} rrc_state_t; +static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", + "CONNECTED"}; + +} // namespace srsue + + +#endif // SRSUE_RRC_COMMON_H diff --git a/srsue/hdr/upper/usim.h b/srsue/hdr/upper/usim.h new file mode 100644 index 0000000..ce42e30 --- /dev/null +++ b/srsue/hdr/upper/usim.h @@ -0,0 +1,143 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_USIM_H +#define SRSUE_USIM_H + +#include +#include "usim_base.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" + +namespace srsue { + +class usim + :public usim_base +{ +public: + usim(); + int init(usim_args_t *args, srslte::log *usim_log_); + void stop(); + + // NAS interface + std::string get_imsi_str(); + std::string get_imei_str(); + + bool get_imsi_vec(uint8_t* imsi_, uint32_t n); + bool get_imei_vec(uint8_t* imei_, uint32_t n); + bool get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id); + + auth_result_t generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); + + void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + // RRC interface + void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + +private: + auth_result_t gen_auth_res_milenage(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); + auth_result_t gen_auth_res_xor(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme); + void str_to_hex(std::string str, uint8_t *hex); + + srslte::log *usim_log; + + // User data + auth_algo_t auth_algo; + uint8_t amf[2]; // 3GPP 33.102 v10.0.0 Annex H + uint8_t op[16]; + uint8_t opc[16]; + uint64_t imsi; + uint64_t imei; + uint8_t k[16]; + + std::string imsi_str; + std::string imei_str; + + // Security variables + uint8_t rand[16]; + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + uint8_t mac[8]; + uint8_t autn[16]; + uint8_t k_asme[32]; + uint8_t nh[32]; + uint8_t k_enb_initial[32]; + uint8_t k_enb[32]; + uint8_t k_enb_star[32]; + + uint32_t current_ncc; + + bool initiated; + +}; + +} // namespace srsue + + +#endif // SRSUE_USIM_H diff --git a/srsue/hdr/upper/usim_base.h b/srsue/hdr/upper/usim_base.h new file mode 100644 index 0000000..b9a6ee4 --- /dev/null +++ b/srsue/hdr/upper/usim_base.h @@ -0,0 +1,113 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#ifndef SRSUE_USIM_BASE_H +#define SRSUE_USIM_BASE_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/security.h" + +namespace srsue { + +typedef enum{ + auth_algo_milenage = 0, + auth_algo_xor, +}auth_algo_t; + +typedef struct{ + std::string mode; + std::string algo; + bool using_op; + std::string op; + std::string opc; + std::string imsi; + std::string imei; + std::string k; + std::string pin; + std::string reader; +}usim_args_t; + +class usim_base + :public usim_interface_nas + ,public usim_interface_rrc +{ +public: + usim_base(); + virtual ~usim_base(); + static usim_base* get_instance(usim_args_t *args, srslte::log *usim_log_); + + virtual int init(usim_args_t *args, srslte::log *usim_log_) = 0; + virtual void stop() = 0; + + // NAS interface + virtual std::string get_imsi_str() = 0; + virtual std::string get_imei_str() = 0; + + virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; + virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; + virtual bool get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) = 0; + + virtual auth_result_t generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) = 0; + + virtual void generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + + // RRC interface + virtual void generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + + virtual void generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; +}; + +} // namespace srsue + +#endif // SRSUE_USIM_BASE_H diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt new file mode 100644 index 0000000..0755a64 --- /dev/null +++ b/srsue/src/CMakeLists.txt @@ -0,0 +1,61 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(phy) +add_subdirectory(mac) +add_subdirectory(upper) + +# Link libstdc++ and libgcc +if(BUILD_STATIC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") +endif(BUILD_STATIC) + +if (RPATH) + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + +add_executable(srsue main.cc ue_base.cc ue.cc metrics_stdout.cc metrics_csv.cc) +target_link_libraries(srsue srsue_mac + srsue_phy + srsue_upper + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +if (RPATH) + set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") +endif (RPATH) + +install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILDUE_CMD} STREQUAL "") + message(STATUS "Added custom post-build-UE command: ${BUILDUE_CMD}") + add_custom_command(TARGET srsue POST_BUILD COMMAND ${BUILDUE_CMD}) +else(NOT ${BUILDUE_CMD} STREQUAL "") + message(STATUS "No post-build-UE command defined") +endif (NOT ${BUILDUE_CMD} STREQUAL "") + +install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/mac/CMakeLists.txt b/srsue/src/mac/CMakeLists.txt new file mode 100644 index 0000000..fb5783b --- /dev/null +++ b/srsue/src/mac/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsue_mac STATIC ${SOURCES}) +install(TARGETS srsue_mac DESTINATION ${LIBRARY_DIR}) diff --git a/srsue/src/mac/demux.cc b/srsue/src/mac/demux.cc new file mode 100644 index 0000000..28e17d5 --- /dev/null +++ b/srsue/src/mac/demux.cc @@ -0,0 +1,263 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srsue/hdr/mac/mac.h" +#include "srsue/hdr/mac/demux.h" +#include "srslte/interfaces/ue_interfaces.h" + +namespace srsue { + +demux::demux() : mac_msg(20), mch_mac_msg(20), pending_mac_msg(20), rlc(NULL) +{ +} + +void demux::init(phy_interface_mac_common* phy_h_, rlc_interface_mac *rlc_, srslte::log* log_h_, srslte::timers::timer* time_alignment_timer_) +{ + phy_h = phy_h_; + log_h = log_h_; + rlc = rlc_; + time_alignment_timer = time_alignment_timer_; + pdus.init(this, log_h); + bzero(&mch_lcids, SRSLTE_N_MCH_LCIDS); +} + +void demux::set_uecrid_callback(bool (*callback)(void*,uint64_t), void *arg) { + uecrid_callback = callback; + uecrid_callback_arg = arg; +} + +bool demux::get_uecrid_successful() { + return is_uecrid_successful; +} + +void demux::deallocate(uint8_t* payload_buffer_ptr) +{ + if (payload_buffer_ptr != bcch_buffer) { + pdus.deallocate(payload_buffer_ptr); + } +} +uint8_t* demux::request_buffer_bcch(uint32_t len) +{ + if (len < MAX_BCCH_PDU_LEN) { + return bcch_buffer; + } else { + return NULL; + } +} + +uint8_t* demux::request_buffer(uint32_t len) +{ + return pdus.request(len); +} + +/* Demultiplexing of MAC PDU associated with a Temporal C-RNTI. The PDU will + * remain in buffer until demultiplex_pending_pdu() is called. + * This features is provided to enable the Random Access Procedure to decide + * wether the PDU shall pass to upper layers or not, which depends on the + * Contention Resolution result. + * + * Warning: this function does some processing here assuming ACK deadline is not an + * issue here because Temp C-RNTI messages have small payloads + */ +void demux::push_pdu_temp_crnti(uint8_t *buff, uint32_t nof_bytes) +{ + if (nof_bytes > 0) { + // Unpack DLSCH MAC PDU + pending_mac_msg.init_rx(nof_bytes); + pending_mac_msg.parse_packet(buff); + + // Look for Contention Resolution UE ID + is_uecrid_successful = false; + while(pending_mac_msg.next() && !is_uecrid_successful) { + if (pending_mac_msg.get()->ce_type() == srslte::sch_subh::CON_RES_ID) { + Debug("Found Contention Resolution ID CE\n"); + is_uecrid_successful = uecrid_callback(uecrid_callback_arg, pending_mac_msg.get()->get_con_res_id()); + } + } + + pending_mac_msg.reset(); + + Debug("Saved MAC PDU with Temporal C-RNTI in buffer\n"); + + pdus.push(buff, nof_bytes, srslte::pdu_queue::DCH); + } else { + Warning("Trying to push PDU with payload size zero\n"); + } +} + +/* Demultiplexing of logical channels and dissassemble of MAC CE + * This function enqueues the packet and returns quickly because ACK + * deadline is important here. + */ +void demux::push_pdu(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp) { + return pdus.push(buff, nof_bytes, srslte::pdu_queue::DCH, tstamp); +} + +/* Demultiplexing of MAC PDU associated with SI-RNTI. The PDU passes through +* the MAC in transparent mode. +*/ +void demux::push_pdu_bcch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp) { + pdus.push(buff, nof_bytes, srslte::pdu_queue::BCH, tstamp); +} + +void demux::push_pdu_mch(uint8_t *buff, uint32_t nof_bytes, uint32_t tstamp) { + uint8_t *mch_buffer_ptr = request_buffer(nof_bytes); + memcpy(mch_buffer_ptr, buff, nof_bytes); + pdus.push(mch_buffer_ptr, nof_bytes, srslte::pdu_queue::MCH, tstamp); + mch_buffer_ptr = NULL; +} + +bool demux::process_pdus() +{ + return pdus.process_pdus(); +} + +void demux::process_pdu(uint8_t *mac_pdu, uint32_t nof_bytes, srslte::pdu_queue::channel_t channel, uint32_t tstamp) +{ + Debug("Processing MAC PDU channel %d\n", channel); + switch(channel) { + case srslte::pdu_queue::DCH: + // Unpack DLSCH MAC PDU + mac_msg.init_rx(nof_bytes); + mac_msg.parse_packet(mac_pdu); + process_sch_pdu(&mac_msg); + pdus.deallocate(mac_pdu); + break; + case srslte::pdu_queue::BCH: + rlc->write_pdu_bcch_dlsch(mac_pdu, nof_bytes); + break; + case srslte::pdu_queue::MCH: + mch_mac_msg.init_rx(nof_bytes); + mch_mac_msg.parse_packet(mac_pdu); + deallocate(mac_pdu); + process_mch_pdu(&mch_mac_msg); + // Process downlink MCH + break; + } +} + +void demux::process_sch_pdu(srslte::sch_pdu *pdu_msg) +{ + while(pdu_msg->next()) { + if (pdu_msg->get()->is_sdu()) { + bool route_pdu = true; + if (pdu_msg->get()->get_sdu_lcid() == 0) { + uint8_t *x = pdu_msg->get()->get_sdu_ptr(); + uint32_t sum = 0; + for (uint32_t i=0;iget()->get_payload_size();i++) { + sum += x[i]; + } + if (sum == 0) { + route_pdu = false; + Warning("Received all zero PDU\n"); + } + } + // Route logical channel + if (route_pdu) { + Info("Delivering PDU for lcid=%d, %d bytes\n", pdu_msg->get()->get_sdu_lcid(), pdu_msg->get()->get_payload_size()); + if (pdu_msg->get()->get_payload_size() < MAX_PDU_LEN) { + rlc->write_pdu(pdu_msg->get()->get_sdu_lcid(), pdu_msg->get()->get_sdu_ptr(), pdu_msg->get()->get_payload_size()); + } else { + char tmp[1024]; + srslte_vec_sprint_hex(tmp, sizeof(tmp), pdu_msg->get()->get_sdu_ptr(), 32); + Error("PDU size %d exceeds maximum PDU buffer size, lcid=%d, hex=[%s]\n", + pdu_msg->get()->get_payload_size(), pdu_msg->get()->get_sdu_lcid(), tmp); + } + } + } else { + // Process MAC Control Element + if (!process_ce(pdu_msg->get())) { + Warning("Received Subheader with invalid or unkonwn LCID\n"); + } + } + } +} +void demux::process_mch_pdu(srslte::mch_pdu *mch_msg){ + //disgarding headers that have already been processed + while(mch_msg->next()){ + + if(srslte::mch_subh::MCH_SCHED_INFO == mch_msg->get()->ce_type()){ + uint16_t stop; + uint8_t lcid; + if(mch_msg->get()->get_next_mch_sched_info(&lcid, &stop)) { + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", lcid, stop, phy_h->get_current_tti()); + } + } + if(mch_msg->get()->is_sdu()) { + uint32_t lcid = mch_msg->get()->get_sdu_lcid(); + + if(lcid >= SRSLTE_N_MCH_LCIDS) { + Error("Radio bearer id must be in [0:%d] - %d\n", SRSLTE_N_MCH_LCIDS, lcid); + return; + } + Debug("Wrote MCH LCID=%d to RLC\n", lcid); + if(1 == mch_lcids[lcid]) { + rlc->write_pdu_mch(lcid, mch_msg->get()->get_sdu_ptr(), mch_msg->get()->get_payload_size()); + } + } + } +} + +void demux::mch_start_rx(uint32_t lcid) +{ + if(lcid < 32) { + Info("MCH Channel Setup: LCID=%d\n", lcid); + mch_lcids[lcid] = 1; + } else { + Error("MCH Channel Setup: invalid LCID=%d\n", lcid); + } +} + +bool demux::process_ce(srslte::sch_subh *subh) { + switch(subh->ce_type()) { + case srslte::sch_subh::CON_RES_ID: + // Do nothing + break; + case srslte::sch_subh::TA_CMD: + phy_h->set_timeadv(subh->get_ta_cmd()); + Info("Received TA=%d\n", subh->get_ta_cmd()); + + // Start or restart timeAlignmentTimer + time_alignment_timer->reset(); + time_alignment_timer->run(); + break; + case srslte::sch_subh::PADDING: + break; + default: + Error("MAC CE 0x%x not supported\n", subh->ce_type()); + break; + } + return true; +} + + +} diff --git a/srsue/src/mac/mac.cc b/srsue/src/mac/mac.cc new file mode 100644 index 0000000..ffa0ca1 --- /dev/null +++ b/srsue/src/mac/mac.cc @@ -0,0 +1,564 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include +#include +#include +#include + +#include "srslte/common/log.h" +#include "srsue/hdr/mac/mac.h" +#include "srslte/common/pcap.h" + + +namespace srsue { + +mac::mac() : timers(64), + mux_unit(MAC_NOF_HARQ_PROC), + pdu_process_thread(&demux_unit), + mch_msg(10) +{ + pcap = NULL; + bzero(&metrics, sizeof(mac_metrics_t)); +} + +bool mac::init(phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h_) +{ + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; + log_h = log_h_; + tti = 0; + + srslte_softbuffer_rx_init(&pch_softbuffer, 100); + srslte_softbuffer_rx_init(&mch_softbuffer, 100); + + timer_alignment = timers.get_unique_id(); + contention_resolution_timer = timers.get_unique_id(); + + bsr_procedure.init( rlc_h, log_h, &config, &timers); + phr_procedure.init(phy_h, log_h, &config, &timers); + mux_unit.init ( rlc_h, log_h, &bsr_procedure, &phr_procedure); + demux_unit.init (phy_h, rlc_h, log_h, timers.get(timer_alignment)); + ra_procedure.init (phy_h, rrc, log_h, &uernti, &config, timers.get(timer_alignment), timers.get(contention_resolution_timer), &mux_unit, &demux_unit); + sr_procedure.init (phy_h, rrc, log_h, &config); + ul_harq.init ( log_h, &uernti, &config.ul_harq_params, timers.get(contention_resolution_timer), &mux_unit); + dl_harq.init ( log_h, timers.get(timer_alignment), &demux_unit); + + reset(); + + start_periodic(1000, MAC_MAIN_THREAD_PRIO); + + return true; +} + +void mac::stop() +{ + srslte_softbuffer_rx_free(&pch_softbuffer); + + pdu_process_thread.stop(); + stop_thread(); + wait_thread_finish(); +} + +void mac::start_pcap(srslte::mac_pcap* pcap_) +{ + pcap = pcap_; + dl_harq.start_pcap(pcap); + ul_harq.start_pcap(pcap); + ra_procedure.start_pcap(pcap); +} + +// Implement Section 5.8 +void mac::reconfiguration() +{ + +} + +void mac::wait_uplink() { + int cnt=0; + Info("Waiting to uplink...\n"); + while(mux_unit.is_pending_any_sdu() && cnt<20) { + usleep(1000); + cnt++; + } +} + +// Implement Section 5.9 +void mac::reset() +{ + bzero(&metrics, sizeof(mac_metrics_t)); + + Info("Resetting MAC\n"); + + timers.get(timer_alignment)->stop(); + timers.get(contention_resolution_timer)->stop(); + + ul_harq.reset_ndi(); + + mux_unit.msg3_flush(); + mux_unit.reset(); + + ra_procedure.reset(); + sr_procedure.reset(); + bsr_procedure.reset(); + phr_procedure.reset(); + + dl_harq.reset(); + phy_h->pdcch_dl_search_reset(); + phy_h->pdcch_ul_search_reset(); + + is_first_ul_grant = true; + + bzero(&uernti, sizeof(ue_rnti_t)); +} + +void mac::run_period() { + + /* Warning: Here order of invocation of procedures is important!! */ + + tti = phy_h->get_current_tti(); + + log_h->step(tti); + + // Step all procedures + bsr_procedure.step(tti); + phr_procedure.step(tti); + + // Check if BSR procedure need to start SR + + if (bsr_procedure.need_to_send_sr(tti)) { + Debug("Starting SR procedure by BSR request, PHY TTI=%d\n", tti); + sr_procedure.start(); + } + if (bsr_procedure.need_to_reset_sr()) { + Debug("Resetting SR procedure by BSR request\n"); + sr_procedure.reset(); + } + sr_procedure.step(tti); + + // Check SR if we need to start RA + if (sr_procedure.need_random_access()) { + ra_procedure.start_mac_order(); + } + + ra_procedure.step(tti); + timers.step_all(); + rrc_h->run_tti(tti); +} + +void mac::bcch_start_rx() +{ + bcch_start_rx(tti, -1); +} + +void mac::bcch_start_rx(int si_window_start, int si_window_length) +{ + if (si_window_length >= 0 && si_window_start >= 0) { + dl_harq.set_si_window_start(si_window_start); + phy_h->pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, si_window_start, si_window_start+si_window_length); + } else { + phy_h->pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, si_window_start); + } + Info("SCHED: Searching for DL grant for SI-RNTI window_st=%d, window_len=%d\n", si_window_start, si_window_length); +} + +void mac::pcch_start_rx() +{ + phy_h->pdcch_dl_search(SRSLTE_RNTI_PCH, SRSLTE_PRNTI); + Info("SCHED: Searching for DL grant for P-RNTI\n"); +} + +void mac::clear_rntis() +{ + phy_h->pdcch_dl_search_reset(); +} + +void mac::bch_decoded_ok(uint8_t* payload, uint32_t len) +{ + // Send MIB to RLC + rlc_h->write_pdu_bcch_bch(payload, len); + + if (pcap) { + pcap->write_dl_bch(payload, len, true, phy_h->get_current_tti()); + } +} + +void mac::pch_decoded_ok(uint32_t len) +{ + // Send PCH payload to RLC + rlc_h->write_pdu_pcch(pch_payload_buffer, len); + + if (pcap) { + pcap->write_dl_pch(pch_payload_buffer, len, true, phy_h->get_current_tti()); + } +} + +void mac::mch_decoded_ok(uint32_t len) +{ + // Parse MAC header + mch_msg.init_rx(len); + + mch_msg.parse_packet(mch_payload_buffer); + while(mch_msg.next()) { + for(uint32_t i = 0; i < phy_mbsfn_cfg.nof_mbsfn_services;i++) { + if(srslte::mch_subh::MCH_SCHED_INFO == mch_msg.get()->ce_type()) { + uint16_t stop; + uint8_t lcid; + if(mch_msg.get()->get_next_mch_sched_info(&lcid, &stop)) { + phy_h->set_mch_period_stop(stop); + Info("MCH Sched Info: LCID: %d, Stop: %d, tti is %d \n", lcid, stop, phy_h->get_current_tti()); + } + } + } + } + + demux_unit.push_pdu_mch(mch_payload_buffer, len, 0); + pdu_process_thread.notify(); + if (pcap) { + pcap->write_dl_mch(mch_payload_buffer, len, true, phy_h->get_current_tti()); + } + + metrics.rx_brate += len*8; +} + +void mac::tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) +{ + if (rnti_type == SRSLTE_RNTI_RAR) { + if (ack) { + ra_procedure.tb_decoded_ok(); + } + } else { + dl_harq.tb_decoded(ack, tb_idx, rnti_type, harq_pid); + if (ack) { + pdu_process_thread.notify(); + metrics.rx_brate += dl_harq.get_current_tbs(harq_pid, tb_idx); + } else { + metrics.rx_errors++; + } + metrics.rx_pkts++; + } +} + +void mac::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action) +{ + if (grant.rnti_type == SRSLTE_RNTI_RAR) { + ra_procedure.new_grant_dl(grant, action); + } else if (grant.rnti_type == SRSLTE_RNTI_PCH) { + + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->generate_ack = false; + action->decode_enabled[0] = true; + action->decode_enabled[1] = false; + srslte_softbuffer_rx_reset_cb(&pch_softbuffer, 1); + action->payload_ptr[0] = pch_payload_buffer; + action->softbuffers[0] = &pch_softbuffer; + action->rnti = grant.rnti; + action->rv[0] = grant.rv[0]; + if (grant.n_bytes[0] > pch_payload_buffer_sz) { + Error("Received grant for PCH (%d bytes) exceeds buffer (%d bytes)\n", grant.n_bytes[0], pch_payload_buffer_sz); + action->decode_enabled[0] = false; + } + } else { + // If PDCCH for C-RNTI and RA procedure in Contention Resolution, notify it + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(false); + } + dl_harq.new_grant_dl(grant, action); + } +} + +uint32_t mac::get_current_tti() +{ + return phy_h->get_current_tti(); +} + +void mac::new_grant_ul(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_ul_t* action) +{ + /* Start PHR Periodic timer on first UL grant */ + if (is_first_ul_grant) { + is_first_ul_grant = false; + phr_procedure.start_timer(); + } + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(true); + } + ul_harq.new_grant_ul(grant, action); + metrics.tx_pkts++; +} + +void mac::new_grant_ul_ack(mac_interface_phy::mac_grant_t grant, bool ack, mac_interface_phy::tb_action_ul_t* action) +{ + int tbs = ul_harq.get_current_tbs(tti); + ul_harq.new_grant_ul_ack(grant, &ack, action); + if (!ack) { + metrics.tx_errors++; + } else { + metrics.tx_brate += tbs; + } + metrics.tx_pkts++; + if (!ack && ra_procedure.is_contention_resolution()) { + ra_procedure.harq_retx(); + } + if (grant.rnti_type == SRSLTE_RNTI_USER && ra_procedure.is_contention_resolution()) { + ra_procedure.pdcch_to_crnti(true); + } +} + +void mac::new_mch_dl(srslte_ra_dl_grant_t phy_grant, tb_action_dl_t *action) +{ + memcpy(&action->phy_grant, &phy_grant, sizeof(srslte_phy_grant_t)); + action->generate_ack = false; + action->decode_enabled[0] = true; + srslte_softbuffer_rx_reset_cb(&mch_softbuffer, 1); + action->payload_ptr[0] = mch_payload_buffer; + action->softbuffers[0] = &mch_softbuffer; +} + +void mac::harq_recv(uint32_t tti, bool ack, mac_interface_phy::tb_action_ul_t* action) +{ + int tbs = ul_harq.get_current_tbs(tti); + ul_harq.harq_recv(tti, ack, action); + if (!ack) { + metrics.tx_errors++; + metrics.tx_pkts++; + } else { + metrics.tx_brate += tbs; + } + if (!ack && ra_procedure.is_contention_resolution()) { + ra_procedure.harq_retx(); + } +} + +void mac::setup_timers() +{ + // stop currently running time alignment timer + if (timers.get(timer_alignment)->is_running()) { + timers.get(timer_alignment)->stop(); + } + + int value = liblte_rrc_time_alignment_timer_num[config.main.time_alignment_timer]; + if (value > 0) { + timers.get(timer_alignment)->set(this, value); + } +} + +void mac::timer_expired(uint32_t timer_id) +{ + if(timer_id == timer_alignment) { + timer_alignment_expire(); + } else { + Warning("Received callback from unknown timer_id=%d\n", timer_id); + } +} + +/* Function called on expiry of TimeAlignmentTimer */ +void mac::timer_alignment_expire() +{ + printf("TimeAlignment timer has expired value=%d ms\n", timers.get(timer_alignment)->get_timeout()); + rrc_h->release_pucch_srs(); + dl_harq.reset(); + ul_harq.reset(); +} + +void mac::get_rntis(ue_rnti_t* rntis) +{ + memcpy(rntis, &uernti, sizeof(ue_rnti_t)); +} + +void mac::set_ho_rnti(uint16_t crnti, uint16_t target_pci) { + phy_h->pdcch_dl_search_reset(); + phy_h->pdcch_ul_search_reset(); + uernti.crnti = crnti; + if (pcap) { + pcap->set_ue_id(target_pci); + } +} + +void mac::set_contention_id(uint64_t uecri) +{ + uernti.contention_id = uecri; +} + +void mac::start_noncont_ho(uint32_t preamble_index, uint32_t prach_mask) +{ + ra_procedure.start_noncont(preamble_index, prach_mask); +} + +void mac::start_cont_ho() +{ + ra_procedure.start_mac_order(56, true); +} + +void mac::get_config(mac_cfg_t* mac_cfg) +{ + memcpy(mac_cfg, &config, sizeof(mac_cfg_t)); +} + +void mac::set_mbsfn_config(uint32_t nof_mbsfn_services) +{ + //cfg->nof_mbsfn_services = config.mbsfn.mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size; + phy_mbsfn_cfg.nof_mbsfn_services = nof_mbsfn_services; +} + + +void mac::set_config(mac_cfg_t* mac_cfg) +{ + memcpy(&config, mac_cfg, sizeof(mac_cfg_t)); + setup_timers(); +} + +void mac::set_config_main(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT* main_cfg) +{ + memcpy(&config.main, main_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + setup_timers(); +} + +void mac::set_config_rach(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT* rach_cfg, uint32_t prach_config_index) +{ + memcpy(&config.rach, rach_cfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + config.prach_config_index = prach_config_index; +} + +void mac::set_config_sr(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT* sr_cfg) +{ + memcpy(&config.sr, sr_cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); +} + +void mac::setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority, int PBR_x_tti, uint32_t BSD) +{ + Info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSd=%d\n", + lcid, lcg, priority, PBR_x_tti, BSD); + mux_unit.set_priority(lcid, priority, PBR_x_tti, BSD); + bsr_procedure.setup_lcg(lcid, lcg); + bsr_procedure.set_priority(lcid, priority); +} + +void mac::mch_start_rx(uint32_t lcid) +{ + demux_unit.mch_start_rx(lcid); +} + +void mac::get_metrics(mac_metrics_t &m) +{ + Info("DL retx: %.2f \%%, perpkt: %.2f, UL retx: %.2f \%% perpkt: %.2f\n", + metrics.rx_pkts?((float) 100*metrics.rx_errors/metrics.rx_pkts):0.0, + dl_harq.get_average_retx(), + metrics.tx_pkts?((float) 100*metrics.tx_errors/metrics.tx_pkts):0.0, + ul_harq.get_average_retx()); + + metrics.ul_buffer = (int) bsr_procedure.get_buffer_state(); + m = metrics; + bzero(&metrics, sizeof(mac_metrics_t)); +} + + + + +/******************************************************** + * + * Interface for timers used by upper layers + * + *******************************************************/ +srslte::timers::timer* mac::timer_get(uint32_t timer_id) +{ + return timers.get(timer_id); +} + +void mac::timer_release_id(uint32_t timer_id) +{ + timers.release_id(timer_id); +} + +uint32_t mac::timer_get_unique_id() +{ + return timers.get_unique_id(); +} + + +/******************************************************** + * + * Class that runs a thread to process DL MAC PDUs from + * DEMUX unit + * + *******************************************************/ +mac::pdu_process::pdu_process(demux *demux_unit_) +{ + demux_unit = demux_unit_; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + have_data = false; + start(MAC_PDU_THREAD_PRIO); +} + +void mac::pdu_process::stop() +{ + pthread_mutex_lock(&mutex); + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void mac::pdu_process::notify() +{ + pthread_mutex_lock(&mutex); + have_data = true; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); +} + +void mac::pdu_process::run_thread() +{ + running = true; + while(running) { + have_data = demux_unit->process_pdus(); + if (!have_data) { + pthread_mutex_lock(&mutex); + while(!have_data && running) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + } + } +} + + + + + + + +} + + + diff --git a/srsue/src/mac/mux.cc b/srsue/src/mac/mux.cc new file mode 100644 index 0000000..361c0be --- /dev/null +++ b/srsue/src/mac/mux.cc @@ -0,0 +1,388 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srsue/hdr/mac/mux.h" +#include "srsue/hdr/mac/mac.h" + +#include +#include + +namespace srsue { + +mux::mux(uint8_t nof_harq_proc_) : pdu_msg(MAX_NOF_SUBHEADERS), pid_has_bsr(nof_harq_proc_), nof_harq_proc(nof_harq_proc_) +{ + pthread_mutex_init(&mutex, NULL); + + pending_crnti_ce = 0; + + log_h = NULL; + rlc = NULL; + bsr_procedure = NULL; + phr_procedure = NULL; + msg3_buff_start_pdu = NULL; + + msg3_flush(); +} + +void mux::init(rlc_interface_mac *rlc_, srslte::log *log_h_, bsr_interface_mux *bsr_procedure_, phr_proc *phr_procedure_) +{ + log_h = log_h_; + rlc = rlc_; + bsr_procedure = bsr_procedure_; + phr_procedure = phr_procedure_; + reset(); +} + +void mux::reset() +{ + for (uint32_t i=0;iget_buffer_state(lch[i].id)) { + return true; + } + } + return false; +} + +bool mux::is_pending_sdu(uint32_t lch_id) { + return rlc->get_buffer_state(lch_id)>0; +} + +int mux::find_lchid(uint32_t lcid) +{ + for (uint32_t i=0;i= 0) { + lch.erase(lch.begin()+pos); + } else { + Error("Deleting logical channel id %d. Does not exist\n", lch_id); + } +} + +void mux::set_priority(uint32_t lch_id, uint32_t new_priority, int set_PBR, uint32_t set_BSD) +{ + int pos = find_lchid(lch_id); + + // Create new channel if it does not exist + if (pos < 0) { + lchid_t ch; + ch.id = lch_id; + ch.priority = new_priority; + ch.BSD = set_BSD; + ch.PBR = set_PBR; + ch.Bj = 0; + lch.push_back(ch); + } else { + lch[pos].priority = new_priority; + lch[pos].PBR = set_PBR; + lch[pos].BSD = set_BSD; + } + + // sort according to priority (increasing is lower priority) + std::sort(lch.begin(), lch.end(), sortPriority); +} + +srslte::sch_subh::cetype bsr_format_convert(bsr_proc::bsr_format_t format) { + switch(format) { + case bsr_proc::LONG_BSR: + return srslte::sch_subh::LONG_BSR; + case bsr_proc::TRUNC_BSR: + return srslte::sch_subh::TRUNC_BSR; + case bsr_proc::SHORT_BSR: + default: + return srslte::sch_subh::SHORT_BSR; + } +} + +void mux::pusch_retx(uint32_t tx_tti, uint32_t pid) +{ + if (pid_has_bsr[pid%nof_harq_proc]) { + bsr_procedure->set_tx_tti(tx_tti); + } +} + +// Multiplexing and logical channel priorization as defined in Section 5.4.3 +uint8_t* mux::pdu_get(uint8_t *payload, uint32_t pdu_sz, uint32_t tx_tti, uint32_t pid) +{ + pthread_mutex_lock(&mutex); + + // Update Bj + for (uint32_t i=0;i= 0) { + lch[i].Bj += lch[i].PBR; + } + if (lch[i].Bj >= (int)lch[i].BSD) { + lch[i].Bj = lch[i].BSD*lch[i].PBR; + } + } + + // Logical Channel Procedure + bool is_rar = false; + + pdu_msg.init_tx(payload, pdu_sz, true); + + // MAC control element for C-RNTI or data from UL-CCCH + if (!allocate_sdu(0, &pdu_msg, -1)) { + if (pending_crnti_ce) { + is_rar = true; + if (pdu_msg.new_subh()) { + if (!pdu_msg.get()->set_c_rnti(pending_crnti_ce)) { + Warning("Pending C-RNTI CE could not be inserted in MAC PDU\n"); + } + } + } + } else { + is_rar = true; + } + pending_crnti_ce = 0; + + bsr_proc::bsr_t bsr; + bool regular_bsr = bsr_procedure->need_to_send_bsr_on_ul_grant(pdu_msg.rem_size(), &bsr); + bool bsr_is_inserted = false; + + // MAC control element for BSR, with exception of BSR included for padding; + if (regular_bsr) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_bsr(bsr.buff_size, bsr_format_convert(bsr.format)); + bsr_is_inserted = true; + } + } + + // MAC control element for PHR + if (phr_procedure) { + float phr_value; + if (phr_procedure->generate_phr_on_ul_grant(&phr_value)) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_phr(phr_value); + } + } + } + + if (!is_rar) { + // Update buffer states for all logical channels + int sdu_space = pdu_msg.get_sdu_space(); + for (uint32_t i=0;iget_buffer_state(lch[i].id); + lch[i].sched_len = 0; + } + + // data from any Logical Channel, except data from UL-CCCH; + // first only those with positive Bj + for (uint32_t i=0;i= 0) { + lch[i].Bj -= lch[i].sched_len; + } + } + } + + // If resources remain, allocate regardless of their Bj value + for (uint32_t i=0;i 0) { + for (int i=(int)lch.size()-1;i>=0;i--) { + if (lch[i].sched_len > 0) { + lch[i].sched_len = -1; + break; + } + } + } + for (uint32_t i=0;igenerate_padding_bsr(pdu_msg.rem_size(), &bsr)) { + if (pdu_msg.new_subh()) { + pdu_msg.get()->set_bsr(bsr.buff_size, bsr_format_convert(bsr.format)); + bsr_is_inserted = true; + } + } + } + + log_h->debug("Assembled MAC PDU msg size %d/%d bytes\n", pdu_msg.get_pdu_len()-pdu_msg.rem_size(), pdu_sz); + + /* Generate MAC PDU and save to buffer */ + uint8_t *ret = pdu_msg.write_packet(log_h); + + pid_has_bsr[pid%nof_harq_proc] = bsr_is_inserted; + if (bsr_is_inserted) { + bsr_procedure->set_tx_tti(tx_tti); + } + + pthread_mutex_unlock(&mutex); + + + return ret; +} + +void mux::append_crnti_ce_next_tx(uint16_t crnti) { + pending_crnti_ce = crnti; +} + +bool mux::sched_sdu(lchid_t *ch, int *sdu_space, int max_sdu_sz) +{ + + if (*sdu_space > 0) { + // Get n-th pending SDU pointer and length + int sched_len = ch->buffer_len; + if (sched_len > 0) { // there is pending SDU to allocate + if (sched_len > max_sdu_sz && max_sdu_sz >= 0) { + sched_len = max_sdu_sz; + } + if (sched_len > *sdu_space) { + sched_len = *sdu_space; + } + + log_h->debug("SDU: scheduled lcid=%d, rlc_buffer=%d, allocated=%d/%d\n", + ch->id, ch->buffer_len, sched_len, sdu_space?*sdu_space:0); + + *sdu_space -= sched_len; + ch->buffer_len -= sched_len; + ch->sched_len += sched_len; + return true; + } + } + return false; +} + +bool mux::allocate_sdu(uint32_t lcid, srslte::sch_pdu* pdu_msg, int max_sdu_sz) +{ + + // Get n-th pending SDU pointer and length + int sdu_len = rlc->get_buffer_state(lcid); + + if (sdu_len > 0) { // there is pending SDU to allocate + int buffer_state = sdu_len; + if (sdu_len > max_sdu_sz && max_sdu_sz >= 0) { + sdu_len = max_sdu_sz; + } + int sdu_space = pdu_msg->get_sdu_space(); + if (sdu_len > sdu_space || max_sdu_sz < 0) { + sdu_len = sdu_space; + } + if (sdu_len > MIN_RLC_SDU_LEN) { + if (pdu_msg->new_subh()) { // there is space for a new subheader + sdu_len = pdu_msg->get()->set_sdu(lcid, sdu_len, rlc); + if (sdu_len > 0) { // new SDU could be added + Debug("SDU: allocated lcid=%d, rlc_buffer=%d, allocated=%d/%d, max_sdu_sz=%d, remaining=%d\n", + lcid, buffer_state, sdu_len, sdu_space, max_sdu_sz, pdu_msg->rem_size()); + return true; + } else { + Warning("SDU: rlc_buffer=%d, allocated=%d/%d, remaining=%d\n", + buffer_state, sdu_len, sdu_space, pdu_msg->rem_size()); + pdu_msg->del_subh(); + } + } + } + } + return false; +} + +void mux::msg3_flush() +{ + if (log_h) { + Debug("Msg3 buffer flushed\n"); + } + msg3_has_been_transmitted = false; + msg3_pending = false; + bzero(msg3_buff, sizeof(MSG3_BUFF_SZ)); + msg3_buff_start_pdu = NULL; +} + +bool mux::msg3_is_transmitted() +{ + return msg3_has_been_transmitted; +} + +void mux::msg3_prepare() { + msg3_pending = true; +} + +bool mux::msg3_is_pending() { + return msg3_pending; +} + +/* Returns a pointer to the Msg3 buffer */ +uint8_t* mux::msg3_get(uint8_t *payload, uint32_t pdu_sz) +{ + if (pdu_sz < MSG3_BUFF_SZ - 32) { + if (!msg3_buff_start_pdu) { + msg3_buff_start_pdu = pdu_get(msg3_buff, pdu_sz, 0, 0); + if (!msg3_buff_start_pdu) { + Error("Moving PDU from Mux unit to Msg3 buffer\n"); + return NULL; + } + msg3_has_been_transmitted = true; + msg3_pending = false; + } + } else { + Error("Msg3 size (%d) is longer than internal msg3_buff size=%d, (see mux.h)\n", pdu_sz, MSG3_BUFF_SZ-32); + return NULL; + } + memcpy(payload, msg3_buff_start_pdu, sizeof(uint8_t)*pdu_sz); + msg3_has_been_transmitted = true; + return payload; +} + + +} diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc new file mode 100644 index 0000000..bd49671 --- /dev/null +++ b/srsue/src/mac/proc_bsr.cc @@ -0,0 +1,409 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srsue/hdr/mac/proc_bsr.h" +#include "srsue/hdr/mac/mac.h" +#include "srsue/hdr/mac/mux.h" + + + namespace srsue { + +bsr_proc::bsr_proc() +{ + initiated = false; + last_print = 0; + next_tx_tti = 0; + triggered_bsr_type=NONE; + +} + +void bsr_proc::init(rlc_interface_mac *rlc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_) +{ + log_h = log_h_; + rlc = rlc_; + mac_cfg = mac_cfg_; + timers_db = timers_db_; + + timer_periodic_id = timers_db->get_unique_id(); + timer_retx_id = timers_db->get_unique_id(); + + reset(); + initiated = true; +} + +void bsr_proc::reset() +{ + timers_db->get(timer_periodic_id)->stop(); + timers_db->get(timer_periodic_id)->reset(); + timers_db->get(timer_retx_id)->stop(); + timers_db->get(timer_retx_id)->reset(); + + reset_sr = false; + sr_is_sent = false; + triggered_bsr_type = NONE; + for (int i=0;imain.ulsch_cnfg.periodic_bsr_timer]; + if (periodic >= 0) { + triggered_bsr_type = REGULAR; + Debug("BSR: Triggering BSR reTX\n"); + sr_is_sent = false; + } + } +} + +// Checks if data is available for a a channel with higher priority than others +bool bsr_proc::check_highest_channel() { + int pending_data_lcid = -1; + + for (int i=0;i= 0) { + if (rlc->get_buffer_state(i) > 0) { + pending_data_lcid = i; + for (int j=0;jget_buffer_state(j) > 0) { + if (priorities[j] > priorities[i]) { + pending_data_lcid = -1; + } + } + } + } + } + } + if (pending_data_lcid >= 0) { + // If there is new data available for this logical channel + uint32_t nbytes = rlc->get_buffer_state(pending_data_lcid); + if (nbytes > last_pending_data[pending_data_lcid]) + { + if (triggered_bsr_type != REGULAR) { + Debug("BSR: Triggered REGULAR BSR for Max Priority LCID=%d\n", pending_data_lcid); + } + triggered_bsr_type = REGULAR; + return true; + } + } + return false; +} + +uint32_t bsr_proc::get_buffer_state() { + uint32_t buffer = 0; + for (int i=0;i= 0) { + buffer += rlc->get_buffer_state(i); + } + } + return buffer; +} + +// Checks if only one logical channel has data avaiable for Tx +bool bsr_proc::check_single_channel() { + uint32_t pending_data_lcid = 0; + uint32_t nof_nonzero_lcid = 0; + + for (int i=0;i= 0) { + if (rlc->get_buffer_state(i) > 0) { + pending_data_lcid = i; + nof_nonzero_lcid++; + } + } + } + if (nof_nonzero_lcid == 1) { + uint32_t nbytes = rlc->get_buffer_state(pending_data_lcid); + // If there is new data available for this logical channel + if (nbytes > last_pending_data[pending_data_lcid]) { + triggered_bsr_type = REGULAR; + Debug("BSR: Triggered REGULAR BSR for single LCID=%d\n", pending_data_lcid); + return true; + } + } + return false; +} + +void bsr_proc::update_pending_data() { + for (int i=0;iget_buffer_state(i); + } +} + +bool bsr_proc::generate_bsr(bsr_t *bsr, uint32_t nof_padding_bytes) { + bool ret = false; + uint32_t nof_lcg=0; + bzero(bsr, sizeof(bsr_t)); + for (int i=0;i= 0) { + uint32_t n = rlc->get_buffer_state(i); + bsr->buff_size[lcg[i]] += n; + if (n > 0) { + nof_lcg++; + ret = true; + } + } + } + if (triggered_bsr_type == PADDING) { + if (nof_padding_bytes < 4) { + // If space only for short + if (nof_lcg > 1) { + bsr->format = TRUNC_BSR; + uint32_t max_prio_ch = find_max_priority_lcid(); + for (int i=0;i<4;i++) { + if (lcg[max_prio_ch] != i) { + bsr->buff_size[i] = 0; + } + } + } else { + bsr->format = SHORT_BSR; + } + } else { + // If space for long BSR + bsr->format = LONG_BSR; + } + } else { + bsr->format = SHORT_BSR; + if (nof_lcg > 1) { + bsr->format = LONG_BSR; + } + } + Info("BSR: Type %s, Format %s, Value=%d,%d,%d,%d\n", + bsr_type_tostring(triggered_bsr_type), bsr_format_tostring(bsr->format), + bsr->buff_size[0], bsr->buff_size[1], bsr->buff_size[2], bsr->buff_size[3]); + + return ret; +} + +// Checks if Regular BSR must be assembled, as defined in 5.4.5 +// Padding BSR is assembled when called by mux_unit when UL grant is received +// Periodic BSR is triggered by the expiration of the timers +void bsr_proc::step(uint32_t tti) +{ + if (!initiated) { + return; + } + + int periodic = liblte_rrc_periodic_bsr_timer_num[mac_cfg->main.ulsch_cnfg.periodic_bsr_timer]; + if (periodic > 0 && (uint32_t)periodic != timers_db->get(timer_periodic_id)->get_timeout()) + { + timers_db->get(timer_periodic_id)->set(this, periodic); + timers_db->get(timer_periodic_id)->run(); + Info("BSR: Configured timer periodic %d ms\n", periodic); + } + int retx = liblte_rrc_retransmission_bsr_timer_num[mac_cfg->main.ulsch_cnfg.retx_bsr_timer]; + if (retx > 0 && (uint32_t)retx != timers_db->get(timer_retx_id)->get_timeout()) + { + timers_db->get(timer_retx_id)->set(this, retx); + timers_db->get(timer_retx_id)->run(); + Info("BSR: Configured timer reTX %d ms\n", retx); + } + + // Check condition 1 in Sec 5.4.5 + if (triggered_bsr_type == NONE) { + check_single_channel(); + } + // Higher priority channel is reported regardless of a BSR being already triggered + check_highest_channel(); + + update_pending_data(); + + + if ((tti - last_print)%10240 > QUEUE_STATUS_PERIOD_MS) { + char str[128]; + bzero(str, 128); + for (int i=0;iget_buffer_state(i), last_pending_data[i]); + } + Info("BSR: QUEUE status: %s\n", str); + last_print = tti; + } + +} + +char* bsr_proc::bsr_type_tostring(triggered_bsr_type_t type) { + switch(type) { + case bsr_proc::REGULAR: + return (char*) "Regular"; + case bsr_proc::PADDING: + return (char*) "Padding"; + case bsr_proc::PERIODIC: + return (char*) "Periodic"; + default: + return (char*) "Regular"; + } +} + +char* bsr_proc::bsr_format_tostring(bsr_format_t format) { + switch(format) { + case bsr_proc::LONG_BSR: + return (char*) "Long"; + case bsr_proc::SHORT_BSR: + return (char*) "Short"; + case bsr_proc::TRUNC_BSR: + return (char*) "Truncated"; + default: + return (char*) "Short"; + } +} + +bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) +{ + bool ret = false; + + uint32_t bsr_sz = 0; + if (triggered_bsr_type == PERIODIC || triggered_bsr_type == REGULAR) { + /* Check if grant + MAC SDU headers is enough to accomodate all pending data */ + int total_data = 0; + for (int i=0;iget_buffer_state(i))+rlc->get_buffer_state(i); + } + total_data--; // Because last SDU has no size header + + /* All triggered BSRs shall be cancelled in case the UL grant can accommodate all pending data available for transmission + but is not sufficient to additionally accommodate the BSR MAC control element plus its subheader. + */ + generate_bsr(bsr, 0); + bsr_sz = bsr->format==LONG_BSR?3:1; + if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { + Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + } else { + Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", + grant_size, total_data, bsr_sz); + ret = true; + } + if (timers_db->get(timer_periodic_id)->get_timeout() && bsr->format != TRUNC_BSR) { + timers_db->get(timer_periodic_id)->reset(); + timers_db->get(timer_periodic_id)->run(); + } + } + // Cancel all triggered BSR and SR + triggered_bsr_type = NONE; + reset_sr = true; + // Restart or Start ReTX timer + if (timers_db->get(timer_retx_id)->get_timeout()) { + timers_db->get(timer_retx_id)->reset(); + timers_db->get(timer_retx_id)->run(); + } + return ret; +} + +bool bsr_proc::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t *bsr) +{ + bool ret = false; + + if (triggered_bsr_type != NONE || nof_padding_bytes >= 2) { + + if (triggered_bsr_type == NONE) { + triggered_bsr_type = PADDING; + } + generate_bsr(bsr, nof_padding_bytes); + ret = true; + + if (timers_db->get(timer_periodic_id)->get_timeout() && bsr->format != TRUNC_BSR) { + timers_db->get(timer_periodic_id)->reset(); + timers_db->get(timer_periodic_id)->run(); + } + + } + return ret; +} + +void bsr_proc::set_tx_tti(uint32_t tti) { + Debug("BSR: Set next_tx_tti=%d\n", tti); + next_tx_tti = tti; +} + +bool bsr_proc::need_to_reset_sr() { + if (reset_sr) { + reset_sr = false; + sr_is_sent = false; + Debug("BSR: SR reset. sr_is_sent and reset_rs false\n"); + return true; + } else { + return false; + } +} + +bool bsr_proc::need_to_send_sr(uint32_t tti) { + if (!sr_is_sent && triggered_bsr_type == REGULAR) { + if (srslte_tti_interval(tti,next_tx_tti)>0 && srslte_tti_interval(tti,next_tx_tti) < 10240-HARQ_DELAY_MS) { + reset_sr = false; + sr_is_sent = true; + Debug("BSR: Need to send sr: sr_is_sent=true, reset_sr=false, tti=%d, next_tx_tti=%d\n", tti, next_tx_tti); + return true; + } else { + Debug("BSR: Not sending SR because tti=%d, next_tx_tti=%d\n", tti, next_tx_tti); + } + } + return false; +} + +void bsr_proc::setup_lcg(uint32_t lcid, uint32_t new_lcg) +{ + if (lcid < MAX_LCID && new_lcg < 4) { + lcg[lcid] = new_lcg; + } +} + +void bsr_proc::set_priority(uint32_t lcid, uint32_t priority) { + if (lcid < MAX_LCID) { + priorities[lcid] = priority; + } +} + +uint32_t bsr_proc::find_max_priority_lcid() { + int32_t max_prio = 0; + uint32_t max_idx = 0; + for (int i=0;i max_prio) { + max_prio = priorities[i]; + max_idx = i; + } + } + return max_idx; +} + +} diff --git a/srsue/src/mac/proc_phr.cc b/srsue/src/mac/proc_phr.cc new file mode 100644 index 0000000..69a5c0d --- /dev/null +++ b/srsue/src/mac/proc_phr.cc @@ -0,0 +1,163 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srsue/hdr/mac/proc_phr.h" +#include "srsue/hdr/mac/mac.h" +#include "srsue/hdr/mac/mux.h" +#include "srslte/interfaces/ue_interfaces.h" + + + namespace srsue { + +phr_proc::phr_proc() +{ + initiated = false; +} + +void phr_proc::init(phy_interface_mac* phy_h_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_) +{ + phy_h = phy_h_; + log_h = log_h_; + mac_cfg = mac_cfg_; + timers_db = timers_db_; + initiated = true; + + timer_periodic_id = timers_db->get_unique_id(); + timer_prohibit_id = timers_db->get_unique_id(); + + reset(); +} + +void phr_proc::reset() +{ + phr_is_triggered = false; + timer_periodic_value = -2; + timer_prohibit_value = -2; + dl_pathloss_change = -2; +} + +bool phr_proc::pathloss_changed() { + + int min_change = liblte_rrc_dl_pathloss_change_num[mac_cfg->main.phr_cnfg.dl_pathloss_change]; + int cur_pathloss_db = (int) phy_h->get_pathloss_db(); + + if (abs(cur_pathloss_db - last_pathloss_db) > min_change && min_change > 0) { + last_pathloss_db = cur_pathloss_db; + return true; + } else { + return false; + } +} + +void phr_proc::start_timer() { + timers_db->get(timer_periodic_id)->run(); +} + +/* Trigger PHR when timers exire */ +void phr_proc::timer_expired(uint32_t timer_id) { + if(timer_id == timer_periodic_id) { + timers_db->get(timer_periodic_id)->reset(); + timers_db->get(timer_periodic_id)->run(); + Debug("PHR: Triggered by timer periodic (timer expired).\n"); + phr_is_triggered = true; + } else if (timer_id == timer_prohibit_id) { + int pathloss_db = liblte_rrc_dl_pathloss_change_num[mac_cfg->main.phr_cnfg.dl_pathloss_change]; + if (pathloss_changed()) { + Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%d (timer expired)\n", last_pathloss_db); + phr_is_triggered = true; + } + } else { + log_h->warning("Received timer callback from unknown timer_id=%d\n", timer_id); + } +} + +void phr_proc::step(uint32_t tti) +{ + if (!initiated) { + return; + } + + if (mac_cfg->main.phr_cnfg.setup_present) { + int cfg_timer_periodic = liblte_rrc_periodic_phr_timer_num[mac_cfg->main.phr_cnfg.periodic_phr_timer]; + + // Setup timers and trigger PHR when configuration changed by higher layers + if (timer_periodic_value != cfg_timer_periodic && cfg_timer_periodic > 0) + { + timer_periodic_value = cfg_timer_periodic; + timers_db->get(timer_periodic_id)->set(this, timer_periodic_value); + timers_db->get(timer_periodic_id)->run(); + phr_is_triggered = true; + Info("PHR: Configured timer periodic %d ms\n", timer_periodic_value); + } + + } + + int cfg_timer_prohibit = liblte_rrc_prohibit_phr_timer_num[mac_cfg->main.phr_cnfg.prohibit_phr_timer]; + + if (timer_prohibit_value != cfg_timer_prohibit && cfg_timer_prohibit > 0) + { + timer_prohibit_value = cfg_timer_prohibit; + timers_db->get(timer_prohibit_id)->set(this, timer_prohibit_value); + timers_db->get(timer_prohibit_id)->run(); + Info("PHR: Configured timer prohibit %d ms\n", timer_prohibit_value); + phr_is_triggered = true; + } + if (pathloss_changed() && timers_db->get(timer_prohibit_id)->is_expired()) + { + Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%d\n", last_pathloss_db); + phr_is_triggered = true; + } +} + +bool phr_proc::generate_phr_on_ul_grant(float *phr) +{ + + if (phr_is_triggered) { + if (phr) { + *phr = phy_h->get_phr(); + } + + Debug("PHR: Generating PHR=%f\n", phr?*phr:0.0); + + timers_db->get(timer_periodic_id)->reset(); + timers_db->get(timer_prohibit_id)->reset(); + timers_db->get(timer_periodic_id)->run(); + timers_db->get(timer_prohibit_id)->run(); + + phr_is_triggered = false; + + return true; + } else { + return false; + } +} + +} diff --git a/srsue/src/mac/proc_ra.cc b/srsue/src/mac/proc_ra.cc new file mode 100644 index 0000000..5b5b56e --- /dev/null +++ b/srsue/src/mac/proc_ra.cc @@ -0,0 +1,604 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include +#include +#include + +#include "srsue/hdr/mac/proc_ra.h" +#include "srsue/hdr/mac/mac.h" +#include "srsue/hdr/mac/mux.h" + +/* Random access procedure as specified in Section 5.1 of 36.321 */ + + +namespace srsue { + +// Table 7.2-1. Backoff Parameter values +uint32_t backoff_table[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, 480, 960, 960, 960, 960}; + +// Table 7.6-1: DELTA_PREAMBLE values. +int delta_preamble_db_table[5] = {0, 0, -3, -3, 8}; + +void ra_proc::init(phy_interface_mac* phy_h_, + rrc_interface_mac *rrc_, + srslte::log* log_h_, + mac_interface_rrc::ue_rnti_t *rntis_, + mac_interface_rrc::mac_cfg_t *mac_cfg_, + srslte::timers::timer* time_alignment_timer_, + srslte::timers::timer* contention_resolution_timer_, + mux* mux_unit_, + demux* demux_unit_) +{ + phy_h = phy_h_; + log_h = log_h_; + mac_cfg = mac_cfg_; + rntis = rntis_; + mux_unit = mux_unit_; + demux_unit= demux_unit_; + rrc = rrc_; + + time_alignment_timer = time_alignment_timer_; + contention_resolution_timer = contention_resolution_timer_; + + srslte_softbuffer_rx_init(&softbuffer_rar, 10); + + // Tell demux to call us when a UE CRID is received + demux_unit->set_uecrid_callback(uecrid_callback, this); + + reset(); +} + +ra_proc::~ra_proc() { + srslte_softbuffer_rx_free(&softbuffer_rar); +} + +void ra_proc::reset() { + state = IDLE; + msg3_transmitted = false; + started_by_pdcch = false; +} + +void ra_proc::start_pcap(srslte::mac_pcap* pcap_) +{ + pcap = pcap_; +} + +void ra_proc::read_params() { + + // Read initialization parameters + configIndex = mac_cfg->prach_config_index; + if (noncontention_enabled) { + preambleIndex = next_preamble_idx; + maskIndex = next_prach_mask; + noncontention_enabled = false; + } else { + preambleIndex = 0; // pass when called from higher layers for non-contention based RA + maskIndex = 0; // same + } + nof_preambles = liblte_rrc_number_of_ra_preambles_num[mac_cfg->rach.num_ra_preambles]; + if (mac_cfg->rach.preambles_group_a_cnfg.present) { + nof_groupA_preambles = liblte_rrc_size_of_ra_preambles_group_a_num[mac_cfg->rach.preambles_group_a_cnfg.size_of_ra]; + } else { + nof_groupA_preambles = nof_preambles; + } + + if (nof_groupA_preambles > nof_preambles) { + nof_groupA_preambles = nof_preambles; + } + + nof_groupB_preambles = nof_preambles - nof_groupA_preambles; + if (nof_groupB_preambles) { + messagePowerOffsetGroupB= liblte_rrc_message_power_offset_group_b_num[mac_cfg->rach.preambles_group_a_cnfg.msg_pwr_offset_group_b]; + messageSizeGroupA = liblte_rrc_message_size_group_a_num[mac_cfg->rach.preambles_group_a_cnfg.msg_size]; + } + responseWindowSize = liblte_rrc_ra_response_window_size_num[mac_cfg->rach.ra_resp_win_size]; + powerRampingStep = liblte_rrc_power_ramping_step_num[mac_cfg->rach.pwr_ramping_step]; + preambleTransMax = liblte_rrc_preamble_trans_max_num[mac_cfg->rach.preamble_trans_max]; + iniReceivedTargetPower = liblte_rrc_preamble_initial_received_target_power_num[mac_cfg->rach.preamble_init_rx_target_pwr]; + contentionResolutionTimer = liblte_rrc_mac_contention_resolution_timer_num[mac_cfg->rach.mac_con_res_timer]; + + delta_preamble_db = delta_preamble_db_table[configIndex%5]; + + if (contentionResolutionTimer > 0) { + contention_resolution_timer->set(this, contentionResolutionTimer); + } + +} + +bool ra_proc::in_progress() +{ + return (state > IDLE && state != COMPLETION_DONE); +} + +bool ra_proc::is_successful() { + return state == COMPLETION_DONE; +} + +bool ra_proc::is_response_error() { + return state == RESPONSE_ERROR; +} + +bool ra_proc::is_contention_resolution() { + return state == CONTENTION_RESOLUTION; +} + +bool ra_proc::is_error() { + return state == RA_PROBLEM; +} + +const char* state_str[12] = {"Idle", + "RA: INIT: ", + "RA: Select: ", + "RA: TX: ", + "RA: PDCCH: ", + "RA: Rx: ", + "RA: RxErr: ", + "RA: Backof: ", + "RA: ConRes: ", + "RA: Done: ", + "RA: Done: ", + "RA: Error: "}; + + +#define rError(fmt, ...) Error("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rInfo(fmt, ...) Info("%s" fmt, state_str[state], ##__VA_ARGS__) +#define rDebug(fmt, ...) Debug("%s" fmt, state_str[state], ##__VA_ARGS__) + + +// Process Timing Advance Command as defined in Section 5.2 +void ra_proc::process_timeadv_cmd(uint32_t ta) { + if (preambleIndex == 0) { + // Preamble not selected by UE MAC + phy_h->set_timeadv_rar(ta); + time_alignment_timer->reset(); + time_alignment_timer->run(); + Debug("Applying RAR TA CMD %d\n", ta); + } else { + // Preamble selected by UE MAC + if (!time_alignment_timer->is_running()) { + phy_h->set_timeadv_rar(ta); + time_alignment_timer->run(); + Debug("Applying RAR TA CMD %d\n", ta); + } else { + // Ignore TA CMD + Warning("Ignoring RAR TA CMD because timeAlignmentTimer still running\n"); + } + } +} + +void ra_proc::step_initialization() { + read_params(); + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + transmitted_contention_id = 0; + preambleTransmissionCounter = 1; + first_rar_received = true; + mux_unit->msg3_flush(); + msg3_flushed = false; + backoff_param_ms = 0; + + // FIXME: This is because RA in Connected state not working in amarisoft + transmitted_crnti = rntis->crnti; + if(transmitted_crnti) { + state = RESPONSE_ERROR; + } + + // Instruct phy to configure PRACH + phy_h->configure_prach_params(); + state = RESOURCE_SELECTION; +} + +void ra_proc::step_resource_selection() { + ra_group_t sel_group; + + if (preambleIndex > 0) { + // Preamble is chosen by Higher layers (ie Network) + sel_maskIndex = maskIndex; + sel_preamble = (uint32_t) preambleIndex; + } else { + // Preamble is chosen by MAC UE + if (!msg3_transmitted) { + if (nof_groupB_preambles > 0 && new_ra_msg_len > messageSizeGroupA) { // Check also pathloss (Pcmax,deltaPreamble and powerOffset) + sel_group = RA_GROUP_B; + } else { + sel_group = RA_GROUP_A; + } + last_msg3_group = sel_group; + } else { + sel_group = last_msg3_group; + } + if (sel_group == RA_GROUP_A) { + if (nof_groupA_preambles) { + sel_preamble = preambleTransmissionCounter%nof_groupA_preambles; + } else { + rError("Selected group preamble A but nof_groupA_preambles=0\n"); + state = RA_PROBLEM; + return; + } + } else { + if (nof_groupB_preambles) { + sel_preamble = nof_groupA_preambles + rand()%nof_groupB_preambles; + } else { + rError("Selected group preamble B but nof_groupA_preambles=0\n"); + state = RA_PROBLEM; + return; + } + } + sel_maskIndex = 0; + } + + rDebug("Selected preambleIndex=%d maskIndex=%d GroupA=%d, GroupB=%d\n", + sel_preamble, sel_maskIndex,nof_groupA_preambles, nof_groupB_preambles); + state = PREAMBLE_TRANSMISSION; +} + +void ra_proc::step_preamble_transmission() { + received_target_power_dbm = iniReceivedTargetPower + + delta_preamble_db + + (preambleTransmissionCounter-1)*powerRampingStep; + + rar_received = false; + phy_h->prach_send(sel_preamble, sel_maskIndex - 1, received_target_power_dbm); + state = PDCCH_SETUP; +} + +void ra_proc::step_pdcch_setup() { + + int ra_tti = phy_h->prach_tx_tti(); + if (ra_tti > 0) { + ra_rnti = 1+ra_tti%10; + rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d\n", sel_preamble, ra_rnti, ra_tti); + log_h->console("Random Access Transmission: seq=%d, ra-rnti=0x%x\n", sel_preamble, ra_rnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_RAR, ra_rnti, ra_tti+3, ra_tti+3+responseWindowSize); + state = RESPONSE_RECEPTION; + } +} + +void ra_proc::new_grant_dl(mac_interface_phy::mac_grant_t grant, mac_interface_phy::tb_action_dl_t* action) +{ + if (grant.n_bytes[0] < MAX_RAR_PDU_LEN) { + rDebug("DL grant found RA-RNTI=%d\n", ra_rnti); + action->decode_enabled[0] = true; + action->decode_enabled[1] = false; + action->default_ack[0] = false; + action->generate_ack = false; + action->payload_ptr[0] = rar_pdu_buffer; + action->rnti = grant.rnti; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->rv[0] = grant.rv[0]; + action->softbuffers[0] = &softbuffer_rar; + rar_grant_nbytes = grant.n_bytes[0]; + rar_grant_tti = grant.tti; + if (action->rv[0] == 0) { + srslte_softbuffer_rx_reset(&softbuffer_rar); + } + } else { + rError("Received RAR grant exceeds buffer length (%d>%d)\n", grant.n_bytes[0], MAX_RAR_PDU_LEN); + action->decode_enabled[0] = false; + action->decode_enabled[1] = false; + state = RESPONSE_ERROR; + } +} + +void ra_proc::tb_decoded_ok() { + if (pcap) { + pcap->write_dl_ranti(rar_pdu_buffer, rar_grant_nbytes, ra_rnti, true, rar_grant_tti); + } + + rDebug("RAR decoded successfully TBS=%d\n", rar_grant_nbytes); + + rar_pdu_msg.init_rx(rar_grant_nbytes); + rar_pdu_msg.parse_packet(rar_pdu_buffer); + // Set Backoff parameter + if (rar_pdu_msg.has_backoff()) { + backoff_param_ms = backoff_table[rar_pdu_msg.get_backoff()%16]; + } else { + backoff_param_ms = 0; + } + + current_ta = 0; + + while(rar_pdu_msg.next()) { + if (rar_pdu_msg.get()->get_rapid() == sel_preamble) { + + rar_received = true; + process_timeadv_cmd(rar_pdu_msg.get()->get_ta_cmd()); + + // FIXME: Indicate received target power + //phy_h->set_target_power_rar(iniReceivedTargetPower, (preambleTransmissionCounter-1)*powerRampingStep); + + uint8_t grant[srslte::rar_subh::RAR_GRANT_LEN]; + rar_pdu_msg.get()->get_sched_grant(grant); + + phy_h->pdcch_dl_search_reset(); + + phy_h->set_rar_grant(rar_grant_tti, grant); + + current_ta = rar_pdu_msg.get()->get_ta_cmd(); + + rInfo("RAPID=%d, TA=%d\n", sel_preamble, rar_pdu_msg.get()->get_ta_cmd()); + + if (preambleIndex > 0) { + // Preamble selected by Network + state = COMPLETION; + } else { + // Preamble selected by UE MAC + mux_unit->msg3_prepare(); + rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti(); + phy_h->pdcch_dl_search(SRSLTE_RNTI_TEMP, rar_pdu_msg.get()->get_temp_crnti()); + + if (first_rar_received) { + first_rar_received = false; + + // Save transmitted C-RNTI (if any) + transmitted_crnti = rntis->crnti; + + // If we have a C-RNTI, tell Mux unit to append C-RNTI CE if no CCCH SDU transmission + if (transmitted_crnti) { + rInfo("Appending C-RNTI MAC CE 0x%x in next transmission\n", transmitted_crnti); + mux_unit->append_crnti_ce_next_tx(transmitted_crnti); + phy_h->pdcch_ul_search(SRSLTE_RNTI_USER, transmitted_crnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_USER, transmitted_crnti); + } + } + rDebug("Going to Contention Resolution state\n"); + state = CONTENTION_RESOLUTION; + + // Start contention resolution timer + contention_resolution_timer->reset(); + contention_resolution_timer->run(); + } + } else { + rInfo("Found RAR for preamble %d\n", rar_pdu_msg.get()->get_rapid()); + } + } +} + +void ra_proc::step_response_reception() { + // do nothing. Processing done in tb_decoded_ok() + int ra_tti = phy_h->prach_tx_tti(); + if (ra_tti >= 0 && !rar_received) { + uint32_t interval = srslte_tti_interval(phy_h->get_current_tti(), ra_tti+3+responseWindowSize); + if (interval > 1 && interval < 100) { + Error("RA response not received within the response window\n"); + state = RESPONSE_ERROR; + } + } +} + +void ra_proc::step_response_error() +{ + preambleTransmissionCounter++; + if (preambleTransmissionCounter >= preambleTransMax + 1) { + rError("Maximum number of transmissions reached (%d)\n", preambleTransMax); + rrc->ra_problem(); + state = RA_PROBLEM; + if (ra_is_ho) { + rrc->ho_ra_completed(false); + } + } else { + backoff_interval_start = phy_h->get_current_tti(); + if (backoff_param_ms) { + backoff_inteval = rand()%backoff_param_ms; + } else { + backoff_inteval = 0; + } + if (backoff_inteval) { + rDebug("Backoff wait interval %d\n", backoff_inteval); + state = BACKOFF_WAIT; + } else { + rDebug("Transmitting inmediatly (%d/%d)\n", preambleTransmissionCounter, preambleTransMax); + state = RESOURCE_SELECTION; + } + } +} + +void ra_proc::step_backoff_wait() { + if (srslte_tti_interval(phy_h->get_current_tti(), backoff_interval_start) >= backoff_inteval) { + state = RESOURCE_SELECTION; + } +} + +bool ra_proc::uecrid_callback(void *arg, uint64_t uecri) { + return ((ra_proc*) arg)->contention_resolution_id_received(uecri); +} + +// Random Access initiated by RRC by the transmission of CCCH SDU +bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) { + bool uecri_successful = false; + + rDebug("MAC PDU Contains Contention Resolution ID CE\n"); + + // MAC PDU successfully decoded and contains MAC CE contention Id + contention_resolution_timer->stop(); + + if (transmitted_contention_id == rx_contention_id) + { + // UE Contention Resolution ID included in MAC CE matches the CCCH SDU transmitted in Msg3 + rntis->crnti = rntis->temp_rnti; + // finish the disassembly and demultiplexing of the MAC PDU + uecri_successful = true; + state = COMPLETION; + } else { + rInfo("Transmitted UE Contention Id differs from received Contention ID (0x%lx != 0x%lx)\n", + transmitted_contention_id, rx_contention_id); + // Discard MAC PDU + uecri_successful = false; + + // Contention Resolution not successfully is like RAR not successful + // FIXME: Need to flush Msg3 HARQ buffer. Why? + state = RESPONSE_ERROR; + } + rntis->temp_rnti = 0; + + return uecri_successful; +} + +void ra_proc::step_contention_resolution() { + // If Msg3 has been sent + if (mux_unit->msg3_is_transmitted()) + { + msg3_transmitted = true; + if (transmitted_crnti) + { + // Random Access with transmission of MAC C-RNTI CE + if ((!started_by_pdcch && pdcch_to_crnti_received == PDCCH_CRNTI_UL_GRANT) || + (started_by_pdcch && pdcch_to_crnti_received != PDCCH_CRNTI_NOT_RECEIVED)) + { + rDebug("PDCCH for C-RNTI received\n"); + contention_resolution_timer->stop(); + rntis->temp_rnti = 0; + state = COMPLETION; + } + pdcch_to_crnti_received = PDCCH_CRNTI_NOT_RECEIVED; + } else { + // RA with transmission of CCCH SDU is resolved in contention_resolution_id_received() callback function + if (!transmitted_contention_id) { + // Save transmitted UE contention id, as defined by higher layers + transmitted_contention_id = rntis->contention_id; + rntis->contention_id = 0; + } + } + } else { + rDebug("Msg3 not yet transmitted\n"); + } + +} + +void ra_proc::step_completition() { + log_h->console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); + rInfo("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); + if (!msg3_flushed) { + mux_unit->msg3_flush(); + msg3_flushed = true; + } + // Configure PHY to look for UL C-RNTI grants + phy_h->pdcch_ul_search(SRSLTE_RNTI_USER, rntis->crnti); + phy_h->pdcch_dl_search(SRSLTE_RNTI_USER, rntis->crnti); + + phy_h->set_crnti(rntis->crnti); + + msg3_transmitted = false; + state = COMPLETION_DONE; + if (ra_is_ho) { + rrc->ho_ra_completed(true); + } +} + +void ra_proc::step(uint32_t tti_) +{ + switch(state) { + case IDLE: + break; + case INITIALIZATION: + step_initialization(); + break; + case RESOURCE_SELECTION: + step_resource_selection(); + break; + case PREAMBLE_TRANSMISSION: + step_preamble_transmission(); + break; + case PDCCH_SETUP: + step_pdcch_setup(); + break; + case RESPONSE_RECEPTION: + step_response_reception(); + break; + case RESPONSE_ERROR: + step_response_error(); + break; + case BACKOFF_WAIT: + step_backoff_wait(); + break; + case CONTENTION_RESOLUTION: + step_contention_resolution(); + break; + case COMPLETION: + step_completition(); + case COMPLETION_DONE: + case RA_PROBLEM: + break; + } +} + +void ra_proc::start_noncont(uint32_t preamble_index, uint32_t prach_mask) { + next_preamble_idx = preamble_index; + next_prach_mask = prach_mask; + noncontention_enabled = true; + start_mac_order(56, true); +} + +void ra_proc::start_mac_order(uint32_t msg_len_bits, bool is_ho) +{ + if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + ra_is_ho = is_ho; + started_by_pdcch = false; + new_ra_msg_len = msg_len_bits; + state = INITIALIZATION; + rInfo("Starting PRACH by MAC order\n"); + } +} + +void ra_proc::start_pdcch_order() +{ + if (state == IDLE || state == COMPLETION_DONE || state == RA_PROBLEM) { + started_by_pdcch = true; + state = INITIALIZATION; + rInfo("Starting PRACH by PDCCH order\n"); + } +} + +// Contention Resolution Timer is expired (Section 5.1.5) +void ra_proc::timer_expired(uint32_t timer_id) +{ + rInfo("Contention Resolution Timer expired. Stopping PDCCH Search and going to Response Error\n"); + rntis->temp_rnti = 0; + state = RESPONSE_ERROR; + phy_h->pdcch_dl_search_reset(); +} + +void ra_proc::pdcch_to_crnti(bool contains_uplink_grant) { + rDebug("PDCCH to C-RNTI received %s UL grant\n", contains_uplink_grant?"with":"without"); + if (contains_uplink_grant) { + pdcch_to_crnti_received = PDCCH_CRNTI_UL_GRANT; + } else if (pdcch_to_crnti_received == PDCCH_CRNTI_NOT_RECEIVED) { + pdcch_to_crnti_received = PDCCH_CRNTI_DL_GRANT; + } +} + +void ra_proc::harq_retx() +{ + contention_resolution_timer->reset(); +} + +} + diff --git a/srsue/src/mac/proc_sr.cc b/srsue/src/mac/proc_sr.cc new file mode 100644 index 0000000..57cd8bd --- /dev/null +++ b/srsue/src/mac/proc_sr.cc @@ -0,0 +1,129 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/. + * + */ + +#define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) + +#include "srsue/hdr/mac/proc_sr.h" + + +namespace srsue { + +sr_proc::sr_proc() { + initiated = false; +} + +void sr_proc::init(phy_interface_mac* phy_h_, rrc_interface_mac *rrc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_) +{ + log_h = log_h_; + rrc = rrc_; + mac_cfg = mac_cfg_; + phy_h = phy_h_; + initiated = true; + do_ra = false; +} + +void sr_proc::reset() +{ + is_pending_sr = false; +} + +bool sr_proc::need_tx(uint32_t tti) +{ + int last_tx_tti = phy_h->sr_last_tx_tti(); + if (last_tx_tti >= 0) { + if (tti > (uint32_t)last_tx_tti) { + if (tti - last_tx_tti > 8) { + return true; + } + } else { + uint32_t interval = 10240-last_tx_tti+tti; + if (interval > 8 && tti < 8) { + return true; + } + } + } + return false; +} + +void sr_proc::step(uint32_t tti) +{ + if (initiated) { + if (is_pending_sr) { + if (mac_cfg->sr.setup_present) { + if (sr_counter < dsr_transmax) { + if (sr_counter == 0 || need_tx(tti)) { + sr_counter++; + Info("SR: Signalling PHY sr_counter=%d\n", sr_counter); + phy_h->sr_send(); + } + } else { + if (need_tx(tti)) { + Info("SR: Releasing PUCCH/SRS resources, sr_counter=%d, dsr_transmax=%d\n", + sr_counter, dsr_transmax); + log_h->console("Scheduling request failed: releasing RRC connection...\n"); + rrc->release_pucch_srs(); + do_ra = true; + is_pending_sr = false; + } + } + } else { + Info("SR: PUCCH not configured. Starting RA procedure\n"); + do_ra = true; + reset(); + } + } + } +} + +bool sr_proc::need_random_access() { + if (initiated) { + if (do_ra) { + do_ra = false; + return true; + } else { + return false; + } + } + return false; +} + +void sr_proc::start() +{ + if (initiated) { + if (!is_pending_sr) { + sr_counter = 0; + is_pending_sr = true; + } + dsr_transmax = liblte_rrc_dsr_trans_max_num[mac_cfg->sr.dsr_trans_max]; + Debug("SR: Starting Procedure. dsrTransMax=%d\n", dsr_transmax); + } +} + +} + diff --git a/srsue/src/main.cc b/srsue/src/main.cc new file mode 100644 index 0000000..f71f9ac --- /dev/null +++ b/srsue/src/main.cc @@ -0,0 +1,593 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "srsue/hdr/ue.h" +#include "srslte/common/config_file.h" +#include "srslte/srslte.h" +#include "srsue/hdr/metrics_stdout.h" +#include "srsue/hdr/metrics_csv.h" +#include "srslte/common/metrics_hub.h" +#include "srslte/version.h" + +using namespace std; +using namespace srsue; +namespace bpo = boost::program_options; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void parse_args(all_args_t *args, int argc, char *argv[]) { + + // Command line only options + bpo::options_description general("General options"); + + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit"); + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + ("rf.dl_earfcn", bpo::value(&args->rf.dl_earfcn)->default_value(3400), "Downlink EARFCN") + ("rf.freq_offset", bpo::value(&args->rf.freq_offset)->default_value(0), "(optional) Frequency offset") + ("rf.dl_freq", bpo::value(&args->rf.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") + ("rf.ul_freq", bpo::value(&args->rf.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") + ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(-1), "Front-end receiver gain") + ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(-1), "Front-end transmitter gain") + ("rf.nof_rx_ant", bpo::value(&args->rf.nof_rx_ant)->default_value(1), "Number of RX antennas") + + ("rf.device_name", bpo::value(&args->rf.device_name)->default_value("auto"), "Front-end device name") + ("rf.device_args", bpo::value(&args->rf.device_args)->default_value("auto"), "Front-end device arguments") + ("rf.time_adv_nsamples", bpo::value(&args->rf.time_adv_nsamples)->default_value("auto"), + "Transmission time advance") + ("rf.burst_preamble_us", bpo::value(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance") + ("rf.continuous_tx", bpo::value(&args->rf.continuous_tx)->default_value("auto"), "Transmit samples continuously to the radio or on bursts (auto/yes/no). Default is auto (yes for UHD, no for rest)") + + ("rrc.feature_group", bpo::value(&args->rrc.feature_group)->default_value(0xe6041000), "Hex value of the featureGroupIndicators field in the" + "UECapabilityInformation message. Default 0xe6041000") + ("rrc.ue_category", bpo::value(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)") + + ("nas.apn", bpo::value(&args->nas.apn_name)->default_value(""), "Set Access Point Name (APN) for data services") + ("nas.user", bpo::value(&args->nas.apn_user)->default_value(""), "Username for CHAP authentication") + ("nas.pass", bpo::value(&args->nas.apn_pass)->default_value(""), "Password for CHAP authentication") + ("nas.force_imsi_attach", bpo::value(&args->nas.force_imsi_attach)->default_value(false), "Whether to always perform an IMSI attach") + + + ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") + ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") + ("pcap.nas_enable", bpo::value(&args->pcap.nas_enable)->default_value(false), "Enable NAS packet captures for wireshark") + ("pcap.nas_filename", bpo::value(&args->pcap.nas_filename)->default_value("ue_nas.pcap"), "NAS layer capture filename (useful when NAS encryption is enabled)") + + + ("trace.enable", bpo::value(&args->trace.enable)->default_value(false), "Enable PHY and radio timing traces") + ("trace.phy_filename", bpo::value(&args->trace.phy_filename)->default_value("ue.phy_trace"), + "PHY timing traces filename") + ("trace.radio_filename", bpo::value(&args->trace.radio_filename)->default_value("ue.radio_trace"), + "Radio timing traces filename") + + ("gui.enable", bpo::value(&args->gui.enable)->default_value(false), "Enable GUI plots") + + ("log.phy_level", bpo::value(&args->log.phy_level), "PHY log level") + ("log.phy_lib_level", bpo::value(&args->log.phy_lib_level), "PHY lib log level") + ("log.phy_hex_limit", bpo::value(&args->log.phy_hex_limit), "PHY log hex dump limit") + ("log.mac_level", bpo::value(&args->log.mac_level), "MAC log level") + ("log.mac_hex_limit", bpo::value(&args->log.mac_hex_limit), "MAC log hex dump limit") + ("log.rlc_level", bpo::value(&args->log.rlc_level), "RLC log level") + ("log.rlc_hex_limit", bpo::value(&args->log.rlc_hex_limit), "RLC log hex dump limit") + ("log.pdcp_level", bpo::value(&args->log.pdcp_level), "PDCP log level") + ("log.pdcp_hex_limit", bpo::value(&args->log.pdcp_hex_limit), "PDCP log hex dump limit") + ("log.rrc_level", bpo::value(&args->log.rrc_level), "RRC log level") + ("log.rrc_hex_limit", bpo::value(&args->log.rrc_hex_limit), "RRC log hex dump limit") + ("log.gw_level", bpo::value(&args->log.gw_level), "GW log level") + ("log.gw_hex_limit", bpo::value(&args->log.gw_hex_limit), "GW log hex dump limit") + ("log.nas_level", bpo::value(&args->log.nas_level), "NAS log level") + ("log.nas_hex_limit", bpo::value(&args->log.nas_hex_limit), "NAS log hex dump limit") + ("log.usim_level", bpo::value(&args->log.usim_level), "USIM log level") + ("log.usim_hex_limit", bpo::value(&args->log.usim_hex_limit), "USIM log hex dump limit") + + + ("log.all_level", bpo::value(&args->log.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log.filename)->default_value("/tmp/ue.log"), "Log filename") + ("log.file_max_size", bpo::value(&args->log.file_max_size)->default_value(-1), "Maximum file size (in kilobytes). When passed, multiple files are created. Default -1 (single file)") + + ("usim.mode", bpo::value(&args->usim.mode)->default_value("soft"), "USIM mode (soft or pcsc)") + ("usim.algo", bpo::value(&args->usim.algo), "USIM authentication algorithm") + ("usim.op", bpo::value(&args->usim.op), "USIM operator code") + ("usim.opc", bpo::value(&args->usim.opc), "USIM operator code (ciphered variant)") + ("usim.imsi", bpo::value(&args->usim.imsi), "USIM IMSI") + ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") + ("usim.k", bpo::value(&args->usim.k), "USIM K") + ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + + /* Expert section */ + ("expert.ip_netmask", + bpo::value(&args->expert.ip_netmask)->default_value("255.255.255.0"), + "Netmask of the tun_srsue device") + + ("expert.mbms_service", + bpo::value(&args->expert.mbms_service)->default_value(-1), + "automatically starts an mbms service of the number given") + + ("expert.phy.worker_cpu_mask", + bpo::value(&args->expert.phy.worker_cpu_mask)->default_value(-1), + "cpu bit mask (eg 255 = 1111 1111)") + + ("expert.phy.sync_cpu_affinity", + bpo::value(&args->expert.phy.sync_cpu_affinity)->default_value(-1), + "index of the core used by the sync thread") + + ("expert.metrics_period_secs", + bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), + "Periodicity for metrics in seconds") + + ("expert.metrics_csv_enable", + bpo::value(&args->expert.metrics_csv_enable)->default_value(false), + "Write UE metrics to CSV file") + + ("expert.metrics_csv_filename", + bpo::value(&args->expert.metrics_csv_filename)->default_value("/tmp/ue_metrics.csv"), + "Metrics CSV filename") + + ("expert.pregenerate_signals", + bpo::value(&args->expert.pregenerate_signals)->default_value(false), + "Pregenerate uplink signals after attach. Improves CPU performance.") + + ("expert.print_buffer_state", + bpo::value(&args->expert.print_buffer_state)->default_value(false), + "Prints on the console the buffer state every 10 seconds") + + ("expert.rssi_sensor_enabled", + bpo::value(&args->expert.phy.rssi_sensor_enabled)->default_value(false), + "Enable or disable RF frontend RSSI sensor. In some USRP devices can cause segmentation fault") + + ("expert.rx_gain_offset", + bpo::value(&args->expert.phy.rx_gain_offset)->default_value(62), + "RX Gain offset to add to rx_gain to correct RSRP value") + + ("expert.prach_gain", + bpo::value(&args->expert.phy.prach_gain)->default_value(-1.0), + "Disable PRACH power control") + + ("expert.cqi_max", + bpo::value(&args->expert.phy.cqi_max)->default_value(15), + "Upper bound on the maximum CQI to be reported. Default 15.") + + ("expert.cqi_fixed", + bpo::value(&args->expert.phy.cqi_fixed)->default_value(-1), + "Fixes the reported CQI to a constant value. Default disabled.") + + ("expert.sfo_correct_period", + bpo::value(&args->expert.phy.sfo_correct_period)->default_value(DEFAULT_SAMPLE_OFFSET_CORRECT_PERIOD), + "Period in ms to correct sample time") + + ("expert.sfo_emma", + bpo::value(&args->expert.phy.sfo_ema)->default_value(DEFAULT_SFO_EMA_COEFF), + "EMA coefficient to average sample offsets used to compute SFO") + + ("expert.snr_ema_coeff", + bpo::value(&args->expert.phy.snr_ema_coeff)->default_value(0.1), + "Sets the SNR exponential moving average coefficient (Default 0.1)") + + ("expert.snr_estim_alg", + bpo::value(&args->expert.phy.snr_estim_alg)->default_value("refs"), + "Sets the noise estimation algorithm. (Default refs)") + + ("expert.pdsch_max_its", + bpo::value(&args->expert.phy.pdsch_max_its)->default_value(4), + "Maximum number of turbo decoder iterations") + + ("expert.attach_enable_64qam", + bpo::value(&args->expert.phy.attach_enable_64qam)->default_value(false), + "PUSCH 64QAM modulation before attachment") + + ("expert.nof_phy_threads", + bpo::value(&args->expert.phy.nof_phy_threads)->default_value(2), + "Number of PHY threads") + + ("expert.equalizer_mode", + bpo::value(&args->expert.phy.equalizer_mode)->default_value("mmse"), + "Equalizer mode") + + ("expert.intra_freq_meas_len_ms", + bpo::value(&args->expert.phy.intra_freq_meas_len_ms)->default_value(20), + "Duration of the intra-frequency neighbour cell measurement in ms.") + + ("expert.intra_freq_meas_period_ms", + bpo::value(&args->expert.phy.intra_freq_meas_period_ms)->default_value(200), + "Period of intra-frequency neighbour cell measurement in ms. Maximum as per 3GPP is 200 ms.") + + ("expert.cfo_is_doppler", + bpo::value(&args->expert.phy.cfo_is_doppler)->default_value(false), + "Assume detected CFO is doppler and correct the UL in the same direction. If disabled, the CFO is assumed" + "to be caused by the local oscillator and the UL correction is in the opposite direction. Default assumes oscillator.") + + ("expert.cfo_integer_enabled", + bpo::value(&args->expert.phy.cfo_integer_enabled)->default_value(false), + "Enables integer CFO estimation and correction.") + + ("expert.cfo_correct_tol_hz", + bpo::value(&args->expert.phy.cfo_correct_tol_hz)->default_value(1.0), + "Tolerance (in Hz) for digital CFO compensation (needs to be low if average_subframe_enabled=true.") + + ("expert.cfo_pss_ema", + bpo::value(&args->expert.phy.cfo_pss_ema)->default_value(DEFAULT_CFO_EMA_TRACK), + "CFO Exponential Moving Average coefficient for PSS estimation during TRACK.") + + ("expert.cfo_ref_mask", + bpo::value(&args->expert.phy.cfo_ref_mask)->default_value(1023), + "Bitmask for subframes on which to run RS estimation (set to 0 to disable, default all sf)") + + ("expert.cfo_loop_bw_pss", + bpo::value(&args->expert.phy.cfo_loop_bw_pss)->default_value(DEFAULT_CFO_BW_PSS), + "CFO feedback loop bandwidth for samples from PSS") + + ("expert.cfo_loop_bw_ref", + bpo::value(&args->expert.phy.cfo_loop_bw_ref)->default_value(DEFAULT_CFO_BW_REF), + "CFO feedback loop bandwidth for samples from RS") + + ("expert.cfo_loop_pss_tol", + bpo::value(&args->expert.phy.cfo_loop_pss_tol)->default_value(DEFAULT_CFO_PSS_MIN), + "Tolerance (in Hz) of the PSS estimation method. Below this value, PSS estimation does not feeds back the loop" + "and RS estimations are used instead (when available)") + + ("expert.cfo_loop_ref_min", + bpo::value(&args->expert.phy.cfo_loop_ref_min)->default_value(DEFAULT_CFO_REF_MIN), + "Tolerance (in Hz) of the RS estimation method. Below this value, RS estimation does not feeds back the loop") + + ("expert.cfo_loop_pss_conv", + bpo::value(&args->expert.phy.cfo_loop_pss_conv)->default_value(DEFAULT_PSS_STABLE_TIMEOUT), + "After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, RS adjustments are allowed.") + + ("expert.sic_pss_enabled", + bpo::value(&args->expert.phy.sic_pss_enabled)->default_value(false), + "Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. Must be disabled if cells have identical channel and timing.") + + ("expert.average_subframe_enabled", + bpo::value(&args->expert.phy.average_subframe_enabled)->default_value(true), + "Averages in the time domain the channel estimates within 1 subframe. Needs accurate CFO correction.") + + ("expert.estimator_fil_auto", + bpo::value(&args->expert.phy.estimator_fil_auto)->default_value(false), + "The channel estimator smooths the channel estimate with an adaptative filter.") + + ("expert.estimator_fil_stddev", + bpo::value(&args->expert.phy.estimator_fil_stddev)->default_value(1.0f), + "Sets the channel estimator smooth gaussian filter standard deviation.") + + ("expert.estimator_fil_order", + bpo::value(&args->expert.phy.estimator_fil_order)->default_value(4), + "Sets the channel estimator smooth gaussian filter order (even values perform better).") + + ("expert.sss_algorithm", + bpo::value(&args->expert.phy.sss_algorithm)->default_value("full"), + "Selects the SSS estimation algorithm.") + + ("expert.pdsch_csi_enabled", + bpo::value(&args->expert.phy.pdsch_csi_enabled)->default_value(true), + "Stores the Channel State Information and uses it for weightening the softbits. It is only used in TM1.") + + ("rf_calibration.tx_corr_dc_gain", bpo::value(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), + "TX DC offset gain correction") + ("rf_calibration.tx_corr_dc_phase", bpo::value(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), + "TX DC offset phase correction") + ("rf_calibration.tx_corr_iq_i", bpo::value(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), + "TX IQ imbalance inphase correction") + ("rf_calibration.tx_corr_iq_q", bpo::value(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), + "TX IQ imbalance quadrature correction"); + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value(&config_file), "UE configuration file"); + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + // print version number and exit + if (vm.count("version")) { + cout << "Version " << + srslte_get_version_major() << "." << + srslte_get_version_minor() << "." << + srslte_get_version_patch() << endl; + exit(0); + } + + // if no config file given, check users home path + if (!vm.count("config_file")) { + + if (!config_exists(config_file, "ue.conf")) { + cout << "Failed to read UE configuration file " << config_file << " - exiting" << endl; + exit(1); + } + } + + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if (conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + + // parse config file and handle errors gracefully + try { + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + } catch (const boost::program_options::error& e) { + cerr << e.what() << endl; + exit(1); + } + + //Check conflicting OP/OPc options and which is being used + if (vm.count("usim.op") && !vm["usim.op"].defaulted() && + vm.count("usim.opc") && !vm["usim.opc"].defaulted()) + { + cout << "Conflicting options OP and OPc. Please configure either one or the other." << endl; + exit(1); + } + else { + args->usim.using_op = vm.count("usim.op"); + } + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if (!vm.count("log.phy_level")) { + args->log.phy_level = args->log.all_level; + } + if (!vm.count("log.phy_lib_level")) { + args->log.phy_lib_level = args->log.all_level; + } + if (!vm.count("log.mac_level")) { + args->log.mac_level = args->log.all_level; + } + if (!vm.count("log.rlc_level")) { + args->log.rlc_level = args->log.all_level; + } + if (!vm.count("log.pdcp_level")) { + args->log.pdcp_level = args->log.all_level; + } + if (!vm.count("log.rrc_level")) { + args->log.rrc_level = args->log.all_level; + } + if (!vm.count("log.nas_level")) { + args->log.nas_level = args->log.all_level; + } + if (!vm.count("log.gw_level")) { + args->log.gw_level = args->log.all_level; + } + if (!vm.count("log.usim_level")) { + args->log.usim_level = args->log.all_level; + } + } + + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if (!vm.count("log.phy_hex_limit")) { + args->log.phy_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.mac_hex_limit")) { + args->log.mac_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.rlc_hex_limit")) { + args->log.rlc_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.pdcp_hex_limit")) { + args->log.pdcp_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.rrc_hex_limit")) { + args->log.rrc_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.nas_hex_limit")) { + args->log.nas_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.gw_hex_limit")) { + args->log.gw_hex_limit = args->log.all_hex_limit; + } + if (!vm.count("log.usim_hex_limit")) { + args->log.usim_hex_limit = args->log.all_hex_limit; + } + } +} + +static int sigcnt = 0; +static bool running = true; +static bool do_metrics = false; +metrics_stdout metrics_screen; +static bool show_mbms = false; +static bool mbms_service_start = false; +uint32_t serv, port; + +void sig_int_handler(int signo) { + sigcnt++; + running = false; + printf("Stopping srsUE... Press Ctrl+C %d more times to force stop\n", 10-sigcnt); + if (sigcnt >= 10) { + exit(-1); + } +} + +void *input_loop(void *m) { + string key; + while (running) { + getline(cin, key); + if (cin.eof() || cin.bad()) { + cout << "Closing stdin thread." << endl; + break; + } else { + if (0 == key.compare("t")) { + do_metrics = !do_metrics; + if (do_metrics) { + cout << "Enter t to stop trace." << endl; + } else { + cout << "Enter t to restart trace." << endl; + } + metrics_screen.toggle_print(do_metrics); + } else + if (0 == key.compare("q")) { + running = false; + } + else if (0 == key.compare("mbms")) { + show_mbms = true; + } else if (key.find("mbms_service_start") != string::npos) { + + char *dup = strdup(key.c_str()); + strtok(dup, " "); + char *s = strtok(NULL, " "); + if(NULL == s) { + cout << "Usage: mbms_service_start " << endl; + continue; + } + serv = atoi(s); + char* p = strtok(NULL, " "); + if(NULL == p) { + cout << "Usage: mbms_service_start " << endl; + continue; + } + port = atoi(p); + mbms_service_start = true; + free(dup); + } + } + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + srslte::metrics_hub metricshub; + signal(SIGINT, sig_int_handler); + signal(SIGTERM, sig_int_handler); + all_args_t args; + + srslte_debug_handle_crash(argc, argv); + + parse_args(&args, argc, argv); + + srsue_instance_type_t type = LTE; + ue_base *ue = ue_base::get_instance(type); + if (!ue) { + cout << "Error creating UE instance." << endl << endl; + exit(1); + } + + cout << "--- Software Radio Systems " << srsue_instance_type_text[type] << " UE ---" << endl << endl; + if (!ue->init(&args)) { + exit(1); + } + + metricshub.init(ue, args.expert.metrics_period_secs); + metricshub.add_listener(&metrics_screen); + metrics_screen.set_ue_handle(ue); + + metrics_csv metrics_file(args.expert.metrics_csv_filename); + if (args.expert.metrics_csv_enable) { + metricshub.add_listener(&metrics_file); + metrics_file.set_ue_handle(ue); + } + + pthread_t input; + pthread_create(&input, NULL, &input_loop, &args); + + printf("Attaching UE...\n"); + while (!ue->attach() && running) { + sleep(1); + } + if (running) { + if (args.expert.pregenerate_signals) { + printf("Pre-generating signals...\n"); + ue->pregenerate_signals(true); + printf("Done pregenerating signals.\n"); + } + if (args.gui.enable) { + ue->start_plot(); + } + // Auto-start MBMS service by default + if(args.expert.mbms_service > -1){ + serv = args.expert.mbms_service; + port = 4321; + mbms_service_start = true; + } + } + int cnt=0; + while (running) { + // + if(mbms_service_start) { + if(ue->mbms_service_start(serv, port)){ + mbms_service_start = false; + } + } + if(show_mbms) { + show_mbms = false; + ue->print_mbms(); + } + if (args.expert.print_buffer_state) { + cnt++; + if (cnt==10) { + cnt=0; + ue->print_pool(); + } + } + sleep(1); + } + pthread_cancel(input); + metricshub.stop(); + ue->stop(); + ue->cleanup(); + cout << "--- exiting ---" << endl; + exit(0); +} diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc new file mode 100644 index 0000000..fa8760f --- /dev/null +++ b/srsue/src/metrics_csv.cc @@ -0,0 +1,121 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2015 The srsUE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/hdr/metrics_csv.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsue{ + +metrics_csv::metrics_csv(std::string filename) + :n_reports(0) + ,metrics_report_period(1.0) + ,ue(NULL) +{ + file.open(filename.c_str(), std::ios_base::out); +} + +metrics_csv::~metrics_csv() +{ + stop(); +} + +void metrics_csv::set_ue_handle(ue_metrics_interface *ue_) +{ + ue = ue_; +} + +void metrics_csv::stop() +{ + if (file.is_open()) { + file << "#eof\n"; + file.flush(); + file.close(); + } +} + +void metrics_csv::set_metrics(ue_metrics_t &metrics, const uint32_t period_usec) +{ + if (file.is_open() && ue != NULL) { + if(n_reports == 0) { + file << "time;rsrp;pl;cfo;dl_mcs;dl_snr;dl_turbo;dl_brate;dl_bler;ul_ta;ul_mcs;ul_buff;ul_brate;ul_bler;rf_o;rf_u;rf_l;is_attached\n"; + } + file << (metrics_report_period*n_reports) << ";"; + file << float_to_string(metrics.phy.dl.rsrp, 2); + file << float_to_string(metrics.phy.dl.pathloss, 2); + file << float_to_string(metrics.phy.sync.cfo, 2); + file << float_to_string(metrics.phy.dl.mcs, 2); + file << float_to_string(metrics.phy.dl.sinr, 2); + file << float_to_string(metrics.phy.dl.turbo_iters, 2); + file << float_to_string((float) metrics.mac.rx_brate/period_usec*1e6, 2); + if (metrics.mac.rx_pkts > 0) { + file << float_to_string((float) 100*metrics.mac.rx_errors/metrics.mac.rx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + file << float_to_string(metrics.phy.sync.ta_us, 2); + file << float_to_string(metrics.phy.ul.mcs, 2); + file << float_to_string((float) metrics.mac.ul_buffer, 2); + file << float_to_string((float) metrics.mac.tx_brate/period_usec*1e6, 2); + if (metrics.mac.tx_pkts > 0) { + file << float_to_string((float) 100*metrics.mac.tx_errors/metrics.mac.tx_pkts, 1); + } else { + file << float_to_string(0, 2); + } + file << float_to_string(metrics.rf.rf_o, 2); + file << float_to_string(metrics.rf.rf_u, 2); + file << float_to_string(metrics.rf.rf_l, 2); + file << (ue->is_attached() ? "1.0" : "0.0"); + file << "\n"; + + n_reports++; + } else { + std::cout << "Error, couldn't write CSV file." << std::endl; + } +} + +std::string metrics_csv::float_to_string(float f, int digits, bool add_semicolon) +{ + std::ostringstream os; + const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON; + os << std::fixed << std::setprecision(precision) << f; + if (add_semicolon) + os << ';'; + return os.str(); +} + +} // namespace srsue diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc new file mode 100644 index 0000000..0db903c --- /dev/null +++ b/srsue/src/metrics_stdout.cc @@ -0,0 +1,145 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2015 The srsUE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/hdr/metrics_stdout.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsue{ + +char const * const prefixes[2][9] = +{ + { "", "m", "u", "n", "p", "f", "a", "z", "y", }, + { "", "k", "M", "G", "T", "P", "E", "Z", "Y", }, +}; + +metrics_stdout::metrics_stdout() + :do_print(false) + ,n_reports(10) + ,ue(NULL) +{ +} + +void metrics_stdout::set_ue_handle(ue_metrics_interface *ue_) +{ + ue = ue_; +} + +void metrics_stdout::toggle_print(bool b) +{ + do_print = b; +} + +void metrics_stdout::set_metrics(ue_metrics_t &metrics, const uint32_t period_usec) +{ + if(!do_print || ue == NULL) + return; + + if (!ue->is_attached()) { + cout << "--- disconnected ---" << endl; + return; + } + + if(++n_reports > 10) + { + n_reports = 0; + cout << endl; + cout << "--Signal--------------DL------------------------------UL----------------------" << endl; + cout << " rsrp pl cfo mcs snr turbo brate bler ta_us mcs buff brate bler" << endl; + } + cout << float_to_string(metrics.phy.dl.rsrp, 2); + cout << float_to_string(metrics.phy.dl.pathloss, 2); + cout << float_to_eng_string(metrics.phy.sync.cfo, 2); + cout << float_to_string(metrics.phy.dl.mcs, 2); + cout << float_to_string(metrics.phy.dl.sinr, 2); + cout << float_to_string(metrics.phy.dl.turbo_iters, 2); + cout << float_to_eng_string((float) metrics.mac.rx_brate/period_usec*1e6, 2); + if (metrics.mac.rx_pkts > 0) { + cout << float_to_string((float) 100*metrics.mac.rx_errors/metrics.mac.rx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + cout << float_to_string(metrics.phy.sync.ta_us, 2); + cout << float_to_string(metrics.phy.ul.mcs, 2); + cout << float_to_eng_string((float) metrics.mac.ul_buffer, 2); + cout << float_to_eng_string((float) metrics.mac.tx_brate/period_usec*1e6, 2); + if (metrics.mac.tx_pkts > 0) { + cout << float_to_string((float) 100*metrics.mac.tx_errors/metrics.mac.tx_pkts, 1) << "%"; + } else { + cout << float_to_string(0, 1) << "%"; + } + cout << endl; + + if(metrics.rf.rf_error) { + printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); + } + +} + +std::string metrics_stdout::float_to_string(float f, int digits) +{ + std::ostringstream os; + const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON; + os << std::setw(6) << std::fixed << std::setprecision(precision) << f; + return os.str(); +} + +std::string metrics_stdout::float_to_eng_string(float f, int digits) +{ + const int degree = (f == 0.0) ? 0 : lrint( floor( log10( fabs( f ) ) / 3) ); + + std::string factor; + + if ( abs( degree ) < 9 ) + { + if(degree < 0) + factor = prefixes[0][ abs( degree ) ]; + else + factor = prefixes[1][ abs( degree ) ]; + } else { + return "failed"; + } + + const double scaled = f * pow( 1000.0, -degree ); + if (degree != 0) { + return float_to_string(scaled, digits) + factor; + } else { + return " " + float_to_string(scaled, digits) + factor; + } +} + +} // namespace srsue diff --git a/srsue/src/phy/CMakeLists.txt b/srsue/src/phy/CMakeLists.txt new file mode 100644 index 0000000..d3e4dee --- /dev/null +++ b/srsue/src/phy/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright 2013-2017 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsue_phy STATIC ${SOURCES}) + +if(ENABLE_GUI AND SRSGUI_FOUND) + target_link_libraries(srsue_phy ${SRSGUI_LIBRARIES}) +endif(ENABLE_GUI AND SRSGUI_FOUND) + +install(TARGETS srsue_phy DESTINATION ${LIBRARY_DIR}) diff --git a/srsue/src/phy/phch_common.cc b/srsue/src/phy/phch_common.cc new file mode 100644 index 0000000..d09338c --- /dev/null +++ b/srsue/src/phy/phch_common.cc @@ -0,0 +1,542 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include "srslte/srslte.h" +#include "srsue/hdr/phy/phch_common.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsue { + +cf_t zeros[50000]; + +phch_common::phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) +{ + config = NULL; + args = NULL; + log_h = NULL; + radio_h = NULL; + mac = NULL; + max_mutex = max_mutex_; + nof_mutex = 0; + rx_gain_offset = 0; + last_ri = 0; + last_pmi = 0; + //have_mtch_stop = false; + + bzero(&dl_metrics, sizeof(dl_metrics_t)); + dl_metrics_read = true; + dl_metrics_count = 0; + bzero(&ul_metrics, sizeof(ul_metrics_t)); + ul_metrics_read = true; + ul_metrics_count = 0; + bzero(&sync_metrics, sizeof(sync_metrics_t)); + sync_metrics_read = true; + sync_metrics_count = 0; + + bzero(zeros, 50000*sizeof(cf_t)); + + // FIXME: This is an ugly fix to avoid the TX filters to empty + /* + for (int i=0;i<50000;i++) { + zeros[i] = 0.01*cexpf(((float) i/50000)*0.1*_Complex_I); + }*/ + + reset(); + + sib13_configured = false; + mcch_configured = false; +} + +void phch_common::init(phy_interface_rrc::phy_cfg_t *_config, phy_args_t *_args, srslte::log *_log, srslte::radio *_radio, rrc_interface_phy *_rrc, mac_interface_phy *_mac) +{ + log_h = _log; + radio_h = _radio; + rrc = _rrc; + mac = _mac; + config = _config; + args = _args; + is_first_tx = true; + sr_last_tx_tti = -1; + + for (uint32_t i=0;i= ul_rnti_start && ul_rnti_start >= 0) || ul_rnti_start < 0) && + (((int)tti < ul_rnti_end && ul_rnti_end >= 0) || ul_rnti_end < 0)) + { + return true; + } else { + return false; + } +} + +bool phch_common::dl_rnti_active(uint32_t tti) { + Debug("tti=%d, dl_rnti_start=%d, dl_rnti_end=%d, dl_rnti=0x%x\n", tti, dl_rnti_start, dl_rnti_end, dl_rnti); + if ((((int)tti >= dl_rnti_start && dl_rnti_start >= 0) || dl_rnti_start < 0) && + (((int)tti < dl_rnti_end && dl_rnti_end >= 0) || dl_rnti_end < 0)) + { + bool ret = true; + // FIXME: This scheduling decision belongs to RRC + if (dl_rnti_type == SRSLTE_RNTI_SI) { + if (dl_rnti_end - dl_rnti_start > 1) { // This is not a SIB1 + if ((tti/10)%2 == 0 && (tti%10) == 5) { // Skip subframe #5 for which SFN mod 2 = 0 + ret = false; + } + } + } + return ret; + } else { + return false; + } +} + +srslte::radio* phch_common::get_radio() +{ + return radio_h; +} + +// Unpack RAR grant as defined in Section 6.2 of 36.213 +void phch_common::set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) +{ + srslte_dci_rar_grant_unpack(&rar_grant, grant_payload); + rar_grant_pending = true; + if (MSG3_DELAY_MS < 0) { + fprintf(stderr, "Error MSG3_DELAY_MS can't be negative\n"); + } + if (rar_grant.ul_delay) { + rar_grant_tti = (tti + MSG3_DELAY_MS + 1) % 10240; + } else { + rar_grant_tti = (tti + MSG3_DELAY_MS) % 10240; + } +} + +bool phch_common::get_pending_rar(uint32_t tti, srslte_dci_rar_grant_t *rar_grant_) +{ + if (rar_grant_pending && tti >= rar_grant_tti) { + if (rar_grant_) { + rar_grant_pending = false; + memcpy(rar_grant_, &rar_grant, sizeof(srslte_dci_rar_grant_t)); + } + return true; + } + return false; +} + +/* Common variables used by all phy workers */ +uint16_t phch_common::get_ul_rnti(uint32_t tti) { + if (ul_rnti_active(tti)) { + return ul_rnti; + } else { + return 0; + } +} +srslte_rnti_type_t phch_common::get_ul_rnti_type() { + return ul_rnti_type; +} +void phch_common::set_ul_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start, int tti_end) { + ul_rnti = rnti_value; + ul_rnti_type = type; + ul_rnti_start = tti_start; + ul_rnti_end = tti_end; +} +uint16_t phch_common::get_dl_rnti(uint32_t tti) { + if (dl_rnti_active(tti)) { + return dl_rnti; + } else { + return 0; + } +} +srslte_rnti_type_t phch_common::get_dl_rnti_type() { + return dl_rnti_type; +} +void phch_common::set_dl_rnti(srslte_rnti_type_t type, uint16_t rnti_value, int tti_start, int tti_end) { + dl_rnti = rnti_value; + dl_rnti_type = type; + dl_rnti_start = tti_start; + dl_rnti_end = tti_end; + if (log_h) { + Debug("Set DL rnti: start=%d, end=%d, value=0x%x\n", tti_start, tti_end, rnti_value); + } +} + +void phch_common::reset_pending_ack(uint32_t tti) { + pending_ack[TTIMOD(tti)].enabled = false; +} + +void phch_common::set_pending_ack(uint32_t tti, uint32_t I_lowest, uint32_t n_dmrs) { + pending_ack[TTIMOD(tti)].enabled = true; + pending_ack[TTIMOD(tti)].I_lowest = I_lowest; + pending_ack[TTIMOD(tti)].n_dmrs = n_dmrs; + Debug("Set pending ACK for tti=%d I_lowest=%d, n_dmrs=%d\n", tti, I_lowest, n_dmrs); +} + +bool phch_common::get_pending_ack(uint32_t tti) { + return get_pending_ack(tti, NULL, NULL); +} + +bool phch_common::get_pending_ack(uint32_t tti, uint32_t *I_lowest, uint32_t *n_dmrs) { + if (I_lowest) { + *I_lowest = pending_ack[TTIMOD(tti)].I_lowest; + } + if (n_dmrs) { + *n_dmrs = pending_ack[TTIMOD(tti)].n_dmrs; + } + return pending_ack[TTIMOD(tti)].enabled; +} + +bool phch_common::is_any_pending_ack() { + for (int i=0;iset_tti(tti); + if (tx_enable) { + radio_h->tx_single(buffer, nof_samples, tx_time); + is_first_of_burst = false; + } else { + if (radio_h->is_continuous_tx()) { + if (!is_first_of_burst) { + radio_h->tx_single(zeros, nof_samples, tx_time); + } + } else { + if (!is_first_of_burst) { + radio_h->tx_end(); + is_first_of_burst = true; + } + } + } + // Trigger next transmission + pthread_mutex_unlock(&tx_mutex[(tti+1)%nof_mutex]); +} + + +void phch_common::set_cell(const srslte_cell_t &c) { + cell = c; +} + +uint32_t phch_common::get_nof_prb() { + return cell.nof_prb; +} + +void phch_common::set_dl_metrics(const dl_metrics_t &m) { + if(dl_metrics_read) { + dl_metrics = m; + dl_metrics_count = 1; + dl_metrics_read = false; + } else { + dl_metrics_count++; + dl_metrics.mcs = dl_metrics.mcs + (m.mcs - dl_metrics.mcs)/dl_metrics_count; + dl_metrics.n = dl_metrics.n + (m.n - dl_metrics.n)/dl_metrics_count; + dl_metrics.rsrp = dl_metrics.rsrp + (m.rsrp - dl_metrics.rsrp)/dl_metrics_count; + dl_metrics.rsrq = dl_metrics.rsrq + (m.rsrq - dl_metrics.rsrq)/dl_metrics_count; + dl_metrics.rssi = dl_metrics.rssi + (m.rssi - dl_metrics.rssi)/dl_metrics_count; + dl_metrics.sinr = dl_metrics.sinr + (m.sinr - dl_metrics.sinr)/dl_metrics_count; + dl_metrics.pathloss = dl_metrics.pathloss + (m.pathloss - dl_metrics.pathloss)/dl_metrics_count; + dl_metrics.turbo_iters = dl_metrics.turbo_iters + (m.turbo_iters - dl_metrics.turbo_iters)/dl_metrics_count; + } +} + +void phch_common::get_dl_metrics(dl_metrics_t &m) { + m = dl_metrics; + dl_metrics_read = true; +} + +void phch_common::set_ul_metrics(const ul_metrics_t &m) { + if(ul_metrics_read) { + ul_metrics = m; + ul_metrics_count = 1; + ul_metrics_read = false; + } else { + ul_metrics_count++; + ul_metrics.mcs = ul_metrics.mcs + (m.mcs - ul_metrics.mcs)/ul_metrics_count; + ul_metrics.power = ul_metrics.power + (m.power - ul_metrics.power)/ul_metrics_count; + } +} + +void phch_common::get_ul_metrics(ul_metrics_t &m) { + m = ul_metrics; + ul_metrics_read = true; +} + +void phch_common::set_sync_metrics(const sync_metrics_t &m) { + + if(sync_metrics_read) { + sync_metrics = m; + sync_metrics_count = 1; + sync_metrics_read = false; + } else { + sync_metrics_count++; + sync_metrics.cfo = sync_metrics.cfo + (m.cfo - sync_metrics.cfo)/sync_metrics_count; + sync_metrics.sfo = sync_metrics.sfo + (m.sfo - sync_metrics.sfo)/sync_metrics_count; + } +} + +void phch_common::get_sync_metrics(sync_metrics_t &m) { + m = sync_metrics; + sync_metrics_read = true; +} + +void phch_common::reset() { + sr_enabled = false; + is_first_of_burst = true; + is_first_tx = true; + rar_grant_pending = false; + pathloss = 0; + cur_pathloss = 0; + cur_pusch_power = 0; + p0_preamble = 0; + cur_radio_power = 0; + sr_last_tx_tti = -1; + cur_pusch_power = 0; + avg_snr_db_cqi = 0; + avg_rsrp = 0; + avg_rsrp_dbm = 0; + avg_rsrq_db = 0; + + pcell_report_period = 20; + + bzero(pending_ack, sizeof(pending_ack_t)*TTIMOD_SZ); + +} + +void phch_common::reset_ul() +{ + /* + is_first_tx = true; + is_first_of_burst = true; + + for (uint32_t i=0;itx_end(); + */ +} + +/* Convert 6-bit maps to 10-element subframe tables + bitmap = |0|0|0|0|0|0| + subframe index = |1|2|3|6|7|8| +*/ +void phch_common::build_mch_table() +{ + // First reset tables + bzero(&mch_table[0], sizeof(uint8_t)*40); + + // 40 element table represents 4 frames (40 subframes) + generate_mch_table(&mch_table[0], config->mbsfn.mbsfn_subfr_cnfg.subfr_alloc,(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == config->mbsfn.mbsfn_subfr_cnfg.subfr_alloc_num_frames)?1:4); + // Debug + + + std::stringstream ss; + ss << "|"; + for(uint32_t j=0; j<40; j++) { + ss << (int) mch_table[j] << "|"; + } + Info("MCH table: %s\n", ss.str().c_str()); +} + +void phch_common::build_mcch_table() +{ + // First reset tables + bzero(&mcch_table[0], sizeof(uint8_t)*10); + generate_mcch_table(&mcch_table[0], config->mbsfn.mbsfn_area_info.sf_alloc_info_r9); + // Debug + std::stringstream ss; + ss << "|"; + for(uint32_t j=0; j<10; j++) { + ss << (int) mcch_table[j] << "|"; + } + Info("MCCH table: %s\n", ss.str().c_str()); + sib13_configured = true; +} + +void phch_common::set_mcch() +{ + mcch_configured = true; +} + +void phch_common::set_mch_period_stop(uint32_t stop) +{ + pthread_mutex_lock(&mtch_mutex); + have_mtch_stop = true; + mch_period_stop = stop; + pthread_cond_signal(&mtch_cvar); + pthread_mutex_unlock(&mtch_mutex); + +} + +bool phch_common::is_mch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + // Set some defaults + cfg->mbsfn_area_id = 0; + cfg->non_mbsfn_region_length = 1; + cfg->mbsfn_mcs = 2; + cfg->mbsfn_decode = false; + cfg->is_mcch = false; + // Check for MCCH + if(is_mcch_subframe(cfg, phy_tti)) { + cfg->is_mcch = true; + return true; + } + + // Not MCCH, check for MCH + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &config->mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &config->mbsfn.mbsfn_area_info; + offset = subfr_cnfg->radio_fr_alloc_offset; + period = liblte_rrc_radio_frame_allocation_period_num[subfr_cnfg->radio_fr_alloc_period]; + + if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_ONE == subfr_cnfg->subfr_alloc_num_frames) { + if((sfn%period == offset) && (mch_table[sf] > 0)) { + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + if(mcch_configured) { + // Iterate through PMCH configs to see which one applies in the current frame + LIBLTE_RRC_MCCH_MSG_STRUCT *mcch = &config->mbsfn.mcch; + uint32_t mbsfn_per_frame = mcch->pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9/liblte_rrc_mch_scheduling_period_r9_num[mcch->pmch_infolist_r9[0].pmch_config_r9.mch_schedulingperiod_r9]; + uint32_t frame_alloc_idx = sfn%liblte_rrc_mbsfn_common_sf_alloc_period_r9_num[mcch->commonsf_allocperiod_r9]; + uint32_t sf_alloc_idx = frame_alloc_idx*mbsfn_per_frame + ((sf<4)?sf-1:sf-3); + pthread_mutex_lock(&mtch_mutex); + while(!have_mtch_stop) { + pthread_cond_wait(&mtch_cvar, &mtch_mutex); + } + pthread_mutex_unlock(&mtch_mutex); + + for(uint32_t i=0; ipmch_infolist_r9_size; i++) { + if(sf_alloc_idx <= mch_period_stop) { + //trigger conditional variable, has ot be untriggered by mtch stop location + cfg->mbsfn_mcs = mcch->pmch_infolist_r9[i].pmch_config_r9.datamcs_r9; + cfg->mbsfn_decode = true; + } else { + //have_mtch_stop = false; + } + } + Debug("MCH subframe TTI:%d\n", phy_tti); + } + } + return true; + } + }else if(LIBLTE_RRC_SUBFRAME_ALLOCATION_NUM_FRAMES_FOUR == subfr_cnfg->subfr_alloc_num_frames) { + uint8_t idx = sfn%period; + if((idx >= offset) && (idx < offset+4)) { + if(mch_table[(idx*10)+sf] > 0){ + if(sib13_configured) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + // TODO: check for MCCH configuration, set MCS and decode + + } + return true; + } + } + } + + return false; +} + +bool phch_common::is_mcch_subframe(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + uint32_t sfn; // System Frame Number + uint8_t sf; // Subframe + uint8_t offset; + uint8_t period; + + sfn = phy_tti/10; + sf = phy_tti%10; + + if(sib13_configured) { + LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT *subfr_cnfg = &config->mbsfn.mbsfn_subfr_cnfg; + LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT *area_info = &config->mbsfn.mbsfn_area_info; + + offset = area_info->mcch_offset_r9; + period = liblte_rrc_mcch_repetition_period_r9_num[area_info->mcch_repetition_period_r9]; + + if((sfn%period == offset) && mcch_table[sf] > 0) { + cfg->mbsfn_area_id = area_info->mbsfn_area_id_r9; + cfg->non_mbsfn_region_length = liblte_rrc_non_mbsfn_region_length_num[area_info->non_mbsfn_region_length]; + cfg->mbsfn_mcs = liblte_rrc_mcch_signalling_mcs_r9_num[area_info->signalling_mcs_r9]; + cfg->mbsfn_decode = true; + have_mtch_stop = false; + Debug("MCCH subframe TTI:%d\n", phy_tti); + return true; + } + } + return false; +} + +void phch_common::get_sf_config(subframe_cfg_t *cfg, uint32_t phy_tti) +{ + if(is_mch_subframe(cfg, phy_tti)) { + cfg->sf_type = SUBFRAME_TYPE_MBSFN; + }else{ + cfg->sf_type = SUBFRAME_TYPE_REGULAR; + } +} + +} diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc new file mode 100644 index 0000000..9f9ef94 --- /dev/null +++ b/srsue/src/phy/phch_recv.cc @@ -0,0 +1,1654 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/srslte.h" +#include "srslte/common/log.h" +#include "srsue/hdr/phy/phch_worker.h" +#include "srsue/hdr/phy/phch_recv.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsue { + +int radio_recv_callback(void *obj, cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) { + return ((phch_recv*) obj)->radio_recv_fnc(data, nsamples, rx_time); +} + +double callback_set_rx_gain(void *h, double gain) { + return ((phch_recv*) h)->set_rx_gain(gain); +} + + + +phch_recv::phch_recv() { + dl_freq = -1; + ul_freq = -1; + bzero(&cell, sizeof(srslte_cell_t)); + bzero(&metrics, sizeof(sync_metrics_t)); + running = false; + worker_com = NULL; +} + +void phch_recv::init(srslte::radio_multi *_radio_handler, mac_interface_phy *_mac, rrc_interface_phy *_rrc, + prach *_prach_buffer, srslte::thread_pool *_workers_pool, + phch_common *_worker_com, srslte::log *_log_h, srslte::log *_log_phy_lib_h, uint32_t nof_rx_antennas_, uint32_t prio, + int sync_cpu_affinity) +{ + radio_h = _radio_handler; + log_h = _log_h; + log_phy_lib_h = _log_phy_lib_h; + mac = _mac; + rrc = _rrc; + workers_pool = _workers_pool; + worker_com = _worker_com; + prach_buffer = _prach_buffer; + nof_rx_antennas = nof_rx_antennas_; + + for (uint32_t i = 0; i < nof_rx_antennas; i++) { + sf_buffer[i] = (cf_t *) srslte_vec_malloc(sizeof(cf_t) * 3 * SRSLTE_SF_LEN_PRB(100)); + } + + if (srslte_ue_sync_init_multi(&ue_sync, SRSLTE_MAX_PRB, false, radio_recv_callback, nof_rx_antennas, this)) { + Error("SYNC: Initiating ue_sync\n"); + return; + } + + nof_tx_mutex = MUTEX_X_WORKER * workers_pool->get_nof_workers(); + worker_com->set_nof_mutex(nof_tx_mutex); + + // Initialize cell searcher + search_p.init(sf_buffer, log_h, nof_rx_antennas, this); + + // Initialize SFN synchronizer + sfn_p.init(&ue_sync, sf_buffer, log_h); + + // Start intra-frequency measurement + intra_freq_meas.init(worker_com, rrc, log_h); + + pthread_mutex_init(&rrc_mutex, NULL); + + reset(); + running = true; + // Start main thread + if (sync_cpu_affinity < 0) { + start(prio); + } else { + start_cpu(prio, sync_cpu_affinity); + } +} + +phch_recv::~phch_recv() { + for (uint32_t i = 0; i < nof_rx_antennas; i++) { + if (sf_buffer[i]) { + free(sf_buffer[i]); + } + } + pthread_mutex_destroy(&rrc_mutex); + srslte_ue_sync_free(&ue_sync); +} + +void phch_recv::stop() +{ + intra_freq_meas.stop(); + running = false; + wait_thread_finish(); +} + +void phch_recv::reset() +{ + radio_is_overflow = false; + radio_overflow_return = false; + in_sync_cnt = 0; + out_of_sync_cnt = 0; + tx_mutex_cnt = 0; + time_adv_sec = 0; + next_offset = 0; + srate_mode = SRATE_NONE; + current_earfcn = -1; + sfn_p.reset(); + search_p.reset(); + phy_state.go_idle(); + +} + + + + + + + +/** + * Higher layers API. + * + * These functions are called by higher layers (RRC) to control the Cell search and cell selection procedures. + * They manipulate the SYNC state machine to switch states and perform different actions. In order to ensure mutual + * exclusion any change of state variables such as cell configuration, MIB decoder, etc. must be done while the + * SYNC thread is in IDLE. + * + * Functions will manipulate the SYNC state machine (sync_state class) to jump to states and wait for result then + * return the result to the higher layers. + * + * Cell Search: + * It's the process of searching for cells in the bands or set of EARFCNs supported by the UE. Cell search is performed + * at 1.92 MHz sampling rate and involves PSS/SSS synchronization (PCI extraction) and MIB decoding for number of Ports and PRB. + * + * + * Cell Select: + * It's the process of moving the cell state from IDLE->CAMPING or from CAMPING->IDLE->CAMPING when RRC indicates to + * select a different cell. + * + * If it is a new cell, the reconfiguration must take place while sync_state is on IDLE. + * + * cell_search() and cell_select() functions can not be called concurrently. A mutex is used to prevent it from happening. + * + */ + + +/* A call to cell_search() finds the strongest cell in the set of supported EARFCNs. When the first cell is found, + * returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not found in the current + * frequency it moves to the next one and the next call to cell_search() will look in the next EARFCN in the set. + * If no cells are found in any frequency it returns 0. If error returns -1. + */ + +phy_interface_rrc::cell_search_ret_t phch_recv::cell_search(phy_interface_rrc::phy_cell_t *found_cell) +{ + phy_interface_rrc::cell_search_ret_t ret; + + ret.found = phy_interface_rrc::cell_search_ret_t::ERROR; + ret.last_freq = phy_interface_rrc::cell_search_ret_t::NO_MORE_FREQS; + + pthread_mutex_lock(&rrc_mutex); + + // Move state to IDLE + Info("Cell Search: Start EARFCN index=%u/%lu\n", cellsearch_earfcn_index, earfcn.size()); + phy_state.go_idle(); + + if (current_earfcn != (int) earfcn[cellsearch_earfcn_index]) { + current_earfcn = (int) earfcn[cellsearch_earfcn_index]; + Info("Cell Search: changing frequency to EARFCN=%d\n", current_earfcn); + set_frequency(); + } + + // Move to CELL SEARCH and wait to finish + Info("Cell Search: Setting Cell search state\n"); + phy_state.run_cell_search(); + + // Check return state + switch(cell_search_ret) { + case search::CELL_FOUND: + // If a cell is found, configure it, synchronize and measure it + if (set_cell()) { + + Info("Cell Search: Setting sampling rate and synchronizing SFN...\n"); + set_sampling_rate(); + phy_state.run_sfn_sync(); + + if (phy_state.is_camping()) { + log_h->info("Cell Search: Sync OK. Camping on cell PCI=%d\n", cell.id); + if (found_cell) { + found_cell->earfcn = current_earfcn; + found_cell->cell = cell; + } + ret.found = phy_interface_rrc::cell_search_ret_t::CELL_FOUND; + } else { + log_h->info("Cell Search: Could not synchronize with cell\n"); + ret.found = phy_interface_rrc::cell_search_ret_t::CELL_NOT_FOUND; + } + } else { + Error("Cell Search: Setting cell PCI=%d, nof_prb=%d\n", cell.id, cell.nof_prb); + } + break; + case search::CELL_NOT_FOUND: + Info("Cell Search: No cell found in this frequency\n"); + ret.found = phy_interface_rrc::cell_search_ret_t::CELL_NOT_FOUND; + break; + default: + Error("Cell Search: while receiving samples\n"); + radio_error(); + break; + } + + cellsearch_earfcn_index++; + if (cellsearch_earfcn_index >= earfcn.size()) { + Info("Cell Search: No more frequencies in the current EARFCN set\n"); + cellsearch_earfcn_index = 0; + ret.last_freq = phy_interface_rrc::cell_search_ret_t::NO_MORE_FREQS; + } else { + ret.last_freq = phy_interface_rrc::cell_search_ret_t::MORE_FREQS; + } + + pthread_mutex_unlock(&rrc_mutex); + return ret; +} + +/* Cell select synchronizes to a new cell (e.g. during HO or during cell reselection on IDLE) or + * re-synchronizes with the current cell if cell argument is NULL + */ +bool phch_recv::cell_select(phy_interface_rrc::phy_cell_t *new_cell) { + pthread_mutex_lock(&rrc_mutex); + + bool ret = false; + int cnt = 0; + + // Move state to IDLE + if (!new_cell) { + Info("Cell Select: Starting cell resynchronization\n"); + } else { + if (!srslte_cell_isvalid(&cell)) { + log_h->error("Cell Select: Invalid cell. ID=%d, PRB=%d, ports=%d\n", cell.id, cell.nof_prb, cell.nof_ports); + goto unlock; + } + Info("Cell Select: Starting cell selection for PCI=%d, EARFCN=%d\n", new_cell->cell.id, new_cell->earfcn); + } + + // Wait for any pending PHICH + while(worker_com->is_any_pending_ack() && cnt < 10) { + usleep(1000); + cnt++; + Info("Cell Select: waiting pending PHICH (cnt=%d)\n", cnt); + } + + Info("Cell Select: Going to IDLE\n"); + phy_state.go_idle(); + + /* Reset everything */ + for(uint32_t i=0;iget_nof_workers();i++) { + ((phch_worker*) workers_pool->get_worker(i))->reset(); + } + + worker_com->reset(); + sfn_p.reset(); + search_p.reset(); + srslte_ue_sync_reset(&ue_sync); + + /* Reconfigure cell if necessary */ + if (new_cell) { + if (new_cell->cell.id != cell.id) { + Info("Cell Select: Reconfiguring cell\n"); + cell = new_cell->cell; + if (!set_cell()) { + Error("Cell Select: Reconfiguring cell\n"); + goto unlock; + } + } + + /* Select new frequency if necessary */ + if ((int) new_cell->earfcn != current_earfcn) { + Info("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn); + if (set_frequency()) { + Error("Cell Select: Setting new frequency EARFCN=%d\n", new_cell->earfcn); + goto unlock; + } + current_earfcn = new_cell->earfcn; + } + } + + /* Change sampling rate if necessary */ + if (srate_mode != SRATE_CAMP) { + set_sampling_rate(); + log_h->info("Cell Select: Setting CAMPING sampling rate\n"); + } + + /* SFN synchronization */ + phy_state.run_sfn_sync(); + if (phy_state.is_camping()) { + Info("Cell Select: SFN synchronized. CAMPING...\n"); + ret = true; + } else { + Info("Cell Select: Could not synchronize SFN\n"); + } + +unlock: + pthread_mutex_unlock(&rrc_mutex); + return ret; +} + +bool phch_recv::cell_is_camping() { + return phy_state.is_camping(); +} + + + + +/** + * MAIN THREAD + * + * The main thread process the SYNC state machine. Every state except IDLE must have exclusive access to + * all variables. If any change of cell configuration must be done, the thread must be in IDLE. + * + * On each state except campling, 1 function is called and the thread jumps to the next state based on the output. + * + * It has 3 states: Cell search, SFN syncrhonization, intial measurement and camping. + * - CELL_SEARCH: Initial Cell id and MIB acquisition. Uses 1.92 MHz sampling rate + * - CELL_SYNC: Full sampling rate, uses MIB to obtain SFN. When SFN is obtained, moves to CELL_CAMP + * - CELL_CAMP: Cell camping state. Calls the PHCH workers to process subframes and mantains cell synchronization. + * - IDLE: Receives and discards received samples. Does not mantain synchronization. + * + */ + +void phch_recv::run_thread() +{ + phch_worker *worker = NULL; + phch_worker *last_worker = NULL; + cf_t *buffer[SRSLTE_MAX_PORTS] = {NULL}; + uint32_t sf_idx = 0; + + bool is_end_of_burst = false; + + cf_t *dummy_buffer[SRSLTE_MAX_PORTS]; + for (int i=0;istep(tti); + } + + sf_idx = tti%10; + + switch (phy_state.run_state()) { + case sync_state::CELL_SEARCH: + /* Search for a cell in the current frequency and go to IDLE. + * The function search_p.run() will not return until the search finishes + */ + cell_search_ret = search_p.run(&cell); + phy_state.state_exit(); + break; + case sync_state::SFN_SYNC: + + /* SFN synchronization using MIB. run_subframe() receives and processes 1 subframe + * and returns + */ + switch(sfn_p.run_subframe(&cell, &tti)) { + case sfn_sync::SFN_FOUND: + phy_state.state_exit(); + break; + case sfn_sync::IDLE: + break; + default: + phy_state.state_exit(false); + break; + } + break; + case sync_state::CAMPING: + + worker = (phch_worker *) workers_pool->wait_worker(tti); + if (worker) { + for (uint32_t i = 0; i < SRSLTE_MAX_PORTS; i++) { + buffer[i] = worker->get_buffer(i); + } + + switch(srslte_ue_sync_zerocopy_multi(&ue_sync, buffer)) { + case 1: + + if (last_worker) { + Debug("SF: cfo_tot=%7.1f Hz, ref=%f Hz, pss=%f Hz, snr_sf=%.2f dB, rsrp=%.2f dB, noise=%.2f dB\n", + srslte_ue_sync_get_cfo(&ue_sync), + 15000*last_worker->get_ref_cfo(), + 15000*ue_sync.strack.cfo_pss_mean, + last_worker->get_snr(), last_worker->get_rsrp(), last_worker->get_noise()); + } + + last_worker = worker; + + Debug("SYNC: Worker %d synchronized\n", worker->get_id()); + + metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync); + metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync); + metrics.ta_us = time_adv_sec*1e6; + worker_com->set_sync_metrics(metrics); + + // Check if we need to TX a PRACH + if (prach_buffer->is_ready_to_send(tti)) { + prach_ptr = prach_buffer->generate(get_tx_cfo(), &prach_nof_sf, &prach_power); + if (!prach_ptr) { + Error("Generating PRACH\n"); + } + } + + /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ + srslte_timestamp_t rx_time, tx_time; + srslte_ue_sync_get_last_timestamp(&ue_sync, &rx_time); + srslte_timestamp_copy(&tx_time, &rx_time); + if (prach_ptr) { + srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); + } else { + srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3 - time_adv_sec); + } + + worker->set_prach(prach_ptr?&prach_ptr[prach_sf_cnt*SRSLTE_SF_LEN_PRB(cell.nof_prb)]:NULL, prach_power); + worker->set_cfo(get_tx_cfo()); + worker->set_tti(tti, tx_mutex_cnt); + worker->set_tx_time(tx_time, next_offset); + next_offset = 0; + if (next_time_adv_sec != time_adv_sec) { + time_adv_sec = next_time_adv_sec; + } + tx_mutex_cnt = (tx_mutex_cnt+1) % nof_tx_mutex; + + // Advance/reset prach subframe pointer + if (prach_ptr) { + prach_sf_cnt++; + if (prach_sf_cnt == prach_nof_sf) { + prach_sf_cnt = 0; + prach_ptr = NULL; + } + } + + is_end_of_burst = true; + + + // Start worker + workers_pool->start_worker(worker); + + // Save signal for Intra-frequency measurement + if ((tti%5) == 0 && worker_com->args->sic_pss_enabled) { + srslte_pss_sic(&ue_sync.strack.pss, &buffer[0][SRSLTE_SF_LEN_PRB(cell.nof_prb)/2-ue_sync.strack.fft_size]); + } + if (srslte_cell_isvalid(&cell)) { + intra_freq_meas.write(tti, buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb)); + } + break; + case 0: + Warning("SYNC: Out-of-sync detected in PSS/SSS\n"); + out_of_sync(); + worker->release(); + worker_com->reset_ul(); + break; + default: + radio_error(); + break; + } + } else { + // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here + running = false; + } + break; + case sync_state::IDLE: + + if (radio_h->is_init()) { + uint32_t nsamples = 1920; + if (current_srate > 0) { + nsamples = current_srate/1000; + } + Debug("Discarting %d samples\n", nsamples); + if (!radio_h->rx_now(dummy_buffer, nsamples, NULL)) { + printf("SYNC: Receiving from radio while in IDLE_RX\n"); + } + if (is_end_of_burst) { + radio_h->tx_end(); + is_end_of_burst = true; + } + } else { + usleep(1000); + } + break; + } + + /* Radio overflow detected. If CAMPING, go through SFN sync again and when + * SFN is found again go back to camping + */ + if (!pthread_mutex_trylock(&rrc_mutex)) { + if (radio_is_overflow) { + // If we are coming back from an overflow + if (radio_overflow_return) { + if (phy_state.is_camping()) { + log_h->info("Successfully resynchronized after overflow. Returning to CAMPING\n"); + radio_overflow_return = false; + radio_is_overflow = false; + } else if (phy_state.is_idle()) { + log_h->warning("Could not synchronize SFN after radio overflow. Trying again\n"); + rrc->out_of_sync(); + phy_state.force_sfn_sync(); + } + } else { + // Overflow has occurred now while camping + if (phy_state.is_camping()) { + log_h->warning("Detected radio overflow while camping. Resynchronizing cell\n"); + sfn_p.reset(); + srslte_ue_sync_reset(&ue_sync); + phy_state.force_sfn_sync(); + radio_overflow_return = true; + } else { + radio_is_overflow = false; + } + // If overflow occurs in any other state, it does not harm + } + } + pthread_mutex_unlock(&rrc_mutex); + } + + // Increase TTI counter + tti = (tti+1) % 10240; + } + + for (int i=0;ierror("SYNC: Receiving from radio.\n"); + // Need to find a method to effectively reset radio, reloading the driver does not work + radio_h->reset(); +} + +void phch_recv::in_sync() { + in_sync_cnt++; + // Send RRC in-sync signal after 100 ms consecutive subframes + if (in_sync_cnt == NOF_IN_SYNC_SF) { + rrc->in_sync(); + in_sync_cnt = 0; + out_of_sync_cnt = 0; + } +} + +// Out of sync called by worker or phch_recv every 1 or 5 ms +void phch_recv::out_of_sync() { + // Send RRC out-of-sync signal after 200 ms consecutive subframes + Info("Out-of-sync %d/%d\n", out_of_sync_cnt, NOF_OUT_OF_SYNC_SF); + out_of_sync_cnt++; + if (out_of_sync_cnt == NOF_OUT_OF_SYNC_SF) { + Info("Sending to RRC\n"); + rrc->out_of_sync(); + out_of_sync_cnt = 0; + in_sync_cnt = 0; + } +} + +void phch_recv::set_cfo(float cfo) { + srslte_ue_sync_set_cfo_ref(&ue_sync, cfo); +} + +void phch_recv::set_agc_enable(bool enable) +{ + do_agc = enable; + if (do_agc) { + if (running && radio_h) { + srslte_rf_info_t *rf_info = radio_h->get_info(); + srslte_ue_sync_start_agc(&ue_sync, + callback_set_rx_gain, + rf_info->min_rx_gain, + rf_info->max_rx_gain, + radio_h->get_rx_gain()); + search_p.set_agc_enable(true); + } else { + fprintf(stderr, "Error setting AGC: PHY not initiatec\n"); + } + } else { + fprintf(stderr, "Error stopping AGC: not implemented\n"); + } +} + +void phch_recv::set_time_adv_sec(float time_adv_sec) +{ + // If transmitting earlier, transmit less samples to align time advance. If transmit later just delay next TX + next_offset = floor((this->time_adv_sec-time_adv_sec)*srslte_sampling_freq_hz(cell.nof_prb)); + this->next_time_adv_sec = time_adv_sec; + Info("Applying time_adv_sec=%.1f us, next_offset=%d\n", time_adv_sec*1e6, next_offset); +} + +float phch_recv::get_tx_cfo() +{ + float cfo = srslte_ue_sync_get_cfo(&ue_sync); + + float ret = cfo*ul_dl_factor; + + if (worker_com->args->cfo_is_doppler) { + ret *= -1; + } else { + /* Compensates the radio frequency offset applied equally to DL and UL. Does not work in doppler mode */ + if (radio_h->get_freq_offset() != 0.0f) { + const float offset_hz = (float) radio_h->get_freq_offset() * (1.0f - ul_dl_factor); + ret = cfo - offset_hz; + } + } + + return ret/15000; +} + +void phch_recv::set_ue_sync_opts(srslte_ue_sync_t *q, float cfo) +{ + if (worker_com->args->cfo_integer_enabled) { + srslte_ue_sync_set_cfo_i_enable(q, true); + } + + srslte_ue_sync_set_cfo_ema(q, worker_com->args->cfo_pss_ema); + srslte_ue_sync_set_cfo_tol(q, worker_com->args->cfo_correct_tol_hz); + srslte_ue_sync_set_cfo_loop_bw(q, worker_com->args->cfo_loop_bw_pss, worker_com->args->cfo_loop_bw_ref, + worker_com->args->cfo_loop_pss_tol, + worker_com->args->cfo_loop_ref_min, + worker_com->args->cfo_loop_pss_tol, + worker_com->args->cfo_loop_pss_conv); + + q->strack.pss.chest_on_filter = worker_com->args->sic_pss_enabled; + + // Disable CP based CFO estimation during find + if (cfo != 0) { + q->cfo_current_value = cfo/15000; + q->cfo_is_copied = true; + q->cfo_correct_enable_find = true; + srslte_sync_set_cfo_cp_enable(&q->sfind, false, 0); + } + + // Set SFO ema and correct period + srslte_ue_sync_set_sfo_correct_period(q, worker_com->args->sfo_correct_period); + srslte_ue_sync_set_sfo_ema(q, worker_com->args->sfo_ema); + + sss_alg_t sss_alg = SSS_FULL; + if (!worker_com->args->sss_algorithm.compare("diff")) { + sss_alg = SSS_DIFF; + } else if (!worker_com->args->sss_algorithm.compare("partial")) { + sss_alg = SSS_PARTIAL_3; + } else if (!worker_com->args->sss_algorithm.compare("full")) { + sss_alg = SSS_FULL; + } else { + Warning("SYNC: Invalid SSS algorithm %s. Using 'full'\n", worker_com->args->sss_algorithm.c_str()); + } + srslte_sync_set_sss_algorithm(&q->strack, (sss_alg_t) sss_alg); + srslte_sync_set_sss_algorithm(&q->sfind, (sss_alg_t) sss_alg); +} + +bool phch_recv::set_cell() { + + if (!phy_state.is_idle()) { + Warning("Can not change Cell while not in IDLE\n"); + return false; + } + + // Set cell in all objects + if (srslte_ue_sync_set_cell(&ue_sync, cell)) { + Error("SYNC: Setting cell: initiating ue_sync\n"); + return false; + } + sfn_p.set_cell(cell); + worker_com->set_cell(cell); + intra_freq_meas.set_primay_cell(current_earfcn, cell); + + for (uint32_t i = 0; i < workers_pool->get_nof_workers(); i++) { + if (!((phch_worker *) workers_pool->get_worker(i))->set_cell(cell)) { + Error("SYNC: Setting cell: initiating PHCH worker\n"); + return false; + } + } + + // Set options defined in expert section + set_ue_sync_opts(&ue_sync, search_p.get_last_cfo()); + + // Reset ue_sync and set CFO/gain from search procedure + srslte_ue_sync_reset(&ue_sync); + + return true; +} + +void phch_recv::set_earfcn(std::vector earfcn) { + this->earfcn = earfcn; +} + +void phch_recv::force_freq(float dl_freq, float ul_freq) { + this->dl_freq = dl_freq; + this->ul_freq = ul_freq; +} + +bool phch_recv::set_frequency() +{ + double set_dl_freq = 0; + double set_ul_freq = 0; + + if (this->dl_freq > 0 && this->ul_freq > 0) { + set_dl_freq = this->dl_freq; + set_ul_freq = this->ul_freq; + } else { + set_dl_freq = 1e6*srslte_band_fd(current_earfcn); + set_ul_freq = 1e6*srslte_band_fu(srslte_band_ul_earfcn(current_earfcn)); + } + if (set_dl_freq > 0 && set_ul_freq > 0) { + log_h->info("SYNC: Set DL EARFCN=%d, f_dl=%.1f MHz, f_ul=%.1f MHz\n", + current_earfcn, set_dl_freq / 1e6, set_ul_freq / 1e6); + + log_h->console("Searching cell in DL EARFCN=%d, f_dl=%.1f MHz, f_ul=%.1f MHz\n", + current_earfcn, set_dl_freq / 1e6, set_ul_freq / 1e6); + + radio_h->set_rx_freq(set_dl_freq); + radio_h->set_tx_freq(set_ul_freq); + ul_dl_factor = radio_h->get_tx_freq()/radio_h->get_rx_freq(); + + srslte_ue_sync_reset(&ue_sync); + + return true; + } else { + log_h->error("SYNC: Cell Search: Invalid EARFCN=%d\n", current_earfcn); + return false; + } +} + +void phch_recv::set_sampling_rate() +{ + current_srate = (float) srslte_sampling_freq_hz(cell.nof_prb); + current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb); + if (current_srate != -1) { + Info("SYNC: Setting sampling rate %.2f MHz\n", current_srate/1000000); + +#if 0 + if (((int) current_srate / 1000) % 3072 == 0) { + radio_h->set_master_clock_rate(30.72e6); + } else { + radio_h->set_master_clock_rate(23.04e6); + } +#else + if (current_srate < 10e6) { + radio_h->set_master_clock_rate(4 * current_srate); + } else { + radio_h->set_master_clock_rate(current_srate); + } +#endif + + srate_mode = SRATE_CAMP; + radio_h->set_rx_srate(current_srate); + radio_h->set_tx_srate(current_srate); + } else { + Error("Error setting sampling rate for cell with %d PRBs\n", cell.nof_prb); + } +} + +uint32_t phch_recv::get_current_tti() { + return tti; +} + +void phch_recv::get_current_cell(srslte_cell_t *cell_, uint32_t *earfcn) { + if (cell_) { + memcpy(cell_, &cell, sizeof(srslte_cell_t)); + } + if (earfcn) { + *earfcn = current_earfcn; + } +} + +int phch_recv::radio_recv_fnc(cf_t *data[SRSLTE_MAX_PORTS], uint32_t nsamples, srslte_timestamp_t *rx_time) +{ + if (radio_h->rx_now(data, nsamples, rx_time)) { + int offset = nsamples - current_sflen; + if (abs(offset) < 10 && offset != 0) { + next_offset = offset; + } else if (nsamples < 10) { + next_offset = nsamples; + } + + log_h->debug("SYNC: received %d samples from radio\n", nsamples); + + return nsamples; + } else { + return -1; + } +} + +double phch_recv::set_rx_gain(double gain) { + return radio_h->set_rx_gain_th(gain); +} + + + + + + + +/********* + * Cell search class + */ +phch_recv::search::~search() +{ + srslte_ue_mib_sync_free(&ue_mib_sync); + srslte_ue_cellsearch_free(&cs); +} + +void phch_recv::search::init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, phch_recv *parent) +{ + this->log_h = log_h; + this->p = parent; + + for (int i=0;ibuffer[i] = buffer[i]; + } + + if (srslte_ue_cellsearch_init_multi(&cs, 5, radio_recv_callback, nof_rx_antennas, parent)) { + Error("SYNC: Initiating UE cell search\n"); + } + + if (srslte_ue_mib_sync_init_multi(&ue_mib_sync, radio_recv_callback, nof_rx_antennas, parent)) { + Error("SYNC: Initiating UE MIB synchronization\n"); + } + + srslte_ue_cellsearch_set_nof_valid_frames(&cs, 2); + + // Set options defined in expert section + p->set_ue_sync_opts(&cs.ue_sync, 0); + + force_N_id_2 = -1; +} + +void phch_recv::search::reset() +{ + srslte_ue_sync_reset(&ue_mib_sync.ue_sync); +} + +float phch_recv::search::get_last_cfo() +{ + return srslte_ue_sync_get_cfo(&ue_mib_sync.ue_sync); +} + +void phch_recv::search::set_agc_enable(bool enable) { + if (enable) { + srslte_rf_info_t *rf_info = p->radio_h->get_info(); + srslte_ue_sync_start_agc(&ue_mib_sync.ue_sync, + callback_set_rx_gain, + rf_info->min_rx_gain, + rf_info->max_rx_gain, + p->radio_h->get_rx_gain()); + } else { + fprintf(stderr, "Error stop AGC not implemented\n"); + } +} + +phch_recv::search::ret_code phch_recv::search::run(srslte_cell_t *cell) +{ + + if (!cell) { + return ERROR; + } + + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + + srslte_ue_cellsearch_result_t found_cells[3]; + + bzero(cell, sizeof(srslte_cell_t)); + bzero(found_cells, 3 * sizeof(srslte_ue_cellsearch_result_t)); + + if (p->srate_mode != SRATE_FIND) { + p->srate_mode = SRATE_FIND; + p->radio_h->set_rx_srate(1.92e6); + p->radio_h->set_tx_srate(1.92e6); + Info("SYNC: Setting Cell Search sampling rate\n"); + } + + /* Find a cell in the given N_id_2 or go through the 3 of them to find the strongest */ + uint32_t max_peak_cell = 0; + int ret = SRSLTE_ERROR; + + Info("SYNC: Searching for cell...\n"); + printf("."); fflush(stdout); + + if (force_N_id_2 >= 0 && force_N_id_2 < 3) { + ret = srslte_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); + max_peak_cell = force_N_id_2; + } else { + ret = srslte_ue_cellsearch_scan(&cs, found_cells, &max_peak_cell); + } + + if (ret < 0) { + Error("SYNC: Error decoding MIB: Error searching PSS\n"); + return ERROR; + } else if (ret == 0) { + Info("SYNC: Could not find any cell in this frequency\n"); + return CELL_NOT_FOUND; + } + // Save result + cell->id = found_cells[max_peak_cell].cell_id; + cell->cp = found_cells[max_peak_cell].cp; + float cfo = found_cells[max_peak_cell].cfo; + + printf("\n"); + Info("SYNC: PSS/SSS detected: PCI=%d, CFO=%.1f KHz, CP=%s\n", + cell->id, cfo/1000, srslte_cp_string(cell->cp)); + + if (srslte_ue_mib_sync_set_cell(&ue_mib_sync, cell->id, cell->cp)) { + Error("SYNC: Setting UE MIB cell\n"); + return ERROR; + } + + // Set options defined in expert section + p->set_ue_sync_opts(&ue_mib_sync.ue_sync, cfo); + + srslte_ue_sync_reset(&ue_mib_sync.ue_sync); + + /* Find and decode MIB */ + int sfn_offset; + ret = srslte_ue_mib_sync_decode(&ue_mib_sync, + 40, + bch_payload, &cell->nof_ports, &sfn_offset); + if (ret == 1) { + srslte_pbch_mib_unpack(bch_payload, cell, NULL); + + fprintf(stdout, "Found Cell: PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", + cell->id, cell->nof_prb, cell->nof_ports, cfo/1000); + + Info("SYNC: MIB Decoded: PCI=%d, PRB=%d, Ports=%d, CFO=%.1f KHz\n", + cell->id, cell->nof_prb, cell->nof_ports, cfo/1000); + + if (!srslte_cell_isvalid(cell)) { + Error("SYNC: Detected invalid cell.\n"); + return CELL_NOT_FOUND; + } + return CELL_FOUND; + } else if (ret == 0) { + Warning("SYNC: Found PSS but could not decode PBCH\n"); + return CELL_NOT_FOUND; + } else { + Error("SYNC: Receiving MIB\n"); + return ERROR; + } +} + + + + + + + + +/********* + * SFN synchronizer class + */ + +phch_recv::sfn_sync::~sfn_sync() +{ + srslte_ue_mib_free(&ue_mib); +} + +void phch_recv::sfn_sync::init(srslte_ue_sync_t *ue_sync, cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_subframes) +{ + this->log_h = log_h; + this->ue_sync = ue_sync; + this->timeout = nof_subframes; + + for (int i=0;ibuffer[i] = buffer[i]; + } + + if (srslte_ue_mib_init(&ue_mib, this->buffer, SRSLTE_MAX_PRB)) { + Error("SYNC: Initiating UE MIB decoder\n"); + } +} + +bool phch_recv::sfn_sync::set_cell(srslte_cell_t cell) +{ + if (srslte_ue_mib_set_cell(&ue_mib, cell)) { + Error("SYNC: Setting cell: initiating ue_mib\n"); + return false; + } + reset(); + return true; +} + +void phch_recv::sfn_sync::reset() +{ + cnt = 0; + srslte_ue_mib_reset(&ue_mib); +} + +phch_recv::sfn_sync::ret_code phch_recv::sfn_sync::run_subframe(srslte_cell_t *cell, uint32_t *tti_cnt, bool sfidx_only) +{ + + uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; + + srslte_ue_sync_decode_sss_on_track(ue_sync, true); + int ret = srslte_ue_sync_zerocopy_multi(ue_sync, buffer); + if (ret < 0) { + Error("SYNC: Error calling ue_sync_get_buffer.\n"); + return ERROR; + } + + if (ret == 1) { + if (srslte_ue_sync_get_sfidx(ue_sync) == 0) { + + // Skip MIB decoding if we are only interested in subframe 0 + if (sfidx_only) { + if (tti_cnt) { + *tti_cnt = 0; + } + return SFX0_FOUND; + } + + int sfn_offset = 0; + int n = srslte_ue_mib_decode(&ue_mib, bch_payload, NULL, &sfn_offset); + switch(n) { + default: + Error("SYNC: Error decoding MIB while synchronising SFN"); + return ERROR; + case SRSLTE_UE_MIB_FOUND: + uint32_t sfn; + srslte_pbch_mib_unpack(bch_payload, cell, &sfn); + + sfn = (sfn+sfn_offset)%1024; + if (tti_cnt) { + *tti_cnt = 10*sfn; + Info("SYNC: DONE, SNR=%.1f dB, TTI=%d, sfn_offset=%d\n", + 10*log10(srslte_chest_dl_get_snr(&ue_mib.chest)), *tti_cnt, sfn_offset); + } + + srslte_ue_sync_decode_sss_on_track(ue_sync, true); + reset(); + return SFN_FOUND; + case SRSLTE_UE_MIB_NOTFOUND: + Info("SYNC: Found PSS but could not decode MIB. SNR=%.1f dB (%d/%d)\n", + 10*log10(srslte_chest_dl_get_snr(&ue_mib.chest)), cnt, timeout); + break; + } + } + } else { + Info("SYNC: Waiting for PSS while trying to decode MIB (%d/%d)\n", cnt, timeout); + } + + cnt++; + if (cnt >= timeout) { + cnt = 0; + return SFN_NOFOUND; + } + + return IDLE; +} + + + + + + +/********* + * Measurement class + */ +void phch_recv::measure::init(cf_t *buffer[SRSLTE_MAX_PORTS], srslte::log *log_h, uint32_t nof_rx_antennas, uint32_t nof_subframes) + +{ + this->log_h = log_h; + this->nof_subframes = nof_subframes; + for (int i=0;ibuffer[i] = buffer[i]; + } + + if (srslte_ue_dl_init(&ue_dl, this->buffer, SRSLTE_MAX_PRB, nof_rx_antennas)) { + Error("SYNC: Initiating ue_dl_measure\n"); + return; + } + srslte_chest_dl_set_rsrp_neighbour(&ue_dl.chest, true); + reset(); +} + +phch_recv::measure::~measure() { + srslte_ue_dl_free(&ue_dl); +} + +void phch_recv::measure::reset() { + cnt = 0; + mean_rsrp = 0; + mean_rsrq = 0; + mean_snr = 0; + mean_rssi = 0; +} + +void phch_recv::measure::set_cell(srslte_cell_t cell) +{ + current_prb = cell.nof_prb; + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + Error("SYNC: Setting cell: initiating ue_dl_measure\n"); + } + reset(); +} + +float phch_recv::measure::rssi() { + return 10*log10(mean_rssi); +} + +float phch_recv::measure::rsrp() { + return 10*log10(mean_rsrp) + 30 - rx_gain_offset; +} + +float phch_recv::measure::rsrq() { + return 10*log10(mean_rsrq); +} + +float phch_recv::measure::snr() { + return 10*log10(mean_snr); +} + +uint32_t phch_recv::measure::frame_st_idx() { + return final_offset; +} + +void phch_recv::measure::set_rx_gain_offset(float rx_gain_offset) { + this->rx_gain_offset = rx_gain_offset; +} + +phch_recv::measure::ret_code phch_recv::measure::run_subframe_sync(srslte_ue_sync_t *ue_sync, uint32_t sf_idx) +{ + int sync_res = srslte_ue_sync_zerocopy_multi(ue_sync, buffer); + if (sync_res == 1) { + log_h->info("SYNC: CFO=%.1f KHz\n", srslte_ue_sync_get_cfo(ue_sync)/1000); + return run_subframe(sf_idx); + } else { + log_h->error("SYNC: Measuring RSRP: Sync error\n"); + return ERROR; + } + + return IDLE; +} + +phch_recv::measure::ret_code phch_recv::measure::run_multiple_subframes(cf_t *input_buffer, + uint32_t offset, + uint32_t sf_idx, + uint32_t max_sf) +{ + uint32_t sf_len = SRSLTE_SF_LEN_PRB(current_prb); + + ret_code ret = IDLE; + + int sf_start = offset-sf_len/2; + while (sf_start < 0 && sf_idx < max_sf) { + Info("INTRA: sf_start=%d, sf_idx=%d\n", sf_start, sf_idx); + sf_start += sf_len; + sf_idx ++; + } + +#ifdef FINE_TUNE_OFFSET_WITH_RS + float max_rsrp = -200; + int best_test_sf_start = 0; + int test_sf_start = 0; + bool found_best = false; + + // Fine-tune sf_start using RS + for (uint32_t n=0;n<5;n++) { + + test_sf_start = sf_start-2+n; + if (test_sf_start >= 0) { + + cf_t *buf_m[SRSLTE_MAX_PORTS]; + buf_m[0] = &input_buffer[test_sf_start]; + + uint32_t cfi; + if (srslte_ue_dl_decode_fft_estimate_noguru(&ue_dl, buf_m, sf_idx, &cfi)) { + Error("MEAS: Measuring RSRP: Estimating channel\n"); + return ERROR; + } + + float rsrp = srslte_chest_dl_get_rsrp(&ue_dl.chest); + if (rsrp > max_rsrp) { + max_rsrp = rsrp; + best_test_sf_start = test_sf_start; + found_best = true; + } + } + } + + Debug("INTRA: fine-tuning sf_start: %d, found_best=%d, rem_sf=%d\n", sf_start, found_best, nof_sf); + + sf_start = found_best?best_test_sf_start:sf_start; +#endif + + if (sf_start >= 0 && sf_start < (int) (sf_len*max_sf)) { + + uint32_t nof_sf = (sf_len*max_sf - sf_start)/sf_len; + + final_offset = sf_start; + + for (uint32_t i=0;ierror("SYNC: Measuring RSRP: Estimating channel\n"); + return ERROR; + } + + float rsrp = srslte_chest_dl_get_rsrp_neighbour(&ue_dl.chest); + float rsrq = srslte_chest_dl_get_rsrq(&ue_dl.chest); + float snr = srslte_chest_dl_get_snr(&ue_dl.chest); + float rssi = srslte_vec_avg_power_cf(buffer[0], SRSLTE_SF_LEN_PRB(current_prb)); + + if (cnt == 0) { + mean_rsrp = rsrp; + mean_rsrq = rsrq; + mean_snr = snr; + mean_rssi = rssi; + } else { + mean_rsrp = SRSLTE_VEC_CMA(rsrp, mean_rsrp, cnt); + mean_rsrq = SRSLTE_VEC_CMA(rsrq, mean_rsrq, cnt); + mean_snr = SRSLTE_VEC_CMA(snr, mean_snr, cnt); + mean_rssi = SRSLTE_VEC_CMA(rssi, mean_rssi, cnt); + } + cnt++; + + log_h->debug("SYNC: Measuring RSRP %d/%d, sf_idx=%d, RSRP=%.1f dBm, SNR=%.1f dB\n", + cnt, nof_subframes, sf_idx, rsrp, snr); + + if (cnt >= nof_subframes) { + return MEASURE_OK; + } else { + return IDLE; + } +} + + + + + + +/********** + * Secondary cell receiver + */ + +void phch_recv::scell_recv::init(srslte::log *log_h, bool sic_pss_enabled, uint32_t max_sf_window) +{ + this->log_h = log_h; + this->sic_pss_enabled = sic_pss_enabled; + + // and a separate ue_sync instance + + uint32_t max_fft_sz = srslte_symbol_sz(100); + uint32_t max_sf_size = SRSLTE_SF_LEN(max_fft_sz); + + sf_buffer[0] = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*max_sf_size); + if (!sf_buffer[0]) { + fprintf(stderr, "Error allocating %d samples for scell\n", max_sf_size); + return; + } + measure_p.init(sf_buffer, log_h, 1, max_sf_window); + + //do this different we don't need all this search window. + if(srslte_sync_init(&sync_find, max_sf_window*max_sf_size, 5*max_sf_size, max_fft_sz)) { + fprintf(stderr, "Error initiating sync_find\n"); + return; + } + srslte_sync_set_sss_algorithm(&sync_find, SSS_FULL); + srslte_sync_cp_en(&sync_find, false); + srslte_sync_set_cfo_pss_enable(&sync_find, true); + srslte_sync_set_threshold(&sync_find, 1.7); + srslte_sync_set_em_alpha(&sync_find, 0.3); + + // Configure FIND object behaviour (this configuration is always the same) + srslte_sync_set_cfo_ema_alpha(&sync_find, 1.0); + srslte_sync_set_cfo_i_enable(&sync_find, false); + srslte_sync_set_cfo_pss_enable(&sync_find, true); + srslte_sync_set_pss_filt_enable(&sync_find, true); + srslte_sync_set_sss_eq_enable(&sync_find, true); + + sync_find.pss.chest_on_filter = true; + sync_find.sss_channel_equalize = false; + + reset(); +} + +void phch_recv::scell_recv::deinit() { + srslte_sync_free(&sync_find); + free(sf_buffer[0]); +} + +void phch_recv::scell_recv::reset() +{ + current_fft_sz = 0; + measure_p.reset(); +} + +int phch_recv::scell_recv::find_cells(cf_t *input_buffer, float rx_gain_offset, srslte_cell_t cell, uint32_t nof_sf, cell_info_t cells[MAX_CELLS]) +{ + uint32_t fft_sz = srslte_symbol_sz(cell.nof_prb); + uint32_t sf_len = SRSLTE_SF_LEN(fft_sz); + + if (fft_sz != current_fft_sz) { + if (srslte_sync_resize(&sync_find, nof_sf*sf_len, 5*sf_len, fft_sz)) { + fprintf(stderr, "Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d\n", nof_sf, sf_len, fft_sz); + return SRSLTE_ERROR; + } + current_fft_sz = fft_sz; + } + + int nof_cells = 0; + uint32_t peak_idx = 0; + uint32_t sf_idx = 0; + int cell_id = 0; + + srslte_cell_t found_cell; + memcpy(&found_cell, &cell, sizeof(srslte_cell_t)); + + measure_p.set_rx_gain_offset(rx_gain_offset); + + for (uint32_t n_id_2=0;n_id_2<3;n_id_2++) { + + found_cell.id = 10000; + + if (n_id_2 != (cell.id%3) || sic_pss_enabled) { + srslte_sync_set_N_id_2(&sync_find, n_id_2); + + srslte_sync_find_ret_t sync_res; + + do { + srslte_sync_reset(&sync_find); + srslte_sync_cfo_reset(&sync_find); + + sync_res = SRSLTE_SYNC_NOFOUND; + cell_id = 0; + float max_peak = -1; + uint32_t max_sf5 = 0; + uint32_t max_sf_idx = 0; + + for (uint32_t sf5_cnt=0;sf5_cnt max_peak && sync_res == SRSLTE_SYNC_FOUND) { + max_sf5 = sf5_cnt; + max_sf_idx = srslte_sync_get_sf_idx(&sync_find); + cell_id = srslte_sync_get_cell_id(&sync_find); + } + } + + switch(sync_res) { + case SRSLTE_SYNC_ERROR: + return SRSLTE_ERROR; + fprintf(stderr, "Error finding correlation peak\n"); + return SRSLTE_ERROR; + case SRSLTE_SYNC_FOUND: + + sf_idx = (10-max_sf_idx - 5*(max_sf5%2))%10; + + if (cell_id >= 0) { + // We found the same cell as before, look another N_id_2 + if ((uint32_t) cell_id == found_cell.id || (uint32_t) cell_id == cell.id) { + Debug("INTRA: n_id_2=%d, PCI=%d, found_cell.id=%d, cell.id=%d\n", n_id_2, cell_id, found_cell.id, cell.id); + sync_res = SRSLTE_SYNC_NOFOUND; + } else { + // We found a new cell ID + found_cell.id = cell_id; + found_cell.nof_ports = 1; // Use port 0 only for measurement + measure_p.set_cell(found_cell); + + // Correct CFO + /* + srslte_cfo_correct(&sync_find.cfo_corr_frame, + input_buffer, + input_cfo_corrected, + -srslte_sync_get_cfo(&sync_find)/sync_find.fft_size); + */ + + switch(measure_p.run_multiple_subframes(input_buffer, peak_idx, sf_idx, nof_sf)) + { + default: + // Consider a cell to be detectable 8.1.2.2.1.1 from 36.133. Currently only using first condition + if (measure_p.rsrp() > ABSOLUTE_RSRP_THRESHOLD_DBM) { + cells[nof_cells].pci = found_cell.id; + cells[nof_cells].rsrp = measure_p.rsrp(); + cells[nof_cells].rsrq = measure_p.rsrq(); + cells[nof_cells].offset = measure_p.frame_st_idx(); + + Info( + "INTRA: Found neighbour cell %d: PCI=%03d, RSRP=%5.1f dBm, peak_idx=%5d, peak_value=%3.2f, sf=%d, max_sf=%d, n_id_2=%d, CFO=%6.1f Hz\n", + nof_cells, cell_id, measure_p.rsrp(), measure_p.frame_st_idx(), sync_find.peak_value, + sf_idx, max_sf5, n_id_2, 15000 * srslte_sync_get_cfo(&sync_find)); + + nof_cells++; + + /* + if (sic_pss_enabled) { + srslte_pss_sic(&sync_find.pss, &input_buffer[sf5_cnt * 5 * sf_len + sf_len / 2 - fft_sz]); + }*/ + } else { + Info("INTRA: Found neighbour cell but RSRP=%.1f dBm is below threshold (%.1f dBm)\n", + measure_p.rsrp(), ABSOLUTE_RSRP_THRESHOLD_DBM); + } + break; + case measure::ERROR: + Error("INTRA: Measuring neighbour cell\n"); + return SRSLTE_ERROR; + } + } + } else { + sync_res = SRSLTE_SYNC_NOFOUND; + } + break; + case SRSLTE_SYNC_FOUND_NOSPACE: + /* If a peak was found but there is not enough space for SSS/CP detection, discard a few samples */ + break; + default: + break; + } + } while (sync_res == SRSLTE_SYNC_FOUND && sic_pss_enabled && nof_cells < MAX_CELLS); + } + } + return nof_cells; +} + + + +/********** + * PHY measurements + * + */ + +void phch_recv::meas_reset() { + // Stop all measurements + intra_freq_meas.clear_cells(); +} + +int phch_recv::meas_start(uint32_t earfcn, int pci) { + if ((int) earfcn == current_earfcn) { + if (pci != (int) cell.id) { + intra_freq_meas.add_cell(pci); + } + return 0; + } else { + Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested measurement for %d)\n", + current_earfcn, earfcn); + return -1; + } +} + +int phch_recv::meas_stop(uint32_t earfcn, int pci) { + if ((int) earfcn == current_earfcn) { + intra_freq_meas.rem_cell(pci); + return 0; + } else { + Warning("INTRA: Inter-frequency measurements not supported (current EARFCN=%d, requested stop measurement for %d)\n", + current_earfcn, earfcn); + } + return -1; +} + +phch_recv::intra_measure::~intra_measure() { + srslte_ringbuffer_free(&ring_buffer); + scell.deinit(); + free(search_buffer); +} + +void phch_recv::intra_measure::init(phch_common *common, rrc_interface_phy *rrc, srslte::log *log_h) { + this->rrc = rrc; + this->log_h = log_h; + this->common = common; + receive_enabled = false; + + // Start scell + scell.init(log_h, common->args->sic_pss_enabled, common->args->intra_freq_meas_len_ms); + + search_buffer = (cf_t*) srslte_vec_malloc(common->args->intra_freq_meas_len_ms*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB)*sizeof(cf_t)); + + if (srslte_ringbuffer_init(&ring_buffer, sizeof(cf_t)*common->args->intra_freq_meas_len_ms*2*SRSLTE_SF_LEN_PRB(SRSLTE_MAX_PRB))) { + return; + } + + running = true; + start(INTRA_FREQ_MEAS_PRIO); +} + +void phch_recv::intra_measure::stop() { + running = false; + srslte_ringbuffer_stop(&ring_buffer); + tti_sync.increase(); + wait_thread_finish(); +} + +void phch_recv::intra_measure::set_primay_cell(uint32_t earfcn, srslte_cell_t cell) { + this->current_earfcn = earfcn; + current_sflen = SRSLTE_SF_LEN_PRB(cell.nof_prb); + memcpy(&this->primary_cell, &cell, sizeof(srslte_cell_t)); +} + +void phch_recv::intra_measure::clear_cells() { + active_pci.clear(); + receive_enabled = false; + receiving = false; + receive_cnt = 0; + srslte_ringbuffer_reset(&ring_buffer); +} + +void phch_recv::intra_measure::add_cell(int pci) { + if (std::find(active_pci.begin(), active_pci.end(), pci) == active_pci.end()) { + active_pci.push_back(pci); + receive_enabled = true; + Info("INTRA: Starting intra-frequency measurement for pci=%d\n", pci); + } else { + Debug("INTRA: Requested to start already existing intra-frequency measurement for PCI=%d\n", pci); + } +} + +int phch_recv::intra_measure::get_offset(uint32_t pci) { + for (int i=0;i::iterator newEnd = std::remove(active_pci.begin(), active_pci.end(), pci); + + if (newEnd != active_pci.end()) { + active_pci.erase(newEnd, active_pci.end()); + if (active_pci.size() == 0) { + receive_enabled = false; + } + Info("INTRA: Stopping intra-frequency measurement for pci=%d. Number of cells: %zu\n", pci, active_pci.size()); + } else { + Warning("INTRA: Requested to stop non-existing intra-frequency measurement for PCI=%d\n", pci); + } +} + +void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples) { + if (receive_enabled) { + if ((tti%common->args->intra_freq_meas_period_ms) == 0) { + receiving = true; + receive_cnt = 0; + measure_tti = tti; + srslte_ringbuffer_reset(&ring_buffer); + } + if (receiving == true) { + if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { + Warning("Error writting to ringbuffer\n"); + receiving = false; + } else { + receive_cnt++; + if (receive_cnt == common->args->intra_freq_meas_len_ms) { + tti_sync.increase(); + receiving = false; + } + } + } + } +} + +void phch_recv::intra_measure::run_thread() +{ + while(running) { + if (running) { + tti_sync.wait(); + } + + if (running) { + + // Read data from buffer and find cells in it + srslte_ringbuffer_read(&ring_buffer, search_buffer, common->args->intra_freq_meas_len_ms*current_sflen*sizeof(cf_t)); + int found_cells = scell.find_cells(search_buffer, common->rx_gain_offset, primary_cell, common->args->intra_freq_meas_len_ms, info); + receiving = false; + + for (int i=0;inew_phy_meas(info[i].rsrp, info[i].rsrq, measure_tti, current_earfcn, info[i].pci); + } + // Look for other cells not found automatically + } + } +} + +} diff --git a/srsue/src/phy/phch_worker.cc b/srsue/src/phy/phch_worker.cc new file mode 100644 index 0000000..7f83ac7 --- /dev/null +++ b/srsue/src/phy/phch_worker.cc @@ -0,0 +1,1799 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srsue/hdr/phy/phch_worker.h" +#include "srslte/srslte.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/asn1/liblte_rrc.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + + +/* This is to visualize the channel response */ +#ifdef ENABLE_GUI +#include "srsgui/srsgui.h" +#include + +void init_plots(srsue::phch_worker *worker); +pthread_t plot_thread; +sem_t plot_sem; +static int plot_worker_id = -1; +#else +#warning Compiling without srsGUI support +#endif +/*********************************************/ + + +namespace srsue { + + +phch_worker::phch_worker() : tr_exec(10240) +{ + phy = NULL; + chest_loop = NULL; + + bzero(signal_buffer, sizeof(cf_t*)*SRSLTE_MAX_PORTS); + + mem_initiated = false; + cell_initiated = false; + pregen_enabled = false; + trace_enabled = false; + reset(); +} + + +phch_worker::~phch_worker() +{ + if (mem_initiated) { + for (uint32_t i=0;iargs->nof_rx_ant;i++) { + if (signal_buffer[i]) { + free(signal_buffer[i]); + } + } + srslte_ue_dl_free(&ue_dl); + srslte_ue_ul_free(&ue_ul); + mem_initiated = false; + } +} + +void phch_worker::reset() +{ + bzero(&dl_metrics, sizeof(dl_metrics_t)); + bzero(&ul_metrics, sizeof(ul_metrics_t)); + bzero(&dmrs_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + bzero(&pusch_hopping, sizeof(srslte_pusch_hopping_cfg_t)); + bzero(&uci_cfg, sizeof(srslte_uci_cfg_t)); + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + bzero(&pucch_sched, sizeof(srslte_pucch_sched_t)); + bzero(&srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + bzero(&period_cqi, sizeof(srslte_cqi_periodic_cfg_t)); + sr_configured = false; + rnti_is_set = false; + rar_cqi_request = false; + I_sr = 0; + cfi = 0; + rssi_read_cnt = 0; +} + +void phch_worker::enable_pdsch_coworker() { + srslte_pdsch_enable_coworker(&ue_dl.pdsch); +} + +void phch_worker::set_common(phch_common* phy_) +{ + phy = phy_; +} + +bool phch_worker::init(uint32_t max_prb, srslte::log *log_h, srslte::log *log_phy_lib_h , chest_feedback_itf *chest_loop) +{ + this->log_h = log_h; + this->log_phy_lib_h = log_phy_lib_h; + this->chest_loop = chest_loop; + + // ue_sync in phy.cc requires a buffer for 3 subframes + for (uint32_t i=0;iargs->nof_rx_ant;i++) { + signal_buffer[i] = (cf_t*) srslte_vec_malloc(3 * sizeof(cf_t) * SRSLTE_SF_LEN_PRB(max_prb)); + if (!signal_buffer[i]) { + Error("Allocating memory\n"); + return false; + } + } + + if (srslte_ue_dl_init(&ue_dl, signal_buffer, max_prb, phy->args->nof_rx_ant)) { + Error("Initiating UE DL\n"); + return false; + } + + if (srslte_ue_ul_init(&ue_ul, signal_buffer[0], max_prb)) { + Error("Initiating UE UL\n"); + return false; + } + + + srslte_chest_dl_set_rsrp_neighbour(&ue_dl.chest, true); + srslte_chest_dl_average_subframe(&ue_dl.chest, phy->args->average_subframe_enabled); + srslte_chest_dl_cfo_estimate_enable(&ue_dl.chest, phy->args->cfo_ref_mask!=0, phy->args->cfo_ref_mask); + srslte_ue_ul_set_normalization(&ue_ul, true); + srslte_ue_ul_set_cfo_enable(&ue_ul, true); + srslte_pdsch_enable_csi(&ue_dl.pdsch, phy->args->pdsch_csi_enabled); + + mem_initiated = true; + + pthread_mutex_init(&mutex, NULL); + return true; +} + +bool phch_worker::set_cell(srslte_cell_t cell_) +{ + bool ret = false; + pthread_mutex_lock(&mutex); + if (cell.id != cell_.id || !cell_initiated) { + memcpy(&cell, &cell_, sizeof(srslte_cell_t)); + + if (srslte_ue_dl_set_cell(&ue_dl, cell)) { + Error("Initiating UE DL\n"); + goto unlock; + } + + if(srslte_ue_dl_set_mbsfn_area_id(&ue_dl, 1)){ + Error("Setting mbsfn id\n"); + } + + if (srslte_ue_ul_set_cell(&ue_ul, cell)) { + Error("Initiating UE UL\n"); + goto unlock; + } + srslte_ue_ul_set_normalization(&ue_ul, true); + srslte_ue_ul_set_cfo_enable(&ue_ul, true); + + cell_initiated = true; + } + ret = true; +unlock: + pthread_mutex_unlock(&mutex); + return ret; +} + +cf_t* phch_worker::get_buffer(uint32_t antenna_idx) +{ + return signal_buffer[antenna_idx]; +} + +void phch_worker::set_tti(uint32_t tti_, uint32_t tx_tti_) +{ + tti = tti_; + tx_tti = tx_tti_; + log_h->step(tti); + if (log_phy_lib_h) { + log_phy_lib_h->step(tti); + } +} + +void phch_worker::set_prach(cf_t *prach_ptr, float prach_power) { + this->prach_ptr = prach_ptr; + this->prach_power = prach_power; +} + +void phch_worker::set_cfo(float cfo_) +{ + cfo = cfo_; +} + +void phch_worker::set_crnti(uint16_t rnti) +{ + srslte_ue_dl_set_rnti(&ue_dl, rnti); + srslte_ue_ul_set_rnti(&ue_ul, rnti); + rnti_is_set = true; +} + +float phch_worker::get_ref_cfo() +{ + return srslte_chest_dl_get_cfo(&ue_dl.chest); +} + +float phch_worker::get_snr() +{ + return 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)); +} + +float phch_worker::get_rsrp() +{ + return 10*log10(srslte_chest_dl_get_rsrp(&ue_dl.chest)); +} + +float phch_worker::get_noise() +{ + return 10*log10(srslte_chest_dl_get_noise_estimate(&ue_dl.chest)); +} + + +float phch_worker::get_cfo() +{ + return cfo; +} + +void phch_worker::work_imp() +{ + if (!cell_initiated) { + return; + } + + pthread_mutex_lock(&mutex); + + Debug("TTI %d running\n", tti); + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[1], NULL); +#endif + + tr_log_start(); + + reset_uci(); + + subframe_cfg_t sf_cfg; + phy->get_sf_config(&sf_cfg, tti); + Debug("TTI: %d, Subframe type: %s\n", tti, subframe_type_text[sf_cfg.sf_type]); + + bool dl_grant_available = false; + bool ul_grant_available = false; + bool dl_ack[SRSLTE_MAX_CODEWORDS] = {false}; + + mac_interface_phy::mac_grant_t dl_mac_grant; + mac_interface_phy::tb_action_dl_t dl_action; + + mac_interface_phy::mac_grant_t ul_mac_grant; + mac_interface_phy::tb_action_ul_t ul_action; + + ZERO_OBJECT(dl_mac_grant); + ZERO_OBJECT(dl_action); + ZERO_OBJECT(ul_mac_grant); + ZERO_OBJECT(ul_action); + + /** Calculate RSSI on the input signal before generating the output */ + + // Average RSSI over all symbols (make sure SF length is non-zero) + float rssi_dbm = SRSLTE_SF_LEN_PRB(cell.nof_prb) > 0 ? (10*log10(srslte_vec_avg_power_cf(signal_buffer[0], SRSLTE_SF_LEN_PRB(cell.nof_prb))) + 30) : 0; + if (isnormal(rssi_dbm)) { + phy->avg_rssi_dbm = SRSLTE_VEC_EMA(rssi_dbm, phy->avg_rssi_dbm, phy->args->snr_ema_coeff); + } + + bool mch_decoded = false; + srslte_ra_dl_grant_t mch_grant; + + + // Call feedback loop for chest + if (chest_loop && ((1<<(tti%10)) & phy->args->cfo_ref_mask)) { + chest_loop->set_cfo(srslte_chest_dl_get_cfo(&ue_dl.chest)); + } + bool chest_ok = false; + bool snr_th_ok = false; + + /***** Downlink Processing *******/ + + + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) { + /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ + chest_ok = extract_fft_and_pdcch_llr(sf_cfg); + + snr_th_ok = 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest))>1.0; + + if (chest_ok && snr_th_ok) { + + /***** Downlink Processing *******/ + + /* PDCCH DL + PDSCH */ + dl_grant_available = decode_pdcch_dl(&dl_mac_grant); + if(dl_grant_available) { + /* Send grant to MAC and get action for this TB */ + phy->mac->new_grant_dl(dl_mac_grant, &dl_action); + + /* Set DL ACKs to default */ + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + dl_ack[tb] = dl_action.default_ack[tb]; + } + + /* Decode PDSCH if instructed to do so */ + if (dl_action.decode_enabled[0] || dl_action.decode_enabled[1]) { + decode_pdsch(&dl_action.phy_grant.dl, dl_action.payload_ptr, + dl_action.softbuffers, dl_action.rv, dl_action.rnti, + dl_mac_grant.pid, dl_ack); + } + if (dl_action.generate_ack_callback) { + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (dl_action.decode_enabled[tb]) { + phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); + dl_ack[tb] = dl_action.generate_ack_callback(dl_action.generate_ack_callback_arg); + Debug("Calling generate ACK callback for TB %d returned=%d\n", tb, dl_ack[tb]); + } + } + } + Debug("dl_ack={%d, %d}, generate_ack=%d\n", dl_ack[0], dl_ack[1], dl_action.generate_ack); + if (dl_action.generate_ack) { + set_uci_ack(dl_ack, dl_mac_grant.tb_en); + } + } + } + + } else if(SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type) { + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, sf_cfg.non_mbsfn_region_length); + + /* Do FFT and extract PDCCH LLR, or quit if no actions are required in this subframe */ + if (extract_fft_and_pdcch_llr(sf_cfg)) { + + dl_grant_available = decode_pdcch_dl(&dl_mac_grant); + phy->mac->new_grant_dl(dl_mac_grant, &dl_action); + + /* Set DL ACKs to default */ + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + dl_ack[tb] = dl_action.default_ack[tb]; + } + if(sf_cfg.mbsfn_decode) { + + mch_grant.sf_type = SRSLTE_SF_MBSFN; + mch_grant.mcs[0].idx = sf_cfg.mbsfn_mcs; + mch_grant.tb_en[0] = true; + for(uint32_t i=1;imac->new_mch_dl(mch_grant, &dl_action); + srslte_softbuffer_rx_reset_tbs(dl_action.softbuffers[0], mch_grant.mcs[0].tbs); + Debug("TBS=%d, Softbuffer max_cb=%d\n", mch_grant.mcs[0].tbs, dl_action.softbuffers[0]->max_cb); + if(dl_action.decode_enabled[0]) { + mch_decoded = decode_pmch(&mch_grant, dl_action.payload_ptr[0], dl_action.softbuffers[0], sf_cfg.mbsfn_area_id); + } + } + } + } + + // Process RAR before UL to enable zero-delay Msg3 + bool rar_delivered = false; + if (HARQ_DELAY_MS == MSG3_DELAY_MS && dl_mac_grant.rnti_type == SRSLTE_RNTI_RAR) { + rar_delivered = true; + phy->mac->tb_decoded(dl_ack[0], 0, dl_mac_grant.rnti_type, dl_mac_grant.pid); + } + + // Decode PHICH + bool ul_ack = false; + bool ul_ack_available = decode_phich(&ul_ack); + + + /***** Uplink Processing + Transmission *******/ + + bool signal_ready = false; + cf_t *signal_ptr = NULL; + + /* Transmit PRACH if pending, or PUSCH/PUCCH otherwise */ + if (prach_ptr) { + signal_ready = true; + signal_ptr = prach_ptr; + } else { + /* Generate SR if required*/ + set_uci_sr(); + + /* Check if we have UL grant. ul_phy_grant will be overwritten by new grant */ + ul_grant_available = decode_pdcch_ul(&ul_mac_grant); + + /* Generate CQI reports if required, note that in case both aperiodic + and periodic ones present, only aperiodic is sent (36.213 section 7.2) */ + if (ul_grant_available && ul_mac_grant.has_cqi_request) { + set_uci_aperiodic_cqi(); + } else { + set_uci_periodic_cqi(); + } + + /* TTI offset for UL */ + ul_action.tti_offset = HARQ_DELAY_MS; + + /* Send UL grant or HARQ information (from PHICH) to MAC */ + if (ul_grant_available && ul_ack_available) { + phy->mac->new_grant_ul_ack(ul_mac_grant, ul_ack, &ul_action); + } else if (ul_grant_available && !ul_ack_available) { + phy->mac->new_grant_ul(ul_mac_grant, &ul_action); + } else if (!ul_grant_available && ul_ack_available) { + phy->mac->harq_recv(tti, ul_ack, &ul_action); + } + + /* Set UL CFO before transmission */ + srslte_ue_ul_set_cfo(&ue_ul, cfo); + + /* Transmit PUSCH, PUCCH or SRS */ + if (ul_action.tx_enabled) { + encode_pusch(&ul_action.phy_grant.ul, ul_action.payload_ptr[0], ul_action.current_tx_nb, + &ul_action.softbuffers[0], ul_action.rv[0], ul_action.rnti, ul_mac_grant.is_from_rar); + signal_ready = true; + if (ul_action.expect_ack) { + phy->set_pending_ack(TTI_RX_ACK(tti), ue_ul.pusch_cfg.grant.n_prb_tilde[0], ul_action.phy_grant.ul.ncs_dmrs); + } + + } else if (dl_action.generate_ack || uci_data.scheduling_request || uci_data.uci_cqi_len > 0 || uci_data.uci_ri_len > 0) { + encode_pucch(); + signal_ready = true; + } else if (srs_is_ready_to_send()) { + encode_srs(); + signal_ready = true; + } + signal_ptr = signal_buffer[0]; + } + + + tr_log_end(); + + if (next_offset > 0) { + phy->worker_end(tx_tti, signal_ready, signal_ptr, SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time); + } else { + phy->worker_end(tx_tti, signal_ready, &signal_ptr[-next_offset], SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time); + } + + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) { + if (!dl_action.generate_ack_callback) { + if (dl_mac_grant.rnti_type == SRSLTE_RNTI_PCH && dl_action.decode_enabled[0]) { + if (dl_ack[0]) { + phy->mac->pch_decoded_ok(dl_mac_grant.n_bytes[0]); + } + } else if (!rar_delivered) { + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + if (dl_action.decode_enabled[tb]) { + phy->mac->tb_decoded(dl_ack[tb], tb, dl_mac_grant.rnti_type, dl_mac_grant.pid); + } + } + } + } + } else if (SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type && sf_cfg.mbsfn_decode) { + if(mch_decoded) { + phy->mac->mch_decoded_ok(mch_grant.mcs[0].tbs/8); + } else if(sf_cfg.is_mcch) { + //release lock in phch_common + phy->set_mch_period_stop(0); + } + } + if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type){ + update_measurements(); + } + + if (chest_ok) { + if (phy->avg_rsrp_dbm > -130.0 && phy->avg_snr_db_cqi > -6.0) { + log_h->debug("SNR=%.1f dB, RSRP=%.1f dBm sync=in-sync from channel estimator\n", + phy->avg_snr_db_cqi, phy->avg_rsrp_dbm); + chest_loop->in_sync(); + } else { + log_h->warning("SNR=%.1f dB RSRP=%.1f dBm, sync=out-of-sync from channel estimator\n", + phy->avg_snr_db_cqi, phy->avg_rsrp_dbm); + chest_loop->out_of_sync(); + } + } + + pthread_mutex_unlock(&mutex); + + /* Tell the plotting thread to draw the plots */ +#ifdef ENABLE_GUI + if ((int) get_id() == plot_worker_id) { + sem_post(&plot_sem); + } +#endif +} + +void phch_worker::compute_ri(uint8_t *ri, uint8_t *pmi, float *sinr) { + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3) { + if (ue_dl.nof_rx_antennas > 1) { + /* If 2 ort more receiving antennas, select RI */ + float cn = 0.0f; + srslte_ue_dl_ri_select(&ue_dl, ri, &cn); + if (ri) { + Debug("TM3 RI select %d layers, κ=%fdB\n", (*ri) + 1, cn); + } + } else { + /* If only one receiving antenna, force RI for 1 layer */ + if (ri) { + *ri = 0; + } + } + uci_data.uci_ri_len = 1; + } else if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + if (sinr) { + srslte_ue_dl_ri_pmi_select(&ue_dl, ri, pmi, sinr); + Debug("TM4 ri=%d; pmi=%d; SINR=%.1fdB\n", ue_dl.ri, ue_dl.pmi[ue_dl.ri], 10*log10f(ue_dl.sinr[ue_dl.ri][ue_dl.pmi[ue_dl.ri]])); + } + } +} + + + +bool phch_worker::extract_fft_and_pdcch_llr(subframe_cfg_t sf_cfg) { + bool decode_pdcch = true; + + // Do always channel estimation to keep track of out-of-sync and send measurements to RRC + + // Setup estimator filter + srslte_chest_dl_set_smooth_filter_gauss(&ue_dl.chest, + phy->args->estimator_fil_order, + phy->args->estimator_fil_stddev); + srslte_chest_dl_set_smooth_filter_auto(&ue_dl.chest, phy->args->estimator_fil_auto); + + if (!phy->args->snr_estim_alg.compare("refs")) { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_REFS); + } else if (!phy->args->snr_estim_alg.compare("empty")) { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_EMPTY); + } else { + srslte_chest_dl_set_noise_alg(&ue_dl.chest, SRSLTE_NOISE_ALG_PSS); + } + + + int decode_fft = 0; + if(SUBFRAME_TYPE_MBSFN == sf_cfg.sf_type) { + srslte_ue_dl_set_non_mbsfn_region(&ue_dl, sf_cfg.non_mbsfn_region_length); + decode_fft = srslte_ue_dl_decode_fft_estimate_mbsfn(&ue_dl, tti%10, &cfi, SRSLTE_SF_MBSFN); + }else{ + decode_fft = srslte_ue_dl_decode_fft_estimate(&ue_dl, tti%10, &cfi); + } + if (decode_fft < 0) { + Error("Getting PDCCH FFT estimate\n"); + return false; + } + + chest_done = true; + + + if (chest_done && decode_pdcch) { /* and not in DRX mode */ + + float noise_estimate = phy->avg_noise; + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + if (srslte_pdcch_extract_llr_multi(&ue_dl.pdcch, ue_dl.sf_symbols_m, ue_dl.ce_m, noise_estimate, tti%10, cfi)) { + Error("Extracting PDCCH LLR\n"); + return false; + } + } + return (decode_pdcch || phy->get_pending_ack(tti)); +} + + + + + + + + + +/********************* Downlink processing functions ****************************/ + +bool phch_worker::decode_pdcch_dl(srsue::mac_interface_phy::mac_grant_t* grant) +{ + char timestr[64]; + timestr[0]='\0'; + + dl_rnti = phy->get_dl_rnti(tti); + if (dl_rnti) { + + srslte_rnti_type_t type = phy->get_dl_rnti_type(); + + srslte_dci_msg_t dci_msg; + srslte_ra_dl_dci_t dci_unpacked; + + if (type == SRSLTE_RNTI_RAR) { + Debug("Looking for RNTI=0x%x\n", dl_rnti); + } + + if (srslte_ue_dl_find_dl_dci_type(&ue_dl, phy->config->dedicated.antenna_info_explicit_value.tx_mode, cfi, tti%10, + dl_rnti, type, &dci_msg) != 1) { + if (type == SRSLTE_RNTI_RAR) { + Debug("RAR not found, SNR=%.1f dB, tti=%d, cfi=%d, tx_mode=%d, cell_id=%d\n", + 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)), tti, cfi, + phy->config->dedicated.antenna_info_explicit_value.tx_mode, cell.id); + } + return false; + } + + if (srslte_dci_msg_to_dl_grant(&dci_msg, dl_rnti, cell.nof_prb, cell.nof_ports, &dci_unpacked, &grant->phy_grant.dl)) { + Error("Converting DCI message to DL grant\n"); + return false; + } + + grant->pid = ASYNC_DL_SCHED?dci_unpacked.harq_process:(UL_PIDOF(TTI_TX(tti))); + + // Set last TBS for this TB (pid) in case of mcs>28 (7.1.7.2 of 36.213) + for (int i=0;iphy_grant.dl.mcs[i].idx > 28) { + grant->phy_grant.dl.mcs[i].tbs = phy->last_dl_tbs[grant->pid][i]; + } + if(grant->phy_grant.dl.mcs[i].tbs < 0) { + Info("Invalid TBS size for PDSCH grant\n"); + grant->phy_grant.dl.mcs[i].tbs = 0; + } + // save it + phy->last_dl_tbs[grant->pid][i] = grant->phy_grant.dl.mcs[i].tbs; + } + + /* Fill MAC grant structure */ + grant->ndi[0] = dci_unpacked.ndi; + grant->ndi[1] = dci_unpacked.ndi_1; + grant->n_bytes[0] = grant->phy_grant.dl.mcs[0].tbs / (uint32_t) 8; + grant->n_bytes[1] = grant->phy_grant.dl.mcs[1].tbs / (uint32_t) 8; + grant->tti = tti; + grant->rv[0] = dci_unpacked.rv_idx; + grant->rv[1] = dci_unpacked.rv_idx_1; + grant->rnti = dl_rnti; + grant->rnti_type = type; + grant->last_tti = 0; + grant->tb_en[0] = dci_unpacked.tb_en[0]; + grant->tb_en[1] = dci_unpacked.tb_en[1]; + grant->tb_cw_swap = dci_unpacked.tb_cw_swap; // FIXME: tb_cw_swap not supported + + last_dl_pdcch_ncce = srslte_ue_dl_get_ncce(&ue_dl); + + char hexstr[512]; + hexstr[0]='\0'; + if (log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_vec_sprint_hex(hexstr, sizeof(hexstr), dci_msg.data, dci_msg.nof_bits); + } + Info("PDCCH: DL DCI %s cce_index=%2d, L=%d, n_data_bits=%d, tpc_pucch=%d, hex=%s\n", srslte_dci_format_string(dci_msg.format), + last_dl_pdcch_ncce, (1<tb_en[tb] && (rv[tb] < 0 || rv[tb] > 3)) { + valid_config = false; + Error("Wrong RV (%d) for TB index %d\n", rv[tb], tb); + } + } + + uint32_t nof_tb = SRSLTE_RA_DL_GRANT_NOF_TB(grant); + switch(phy->config->dedicated.antenna_info_explicit_value.tx_mode) { + /* Implemented Tx Modes */ + case LIBLTE_RRC_TRANSMISSION_MODE_1: + mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + if (nof_tb != 1) { + Error("Wrong number of transport blocks (%d) for single antenna.", nof_tb); + valid_config = false; + } + break; + case LIBLTE_RRC_TRANSMISSION_MODE_2: + if (cell.nof_ports > 1) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else { + mimo_type = SRSLTE_MIMO_TYPE_SINGLE_ANTENNA; + } + if (nof_tb != 1) { + Error("Wrong number of transport blocks (%d) for transmit diversity.", nof_tb); + valid_config = false; + } + break; + case LIBLTE_RRC_TRANSMISSION_MODE_3: + if (nof_tb == 1) { + mimo_type = SRSLTE_MIMO_TYPE_TX_DIVERSITY; + } else if (ue_dl.nof_rx_antennas > 1 && nof_tb == 2) { + mimo_type = SRSLTE_MIMO_TYPE_CDD; + } else { + Error("Wrong combination of antennas (%d) or transport blocks (%d) for TM3\n", ue_dl.nof_rx_antennas, + nof_tb); + valid_config = false; + } + break; + case LIBLTE_RRC_TRANSMISSION_MODE_4: + if (nof_tb == 1) { + mimo_type = (grant->pinfo == 0) ? SRSLTE_MIMO_TYPE_TX_DIVERSITY : SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } else if (ue_dl.nof_rx_antennas > 1 && nof_tb == 2) { + mimo_type = SRSLTE_MIMO_TYPE_SPATIAL_MULTIPLEX; + } else { + Error("Wrong combination of antennas (%d) or transport blocks (%d) for TM3\n", ue_dl.nof_rx_antennas, + nof_tb); + valid_config = false; + } + break; + + /* Not implemented cases */ + case LIBLTE_RRC_TRANSMISSION_MODE_5: + case LIBLTE_RRC_TRANSMISSION_MODE_6: + case LIBLTE_RRC_TRANSMISSION_MODE_7: + case LIBLTE_RRC_TRANSMISSION_MODE_8: + Error("Not implemented Tx mode (%d)\n", phy->config->dedicated.antenna_info_explicit_value.tx_mode); + break; + + /* Error cases */ + case LIBLTE_RRC_TRANSMISSION_MODE_N_ITEMS: + default: + Error("Wrong Tx mode (%d)\n", phy->config->dedicated.antenna_info_explicit_value.tx_mode); + valid_config = false; + } + + /* Set power allocation according to 3GPP 36.213 clause 5.2 Downlink power allocation */ + float rho_a = 1.0f, rho_b = 1.0f; + if (phy->config->dedicated.pdsch_cnfg_ded < LIBLTE_RRC_PDSCH_CONFIG_P_A_N_ITEMS) { + float rho_a_db = liblte_rrc_pdsch_config_p_a_num[(int) phy->config->dedicated.pdsch_cnfg_ded]; + rho_a = powf(10.0f, rho_a_db / 20.0f) * ((cell.nof_ports == 1) ? 1.0f : sqrtf(2.0f)); + } + if (phy->config->common.pdsch_cnfg.p_b < 4) { + uint32_t idx0 = (cell.nof_ports == 1) ? 0 : 1; + float cell_specific_ratio = pdsch_cfg_cell_specific_ratio_table[idx0][phy->config->common.pdsch_cnfg.p_b]; + rho_b = sqrtf(cell_specific_ratio); + } + srslte_ue_dl_set_power_alloc(&ue_dl, rho_a, rho_b); + + Debug("DL Buffer TTI %d: Decoding PDSCH\n", tti); + + /* Setup PDSCH configuration for this CFI, SFIDX and RVIDX */ + if (valid_config) { + if (!srslte_ue_dl_cfg_grant(&ue_dl, grant, cfi, tti%10, rv, mimo_type)) { + if ((ue_dl.pdsch_cfg.grant.mcs[0].mod > 0 && ue_dl.pdsch_cfg.grant.mcs[0].tbs >= 0) || + (ue_dl.pdsch_cfg.grant.mcs[1].mod > 0 && ue_dl.pdsch_cfg.grant.mcs[1].tbs >= 0)) { + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + /* Set decoder iterations */ + if (phy->args->pdsch_max_its > 0) { + srslte_pdsch_set_max_noi(&ue_dl.pdsch, phy->args->pdsch_max_its); + } + + + #ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); + #endif + ret = srslte_pdsch_decode(&ue_dl.pdsch, &ue_dl.pdsch_cfg, softbuffers, ue_dl.sf_symbols_m, + ue_dl.ce_m, noise_estimate, rnti, payload, acks); + if (ret) { + Error("ERROR: Decoding PDSCH\n"); + } + #ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); + #endif + + char pinfo_str[16] = {0}; + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + snprintf(pinfo_str, 15, ", pinfo=%x", grant->pinfo); + } + + snprintf(commonstr, 128, "PDSCH: l_crb=%2d, harq=%d, snr=%.1f dB, tx_scheme=%s%s", grant->nof_prb, harq_pid, + 10 * log10(srslte_chest_dl_get_snr(&ue_dl.chest)), srslte_mimotype2str(mimo_type), pinfo_str); + + for (int i=0;itb_en[i]) { + snprintf(tbstr[i], 128, ", CW%d: tbs=%d, mcs=%d, rv=%d, crc=%s, it=%d", + i, grant->mcs[i].tbs/8, grant->mcs[i].idx, rv[i], acks[i] ? "OK" : "KO", + srslte_pdsch_last_noi_cw(&ue_dl.pdsch, i)); + } + } + + Info("%s%s%s%s\n", commonstr, grant->tb_en[0]?tbstr[0]:"", grant->tb_en[1]?tbstr[1]:"", timestr); + + // Store metrics + dl_metrics.mcs = grant->mcs[0].idx; + float niters = srslte_pdsch_last_noi(&ue_dl.pdsch); + if (niters) { + dl_metrics.turbo_iters = niters; + } + } else { + Warning("Received grant for TBS=0\n"); + } + } else { + Error("Error configuring DL grant\n"); + ret = SRSLTE_ERROR; + } + } else { + Error("Error invalid DL config\n"); + ret = SRSLTE_ERROR; + } + return ret; +} + +bool phch_worker::decode_pmch(srslte_ra_dl_grant_t *grant, uint8_t *payload, + srslte_softbuffer_rx_t* softbuffer, uint16_t mbsfn_area_id) +{ + char timestr[64]; + timestr[0]='\0'; + + Debug("DL Buffer TTI %d: Decoding PMCH\n", tti); + /* Setup PMCH configuration */ + srslte_ue_dl_set_mbsfn_area_id(&ue_dl, mbsfn_area_id); + + if (!srslte_ue_dl_cfg_grant(&ue_dl, grant, cfi, tti%10, SRSLTE_PMCH_RV, SRSLTE_MIMO_TYPE_SINGLE_ANTENNA)) { + if (ue_dl.pmch_cfg.grant.mcs[0].mod > 0 && ue_dl.pmch_cfg.grant.mcs[0].tbs >= 0) { + + Debug("Decoding PMCH SF: %d, MBSFN area ID: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, C_prb=%d, cfi=%d\n", + ue_dl.pmch_cfg.sf_idx, mbsfn_area_id, srslte_mod_string(ue_dl.pmch_cfg.grant.mcs[0].mod), ue_dl.pmch_cfg.grant.mcs[0].tbs, ue_dl.pmch_cfg.nbits[0].nof_re, + ue_dl.pmch_cfg.nbits[0].nof_bits, 0, ue_dl.pmch_cfg.grant.nof_prb, ue_dl.pmch_cfg.nbits[0].lstart-1); + + float noise_estimate = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + + if (!phy->args->equalizer_mode.compare("zf")) { + noise_estimate = 0; + } + + /* Set decoder iterations */ + // TODO: Add separate arg for pmch_max_its + if (phy->args->pdsch_max_its > 0) { + srslte_sch_set_max_noi(&ue_dl.pmch.dl_sch, phy->args->pdsch_max_its); + } + +#ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); +#endif + + bool ack = srslte_pmch_decode_multi(&ue_dl.pmch, &ue_dl.pmch_cfg, softbuffer, ue_dl.sf_symbols_m, + ue_dl.ce_m, noise_estimate, mbsfn_area_id, payload) == 0; + +#ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); +#endif + + Info("PMCH: l_crb=%2d, tbs=%d, mcs=%d, crc=%s, snr=%.1f dB, n_iter=%d%s\n", + grant->nof_prb, + grant->mcs[0].tbs/8, grant->mcs[0].idx, + ack?"OK":"KO", + 10*log10(srslte_chest_dl_get_snr(&ue_dl.chest)), + srslte_pmch_last_noi(&ue_dl.pmch), + timestr); + + //printf("tti=%d, cfo=%f\n", tti, cfo*15000); + //srslte_vec_save_file("pdsch", signal_buffer, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + + // Store metrics + dl_metrics.mcs = grant->mcs[0].idx; + + return ack; + } else { + Warning("Received grant for TBS=0\n"); + } + } else { + Error("Error configuring DL grant\n"); + } + return true; +} + +bool phch_worker::decode_phich(bool *ack) +{ + uint32_t I_lowest, n_dmrs; + if (phy->get_pending_ack(tti, &I_lowest, &n_dmrs)) { + if (ack) { + *ack = srslte_ue_dl_decode_phich(&ue_dl, tti%10, I_lowest, n_dmrs); + Info("PHICH: hi=%d, I_lowest=%d, n_dmrs=%d\n", *ack, I_lowest, n_dmrs); + } + phy->reset_pending_ack(tti); + return true; + } else { + return false; + } +} + + + + +/********************* Uplink processing functions ****************************/ + +bool phch_worker::decode_pdcch_ul(mac_interface_phy::mac_grant_t* grant) +{ + char timestr[64]; + timestr[0]='\0'; + + phy->reset_pending_ack(TTI_RX_ACK(tti)); + + srslte_dci_msg_t dci_msg; + srslte_ra_ul_dci_t dci_unpacked; + srslte_dci_rar_grant_t rar_grant; + srslte_rnti_type_t type = phy->get_ul_rnti_type(); + + bool ret = false; + if (phy->get_pending_rar(tti, &rar_grant)) { + + if (srslte_dci_rar_to_ul_grant(&rar_grant, cell.nof_prb, pusch_hopping.hopping_offset, + &dci_unpacked, &grant->phy_grant.ul)) + { + Error("Converting RAR message to UL grant\n"); + return false; + } + grant->rnti_type = SRSLTE_RNTI_TEMP; + grant->is_from_rar = true; + grant->has_cqi_request = false; // In contention-based Random Access CQI request bit is reserved + Debug("RAR grant found for TTI=%d\n", tti); + ret = true; + } else { + ul_rnti = phy->get_ul_rnti(tti); + if (ul_rnti) { + if (srslte_ue_dl_find_ul_dci(&ue_dl, cfi, tti%10, ul_rnti, &dci_msg) != 1) { + return false; + } + + if (srslte_dci_msg_to_ul_grant(&dci_msg, cell.nof_prb, pusch_hopping.hopping_offset, + &dci_unpacked, &grant->phy_grant.ul, tti)) + { + Error("Converting DCI message to UL grant\n"); + return false; + } + grant->rnti_type = type; + grant->is_from_rar = false; + grant->has_cqi_request = dci_unpacked.cqi_request; + ret = true; + + char hexstr[512]; + hexstr[0]='\0'; + if (log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_vec_sprint_hex(hexstr, sizeof(hexstr), dci_msg.data, dci_msg.nof_bits); + } + // Change to last_location_ul + Info("PDCCH: UL DCI Format0 cce_index=%d, L=%d, n_data_bits=%d, tpc_pusch=%d, hex=%s\n", + ue_dl.last_location_ul.ncce, (1<phy_grant.ul.mcs.tbs==0) { + Info("Received PUSCH grant with empty data\n"); + } + } + } + + + // Handle Format0 adaptive retx + if (ret) { + // Use last TBS for this TB in case of mcs>28 + if (grant->phy_grant.ul.mcs.idx > 28 && grant->phy_grant.ul.mcs.mod == SRSLTE_MOD_LAST) { + // Make sure we received a grant in the previous TTI for this PID + grant->phy_grant.ul.mcs.tbs = phy->last_ul_tbs[UL_PIDOF(TTI_TX(tti))]; + grant->phy_grant.ul.mcs.mod = phy->last_ul_mod[UL_PIDOF(TTI_TX(tti))]; + grant->phy_grant.ul.mcs.idx = phy->last_ul_idx[UL_PIDOF(TTI_TX(tti))]; + grant->phy_grant.ul.Qm = srslte_mod_bits_x_symbol(grant->phy_grant.ul.mcs.mod); + } + } + if (ret) { + phy->last_ul_tbs[UL_PIDOF(TTI_TX(tti))] = grant->phy_grant.ul.mcs.tbs; + phy->last_ul_mod[UL_PIDOF(TTI_TX(tti))] = grant->phy_grant.ul.mcs.mod; + phy->last_ul_idx[UL_PIDOF(TTI_TX(tti))] = grant->phy_grant.ul.mcs.idx; + phy->last_ul_tti[UL_PIDOF(TTI_TX(tti))] = TTI_RX_ACK(tti); + /* Limit UL modulation if not supported by the UE or disabled by higher layers */ + if (!phy->config->enable_64qam) { + if (grant->phy_grant.ul.mcs.mod >= SRSLTE_MOD_64QAM) { + grant->phy_grant.ul.mcs.mod = SRSLTE_MOD_16QAM; + grant->phy_grant.ul.Qm = 4; + } + } + } + + /* Make sure the grant is valid */ + if (ret && !srslte_dft_precoding_valid_prb(grant->phy_grant.ul.L_prb) && grant->phy_grant.ul.L_prb <= cell.nof_prb) { + Warning("Received invalid UL grant. L=%d\n", grant->phy_grant.ul.L_prb); + ret = false; + } + + if (ret) { + grant->ndi[0] = dci_unpacked.ndi; + grant->pid = 0; // This is computed by MAC from TTI + grant->n_bytes[0] = grant->phy_grant.ul.mcs.tbs / (uint32_t) 8; + grant->tti = tti; + grant->rnti = ul_rnti; + grant->rv[0] = dci_unpacked.rv_idx; + if (SRSLTE_VERBOSE_ISINFO()) { + srslte_ra_pusch_fprint(stdout, &dci_unpacked, cell.nof_prb); + } + } + + return ret; +} + +void phch_worker::reset_uci() +{ + ZERO_OBJECT(uci_data); + ZERO_OBJECT(cqi_report); +} + +void phch_worker::set_uci_ack(bool ack[SRSLTE_MAX_CODEWORDS], bool tb_en[SRSLTE_MAX_CODEWORDS]) +{ + /* Map ACK according to 3GPP 36.212 clause 5.2.3.1 */ + uint32_t nof_ack = 0; + for (uint32_t tb = 0; tb < SRSLTE_MAX_CODEWORDS; tb++) { + if (tb_en[tb]) { + ((nof_ack == 0)?uci_data.uci_ack:uci_data.uci_ack_2) = (uint8_t)(ack[tb]?1:0); + nof_ack++; + } + } + uci_data.uci_ack_len = nof_ack; +} + +void phch_worker::set_uci_sr() +{ + uci_data.scheduling_request = false; + if (phy->sr_enabled && sr_configured) { + uint32_t sr_tx_tti = TTI_TX(tti); + // Get I_sr parameter + if (srslte_ue_ul_sr_send_tti(I_sr, sr_tx_tti)) { + Info("PUCCH: SR transmission at TTI=%d, I_sr=%d\n", sr_tx_tti, I_sr); + uci_data.scheduling_request = true; + phy->sr_last_tx_tti = sr_tx_tti; + phy->sr_enabled = false; + } + } +} + +void phch_worker::set_uci_periodic_cqi() +{ + int cqi_fixed = phy->args->cqi_fixed; + int cqi_max = phy->args->cqi_max; + + float sinr = ue_dl.sinr[phy->last_ri & SRSLTE_MAX_LAYERS][phy->last_pmi % SRSLTE_MAX_CODEBOOKS]; + + if (period_cqi.configured && rnti_is_set) { + if (period_cqi.ri_idx_present && srslte_ri_send(period_cqi.pmi_idx, period_cqi.ri_idx, TTI_TX(tti))) { + /* Compute RI, PMI and SINR */ + compute_ri(&phy->last_ri, &phy->last_pmi, &sinr); + uci_data.uci_ri = phy->last_ri; + uci_data.uci_ri_len = 1; + uci_data.ri_periodic_report = true; + Debug("PUCCH: Periodic ri=%d, SINR=%.1f\n", phy->last_ri, sinr); + } else if (srslte_cqi_send(period_cqi.pmi_idx, TTI_TX(tti))) { + compute_ri(NULL, NULL, NULL); + phy->last_pmi = (uint8_t) ue_dl.pmi[phy->last_ri % SRSLTE_MAX_LAYERS]; + + ZERO_OBJECT(cqi_report); + + if (period_cqi.format_is_subband) { + // TODO: Implement subband periodic reports + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND; + cqi_report.subband.subband_cqi = srslte_cqi_from_snr(phy->avg_snr_db_cqi); + cqi_report.subband.subband_label = 0; + log_h->console("Warning: Subband CQI periodic reports not implemented\n"); + Debug("PUCCH: Periodic CQI=%d, SNR=%.1f dB\n", cqi_report.subband.subband_cqi, phy->avg_snr_db_cqi); + } else { + cqi_report.type = SRSLTE_CQI_TYPE_WIDEBAND; + if (cqi_fixed >= 0) { + cqi_report.wideband.wideband_cqi = cqi_fixed; + } else { + cqi_report.wideband.wideband_cqi = srslte_cqi_from_snr(phy->avg_snr_db_cqi); + } + if (cqi_max >= 0 && cqi_report.wideband.wideband_cqi > cqi_max) { + cqi_report.wideband.wideband_cqi = cqi_max; + } + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + cqi_report.wideband.pmi_present = true; + cqi_report.wideband.pmi = phy->last_pmi; + cqi_report.wideband.rank_is_not_one = (phy->last_ri != 0); + } + Debug("PUCCH: Periodic CQI=%d, SNR=%.1f dB\n", cqi_report.wideband.wideband_cqi, phy->avg_snr_db_cqi); + } + uci_data.uci_cqi_len = (uint32_t) srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + uci_data.uci_ri = phy->last_ri; + uci_data.uci_ri_len = 0; + rar_cqi_request = false; + } + } +} + +void phch_worker::set_uci_aperiodic_cqi() +{ + float sinr_db = ue_dl.sinr[phy->last_ri % SRSLTE_MAX_LAYERS][phy->last_pmi%SRSLTE_MAX_CODEBOOKS]; + + if (phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic_present) { + /* Compute RI, PMI and SINR */ + compute_ri(&phy->last_ri, &phy->last_pmi, &sinr_db); + + switch(phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic) { + case LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30: + /* only Higher Layer-configured subband feedback support right now, according to TS36.213 section 7.2.1 + - A UE shall report a wideband CQI value which is calculated assuming transmission on set S subbands + - The UE shall also report one subband CQI value for each set S subband. The subband CQI + value is calculated assuming transmission only in the subband + - Both the wideband and subband CQI represent channel quality for the first codeword, + even when RI>1 + - For transmission mode 3 the reported CQI values are calculated conditioned on the + reported RI. For other transmission modes they are reported conditioned on rank 1. + */ + if (rnti_is_set) { + ZERO_OBJECT(cqi_report); + + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + cqi_report.subband_hl.wideband_cqi_cw0 = srslte_cqi_from_snr(phy->avg_snr_db_cqi); + + // TODO: implement subband CQI properly + cqi_report.subband_hl.subband_diff_cqi_cw0 = 0; // Always report zero offset on all subbands + cqi_report.subband_hl.N = (cell.nof_prb > 7) ? (uint32_t) srslte_cqi_hl_get_no_subbands(cell.nof_prb) : 0; + + int cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + if (cqi_len < 0) { + Error("Error packing CQI value (Aperiodic reporting mode RM30)."); + return; + } + uci_data.uci_cqi_len = (uint32_t) cqi_len; + + char cqi_str[SRSLTE_CQI_STR_MAX_CHAR] = {0}; + srslte_cqi_to_str(uci_data.uci_cqi, uci_data.uci_cqi_len, cqi_str, SRSLTE_CQI_STR_MAX_CHAR); + + /* Set RI = 1 */ + if (phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_3 || + phy->config->dedicated.antenna_info_explicit_value.tx_mode == LIBLTE_RRC_TRANSMISSION_MODE_4) { + uci_data.uci_ri = phy->last_ri; + uci_data.uci_ri_len = 1; + } else { + uci_data.uci_ri_len = 0; + } + + Info("PUSCH: Aperiodic RM30 CQI=%s, %sSNR=%.1f dB, for %d subbands\n", + cqi_str, (uci_data.uci_ri_len)?((uci_data.uci_ri == 0)?"ri=0, ":"ri=1, "):"", phy->avg_snr_db_cqi, cqi_report.subband_hl.N); + } + break; + case LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM31: + /* only Higher Layer-configured subband feedback support right now, according to TS36.213 section 7.2.1 + - A single precoding matrix is selected from the codebook subset assuming transmission on set S subbands + - A UE shall report one subband CQI value per codeword for each set S subband which are calculated assuming + the use of the single precoding matrix in all subbands and assuming transmission in the corresponding + subband. + - A UE shall report a wideband CQI value per codeword which is calculated assuming the use of the single + precoding matrix in all subbands and transmission on set S subbands + - The UE shall report the single selected precoding matrix indicator. + - For transmission mode 4 the reported PMI and CQI values are calculated conditioned on the reported RI. For + other transmission modes they are reported conditioned on rank 1. + */ + if (rnti_is_set) { + /* Fill CQI Report */ + srslte_cqi_value_t cqi_report; + ZERO_OBJECT(cqi_report); + + cqi_report.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + + cqi_report.subband_hl.wideband_cqi_cw0 = srslte_cqi_from_snr(sinr_db); + cqi_report.subband_hl.subband_diff_cqi_cw0 = 0; // Always report zero offset on all subbands + + if (phy->last_ri > 0) { + cqi_report.subband_hl.rank_is_not_one = true; + cqi_report.subband_hl.wideband_cqi_cw1 = srslte_cqi_from_snr(sinr_db); + cqi_report.subband_hl.subband_diff_cqi_cw1 = 0; // Always report zero offset on all subbands + } + + cqi_report.subband_hl.pmi = phy->last_pmi; + cqi_report.subband_hl.pmi_present = true; + cqi_report.subband_hl.four_antenna_ports = (cell.nof_ports == 4); + + // TODO: implement subband CQI properly + cqi_report.subband_hl.N = (uint32_t) ((cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(cell.nof_prb) : 0); + + int cqi_len = srslte_cqi_value_pack(&cqi_report, uci_data.uci_cqi); + if (cqi_len < 0) { + Error("Error packing CQI value (Aperiodic reporting mode RM31)."); + return; + } + uci_data.uci_cqi_len = (uint32_t) cqi_len; + uci_data.uci_ri_len = 1; + uci_data.uci_ri = phy->last_ri; + + char cqi_str[SRSLTE_CQI_STR_MAX_CHAR] = {0}; + srslte_cqi_to_str(uci_data.uci_cqi, uci_data.uci_cqi_len, cqi_str, SRSLTE_CQI_STR_MAX_CHAR); + + if (cqi_report.subband_hl.rank_is_not_one) { + Info("PUSCH: Aperiodic RM31 ri~1, CQI=%02d/%02d, SINR=%2.1f/%2.1fdB, pmi=%d for %d subbands\n", + cqi_report.subband_hl.wideband_cqi_cw0, cqi_report.subband_hl.wideband_cqi_cw1, + sinr_db, sinr_db, phy->last_pmi, cqi_report.subband_hl.N); + } else { + Info("PUSCH: Aperiodic RM31 ri=1, CQI=%02d, SINR=%2.1f, pmi=%d for %d subbands\n", + cqi_report.subband_hl.wideband_cqi_cw0, + sinr_db, phy->last_pmi, cqi_report.subband_hl.N); + } + } + break; + default: + Warning("Received CQI request but mode %s is not supported\n", + liblte_rrc_cqi_report_mode_aperiodic_text[phy->config->dedicated.cqi_report_cnfg.report_mode_aperiodic]); + break; + } + } else { + Warning("Received CQI request but aperiodic mode is not configured\n"); + } +} + +bool phch_worker::srs_is_ready_to_send() { + if (srs_cfg.configured) { + if (srslte_refsignal_srs_send_cs(srs_cfg.subframe_config, TTI_TX(tti)%10) == 1 && + srslte_refsignal_srs_send_ue(srs_cfg.I_srs, TTI_TX(tti)) == 1) + { + return true; + } + } + return false; +} + +void phch_worker::set_tx_time(srslte_timestamp_t _tx_time, uint32_t next_offset) +{ + this->next_offset = next_offset; + memcpy(&tx_time, &_tx_time, sizeof(srslte_timestamp_t)); +} + +void phch_worker::encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, uint32_t current_tx_nb, + srslte_softbuffer_tx_t* softbuffer, uint32_t rv, uint16_t rnti, bool is_from_rar) +{ + char timestr[64]; + timestr[0]='\0'; + + if (srslte_ue_ul_cfg_grant(&ue_ul, grant, TTI_TX(tti), rv, current_tx_nb)) { + Error("Configuring UL grant\n"); + } + + if (srslte_ue_ul_pusch_encode_rnti_softbuffer(&ue_ul, + payload, uci_data, + softbuffer, + rnti, + signal_buffer[0])) + { + Error("Encoding PUSCH\n"); + } + + float p0_preamble = 0; + if (is_from_rar) { + p0_preamble = phy->p0_preamble; + } + float tx_power = srslte_ue_ul_pusch_power(&ue_ul, phy->pathloss, p0_preamble); + float gain = set_power(tx_power); + + // Save PUSCH power for PHR calculation + phy->cur_pusch_power = tx_power; + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + get_time_interval(logtime_start); + snprintf(timestr, 64, ", tot_time=%4d us", (int) logtime_start[0].tv_usec); +#endif + + char cqi_str[SRSLTE_CQI_STR_MAX_CHAR] = ""; + if (log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_cqi_value_tostring(&cqi_report, cqi_str, SRSLTE_CQI_STR_MAX_CHAR); + } + + uint8_t dummy[2] = {0,0}; + log_h->info_hex(payload, grant->mcs.tbs/8, + "PUSCH: tti_tx=%d, amp=%.2f, alloc=(%d,%d), tbs=%d, mcs=%d, rv=%d%s%s%s, cfo=%.1f KHz%s%s\n", + (tti + HARQ_DELAY_MS) % 10240, srslte_ue_ul_get_last_amplitude(&ue_ul), + grant->n_prb[0], grant->n_prb[0] + grant->L_prb, + grant->mcs.tbs / 8, grant->mcs.idx, rv, + uci_data.uci_ack_len > 0 ? (uci_data.uci_ack ? ", ack=1" : ", ack=0") : "", + uci_data.uci_ack_len > 1 ? (uci_data.uci_ack_2 ? "1" : "0") : "", + uci_data.uci_ri_len > 0 ? (uci_data.uci_ri ? ", ri=1" : ", ri=0") : "", + cfo * 15, timestr, + uci_data.uci_cqi_len > 0 ? cqi_str : ""); + + // Store metrics + ul_metrics.mcs = grant->mcs.idx; + ul_metrics.power = tx_power; + phy->set_ul_metrics(ul_metrics); +} + +void phch_worker::encode_pucch() +{ + char timestr[64]; + timestr[0]='\0'; + + if (uci_data.scheduling_request || uci_data.uci_ack_len > 0 || uci_data.uci_cqi_len > 0 || uci_data.uci_ri_len > 0) + { + + // Drop CQI if there is collision with ACK + if (!period_cqi.simul_cqi_ack && uci_data.uci_ack_len > 0 && uci_data.uci_cqi_len > 0) { + uci_data.uci_cqi_len = 0; + } + +#ifdef LOG_EXECTIME + struct timeval t[3]; + gettimeofday(&t[1], NULL); +#endif + + if (srslte_ue_ul_pucch_encode(&ue_ul, uci_data, last_dl_pdcch_ncce, TTI_TX(tti), signal_buffer[0])) { + Error("Encoding PUCCH\n"); + } + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + memcpy(&t[2], &logtime_start[2], sizeof(struct timeval)); + get_time_interval(logtime_start); + get_time_interval(t); + snprintf(timestr, 64, ", pucch_time=%d us, tot_time=%d us", (int) t[0].tv_usec, (int) logtime_start[0].tv_usec); +#endif + + float tx_power = srslte_ue_ul_pucch_power(&ue_ul, phy->pathloss, ue_ul.last_pucch_format, uci_data.uci_cqi_len, uci_data.uci_ack_len); + float gain = set_power(tx_power); + + char cqi_str[SRSLTE_CQI_STR_MAX_CHAR] = ""; + if (log_h->get_level() >= srslte::LOG_LEVEL_INFO) { + srslte_cqi_value_tostring(&cqi_report, cqi_str, SRSLTE_CQI_STR_MAX_CHAR); + } + + Info("PUCCH: tti_tx=%d, amp=%.2f, n_pucch=%d, n_prb=%d, ack=%s%s%s%s, sr=%s, cfo=%.1f KHz%s\n", + (tti + 4) % 10240, srslte_ue_ul_get_last_amplitude(&ue_ul), + ue_ul.pucch.last_n_pucch, ue_ul.pucch.last_n_prb, + uci_data.uci_ack_len > 0 ? (uci_data.uci_ack ? "1" : "0") : "no", + uci_data.uci_ack_len > 1 ? (uci_data.uci_ack_2 ? "1" : "0") : "", + uci_data.uci_ri_len > 0 ? (uci_data.uci_ri ? ", ri=1" : ", ri=0") : "", + uci_data.uci_cqi_len > 0 ? cqi_str : "", + uci_data.scheduling_request ? "yes" : "no", + cfo * 15, timestr); + } + + if (uci_data.scheduling_request) { + phy->sr_enabled = false; + } +} + +void phch_worker::encode_srs() +{ + char timestr[64]; + timestr[0]='\0'; + + if (srslte_ue_ul_srs_encode(&ue_ul, TTI_TX(tti), signal_buffer[0])) + { + Error("Encoding SRS\n"); + } + +#ifdef LOG_EXECTIME + gettimeofday(&logtime_start[2], NULL); + get_time_interval(logtime_start); + snprintf(timestr, 64, ", tot_time=%4d us", (int) logtime_start[0].tv_usec); +#endif + + float tx_power = srslte_ue_ul_srs_power(&ue_ul, phy->pathloss); + float gain = set_power(tx_power); + + Info("SRS: power=%.2f dBm, amp=%.2f, tti_tx=%d%s\n", tx_power, srslte_ue_ul_get_last_amplitude(&ue_ul), TTI_TX(tti), timestr); +} + +void phch_worker::enable_pregen_signals(bool enabled) +{ + pregen_enabled = enabled; + if (enabled) { + Info("Pre-generating UL signals worker=%d\n", get_id()); + srslte_ue_ul_pregen_signals(&ue_ul); + Info("Done pre-generating signals worker=%d\n", get_id()); + } +} + +void phch_worker::set_ul_params(bool pregen_disabled) +{ + + phy_interface_rrc::phy_cfg_common_t *common = &phy->config->common; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *dedicated = &phy->config->dedicated; + + Info("Setting new params worker_id=%d, pregen_disabled=%d\n", get_id(), pregen_disabled); + + /* PUSCH DMRS signal configuration */ + bzero(&dmrs_cfg, sizeof(srslte_refsignal_dmrs_pusch_cfg_t)); + dmrs_cfg.group_hopping_en = common->pusch_cnfg.ul_rs.group_hopping_enabled; + dmrs_cfg.sequence_hopping_en = common->pusch_cnfg.ul_rs.sequence_hopping_enabled; + dmrs_cfg.cyclic_shift = common->pusch_cnfg.ul_rs.cyclic_shift; + dmrs_cfg.delta_ss = common->pusch_cnfg.ul_rs.group_assignment_pusch; + + /* PUSCH Hopping configuration */ + bzero(&pusch_hopping, sizeof(srslte_pusch_hopping_cfg_t)); + pusch_hopping.n_sb = common->pusch_cnfg.n_sb; + pusch_hopping.hop_mode = common->pusch_cnfg.hopping_mode == LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME ? + pusch_hopping.SRSLTE_PUSCH_HOP_MODE_INTRA_SF : + pusch_hopping.SRSLTE_PUSCH_HOP_MODE_INTER_SF; + pusch_hopping.hopping_offset = common->pusch_cnfg.pusch_hopping_offset; + + /* PUSCH UCI configuration */ + bzero(&uci_cfg, sizeof(srslte_uci_cfg_t)); + uci_cfg.I_offset_ack = dedicated->pusch_cnfg_ded.beta_offset_ack_idx; + uci_cfg.I_offset_cqi = dedicated->pusch_cnfg_ded.beta_offset_cqi_idx; + uci_cfg.I_offset_ri = dedicated->pusch_cnfg_ded.beta_offset_ri_idx; + + /* PUCCH configuration */ + bzero(&pucch_cfg, sizeof(srslte_pucch_cfg_t)); + pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[common->pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + pucch_cfg.N_cs = common->pucch_cnfg.n_cs_an; + pucch_cfg.n_rb_2 = common->pucch_cnfg.n_rb_cqi; + pucch_cfg.srs_configured = dedicated->srs_ul_cnfg_ded.setup_present; + if (pucch_cfg.srs_configured) { + pucch_cfg.srs_cs_subf_cfg = liblte_rrc_srs_subfr_config_num[common->srs_ul_cnfg.subfr_cnfg%LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS]; + pucch_cfg.srs_simul_ack = common->srs_ul_cnfg.ack_nack_simul_tx; + } + + /* PUCCH Scheduling configuration */ + bzero(&pucch_sched, sizeof(srslte_pucch_sched_t)); + pucch_sched.n_pucch_1[0] = 0; // TODO: n_pucch_1 for SPS + pucch_sched.n_pucch_1[1] = 0; + pucch_sched.n_pucch_1[2] = 0; + pucch_sched.n_pucch_1[3] = 0; + pucch_sched.N_pucch_1 = common->pucch_cnfg.n1_pucch_an; + pucch_sched.n_pucch_2 = dedicated->cqi_report_cnfg.report_periodic.pucch_resource_idx; + pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx; + + /* SRS Configuration */ + bzero(&srs_cfg, sizeof(srslte_refsignal_srs_cfg_t)); + srs_cfg.configured = dedicated->srs_ul_cnfg_ded.setup_present; + if (pucch_cfg.srs_configured) { + srs_cfg.subframe_config = liblte_rrc_srs_subfr_config_num[common->srs_ul_cnfg.subfr_cnfg%LIBLTE_RRC_SRS_SUBFR_CONFIG_N_ITEMS]; + srs_cfg.bw_cfg = liblte_rrc_srs_bw_config_num[common->srs_ul_cnfg.bw_cnfg%LIBLTE_RRC_SRS_BW_CONFIG_N_ITEMS]; + srs_cfg.I_srs = dedicated->srs_ul_cnfg_ded.srs_cnfg_idx; + srs_cfg.B = dedicated->srs_ul_cnfg_ded.srs_bandwidth; + srs_cfg.b_hop = dedicated->srs_ul_cnfg_ded.srs_hopping_bandwidth; + srs_cfg.n_rrc = dedicated->srs_ul_cnfg_ded.freq_domain_pos; + srs_cfg.k_tc = dedicated->srs_ul_cnfg_ded.tx_comb; + srs_cfg.n_srs = dedicated->srs_ul_cnfg_ded.cyclic_shift; + } + + /* UL power control configuration */ + bzero(&power_ctrl, sizeof(srslte_ue_ul_powerctrl_t)); + power_ctrl.p0_nominal_pusch = common->ul_pwr_ctrl.p0_nominal_pusch; + power_ctrl.alpha = liblte_rrc_ul_power_control_alpha_num[common->ul_pwr_ctrl.alpha%LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS]; + power_ctrl.p0_nominal_pucch = common->ul_pwr_ctrl.p0_nominal_pucch; + power_ctrl.delta_f_pucch[0] = liblte_rrc_delta_f_pucch_format_1_num[common->ul_pwr_ctrl.delta_flist_pucch.format_1%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS]; + power_ctrl.delta_f_pucch[1] = liblte_rrc_delta_f_pucch_format_1b_num[common->ul_pwr_ctrl.delta_flist_pucch.format_1b%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS]; + power_ctrl.delta_f_pucch[2] = liblte_rrc_delta_f_pucch_format_2_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS]; + power_ctrl.delta_f_pucch[3] = liblte_rrc_delta_f_pucch_format_2a_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2a%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS]; + power_ctrl.delta_f_pucch[4] = liblte_rrc_delta_f_pucch_format_2b_num[common->ul_pwr_ctrl.delta_flist_pucch.format_2b%LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS]; + + power_ctrl.delta_preamble_msg3 = common->ul_pwr_ctrl.delta_preamble_msg3; + + power_ctrl.p0_ue_pusch = dedicated->ul_pwr_ctrl_ded.p0_ue_pusch; + power_ctrl.delta_mcs_based = dedicated->ul_pwr_ctrl_ded.delta_mcs_en==LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + power_ctrl.acc_enabled = dedicated->ul_pwr_ctrl_ded.accumulation_en; + power_ctrl.p0_ue_pucch = dedicated->ul_pwr_ctrl_ded.p0_ue_pucch; + power_ctrl.p_srs_offset = dedicated->ul_pwr_ctrl_ded.p_srs_offset; + + srslte_ue_ul_set_cfg(&ue_ul, &dmrs_cfg, &srs_cfg, &pucch_cfg, &pucch_sched, &uci_cfg, &pusch_hopping, &power_ctrl); + + /* CQI configuration */ + bzero(&period_cqi, sizeof(srslte_cqi_periodic_cfg_t)); + period_cqi.configured = dedicated->cqi_report_cnfg.report_periodic_setup_present; + period_cqi.pmi_idx = dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx; + period_cqi.simul_cqi_ack = dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi; + period_cqi.format_is_subband = dedicated->cqi_report_cnfg.report_periodic.format_ind_periodic == + LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_SUBBAND_CQI; + period_cqi.subband_size = dedicated->cqi_report_cnfg.report_periodic.format_ind_periodic_subband_k; + + if (dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present) { + period_cqi.ri_idx = dedicated->cqi_report_cnfg.report_periodic.ri_cnfg_idx; + period_cqi.ri_idx_present = true; + } else { + period_cqi.ri_idx_present = false; + } + + /* SR configuration */ + I_sr = dedicated->sched_request_cnfg.sr_cnfg_idx; + sr_configured = true; + + if (pregen_enabled && !pregen_disabled) { + Info("Pre-generating UL signals worker=%d\n", get_id()); + srslte_ue_ul_pregen_signals(&ue_ul); + Info("Done pre-generating signals worker=%d\n", get_id()); + } +} + +float phch_worker::set_power(float tx_power) { + float gain = 0; + /* Check if UL power control is enabled */ + if(phy->args->ul_pwr_ctrl_en) { + /* Adjust maximum power if it changes significantly */ + if (tx_power < phy->cur_radio_power - 5 || tx_power > phy->cur_radio_power + 5) { + phy->cur_radio_power = tx_power; + float radio_tx_power = phy->cur_radio_power; + gain = phy->get_radio()->set_tx_power(radio_tx_power); + } + } + return gain; +} + +void phch_worker::start_plot() { +#ifdef ENABLE_GUI + if (plot_worker_id == -1) { + plot_worker_id = get_id(); + log_h->console("Starting plot for worker_id=%d\n", plot_worker_id); + init_plots(this); + } else { + log_h->console("Trying to start a plot but already started by worker_id=%d\n", plot_worker_id); + } +#else + log_h->console("Trying to start a plot but plots are disabled (ENABLE_GUI constant in phch_worker.cc)\n"); +#endif +} + +int phch_worker::read_ce_abs(float *ce_abs, uint32_t tx_antenna, uint32_t rx_antenna) { + uint32_t i=0; + int sz = srslte_symbol_sz(cell.nof_prb); + bzero(ce_abs, sizeof(float)*sz); + int g = (sz - 12*cell.nof_prb)/2; + for (i = 0; i < 12*cell.nof_prb; i++) { + ce_abs[g+i] = 20 * log10f(cabsf(ue_dl.ce_m[tx_antenna][rx_antenna][i])); + if (isinf(ce_abs[g+i])) { + ce_abs[g+i] = -80; + } + } + return sz; +} + +int phch_worker::read_pdsch_d(cf_t* pdsch_d) +{ + + memcpy(pdsch_d, ue_dl.pdsch.d[0], ue_dl.pdsch_cfg.nbits[0].nof_re*sizeof(cf_t)); + return ue_dl.pdsch_cfg.nbits[0].nof_re; +} + + + +/**************************** Measurements **************************/ + +void phch_worker::update_measurements() +{ + float snr_ema_coeff = phy->args->snr_ema_coeff; + if (chest_done) { + + /* Only worker 0 reads the RSSI sensor every ~1-nof_cores s */ + if (get_id() == 0) { + if (!rssi_read_cnt) { + if (phy->get_radio()->has_rssi() && phy->args->rssi_sensor_enabled) { + phy->last_radio_rssi = phy->get_radio()->get_rssi(); + phy->rx_gain_offset = phy->avg_rssi_dbm - phy->last_radio_rssi + 30; + } else { + phy->rx_gain_offset = phy->get_radio()->get_rx_gain() + phy->args->rx_gain_offset; + } + } + rssi_read_cnt++; + if (rssi_read_cnt == 1000) { + rssi_read_cnt = 0; + } + } + + // Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC + float rsrq_db = 10*log10(srslte_chest_dl_get_rsrq(&ue_dl.chest)); + if (isnormal(rsrq_db)) { + if (!(tti%phy->pcell_report_period) || !phy->avg_rsrq_db) { + phy->avg_rsrq_db = rsrq_db; + } else { + phy->avg_rsrq_db = SRSLTE_VEC_CMA(rsrq_db, phy->avg_rsrq_db, tti%phy->pcell_report_period); + } + } + + // Average RSRP taken from CRS + float rsrp_lin = srslte_chest_dl_get_rsrp(&ue_dl.chest); + if (isnormal(rsrp_lin)) { + if (!phy->avg_rsrp) { + phy->avg_rsrp = SRSLTE_VEC_EMA(rsrp_lin, phy->avg_rsrp, snr_ema_coeff); + } else { + phy->avg_rsrp = rsrp_lin; + } + } + + /* Correct absolute power measurements by RX gain offset */ + float rsrp_dbm = 10*log10(rsrp_lin) + 30 - phy->rx_gain_offset; + + // Serving cell RSRP measurements are averaged over DEFAULT_MEAS_PERIOD_MS then sent to RRC + if (isnormal(rsrp_dbm)) { + if (!(tti%phy->pcell_report_period) || !phy->avg_rsrp_dbm) { + phy->avg_rsrp_dbm = rsrp_dbm; + } else { + phy->avg_rsrp_dbm = SRSLTE_VEC_CMA(rsrp_dbm, phy->avg_rsrp_dbm, tti%phy->pcell_report_period); + } + } + + // Send PCell measurement + if ((tti%phy->pcell_report_period) == phy->pcell_report_period-1) { + phy->rrc->new_phy_meas(phy->avg_rsrp_dbm, phy->avg_rsrq_db, tti); + } + + // Compute PL + float tx_crs_power = phy->config->common.pdsch_cnfg.rs_power; + phy->pathloss = tx_crs_power - phy->avg_rsrp_dbm; + + // Average noise + float cur_noise = srslte_chest_dl_get_noise_estimate(&ue_dl.chest); + if (isnormal(cur_noise)) { + if (!phy->avg_noise) { + phy->avg_noise = cur_noise; + } else { + phy->avg_noise = SRSLTE_VEC_EMA(cur_noise, phy->avg_noise, snr_ema_coeff); + } + } + + phy->avg_snr_db_cqi = 10*log10(phy->avg_rsrp/phy->avg_noise); + + // Store metrics + dl_metrics.n = phy->avg_noise; + dl_metrics.rsrp = phy->avg_rsrp_dbm; + dl_metrics.rsrq = phy->avg_rsrq_db; + dl_metrics.rssi = phy->avg_rssi_dbm; + dl_metrics.pathloss = phy->pathloss; + dl_metrics.sinr = phy->avg_snr_db_cqi; + phy->set_dl_metrics(dl_metrics); + + } +} + + +/********** Execution time trace function ************/ + +void phch_worker::start_trace() { + trace_enabled = true; +} + +void phch_worker::write_trace(std::string filename) { + tr_exec.writeToBinary(filename + ".exec"); +} + +void phch_worker::tr_log_start() +{ + if (trace_enabled) { + gettimeofday(&tr_time[1], NULL); + } +} + +void phch_worker::tr_log_end() +{ + if (trace_enabled) { + gettimeofday(&tr_time[2], NULL); + get_time_interval(tr_time); + tr_exec.push(tti, tr_time[0].tv_usec); + } +} + +} + + + + + + + + +/*********************************************************** + * + * PLOT TO VISUALIZE THE CHANNEL RESPONSEE + * + ***********************************************************/ + + +#ifdef ENABLE_GUI +plot_real_t pce[SRSLTE_MAX_PORTS][SRSLTE_MAX_PORTS]; +plot_scatter_t pconst; +#define SCATTER_PDSCH_BUFFER_LEN (20*6*SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)) +#define SCATTER_PDSCH_PLOT_LEN 4000 +float tmp_plot[SCATTER_PDSCH_BUFFER_LEN]; +cf_t tmp_plot2[SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)]; + +#define CFO_PLOT_LEN 0 /* Set to non zero for enabling CFO plot */ +#if CFO_PLOT_LEN > 0 +static plot_real_t pcfo; +static uint32_t icfo = 0; +static float cfo_buffer[CFO_PLOT_LEN]; +#endif /* CFO_PLOT_LEN > 0 */ + +void *plot_thread_run(void *arg) { + srsue::phch_worker *worker = (srsue::phch_worker*) arg; + + sdrgui_init(); + for (uint32_t tx = 0; tx < worker->get_cell_nof_ports(); tx++) { + for (uint32_t rx = 0; rx < worker->get_rx_nof_antennas(); rx++) { + char str_buf[64]; + snprintf(str_buf, 64, "|H%d%d|", rx, tx); + plot_real_init(&pce[tx][rx]); + plot_real_setTitle(&pce[tx][rx], str_buf); + plot_real_setLabels(&pce[tx][rx], (char *) "Index", (char *) "dB"); + plot_real_setYAxisScale(&pce[tx][rx], -40, 40); + + plot_real_addToWindowGrid(&pce[tx][rx], (char*)"srsue", tx, rx); + } + } + + plot_scatter_init(&pconst); + plot_scatter_setTitle(&pconst, (char*) "PDSCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pconst, -4, 4); + plot_scatter_setYAxisScale(&pconst, -4, 4); + + plot_scatter_addToWindowGrid(&pconst, (char*)"srsue", 0, worker->get_rx_nof_antennas()); + +#if CFO_PLOT_LEN > 0 + plot_real_init(&pcfo); + plot_real_setTitle(&pcfo, (char*) "CFO (Hz)"); + plot_real_setLabels(&pcfo, (char *) "Time", (char *) "Hz"); + plot_real_setYAxisScale(&pcfo, -4000, 4000); + + plot_scatter_addToWindowGrid(&pcfo, (char*)"srsue", 1, worker->get_rx_nof_antennas()); +#endif /* CFO_PLOT_LEN > 0 */ + + int n; + int readed_pdsch_re=0; + while(1) { + sem_wait(&plot_sem); + + if (readed_pdsch_re < SCATTER_PDSCH_PLOT_LEN) { + n = worker->read_pdsch_d(&tmp_plot2[readed_pdsch_re]); + readed_pdsch_re += n; + } else { + for (uint32_t tx = 0; tx < worker->get_cell_nof_ports(); tx++) { + for (uint32_t rx = 0; rx < worker->get_rx_nof_antennas(); rx++) { + n = worker->read_ce_abs(tmp_plot, tx, rx); + if (n > 0) { + plot_real_setNewData(&pce[tx][rx], tmp_plot, n); + } + } + } + if (readed_pdsch_re > 0) { + plot_scatter_setNewData(&pconst, tmp_plot2, readed_pdsch_re); + } + readed_pdsch_re = 0; + } + +#if CFO_PLOT_LEN > 0 + cfo_buffer[icfo] = worker->get_cfo() * 15000.0f; + icfo = (icfo + 1)%CFO_PLOT_LEN; + plot_real_setNewData(&pcfo, cfo_buffer, CFO_PLOT_LEN); +#endif /* CFO_PLOT_LEN > 0 */ + + } + return NULL; +} + + +void init_plots(srsue::phch_worker *worker) { + + if (sem_init(&plot_sem, 0, 0)) { + perror("sem_init"); + exit(-1); + } + + pthread_attr_t attr; + struct sched_param param; + param.sched_priority = 0; + pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + pthread_attr_setschedparam(&attr, ¶m); + if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) { + perror("pthread_create"); + exit(-1); + } +} +#endif + diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc new file mode 100644 index 0000000..4735415 --- /dev/null +++ b/srsue/src/phy/phy.cc @@ -0,0 +1,489 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include +#include + +#include "srslte/srslte.h" + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" +#include "srsue/hdr/phy/phy.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + + + +using namespace std; + + +namespace srsue { + +phy::phy() : workers_pool(MAX_WORKERS), + workers(MAX_WORKERS), + workers_common(phch_recv::MUTEX_X_WORKER*MAX_WORKERS),nof_coworkers(0) +{ +} + +static void srslte_phy_handler(phy_logger_level_t log_level, void *ctx, char *str) { + phy *r = (phy *) ctx; + r->srslte_phy_logger(log_level, str); +} + +void phy::srslte_phy_logger(phy_logger_level_t log_level, char *str) { + if (log_phy_lib_h) { + switch(log_level){ + case LOG_LEVEL_INFO: + log_phy_lib_h->info(" %s", str); + break; + case LOG_LEVEL_DEBUG: + log_phy_lib_h->debug(" %s", str); + break; + case LOG_LEVEL_ERROR: + log_phy_lib_h->error(" %s", str); + break; + default: + break; + } + } else { + printf("[PHY_LIB]: %s\n", str); + } +} + +void phy::set_default_args(phy_args_t *args) +{ + args->nof_rx_ant = 1; + args->ul_pwr_ctrl_en = false; + args->prach_gain = -1; + args->cqi_max = -1; + args->cqi_fixed = -1; + args->snr_ema_coeff = 0.1; + args->snr_estim_alg = "refs"; + args->pdsch_max_its = 4; + args->attach_enable_64qam = false; + args->nof_phy_threads = DEFAULT_WORKERS; + args->equalizer_mode = "mmse"; + args->cfo_integer_enabled = false; + args->cfo_correct_tol_hz = 50; + args->sss_algorithm = "full"; + args->estimator_fil_auto = false; + args->estimator_fil_stddev = 1.0f; + args->estimator_fil_order = 4; +} + +bool phy::check_args(phy_args_t *args) +{ + if (args->nof_phy_threads > MAX_WORKERS * 2) { + log_h->console("Error in PHY args: nof_phy_threads must be 1, 2 or 3\n"); + return false; + } + if (args->snr_ema_coeff > 1.0) { + log_h->console("Error in PHY args: snr_ema_coeff must be 0<=w<=1\n"); + return false; + } + return true; +} + +bool phy::init(srslte::radio_multi* radio_handler, mac_interface_phy *mac, rrc_interface_phy *rrc, + std::vector log_vec, phy_args_t *phy_args) { + + mlockall(MCL_CURRENT | MCL_FUTURE); + + n_ta = 0; + this->log_vec = log_vec; + this->log_h = (srslte::log*) log_vec[0]; + this->radio_handler = radio_handler; + this->mac = mac; + this->rrc = rrc; + + if (!phy_args) { + args = &default_args; + set_default_args(args); + } else { + args = phy_args; + } + + if (!check_args(args)) { + return false; + } + + nof_workers = args->nof_phy_threads; + if (nof_workers > MAX_WORKERS) { + nof_coworkers = SRSLTE_MIN(nof_workers - MAX_WORKERS, MAX_WORKERS); + nof_workers = MAX_WORKERS; + } + if (log_vec[nof_workers]) { + this->log_phy_lib_h = (srslte::log*) log_vec[nof_workers]; + srslte_phy_log_register_handler(this, srslte_phy_handler); + } else { + this->log_phy_lib_h = NULL; + } + + initiated = false; + start(); + return true; +} + +// Initializes PHY in a thread +void phy::run_thread() { + + prach_buffer.init(&config.common.prach_cnfg, SRSLTE_MAX_PRB, args, log_h); + workers_common.init(&config, args, (srslte::log*) log_vec[0], radio_handler, rrc, mac); + + // Add workers to workers pool and start threads + for (uint32_t i=0;iworker_cpu_mask); + } + + for (uint32_t i=0;inof_rx_ant, SF_RECV_THREAD_PRIO, args->sync_cpu_affinity); + + // Disable UL signal pregeneration until the attachment + enable_pregen_signals(false); + + initiated = true; +} + +void phy::wait_initialize() { + wait_thread_finish(); +} + +bool phy::is_initiated() { + return initiated; +} + +void phy::set_agc_enable(bool enabled) +{ + sf_recv.set_agc_enable(enabled); +} + +void phy::start_trace() +{ + for (uint32_t i=0;i( &(ostringstream() << i) )->str(); + workers[i].write_trace(filename + "_" + i_str); + } +} + +void phy::stop() +{ + sf_recv.stop(); + workers_pool.stop(); +} + +void phy::get_metrics(phy_metrics_t &m) { + workers_common.get_dl_metrics(m.dl); + workers_common.get_ul_metrics(m.ul); + workers_common.get_sync_metrics(m.sync); + int dl_tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(m.dl.mcs), workers_common.get_nof_prb()); + int ul_tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(m.ul.mcs), workers_common.get_nof_prb()); + m.dl.mabr_mbps = dl_tbs/1000.0; // TBS is bits/ms - convert to mbps + m.ul.mabr_mbps = ul_tbs/1000.0; // TBS is bits/ms - convert to mbps + Info("PHY: MABR estimates. DL: %4.6f Mbps. UL: %4.6f Mbps.\n", m.dl.mabr_mbps, m.ul.mabr_mbps); +} + +void phy::set_timeadv_rar(uint32_t ta_cmd) { + n_ta = srslte_N_ta_new_rar(ta_cmd); + sf_recv.set_time_adv_sec(((float) n_ta)*SRSLTE_LTE_TS); + Info("PHY: Set TA RAR: ta_cmd: %d, n_ta: %d, ta_usec: %.1f\n", ta_cmd, n_ta, ((float) n_ta)*SRSLTE_LTE_TS*1e6); +} + +void phy::set_timeadv(uint32_t ta_cmd) { + uint32_t new_nta = srslte_N_ta_new(n_ta, ta_cmd); + sf_recv.set_time_adv_sec(((float) new_nta)*SRSLTE_LTE_TS); + Info("PHY: Set TA: ta_cmd: %d, n_ta: %d, old_n_ta: %d, ta_usec: %.1f\n", ta_cmd, new_nta, n_ta, ((float) new_nta)*SRSLTE_LTE_TS*1e6); + n_ta = new_nta; +} + +void phy::configure_prach_params() +{ + Debug("Configuring PRACH parameters\n"); + srslte_cell_t cell; + sf_recv.get_current_cell(&cell); + if (!prach_buffer.set_cell(cell)) { + Error("Configuring PRACH parameters\n"); + } +} + +void phy::configure_ul_params(bool pregen_disabled) +{ + Info("PHY: Configuring UL parameters\n"); + if (is_initiated()) { + for (uint32_t i=0;iget_max_tx_power() - workers_common.cur_pusch_power; + return phr; +} + +float phy::get_pathloss_db() +{ + return workers_common.cur_pathloss; +} + +void phy::pdcch_ul_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start, int tti_end) +{ + workers_common.set_ul_rnti(rnti_type, rnti, tti_start, tti_end); +} + +void phy::pdcch_dl_search(srslte_rnti_type_t rnti_type, uint16_t rnti, int tti_start, int tti_end) +{ + workers_common.set_dl_rnti(rnti_type, rnti, tti_start, tti_end); +} + +void phy::pdcch_dl_search_reset() +{ + workers_common.set_dl_rnti(SRSLTE_RNTI_USER, 0); +} + +void phy::pdcch_ul_search_reset() +{ + workers_common.set_ul_rnti(SRSLTE_RNTI_USER, 0); +} + +void phy::get_current_cell(srslte_cell_t *cell, uint32_t *current_earfcn) +{ + sf_recv.get_current_cell(cell, current_earfcn); +} + +uint32_t phy::get_current_pci() { + srslte_cell_t cell; + sf_recv.get_current_cell(&cell); + return cell.id; +} + +uint32_t phy::get_current_earfcn() { + uint32_t earfcn; + sf_recv.get_current_cell(NULL, &earfcn); + return earfcn; +} + +void phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm) +{ + + if (!prach_buffer.prepare_to_send(preamble_idx, allowed_subframe, target_power_dbm)) { + Error("Preparing PRACH to send\n"); + } +} + +int phy::prach_tx_tti() +{ + return prach_buffer.tx_tti(); +} + +// Handle the case of a radio overflow. Resynchronise inmediatly +void phy::radio_overflow() { + sf_recv.radio_overflow(); +} + +void phy::reset() +{ + Info("Resetting PHY\n"); + n_ta = 0; + sf_recv.set_time_adv_sec(0); + pdcch_dl_search_reset(); + for(uint32_t i=0;i earfcns) +{ + sf_recv.set_earfcn(earfcns); +} + +void phy::force_freq(float dl_freq, float ul_freq) +{ + sf_recv.force_freq(dl_freq, ul_freq); +} + +void phy::set_rar_grant(uint32_t tti, uint8_t grant_payload[SRSLTE_RAR_GRANT_LEN]) +{ + workers_common.set_rar_grant(tti, grant_payload); +} + +void phy::set_crnti(uint16_t rnti) { + for(uint32_t i=0;imbsfn_subfr_cnfg_list_size > 1) { + Warning("SIB2 has %d MBSFN subframe configs - only 1 supported\n", sib2->mbsfn_subfr_cnfg_list_size); + } + if(sib2->mbsfn_subfr_cnfg_list_size > 0) { + memcpy(&config.mbsfn.mbsfn_subfr_cnfg, &sib2->mbsfn_subfr_cnfg_list[0], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); + workers_common.build_mch_table(); + } +} + +void phy::set_config_mbsfn_sib13(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + memcpy(&config.mbsfn.mbsfn_notification_cnfg, &sib13->mbsfn_notification_config, sizeof(LIBLTE_RRC_MBSFN_NOTIFICATION_CONFIG_STRUCT)); + if(sib13->mbsfn_area_info_list_r9_size > 1) { + Warning("SIB13 has %d MBSFN area info elements - only 1 supported\n", sib13->mbsfn_area_info_list_r9_size); + } + if(sib13->mbsfn_area_info_list_r9_size > 0) { + memcpy(&config.mbsfn.mbsfn_area_info, &sib13->mbsfn_area_info_list_r9[0], sizeof(LIBLTE_RRC_MBSFN_AREA_INFO_STRUCT)); + workers_common.build_mcch_table(); + } +} + +void phy::set_config_mbsfn_mcch(LIBLTE_RRC_MCCH_MSG_STRUCT *mcch) +{ + memcpy(&config.mbsfn.mcch, mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + mac->set_mbsfn_config(config.mbsfn.mcch.pmch_infolist_r9[0].mbms_sessioninfolist_r9_size); + workers_common.set_mch_period_stop(config.mbsfn.mcch.pmch_infolist_r9[0].pmch_config_r9.sf_alloc_end_r9); + workers_common.set_mcch(); +} + +void phy::set_mch_period_stop(uint32_t stop) +{ + workers_common.set_mch_period_stop(stop); +} + +} diff --git a/srsue/src/phy/prach.cc b/srsue/src/phy/prach.cc new file mode 100644 index 0000000..05aa56a --- /dev/null +++ b/srsue/src/phy/prach.cc @@ -0,0 +1,207 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include + +#include "srslte/srslte.h" +#include "srslte/common/log.h" +#include "srsue/hdr/phy/prach.h" +#include "srsue/hdr/phy/phy.h" +#include "srslte/interfaces/ue_interfaces.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error(fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning(fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info(fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug(fmt, ##__VA_ARGS__) + +namespace srsue { + + +prach::~prach() { + if (mem_initiated) { + for (int i=0;i<64;i++) { + if (buffer[i]) { + free(buffer[i]); + } + } + if (signal_buffer) { + free(signal_buffer); + } + srslte_cfo_free(&cfo_h); + srslte_prach_free(&prach_obj); + } +} + +void prach::init(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT *config_, uint32_t max_prb, phy_args_t *args_, srslte::log* log_h_) +{ + log_h = log_h_; + config = config_; + args = args_; + + for (int i=0;i<64;i++) { + buffer[i] = (cf_t*) srslte_vec_malloc(SRSLTE_PRACH_MAX_LEN*sizeof(cf_t)); + if(!buffer[i]) { + perror("malloc"); + return; + } + } + if (srslte_cfo_init(&cfo_h, SRSLTE_PRACH_MAX_LEN)) { + fprintf(stderr, "PRACH: Error initiating CFO\n"); + return; + } + srslte_cfo_set_tol(&cfo_h, 0); + signal_buffer = (cf_t *) srslte_vec_malloc(MAX_LEN_SF * 30720 * sizeof(cf_t)); + if (!signal_buffer) { + perror("malloc"); + return; + } + if (srslte_prach_init(&prach_obj, srslte_symbol_sz(max_prb))) { + Error("Initiating PRACH library\n"); + return; + } + mem_initiated = true; +} + +bool prach::set_cell(srslte_cell_t cell_) +{ + if (mem_initiated) { + // TODO: Check if other PRACH parameters changed + if (cell_.id != cell.id || !cell_initiated) { + memcpy(&cell, &cell_, sizeof(srslte_cell_t)); + preamble_idx = -1; + + uint32_t configIdx = config->prach_cnfg_info.prach_config_index; + uint32_t rootSeq = config->root_sequence_index; + uint32_t zeroCorrConfig = config->prach_cnfg_info.zero_correlation_zone_config; + uint32_t freq_offset = config->prach_cnfg_info.prach_freq_offset; + bool highSpeed = config->prach_cnfg_info.high_speed_flag; + + if (6 + freq_offset > cell.nof_prb) { + log_h->console("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d\n", freq_offset, cell.nof_prb); + log_h->error("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d\n", freq_offset, cell.nof_prb); + return false; + } + + Info("PRACH: configIdx=%d, rootSequence=%d, zeroCorrelationConfig=%d, freqOffset=%d\n", + configIdx, rootSeq, zeroCorrConfig, freq_offset); + + if (srslte_prach_set_cell(&prach_obj, srslte_symbol_sz(cell.nof_prb), + configIdx, rootSeq, highSpeed, zeroCorrConfig)) { + Error("Initiating PRACH library\n"); + return false; + } + for (int i=0;i<64;i++) { + if(srslte_prach_gen(&prach_obj, i, freq_offset, buffer[i])) { + Error("Generating PRACH preamble %d\n", i); + return false; + } + } + + len = prach_obj.N_seq + prach_obj.N_cp; + transmitted_tti = -1; + cell_initiated = true; + } + return true; + } else { + fprintf(stderr, "PRACH: Error must call init() first\n"); + return false; + } +} + +bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float target_power_dbm_) +{ + if (cell_initiated && preamble_idx_ < 64) { + preamble_idx = preamble_idx_; + target_power_dbm = target_power_dbm_; + allowed_subframe = allowed_subframe_; + transmitted_tti = -1; + Debug("PRACH: prepare to send preamble %d\n", preamble_idx); + return true; + } else { + if (!cell_initiated) { + Error("PRACH: Cell not configured\n"); + } else if (preamble_idx_ >= 64) { + Error("PRACH: Invalid preamble %d\n", preamble_idx_); + } + return false; + } +} + +bool prach::is_pending() { + return cell_initiated && preamble_idx >= 0 && preamble_idx < 64; +} + +bool prach::is_ready_to_send(uint32_t current_tti_) { + if (is_pending()) { + // consider the number of subframes the transmission must be anticipated + uint32_t tti_tx = TTI_TX(current_tti_); + if (srslte_prach_tti_opportunity(&prach_obj, tti_tx, allowed_subframe)) { + Debug("PRACH Buffer: Ready to send at tti: %d (now is %d)\n", tti_tx, current_tti_); + transmitted_tti = tti_tx; + return true; + } + } + return false; +} + +int prach::tx_tti() { + return transmitted_tti; +} + +cf_t *prach::generate(float cfo, uint32_t *nof_sf, float *target_power) { + + if (cell_initiated && preamble_idx >= 0 && nof_sf && preamble_idx <= 64 && + srslte_cell_isvalid(&cell) && len < MAX_LEN_SF * 30720 && len > 0) { + + // Correct CFO before transmission FIXME: UL SISO Only + srslte_cfo_correct(&cfo_h, buffer[preamble_idx], signal_buffer, cfo / srslte_symbol_sz(cell.nof_prb)); + + // pad guard symbols with zeros + uint32_t nsf = (len-1)/SRSLTE_SF_LEN_PRB(cell.nof_prb)+1; + bzero(&signal_buffer[len], (nsf*SRSLTE_SF_LEN_PRB(cell.nof_prb)-len)*sizeof(cf_t)); + + *nof_sf = nsf; + + if (target_power) { + *target_power = target_power_dbm; + } + + Info("PRACH: Transmitted preamble=%d, CFO=%.2f KHz, nof_sf=%d, target_power=%.1f dBm\n", + preamble_idx, cfo*15, nsf, target_power_dbm); + preamble_idx = -1; + + return signal_buffer; + } else { + Error("PRACH: Invalid parameters: cell_initiated=%d, preamble_idx=%d, cell.nof_prb=%d, len=%d\n", + cell_initiated, preamble_idx, cell.nof_prb, len); + return NULL; + } +} + +} // namespace srsue + diff --git a/srsue/src/set_net_admin_caps.cc b/srsue/src/set_net_admin_caps.cc new file mode 100644 index 0000000..1485a26 --- /dev/null +++ b/srsue/src/set_net_admin_caps.cc @@ -0,0 +1,53 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include + +using namespace std; + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + std::cout << "Please call with the binary to provide net admin capabilities to as a parameter." << std::endl; + std::cout << "E.g. ./set_net_admin_caps myprogCalling " << std::endl; + return -1; + } + + std::string command("setcap 'cap_net_admin=eip' "); + command += argv[1]; + + std::cout << "Calling " << command << " with root rights." << std::endl; + setuid(0); + system(command.c_str()); + + return 0; +} + diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc new file mode 100644 index 0000000..d75b2f9 --- /dev/null +++ b/srsue/src/ue.cc @@ -0,0 +1,370 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/hdr/ue.h" +#include "srslte/srslte.h" +#include +#include +#include +#include +#include + +using namespace srslte; + +namespace srsue{ + +ue::ue() + :started(false) +{ +} + +ue::~ue() +{ + for (uint32_t i = 0; i < phy_log.size(); i++) { + if (phy_log[i]) { + delete(phy_log[i]); + } + } + if (usim) { + delete usim; + } +} + +bool ue::init(all_args_t *args_) { + args = args_; + + int nof_phy_threads = args->expert.phy.nof_phy_threads; + if (nof_phy_threads > 3) { + nof_phy_threads = 3; + } + + if (!args->log.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args->log.filename, args->log.file_max_size); + logger_file.log("\n\n"); + logger_file.log(get_build_string().c_str()); + logger = &logger_file; + } + + rf_log.init("RF ", logger); + // Create array of pointers to phy_logs + for (int i=0;iinit(tmp, logger, true); + phy_log.push_back(mylog); + } + + mac_log.init("MAC ", logger, true); + rlc_log.init("RLC ", logger); + pdcp_log.init("PDCP", logger); + rrc_log.init("RRC ", logger); + nas_log.init("NAS ", logger); + gw_log.init("GW ", logger); + usim_log.init("USIM", logger); + + pool_log.init("POOL", logger); + pool_log.set_level(srslte::LOG_LEVEL_ERROR); + byte_buffer_pool::get_instance()->set_log(&pool_log); + + // Init logs + rf_log.set_level(srslte::LOG_LEVEL_INFO); + rf_log.info("Starting UE\n"); + for (int i=0;iset_level(level(args->log.phy_level)); + } + + /* here we add a log layer to handle logging from the phy library*/ + if (level(args->log.phy_lib_level) != LOG_LEVEL_NONE) { + srslte::log_filter *lib_log = new srslte::log_filter; + char tmp[16]; + sprintf(tmp, "PHY_LIB"); + lib_log->init(tmp, logger, true); + phy_log.push_back(lib_log); + ((srslte::log_filter*) phy_log[nof_phy_threads])->set_level(level(args->log.phy_lib_level)); + } else { + phy_log.push_back(NULL); + } + + + mac_log.set_level(level(args->log.mac_level)); + rlc_log.set_level(level(args->log.rlc_level)); + pdcp_log.set_level(level(args->log.pdcp_level)); + rrc_log.set_level(level(args->log.rrc_level)); + nas_log.set_level(level(args->log.nas_level)); + gw_log.set_level(level(args->log.gw_level)); + usim_log.set_level(level(args->log.usim_level)); + + for (int i=0;iset_hex_limit(args->log.phy_hex_limit); + } + } + mac_log.set_hex_limit(args->log.mac_hex_limit); + rlc_log.set_hex_limit(args->log.rlc_hex_limit); + pdcp_log.set_hex_limit(args->log.pdcp_hex_limit); + rrc_log.set_hex_limit(args->log.rrc_hex_limit); + nas_log.set_hex_limit(args->log.nas_hex_limit); + gw_log.set_hex_limit(args->log.gw_hex_limit); + usim_log.set_hex_limit(args->log.usim_hex_limit); + + // Set up pcap and trace + if(args->pcap.enable) { + mac_pcap.open(args->pcap.filename.c_str()); + mac.start_pcap(&mac_pcap); + } + if(args->pcap.nas_enable) { + nas_pcap.open(args->pcap.nas_filename.c_str()); + nas.start_pcap(&nas_pcap); + } + if(args->trace.enable) { + phy.start_trace(); + radio.start_trace(); + } + + // Init layers + + // Init USIM first to allow early exit in case reader couldn't be found + usim = usim_base::get_instance(&args->usim, &usim_log); + if (usim->init(&args->usim, &usim_log)) { + usim_log.console("Failed to initialize USIM.\n"); + return false; + } + + // PHY inits in background, start before radio + args->expert.phy.nof_rx_ant = args->rf.nof_rx_ant; + phy.init(&radio, &mac, &rrc, phy_log, &args->expert.phy); + + /* Start Radio */ + char *dev_name = NULL; + if (args->rf.device_name.compare("auto")) { + dev_name = (char*) args->rf.device_name.c_str(); + } + + char *dev_args = NULL; + if (args->rf.device_args.compare("auto")) { + dev_args = (char*) args->rf.device_args.c_str(); + } + + printf("Opening RF device with %d RX antennas...\n", args->rf.nof_rx_ant); + if(!radio.init_multi(args->rf.nof_rx_ant, dev_args, dev_name)) { + printf("Failed to find device %s with args %s\n", + args->rf.device_name.c_str(), args->rf.device_args.c_str()); + return false; + } + + // Set RF options + if (args->rf.time_adv_nsamples.compare("auto")) { + radio.set_tx_adv(atoi(args->rf.time_adv_nsamples.c_str())); + } + if (args->rf.burst_preamble.compare("auto")) { + radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str())); + } + if (args->rf.continuous_tx.compare("auto")) { + printf("set continuous %s\n", args->rf.continuous_tx.c_str()); + radio.set_continuous_tx(args->rf.continuous_tx.compare("yes")?false:true); + } + + radio.set_manual_calibration(&args->rf_cal); + + // Set PHY options + + if (args->rf.tx_gain > 0) { + args->expert.phy.ul_pwr_ctrl_en = false; + } else { + args->expert.phy.ul_pwr_ctrl_en = true; + } + + if (args->rf.rx_gain < 0) { + radio.start_agc(false); + } else { + radio.set_rx_gain(args->rf.rx_gain); + } + if (args->rf.tx_gain > 0) { + radio.set_tx_gain(args->rf.tx_gain); + } else { + radio.set_tx_gain(args->rf.rx_gain); + std::cout << std::endl << + "Warning: TX gain was not set. " << + "Using open-loop power control (not working properly)" << std::endl << std::endl; + } + + radio.register_error_handler(rf_msg); + radio.set_freq_offset(args->rf.freq_offset); + + mac.init(&phy, &rlc, &rrc, &mac_log); + rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); + pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); + + srslte_nas_config_t nas_cfg(1, args->nas.apn_name, args->nas.apn_user, args->nas.apn_pass, args->nas.force_imsi_attach); /* RB_ID_SRB1 */ + nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); + gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); + gw.set_netmask(args->expert.ip_netmask); + rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &gw, &mac, &rrc_log); + + // Get current band from provided EARFCN + args->rrc.supported_bands[0] = srslte_band_get_band(args->rf.dl_earfcn); + args->rrc.nof_supported_bands = 1; + args->rrc.ue_category = atoi(args->ue_category_str.c_str()); + rrc.set_args(&args->rrc); + + // Currently EARFCN list is set to only one frequency as indicated in ue.conf + std::vector earfcn_list; + earfcn_list.push_back(args->rf.dl_earfcn); + phy.set_earfcn(earfcn_list); + + if (args->rf.dl_freq > 0 && args->rf.ul_freq > 0) { + phy.force_freq(args->rf.dl_freq, args->rf.ul_freq); + } + + printf("Waiting PHY to initialize...\n"); + phy.wait_initialize(); + phy.configure_ul_params(); + + // Enable AGC once PHY is initialized + if (args->rf.rx_gain < 0) { + phy.set_agc_enable(true); + } + + printf("...\n"); + + started = true; + return true; +} + +void ue::pregenerate_signals(bool enable) +{ + phy.enable_pregen_signals(enable); +} + +void ue::stop() +{ + if(started) + { + usim->stop(); + nas.stop(); + rrc.stop(); + + // Caution here order of stop is very important to avoid locks + + + // Stop RLC and PDCP before GW to avoid locking on queue + rlc.stop(); + pdcp.stop(); + gw.stop(); + + // PHY must be stopped before radio otherwise it will lock on rf_recv() + mac.stop(); + phy.stop(); + radio.stop(); + + usleep(1e5); + if(args->pcap.enable) { + mac_pcap.close(); + } + if(args->pcap.nas_enable) { + nas_pcap.close(); + } + if(args->trace.enable) { + phy.write_trace(args->trace.phy_filename); + radio.write_trace(args->trace.radio_filename); + } + started = false; + } +} + +bool ue::attach() { + return nas.attach_request(); +} + +bool ue::deattach() { + return nas.deattach_request(); +} + +bool ue::is_attached() +{ + return rrc.is_connected(); +} + +void ue::start_plot() { + phy.start_plot(); +} + +void ue::print_pool() { + byte_buffer_pool::get_instance()->print_all_buffers(); +} + +bool ue::get_metrics(ue_metrics_t &m) +{ + bzero(&m, sizeof(ue_metrics_t)); + m.rf = rf_metrics; + bzero(&rf_metrics, sizeof(rf_metrics_t)); + rf_metrics.rf_error = false; // Reset error flag + + if(EMM_STATE_REGISTERED == nas.get_state()) { + if(RRC_STATE_CONNECTED == rrc.get_state()) { + phy.get_metrics(m.phy); + mac.get_metrics(m.mac); + rlc.get_metrics(m.rlc); + gw.get_metrics(m.gw); + return true; + } + } + return false; +} + + +void ue::radio_overflow() { + phy.radio_overflow(); +} +void ue::print_mbms() +{ + rrc.print_mbms(); +} + +bool ue::mbms_service_start(uint32_t serv, uint32_t port) +{ + return rrc.mbms_service_start(serv, port); +} + +void ue::rf_msg(srslte_rf_error_t error) +{ + ue_base *ue = ue_base::get_instance(LTE); + ue->handle_rf_msg(error); + if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + ue->radio_overflow(); + } else + if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_RX) { + ue->stop(); + ue->cleanup(); + exit(-1); + } +} + +} // namespace srsue diff --git a/srsue/src/ue_base.cc b/srsue/src/ue_base.cc new file mode 100644 index 0000000..ed3bd4e --- /dev/null +++ b/srsue/src/ue_base.cc @@ -0,0 +1,149 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/hdr/ue_base.h" +#include "srsue/hdr/ue.h" +#include "srslte/srslte.h" +#include "srslte/build_info.h" +#include +#include +#include +#include +#include +#include + +using namespace srslte; + +namespace srsue{ + +static ue_base* instance = NULL; +pthread_mutex_t ue_instance_mutex = PTHREAD_MUTEX_INITIALIZER; + +ue_base* ue_base::get_instance(srsue_instance_type_t type) +{ + pthread_mutex_lock(&ue_instance_mutex); + if(NULL == instance) { + switch (type) { + case LTE: + instance = new ue(); + break; + default: + perror("Unknown UE type.\n"); + } + } + pthread_mutex_unlock(&ue_instance_mutex); + return(instance); +} + +ue_base::ue_base() { + // print build info + std::cout << std::endl << get_build_string() << std::endl; + + // load FFTW wisdom + srslte_dft_load(); + + pool = byte_buffer_pool::get_instance(); +} + +ue_base::~ue_base() { + byte_buffer_pool::cleanup(); +} + +void ue_base::cleanup(void) +{ + // save FFTW wisdom + srslte_dft_exit(); + + pthread_mutex_lock(&ue_instance_mutex); + if(NULL != instance) { + delete instance; + instance = NULL; + } + pthread_mutex_unlock(&ue_instance_mutex); +} + +void ue_base::handle_rf_msg(srslte_rf_error_t error) +{ + if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + rf_log.warning("Overflow\n"); + }else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) { + rf_metrics.rf_u++; + rf_metrics.rf_error = true; + rf_log.warning("Underflow\n"); + } else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) { + rf_metrics.rf_l++; + rf_metrics.rf_error = true; + rf_log.warning("Late (detected in %s)\n", error.opt?"rx call":"asynchronous thread"); + } else if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OTHER) { + std::string str(error.msg); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); + str.push_back('\n'); + rf_log.info("%s\n", str.c_str()); + } +} + +srslte::LOG_LEVEL_ENUM ue_base::level(std::string l) +{ + std::transform(l.begin(), l.end(), l.begin(), ::toupper); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +std::string ue_base::get_build_mode() +{ + return std::string(srslte_get_build_mode()); +} + +std::string ue_base::get_build_info() +{ + if (std::string(srslte_get_build_info()) == "") { + return std::string(srslte_get_version()); + } + return std::string(srslte_get_build_info()); +} + +std::string ue_base::get_build_string() +{ + std::stringstream ss; + ss << "Built in " << get_build_mode() << " mode using " << get_build_info() << "." << std::endl; + return ss.str(); +} + +} // namespace srsue diff --git a/srsue/src/upper/CMakeLists.txt b/srsue/src/upper/CMakeLists.txt new file mode 100644 index 0000000..766893b --- /dev/null +++ b/srsue/src/upper/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# Copyright 2013-2017 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/. +# + +set(SOURCES gw.cc nas.cc rrc.cc usim_base.cc usim.cc) + +if(HAVE_PCSC) + list(APPEND SOURCES "pcsc_usim.cc") +endif(HAVE_PCSC) + +add_library(srsue_upper STATIC ${SOURCES}) + +if(HAVE_PCSC) + target_link_libraries(srsue_upper ${PCSCLITE_LIBRARY}) +endif(HAVE_PCSC) + +install(TARGETS srsue_upper DESTINATION ${LIBRARY_DIR}) \ No newline at end of file diff --git a/srsue/src/upper/gw.cc b/srsue/src/upper/gw.cc new file mode 100644 index 0000000..3d91791 --- /dev/null +++ b/srsue/src/upper/gw.cc @@ -0,0 +1,392 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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/hdr/upper/gw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace srsue { + +gw::gw() + :if_up(false) +{ + current_ip_addr = 0; + default_netmask = true; +} + +void gw::init(pdcp_interface_gw *pdcp_, nas_interface_gw *nas_, srslte::log *gw_log_, srslte::srslte_gw_config_t cfg_) +{ + pool = srslte::byte_buffer_pool::get_instance(); + pdcp = pdcp_; + nas = nas_; + gw_log = gw_log_; + cfg = cfg_; + run_enable = true; + + gettimeofday(&metrics_time[1], NULL); + dl_tput_bytes = 0; + ul_tput_bytes = 0; + // MBSFN + mbsfn_sock_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (mbsfn_sock_fd < 0) { + gw_log->error("Failed to create MBSFN sink socket\n"); + } + if (fcntl(mbsfn_sock_fd, F_SETFL, O_NONBLOCK)) { + gw_log->error("Failed to set non-blocking MBSFN sink socket\n"); + } + + mbsfn_sock_addr.sin_family = AF_INET; + mbsfn_sock_addr.sin_addr.s_addr =inet_addr("127.0.0.1"); + + bzero(mbsfn_ports, SRSLTE_N_MCH_LCIDS*sizeof(uint32_t)); +} + +void gw::stop() +{ + if(run_enable) + { + run_enable = false; + if(if_up) + { + close(tun_fd); + + // Wait thread to exit gracefully otherwise might leave a mutex locked + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; + } + if (running) { + thread_cancel(); + } + wait_thread_finish(); + + current_ip_addr = 0; + } + // TODO: tear down TUN device? + } + if (mbsfn_sock_fd) { + close(mbsfn_sock_fd); + } +} + +void gw::get_metrics(gw_metrics_t &m) +{ + gettimeofday(&metrics_time[2], NULL); + get_time_interval(metrics_time); + double secs = (double) metrics_time[0].tv_sec+metrics_time[0].tv_usec*1e-6; + + m.dl_tput_mbps = (dl_tput_bytes*8/(double)1e6)/secs; + m.ul_tput_mbps = (ul_tput_bytes*8/(double)1e6)/secs; + gw_log->info("RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n", + m.dl_tput_mbps, m.ul_tput_mbps); + + memcpy(&metrics_time[1], &metrics_time[2], sizeof(struct timeval)); + dl_tput_bytes = 0; + ul_tput_bytes = 0; +} + +void gw::set_netmask(std::string netmask) +{ + default_netmask = false; + this->netmask = netmask; +} + + +/******************************************************************************* + PDCP interface +*******************************************************************************/ +void gw::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX PDU. Stack latency: %ld us\n", pdu->get_latency_us()); + dl_tput_bytes += pdu->N_bytes; + if(!if_up) + { + gw_log->warning("TUN/TAP not up - dropping gw RX message\n"); + }else{ + int n = write(tun_fd, pdu->msg, pdu->N_bytes); + if(n > 0 && (pdu->N_bytes != (uint32_t)n)) + { + gw_log->warning("DL TUN/TAP write failure. Wanted to write %d B but only wrote %d B.\n", pdu->N_bytes, n); + } + } + pool->deallocate(pdu); +} + +void gw::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + if(pdu->N_bytes>2) + { + gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX MCH PDU (%d B). Stack latency: %ld us\n", pdu->N_bytes, pdu->get_latency_us()); + dl_tput_bytes += pdu->N_bytes; + + //Hack to drop initial 2 bytes + pdu->msg +=2; + pdu->N_bytes-=2; + struct in_addr dst_addr; + memcpy(&dst_addr.s_addr, &pdu->msg[16],4); + + if(!if_up) + { + gw_log->warning("TUN/TAP not up - dropping gw RX message\n"); + }else{ + int n = write(tun_fd, pdu->msg, pdu->N_bytes); + if(n > 0 && (pdu->N_bytes != (uint32_t)n)) + { + gw_log->warning("DL TUN/TAP write failure\n"); + } + } + /* + // Strip IP/UDP header + pdu->msg += 28; + pdu->N_bytes -= 28; + + if(mbsfn_sock_fd) { + if(lcid > 0 && lcid < SRSLTE_N_MCH_LCIDS) { + mbsfn_sock_addr.sin_port = htons(mbsfn_ports[lcid]); + if(sendto(mbsfn_sock_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&mbsfn_sock_addr, sizeof(struct sockaddr_in))<0) { + gw_log->error("Failed to send MCH PDU to port %d\n", mbsfn_ports[lcid]); + } + } + }*/ + } + pool->deallocate(pdu); +} + +/******************************************************************************* + NAS interface +*******************************************************************************/ +srslte::error_t gw::setup_if_addr(uint32_t ip_addr, char *err_str) +{ + if (ip_addr != current_ip_addr) { + if(!if_up) + { + if(init_if(err_str)) + { + gw_log->error("init_if failed\n"); + return(srslte::ERROR_CANT_START); + } + } + + // Setup the IP address + sock = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip_addr); + if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) + { + err_str = strerror(errno); + gw_log->debug("Failed to set socket address: %s\n", err_str); + close(tun_fd); + return(srslte::ERROR_CANT_START); + } + ifr.ifr_netmask.sa_family = AF_INET; + const char *mask = "255.255.255.0"; + if (!default_netmask) { + mask = netmask.c_str(); + } + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(mask); + if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) + { + err_str = strerror(errno); + gw_log->debug("Failed to set socket netmask: %s\n", err_str); + close(tun_fd); + return(srslte::ERROR_CANT_START); + } + + current_ip_addr = ip_addr; + + // Setup a thread to receive packets from the TUN device + start(GW_THREAD_PRIO); + } + + return(srslte::ERROR_NONE); +} + +srslte::error_t gw::init_if(char *err_str) +{ + if(if_up) + { + return(srslte::ERROR_ALREADY_STARTED); + } + + char dev[IFNAMSIZ] = "tun_srsue"; + + // Construct the TUN device + tun_fd = open("/dev/net/tun", O_RDWR); + gw_log->info("TUN file descriptor = %d\n", tun_fd); + if(0 > tun_fd) + { + err_str = strerror(errno); + gw_log->debug("Failed to open TUN device: %s\n", err_str); + return(srslte::ERROR_CANT_START); + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); + ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = 0; + if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) + { + err_str = strerror(errno); + gw_log->debug("Failed to set TUN device name: %s\n", err_str); + close(tun_fd); + return(srslte::ERROR_CANT_START); + } + + // Bring up the interface + sock = socket(AF_INET, SOCK_DGRAM, 0); + if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) + { + err_str = strerror(errno); + gw_log->debug("Failed to bring up socket: %s\n", err_str); + close(tun_fd); + return(srslte::ERROR_CANT_START); + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) + { + err_str = strerror(errno); + gw_log->debug("Failed to set socket flags: %s\n", err_str); + close(tun_fd); + return(srslte::ERROR_CANT_START); + } + + if_up = true; + + return(srslte::ERROR_NONE); +} + + +/******************************************************************************* + RRC interface +*******************************************************************************/ +void gw::add_mch_port(uint32_t lcid, uint32_t port) +{ + if(lcid > 0 && lcid < SRSLTE_N_MCH_LCIDS) { + mbsfn_ports[lcid] = port; + } +} + + + +/********************/ +/* GW Receive */ +/********************/ +void gw::run_thread() +{ + struct iphdr *ip_pkt; + uint32 idx = 0; + int32 N_bytes; + srslte::byte_buffer_t *pdu = pool_allocate; + if (!pdu) { + gw_log->error("Fatal Error: Couldn't allocate PDU in run_thread().\n"); + return; + } + + const static uint32_t ATTACH_WAIT_TOUT = 40; // 4 sec + uint32_t attach_wait = 0; + + gw_log->info("GW IP packet receiver thread run_enable\n"); + + running = true; + while(run_enable) + { + if (SRSLTE_MAX_BUFFER_SIZE_BYTES-SRSLTE_BUFFER_HEADER_OFFSET > idx) { + N_bytes = read(tun_fd, &pdu->msg[idx], SRSLTE_MAX_BUFFER_SIZE_BYTES-SRSLTE_BUFFER_HEADER_OFFSET - idx); + } else { + gw_log->error("GW pdu buffer full - gw receive thread exiting.\n"); + gw_log->console("GW pdu buffer full - gw receive thread exiting.\n"); + break; + } + gw_log->debug("Read %d bytes from TUN fd=%d, idx=%d\n", N_bytes, tun_fd, idx); + if(N_bytes > 0) + { + pdu->N_bytes = idx + N_bytes; + ip_pkt = (struct iphdr*)pdu->msg; + + // Warning: Accept only IPv4 packets + if (ip_pkt->version == 4) { + // Check if entire packet was received + if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) + { + gw_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU"); + + while(run_enable && !pdcp->is_drb_enabled(cfg.lcid) && attach_wait < ATTACH_WAIT_TOUT) { + if (!attach_wait) { + gw_log->info("LCID=%d not active, requesting NAS attach (%d/%d)\n", cfg.lcid, attach_wait, ATTACH_WAIT_TOUT); + if (!nas->attach_request()) { + gw_log->warning("Could not re-establish the connection\n"); + } + } + usleep(100000); + attach_wait++; + } + + attach_wait = 0; + + if (!run_enable) { + break; + } + + // Send PDU directly to PDCP + if (pdcp->is_drb_enabled(cfg.lcid)) { + pdu->set_timestamp(); + ul_tput_bytes += pdu->N_bytes; + pdcp->write_sdu(cfg.lcid, pdu); + + do { + pdu = pool_allocate; + if (!pdu) { + gw_log->error("Fatal Error: Couldn't allocate PDU in run_thread().\n"); + usleep(100000); + } + } while(!pdu); + idx = 0; + } + }else{ + idx += N_bytes; + } + } + }else{ + gw_log->error("Failed to read from TUN interface - gw receive thread exiting.\n"); + gw_log->console("Failed to read from TUN interface - gw receive thread exiting.\n"); + break; + } + } + running = false; + gw_log->info("GW IP receiver thread exiting.\n"); +} + +} // namespace srsue diff --git a/srsue/src/upper/nas.cc b/srsue/src/upper/nas.cc new file mode 100644 index 0000000..8672eb0 --- /dev/null +++ b/srsue/src/upper/nas.cc @@ -0,0 +1,1517 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include +#include "srslte/asn1/liblte_rrc.h" +#include "srsue/hdr/upper/nas.h" +#include "srslte/common/security.h" +#include "srslte/common/bcd_helpers.h" + +using namespace srslte; + +namespace srsue { + + +/********************************************************************* + * NAS + ********************************************************************/ + +nas::nas() + : state(EMM_STATE_DEREGISTERED), have_guti(false), have_ctxt(false), ip_addr(0), eps_bearer_id(0) +{ + ctxt.rx_count = 0; + ctxt.tx_count = 0; + ctxt.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; + ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; + plmn_is_selected = false; + chap_id = 0; +} + +void nas::init(usim_interface_nas *usim_, + rrc_interface_nas *rrc_, + gw_interface_nas *gw_, + srslte::log *nas_log_, + srslte::srslte_nas_config_t cfg_) +{ + pool = byte_buffer_pool::get_instance(); + usim = usim_; + rrc = rrc_; + gw = gw_; + nas_log = nas_log_; + state = EMM_STATE_DEREGISTERED; + + if (!usim->get_home_plmn_id(&home_plmn)) { + nas_log->error("Getting Home PLMN Id from USIM. Defaulting to 001-01\n"); + home_plmn.mcc = 61441; // This is 001 + home_plmn.mnc = 65281; // This is 01 + } + cfg = cfg_; + + if((read_ctxt_file(&ctxt))) { + usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, + ctxt.cipher_algo, ctxt.integ_algo); + nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + nas_log->debug_hex(k_nas_int, 32, "NAS integrity key - k_nas_int"); + have_guti = true; + have_ctxt = true; + } + + // set seed for rand (used in CHAP auth) + srand(time(NULL)); + + running = true; +} + +void nas::stop() { + running = false; + write_ctxt_file(ctxt); +} + +emm_state_t nas::get_state() { + return state; +} + +/******************************************************************************* + * UE interface + ******************************************************************************/ + +/** Blocking function to Attach to the network and establish RRC connection if not established. + * The function returns true if the UE could attach correctly or false in case of error or timeout during attachment. + * + */ +bool nas::attach_request() { + rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS]; + int nof_plmns = 0; + + nas_log->info("Attach Request\n"); + switch (state) { + case EMM_STATE_DEREGISTERED: + + // Search PLMN is not selected + if (!plmn_is_selected) { + nas_log->info("No PLMN selected. Starting PLMN Search...\n"); + nof_plmns = rrc->plmn_search(found_plmns); + if (nof_plmns > 0) { + // Save PLMNs + known_plmns.clear(); + for (int i=0;iinfo("Found PLMN: Id=%s, TAC=%d\n", plmn_id_to_string(found_plmns[i].plmn_id).c_str(), + found_plmns[i].tac); + nas_log->console("Found PLMN: Id=%s, TAC=%d\n", plmn_id_to_string(found_plmns[i].plmn_id).c_str(), + found_plmns[i].tac); + } + select_plmn(); + } else if (nof_plmns == 0) { + nas_log->warning("Did not find any PLMN in the set of frequencies\n"); + return false; + } else if (nof_plmns < 0) { + nas_log->error("Error while searching for PLMNs\n"); + return false; + } + } + // Select PLMN in request establishment of RRC connection + if (plmn_is_selected) { + rrc->plmn_select(current_plmn); + if (rrc_connect()) { + nas_log->info("NAS attached successfully.\n"); + return true; + } else { + nas_log->error("Could not attach in attach request\n"); + } + } else { + nas_log->error("PLMN is not selected because no suitable PLMN was found\n"); + } + break; + case EMM_STATE_REGISTERED: + if (rrc->is_connected()) { + nas_log->info("NAS is already registered and RRC connected\n"); + return true; + } else { + nas_log->info("NAS is already registered but RRC disconnected. Connecting now...\n"); + if (rrc_connect()) { + nas_log->info("NAS attached successfully.\n"); + return true; + } else { + nas_log->error("Could not attach from attach_request\n"); + } + } + break; + default: + nas_log->info("Attach request ignored. State = %s\n", emm_state_text[state]); + } + return false; +} + +bool nas::deattach_request() { + state = EMM_STATE_DEREGISTERED_INITIATED; + nas_log->info("Dettach request not supported\n"); + return false; +} + + +bool nas::is_attached() { + return state == EMM_STATE_REGISTERED; +} + +void nas::paging(LIBLTE_RRC_S_TMSI_STRUCT *ue_identiy) { + if (state == EMM_STATE_REGISTERED) { + nas_log->info("Received paging: requesting RRC connection establishment\n"); + if (rrc_connect()) { + nas_log->info("Attached successfully\n"); + } else { + nas_log->error("Could not attach from paging\n"); + } + } else { + nas_log->warning("Received paging while in state %s\n", emm_state_text[state]); + } +} + +void nas::set_barring(barring_t barring) { + current_barring = barring; +} + +/* Internal function that requests RRC connection, waits for positive or negative response and returns true/false + */ +bool nas::rrc_connect() { + if (rrc->is_connected()) { + nas_log->info("Already connected\n"); + return true; + } + + // Generate service request or attach request message + byte_buffer_t *dedicatedInfoNAS = pool_allocate; + if (state == EMM_STATE_REGISTERED) { + gen_service_request(dedicatedInfoNAS); + } else { + gen_attach_request(dedicatedInfoNAS); + } + + // Provide UE-Identity to RRC if have one + if (have_guti) { + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + s_tmsi.mmec = ctxt.guti.mme_code; + s_tmsi.m_tmsi = ctxt.guti.m_tmsi; + rrc->set_ue_idenity(s_tmsi); + } + + // Set establishment cause + LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM establish_cause = LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING; + + if (rrc->connection_request(establish_cause, dedicatedInfoNAS)) + { + nas_log->info("Connection established correctly. Waiting for Attach\n"); + + // Wait until attachment. If doing a service request is already attached + uint32_t tout = 0; + while (tout < 5000 && state != EMM_STATE_REGISTERED && running && rrc->is_connected()) { + usleep(1000); + tout++; + } + if (state == EMM_STATE_REGISTERED) { + nas_log->info("EMM Registered correctly\n"); + return true; + } else if (state == EMM_STATE_DEREGISTERED) { + nas_log->error("Timeout or received attach reject while trying to attach\n"); + nas_log->console("Failed to Attach\n"); + } else if (!rrc->is_connected()) { + nas_log->error("Was disconnected while attaching\n"); + } else { + nas_log->error("Timed out while trying to attach\n"); + } + } else { + nas_log->error("Could not establish RRC connection\n"); + } + return false; +} + +void nas::select_plmn() { + + plmn_is_selected = false; + + // First find if Home PLMN is available + for (uint32_t i=0;iinfo("Selecting Home PLMN Id=%s\n", plmn_id_to_string(known_plmns[i]).c_str()); + plmn_is_selected = true; + current_plmn = known_plmns[i]; + return; + } + } + + // If not, select the first available PLMN + if (known_plmns.size() > 0) { + nas_log->info("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n", + plmn_id_to_string(home_plmn).c_str(), + plmn_id_to_string(known_plmns[0]).c_str()); + + nas_log->console("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n", + plmn_id_to_string(home_plmn).c_str(), + plmn_id_to_string(known_plmns[0]).c_str()); + plmn_is_selected = true; + current_plmn = known_plmns[0]; + } +} + + +void nas::write_pdu(uint32_t lcid, byte_buffer_t *pdu) { + uint8 pd = 0; + uint8 msg_type = 0; + uint8 sec_hdr_type = 0; + bool mac_valid = false; + + nas_log->info_hex(pdu->msg, pdu->N_bytes, "DL %s PDU", rrc->get_rb_name(lcid).c_str()); + + // Parse the message security header + liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*)pdu, &pd, &sec_hdr_type); + switch(sec_hdr_type) + { + case LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS: + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT: + case LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST: + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY: + break; + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED: + mac_valid = integrity_check(pdu); + cipher_decrypt(pdu); + break; + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT: + break; + default: + nas_log->error("Not handling NAS message with SEC_HDR_TYPE=%02X\n", sec_hdr_type); + pool->deallocate(pdu); + return; + } + + // Write NAS pcap + if(pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + // Parse the message header + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) pdu, &pd, &msg_type); + nas_log->info_hex(pdu->msg, pdu->N_bytes, "DL %s Decrypted PDU", rrc->get_rb_name(lcid).c_str()); + // TODO: Check if message type requieres specical security header type and if it isvalid + + switch (msg_type) { + case LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT: + parse_attach_accept(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT: + parse_attach_reject(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST: + parse_authentication_request(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT: + parse_authentication_reject(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST: + parse_identity_request(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND: + parse_security_mode_command(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT: + parse_service_reject(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST: + parse_esm_information_request(lcid, pdu); + break; + case LIBLTE_MME_MSG_TYPE_EMM_INFORMATION: + parse_emm_information(lcid, pdu); + break; + default: + nas_log->error("Not handling NAS message with MSG_TYPE=%02X\n", msg_type); + pool->deallocate(pdu); + return; + } +} + +uint32_t nas::get_ul_count() { + // UL count for RRC key derivation depends on ESM information transfer procedure + if (cfg.apn.empty()) { + // No ESM info transfer has been sent + return ctxt.tx_count - 1; + } else { + return ctxt.tx_count - 2; + } +} + +bool nas::get_k_asme(uint8_t *k_asme_, uint32_t n) { + if(!have_ctxt) { + nas_log->error("K_asme requested before security context established\n"); + return false; + } + if(NULL == k_asme_ || n < 32) { + nas_log->error("Invalid parameters to get_k_asme"); + return false; + } + + memcpy(k_asme_, ctxt.k_asme, 32); + return true; +} + +/******************************************************************************* + PCAP +*******************************************************************************/ + +void nas::start_pcap(srslte::nas_pcap *pcap_) +{ + pcap = pcap_; +} + +/******************************************************************************* + * Security + ******************************************************************************/ + +void nas::integrity_generate(uint8_t *key_128, + uint32_t count, + uint8_t direction, + uint8_t *msg, + uint32_t msg_len, + uint8_t *mac) { + switch (ctxt.integ_algo) { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(key_128, + count, + 0, // Bearer always 0 for NAS + direction, + msg, + msg_len, + mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(key_128, + count, + 0, // Bearer always 0 for NAS + direction, + msg, + msg_len, + mac); + break; + default: + break; + } +} + +// This function depends to a valid k_nas_int. +// This key is generated in the security mode command. + +bool nas::integrity_check(byte_buffer_t *pdu) +{ + if (!pdu) { + nas_log->error("Invalid PDU\n"); + return NULL; + } + if (pdu->N_bytes > 5) { + uint8_t exp_mac[4] = {0}; + uint8_t *mac = &pdu->msg[1]; + int i; + + integrity_generate(&k_nas_int[16], + ctxt.rx_count, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[5], + pdu->N_bytes-5, + &exp_mac[0]); + + // Check if expected mac equals the sent mac + for(i=0; i<4; i++){ + if(exp_mac[i] != mac[i]){ + nas_log->warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " + "Received: count=%d, [%02x %02x %02x %02x]\n", + ctxt.rx_count, exp_mac[0], exp_mac[1], exp_mac[2], exp_mac[3], + pdu->msg[5], mac[0], mac[1], mac[2], mac[3]); + return false; + } + } + nas_log->info("Integrity check ok. Local: count=%d, Received: count=%d\n", + ctxt.rx_count, pdu->msg[5]); + return true; + } else { + nas_log->error("Invalid integrity check PDU size (%d)\n", pdu->N_bytes); + return false; + } +} + +void nas::cipher_encrypt(byte_buffer_t *pdu) +{ + byte_buffer_t pdu_tmp; + switch(ctxt.cipher_algo) + { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&k_nas_enc[16], + pdu->msg[5], + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_UPLINK, + &pdu->msg[6], + pdu->N_bytes-6, + &pdu_tmp.msg[6]); + memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes-6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&k_nas_enc[16], + pdu->msg[5], + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_UPLINK, + &pdu->msg[6], + pdu->N_bytes-6, + &pdu_tmp.msg[6]); + memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes-6); + break; + default: + nas_log->error("Ciphering algorithm not known\n"); + break; + } +} + +void nas::cipher_decrypt(byte_buffer_t *pdu) +{ + byte_buffer_t tmp_pdu; + switch(ctxt.cipher_algo) + { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&k_nas_enc[16], + pdu->msg[5], + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[6], + pdu->N_bytes-6, + &tmp_pdu.msg[6]); + memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes-6); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&k_nas_enc[16], + pdu->msg[5], + 0, // Bearer always 0 for NAS + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[6], + pdu->N_bytes-6, + &tmp_pdu.msg[6]); + nas_log->debug_hex(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); + memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes-6); + break; + default: + nas_log->error("Ciphering algorithms not known\n"); + break; + } +} + +bool nas::check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT *caps) +{ + for(uint32_t i=0; i<8; i++) { + if(caps->eea[i] != eea_caps[i] || caps->eia[i] != eia_caps[i]) { + return false; + } + } + return true; +} + + +/******************************************************************************* + * Parsers + ******************************************************************************/ + +void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) { + + if (!pdu) { + nas_log->error("Invalid PDU\n"); + return; + } + + if (pdu->N_bytes <= 5) { + nas_log->error("Invalid attach accept PDU size (%d)\n", pdu->N_bytes); + return; + } + + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept; + ZERO_OBJECT(attach_accept); + LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT attach_complete; + ZERO_OBJECT(attach_complete); + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req; + ZERO_OBJECT(act_def_eps_bearer_context_req); + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT act_def_eps_bearer_context_accept; + ZERO_OBJECT(act_def_eps_bearer_context_accept); + + nas_log->info("Received Attach Accept\n"); + + liblte_mme_unpack_attach_accept_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &attach_accept); + + if (attach_accept.eps_attach_result == LIBLTE_MME_EPS_ATTACH_RESULT_EPS_ONLY) { + //FIXME: Handle t3412.unit + //FIXME: Handle tai_list + if (attach_accept.guti_present) { + memcpy(&ctxt.guti, &attach_accept.guti.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT)); + have_guti = true; + // Update RRC UE-Idenity + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + s_tmsi.mmec = ctxt.guti.mme_code; + s_tmsi.m_tmsi = ctxt.guti.m_tmsi; + rrc->set_ue_idenity(s_tmsi); + } + if (attach_accept.lai_present) {} + if (attach_accept.ms_id_present) {} + if (attach_accept.emm_cause_present) {} + if (attach_accept.t3402_present) {} + if (attach_accept.t3412_ext_present) {} + if (attach_accept.t3423_present) {} + if (attach_accept.equivalent_plmns_present) {} + if (attach_accept.emerg_num_list_present) {} + if (attach_accept.eps_network_feature_support_present) {} + if (attach_accept.additional_update_result_present) {} + + liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, + &act_def_eps_bearer_context_req); + + if (LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) { + ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[0] << 24; + ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[1] << 16; + ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[2] << 8; + ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[3]; + + nas_log->info("Network attach successful. APN: %s, IP: %u.%u.%u.%u\n", + act_def_eps_bearer_context_req.apn.apn, + act_def_eps_bearer_context_req.pdn_addr.addr[0], + act_def_eps_bearer_context_req.pdn_addr.addr[1], + act_def_eps_bearer_context_req.pdn_addr.addr[2], + act_def_eps_bearer_context_req.pdn_addr.addr[3]); + + nas_log->console("Network attach successful. IP: %u.%u.%u.%u\n", + act_def_eps_bearer_context_req.pdn_addr.addr[0], + act_def_eps_bearer_context_req.pdn_addr.addr[1], + act_def_eps_bearer_context_req.pdn_addr.addr[2], + act_def_eps_bearer_context_req.pdn_addr.addr[3]); + + // Setup GW + char *err_str = NULL; + if (gw->setup_if_addr(ip_addr, err_str)) { + nas_log->error("Failed to set gateway address - %s\n", err_str); + } + } else { + nas_log->error("Not handling IPV6 or IPV4V6\n"); + pool->deallocate(pdu); + return; + } + eps_bearer_id = act_def_eps_bearer_context_req.eps_bearer_id; + if (act_def_eps_bearer_context_req.transaction_id_present) { + transaction_id = act_def_eps_bearer_context_req.proc_transaction_id; + } + + // Search for DNS entry in protocol config options + if (act_def_eps_bearer_context_req.protocol_cnfg_opts_present) { + for (uint32_t i = 0; i < act_def_eps_bearer_context_req.protocol_cnfg_opts.N_opts; i++) { + if (act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].id == LIBLTE_MME_ADDITIONAL_PARAMETERS_DL_DNS_SERVER_IPV4_ADDRESS) { + uint32_t dns_addr = 0; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[0] << 24; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[1] << 16; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[2] << 8; + dns_addr |= act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[3]; + nas_log->info("DNS: %u.%u.%u.%u\n", + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[0], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[1], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[2], + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[i].contents[3]); + } + } + } + + //FIXME: Handle the following parameters +// act_def_eps_bearer_context_req.eps_qos.qci +// act_def_eps_bearer_context_req.eps_qos.br_present +// act_def_eps_bearer_context_req.eps_qos.br_ext_present +// act_def_eps_bearer_context_req.apn.apn +// act_def_eps_bearer_context_req.negotiated_qos_present +// act_def_eps_bearer_context_req.llc_sapi_present +// act_def_eps_bearer_context_req.radio_prio_present +// act_def_eps_bearer_context_req.packet_flow_id_present +// act_def_eps_bearer_context_req.apn_ambr_present +// act_def_eps_bearer_context_req.protocol_cnfg_opts_present +// act_def_eps_bearer_context_req.connectivity_type_present + + // FIXME: Setup the default EPS bearer context + + state = EMM_STATE_REGISTERED; + + ctxt.rx_count++; + + // Send EPS bearer context accept and attach complete + act_def_eps_bearer_context_accept.eps_bearer_id = eps_bearer_id; + act_def_eps_bearer_context_accept.proc_transaction_id = transaction_id; + act_def_eps_bearer_context_accept.protocol_cnfg_opts_present = false; + liblte_mme_pack_activate_default_eps_bearer_context_accept_msg(&act_def_eps_bearer_context_accept, + &attach_complete.esm_msg); + + pdu->reset(); + liblte_mme_pack_attach_complete_msg(&attach_complete, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *) pdu); + // Write NAS pcap + if (pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + cipher_encrypt(pdu); + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); + + // Instruct RRC to enable capabilities + rrc->enable_capabilities(); + + nas_log->info("Sending Attach Complete\n"); + rrc->write_sdu(lcid, pdu); + ctxt.tx_count++; + + } else { + nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result); + state = EMM_STATE_DEREGISTERED; + pool->deallocate(pdu); + } +} + +void nas::parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu) { + LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT attach_rej; + ZERO_OBJECT(attach_rej); + + liblte_mme_unpack_attach_reject_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &attach_rej); + nas_log->warning("Received Attach Reject. Cause= %02X\n", attach_rej.emm_cause); + nas_log->console("Received Attach Reject. Cause= %02X\n", attach_rej.emm_cause); + state = EMM_STATE_DEREGISTERED; + pool->deallocate(pdu); + // FIXME: Command RRC to release? +} + +void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu) { + LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req; + bzero(&auth_req, sizeof(LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT)); + + nas_log->info("Received Authentication Request\n"); + liblte_mme_unpack_authentication_request_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &auth_req); + + // Deallocate PDU after parsing + pool->deallocate(pdu); + + ctxt.rx_count++; + + // Generate authentication response using RAND, AUTN & KSI-ASME + uint16 mcc, mnc; + mcc = rrc->get_mcc(); + mnc = rrc->get_mnc(); + + nas_log->info("MCC=%d, MNC=%d\n", mcc, mnc); + + uint8_t res[16]; + int res_len = 0; + nas_log->debug_hex(auth_req.rand, 16, "Authentication request RAND\n"); + nas_log->debug_hex(auth_req.autn, 16, "Authentication request AUTN\n"); + auth_result_t auth_result = usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, + res, &res_len, ctxt.k_asme); + if(LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE == auth_req.nas_ksi.tsc_flag) { + ctxt.ksi = auth_req.nas_ksi.nas_ksi; + } else { + nas_log->error("NAS mapped security context not currently supported\n"); + nas_log->console("Warning: NAS mapped security context not currently supported\n"); + } + + if (auth_result == AUTH_OK) { + nas_log->info("Network authentication successful\n"); + send_authentication_response(res, res_len); + nas_log->info_hex(ctxt.k_asme, 32, "Generated k_asme:\n"); + } else if (auth_result == AUTH_SYNCH_FAILURE) { + nas_log->error("Network authentication synchronization failure.\n"); + send_authentication_failure(LIBLTE_MME_EMM_CAUSE_SYNCH_FAILURE, res); + } else { + nas_log->warning("Network authentication failure\n"); + nas_log->console("Warning: Network authentication failure\n"); + send_authentication_failure(LIBLTE_MME_EMM_CAUSE_MAC_FAILURE, NULL); + } +} + +void nas::parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu) { + nas_log->warning("Received Authentication Reject\n"); + pool->deallocate(pdu); + state = EMM_STATE_DEREGISTERED; + // FIXME: Command RRC to release? +} + +void nas::parse_identity_request(uint32_t lcid, byte_buffer_t *pdu) { + LIBLTE_MME_ID_REQUEST_MSG_STRUCT id_req; + ZERO_OBJECT(id_req); + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT id_resp; + ZERO_OBJECT(id_resp); + + liblte_mme_unpack_identity_request_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &id_req); + nas_log->info("Received Identity Request. ID type: %d\n", id_req.id_type); + + switch(id_req.id_type) { + case LIBLTE_MME_MOBILE_ID_TYPE_IMSI: + id_resp.mobile_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMSI; + usim->get_imsi_vec(id_resp.mobile_id.imsi, 15); + break; + case LIBLTE_MME_MOBILE_ID_TYPE_IMEI: + id_resp.mobile_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEI; + usim->get_imei_vec(id_resp.mobile_id.imei, 15); + break; + default: + nas_log->error("Unhandled ID type: %d\n", id_req.id_type); + pool->deallocate(pdu); + return; + } + + pdu->reset(); + liblte_mme_pack_identity_response_msg(&id_resp, (LIBLTE_BYTE_MSG_STRUCT *) pdu); + + if(pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + rrc->write_sdu(lcid, pdu); +} + +void nas::parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu) +{ + + if (!pdu) { + nas_log->error("Invalid PDU\n"); + return; + } + + if (pdu->N_bytes <= 5) { + nas_log->error("Invalid security mode command PDU size (%d)\n", pdu->N_bytes); + return; + } + + LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sec_mode_cmd; + bzero(&sec_mode_cmd, sizeof(LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT)); + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp; + bzero(&sec_mode_comp, sizeof(LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT)); + + liblte_mme_unpack_security_mode_command_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &sec_mode_cmd); + nas_log->info("Received Security Mode Command ksi: %d, eea: %s, eia: %s\n", + sec_mode_cmd.nas_ksi.nas_ksi, + ciphering_algorithm_id_text[sec_mode_cmd.selected_nas_sec_algs.type_of_eea], + integrity_algorithm_id_text[sec_mode_cmd.selected_nas_sec_algs.type_of_eia]); + + if(sec_mode_cmd.nas_ksi.tsc_flag != LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE) { + nas_log->error("Mapped security context not supported\n"); + pool->deallocate(pdu); + return; + } + + if (have_ctxt) { + if(sec_mode_cmd.nas_ksi.nas_ksi != ctxt.ksi) { + nas_log->warning("Sending Security Mode Reject due to key set ID mismatch\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED); + pool->deallocate(pdu); + return; + } + } + + // MME is setting up security context + + // TODO: check nonce (not sent by Amari) + + // Check capabilities replay + if(!check_cap_replay(&sec_mode_cmd.ue_security_cap)) { + nas_log->warning("Sending Security Mode Reject due to security capabilities mismatch\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); + pool->deallocate(pdu); + return; + } + + // Reset counters (as per 24.301 5.4.3.2) + ctxt.rx_count = 0; + ctxt.tx_count = 0; + + ctxt.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eea; + ctxt.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) sec_mode_cmd.selected_nas_sec_algs.type_of_eia; + + // Check capabilities + if(!eea_caps[ctxt.cipher_algo] || !eia_caps[ctxt.integ_algo]) { + nas_log->warning("Sending Security Mode Reject due to security capabilities mismatch\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); + pool->deallocate(pdu); + return; + } + + // Generate NAS keys + usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, + ctxt.cipher_algo, ctxt.integ_algo); + nas_log->info_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + nas_log->info_hex(k_nas_int, 32, "NAS integrity key - k_nas_int"); + + nas_log->debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d\n", + ctxt.integ_algo, ctxt.rx_count, lcid); + + if (integrity_check(pdu) != true) { + nas_log->warning("Sending Security Mode Reject due to integrity check failure\n"); + send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_MAC_FAILURE); + pool->deallocate(pdu); + return; + } + + ctxt.rx_count++; + + // Take security context into use + have_ctxt = true; + + if (sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req) { + sec_mode_comp.imeisv_present = true; + sec_mode_comp.imeisv.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV; + usim->get_imei_vec(sec_mode_comp.imeisv.imeisv, 15); + sec_mode_comp.imeisv.imeisv[14] = 5; + sec_mode_comp.imeisv.imeisv[15] = 3; + } else { + sec_mode_comp.imeisv_present = false; + } + + // Send response + pdu->reset(); + liblte_mme_pack_security_mode_complete_msg(&sec_mode_comp, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *) pdu); + if(pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + cipher_encrypt(pdu); + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); + nas_log->info("Sending Security Mode Complete nas_current_ctxt.tx_count=%d, RB=%s\n", + ctxt.tx_count, + rrc->get_rb_name(lcid).c_str()); + rrc->write_sdu(lcid, pdu); + ctxt.tx_count++; +} + +void nas::parse_service_reject(uint32_t lcid, byte_buffer_t *pdu) { + nas_log->error("TODO:parse_service_reject\n"); + pool->deallocate(pdu); +} + +void nas::parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu) { + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT esm_info_req; + liblte_mme_unpack_esm_information_request_msg((LIBLTE_BYTE_MSG_STRUCT *)pdu, &esm_info_req); + + nas_log->info("ESM information request received for beaser=%d, transaction_id=%d\n", esm_info_req.eps_bearer_id, esm_info_req.proc_transaction_id); + ctxt.rx_count++; + pool->deallocate(pdu); + + // send response + send_esm_information_response(esm_info_req.proc_transaction_id); +} + +void nas::parse_emm_information(uint32_t lcid, byte_buffer_t *pdu) { + liblte_mme_unpack_emm_information_msg((LIBLTE_BYTE_MSG_STRUCT *) pdu, &emm_info); + std::string str = emm_info_str(&emm_info); + nas_log->info("Received EMM Information: %s\n", str.c_str()); + nas_log->console("%s\n", str.c_str()); + ctxt.rx_count++; + pool->deallocate(pdu); +} + +/******************************************************************************* + * Senders + ******************************************************************************/ + +void nas::gen_attach_request(byte_buffer_t *msg) { + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in gen_attach_request().\n"); + return; + } + LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req; + bzero(&attach_req, sizeof(LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT)); + + nas_log->info("Generating attach request\n"); + + attach_req.eps_attach_type = LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH; + + for (u_int32_t i = 0; i < 8; i++) { + attach_req.ue_network_cap.eea[i] = eea_caps[i]; + attach_req.ue_network_cap.eia[i] = eia_caps[i]; + } + + attach_req.ue_network_cap.uea_present = false; // UMTS encryption algos + attach_req.ue_network_cap.uia_present = false; // UMTS integrity algos + attach_req.ue_network_cap.ucs2_present = false; + attach_req.ms_network_cap_present = false; // A/Gb mode (2G) or Iu mode (3G) + attach_req.ue_network_cap.lpp_present = false; + attach_req.ue_network_cap.lcs_present = false; + attach_req.ue_network_cap.onexsrvcc_present = false; + attach_req.ue_network_cap.nf_present = false; + attach_req.old_p_tmsi_signature_present = false; + attach_req.additional_guti_present = false; + attach_req.last_visited_registered_tai_present = false; + attach_req.drx_param_present = false; + attach_req.old_lai_present = false; + attach_req.tmsi_status_present = false; + attach_req.ms_cm2_present = false; + attach_req.ms_cm3_present = false; + attach_req.supported_codecs_present = false; + attach_req.additional_update_type_present = false; + attach_req.voice_domain_pref_and_ue_usage_setting_present = false; + attach_req.device_properties_present = false; + attach_req.old_guti_type_present = false; + + // ESM message (PDN connectivity request) for first default bearer + gen_pdn_connectivity_request(&attach_req.esm_msg); + + // GUTI or IMSI attach + if(have_guti && have_ctxt) { + attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI; + memcpy(&attach_req.eps_mobile_id.guti, &ctxt.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT)); + attach_req.old_guti_type = LIBLTE_MME_GUTI_TYPE_NATIVE; + attach_req.old_guti_type_present = true; + attach_req.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + attach_req.nas_ksi.nas_ksi = ctxt.ksi; + nas_log->info("Requesting GUTI attach. " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + ctxt.guti.m_tmsi, ctxt.guti.mcc, ctxt.guti.mnc, ctxt.guti.mme_group_id, ctxt.guti.mme_code); + liblte_mme_pack_attach_request_msg(&attach_req, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *) msg); + + // Add MAC + if (msg->N_bytes > 5) { + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &msg->msg[5], + msg->N_bytes - 5, + &msg->msg[1]); + } else { + nas_log->error("Invalid PDU size %d\n", msg->N_bytes); + } + } else { + attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; + attach_req.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + attach_req.nas_ksi.nas_ksi = 0; + usim->get_imsi_vec(attach_req.eps_mobile_id.imsi, 15); + nas_log->info("Requesting IMSI attach (IMSI=%s)\n", usim->get_imsi_str().c_str()); + liblte_mme_pack_attach_request_msg(&attach_req, (LIBLTE_BYTE_MSG_STRUCT *) msg); + } + + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + + if (have_ctxt) { + ctxt.tx_count++; + } +} + + +void nas::gen_service_request(byte_buffer_t *msg) { + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in gen_service_request().\n"); + return; + } + + nas_log->info("Generating service request\n"); + + // Pack the service request message directly + msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg->N_bytes++; + msg->msg[1] = (ctxt.ksi & 0x07) << 5; + msg->msg[1] |= ctxt.tx_count & 0x1F; + msg->N_bytes++; + + uint8_t mac[4]; + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &msg->msg[0], + 2, + &mac[0]); + // Set the short MAC + msg->msg[2] = mac[2]; + msg->N_bytes++; + msg->msg[3] = mac[3]; + msg->N_bytes++; + + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + + ctxt.tx_count++; +} + +void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) { + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT pdn_con_req; + ZERO_OBJECT(pdn_con_req); + + nas_log->info("Generating PDN Connectivity Request\n"); + + // Set the PDN con req parameters + pdn_con_req.eps_bearer_id = 0x00; // Unassigned bearer ID + pdn_con_req.proc_transaction_id = 0x01; // First transaction ID + pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4; + pdn_con_req.request_type = LIBLTE_MME_REQUEST_TYPE_INITIAL_REQUEST; + pdn_con_req.apn_present = false; + + // Set the optional flags + if (cfg.apn == "") { + pdn_con_req.esm_info_transfer_flag_present = false; + } else { + // request ESM info transfer is APN is specified + pdn_con_req.esm_info_transfer_flag_present = true; + pdn_con_req.esm_info_transfer_flag = LIBLTE_MME_ESM_INFO_TRANSFER_FLAG_REQUIRED; + } + + pdn_con_req.protocol_cnfg_opts_present = false; + pdn_con_req.device_properties_present = false; + + // Pack the message + liblte_mme_pack_pdn_connectivity_request_msg(&pdn_con_req, msg); +} + +void nas::send_security_mode_reject(uint8_t cause) { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_security_mode_reject().\n"); + return; + } + + LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej = {0}; + sec_mode_rej.emm_cause = cause; + liblte_mme_pack_security_mode_reject_msg(&sec_mode_rej, (LIBLTE_BYTE_MSG_STRUCT *) msg); + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + nas_log->info("Sending security mode reject\n"); + rrc->write_sdu(cfg.lcid, msg); +} + + +void nas::send_authentication_response(const uint8_t* res, const size_t res_len) { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_authentication_response().\n"); + return; + } + + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res; + bzero(&auth_res, sizeof(LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT)); + + for (uint32_t i = 0; i < res_len; i++) { + auth_res.res[i] = res[i]; + } + auth_res.res_len = res_len; + liblte_mme_pack_authentication_response_msg(&auth_res, (LIBLTE_BYTE_MSG_STRUCT *)msg); + + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + nas_log->info("Sending Authentication Response\n"); + rrc->write_sdu(cfg.lcid, msg); +} + + +void nas::send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param) { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_authentication_failure().\n"); + return; + } + + LIBLTE_MME_AUTHENTICATION_FAILURE_MSG_STRUCT auth_failure; + auth_failure.emm_cause = cause; + if (auth_fail_param) { + memcpy(auth_failure.auth_fail_param, auth_fail_param, 14); + nas_log->debug_hex(auth_failure.auth_fail_param, 14, "auth_failure.auth_fail_param\n"); + auth_failure.auth_fail_param_present = true; + } else { + auth_failure.auth_fail_param_present = false; + } + + liblte_mme_pack_authentication_failure_msg(&auth_failure, (LIBLTE_BYTE_MSG_STRUCT *)msg); + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + nas_log->info("Sending authentication failure.\n"); + rrc->write_sdu(cfg.lcid, msg); +} + + +void nas::send_identity_response() {} + +void nas::send_service_request() { + byte_buffer_t *msg = pool_allocate; + if (!msg) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_service_request().\n"); + return; + } + + // Pack the service request message directly + msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg->N_bytes++; + msg->msg[1] = (ctxt.ksi & 0x07) << 5; + msg->msg[1] |= ctxt.tx_count & 0x1F; + msg->N_bytes++; + + uint8_t mac[4]; + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &msg->msg[0], + 2, + &mac[0]); + // Set the short MAC + msg->msg[2] = mac[2]; + msg->N_bytes++; + msg->msg[3] = mac[3]; + msg->N_bytes++; + + if(pcap != NULL) { + pcap->write_nas(msg->msg, msg->N_bytes); + } + + nas_log->info("Sending service request\n"); + rrc->write_sdu(cfg.lcid, msg); + ctxt.tx_count++; +} + +void nas::send_esm_information_response(const uint8 proc_transaction_id) { + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT esm_info_resp; + esm_info_resp.proc_transaction_id = proc_transaction_id; + esm_info_resp.eps_bearer_id = 0; // respone shall always have no bearer assigned + + if (cfg.apn == "") { + esm_info_resp.apn_present = false; + } else { + nas_log->debug("Including APN %s in ESM info response\n", cfg.apn.c_str()); + esm_info_resp.apn_present = true; + int len = std::min((int)cfg.apn.length(), LIBLTE_STRING_LEN-1); + strncpy(esm_info_resp.apn.apn, cfg.apn.c_str(), len); + esm_info_resp.apn.apn[len] = '\0'; + } + + + if (cfg.user != "" && cfg.user.length() < LIBLTE_STRING_LEN && + cfg.pass != "" && cfg.pass.length() < LIBLTE_STRING_LEN) { + + nas_log->debug("Including CHAP authentication for user %s in ESM info response\n", cfg.user.c_str()); + + // Generate CHAP challenge + uint16_t len = 1 /* CHAP code */ + + 1 /* ID */ + + 2 /* complete length */ + + 1 /* data value size */ + + 16 /* data value */ + + cfg.user.length(); + + uint8_t challenge[len]; + bzero(challenge, len*sizeof(uint8_t)); + challenge[0] = 0x01; // challenge code + challenge[1] = chap_id; // ID + challenge[2] = (len >> 8) & 0xff; + challenge[3] = len & 0xff; + challenge[4] = 16; + + // Append random challenge value + for (int i = 0; i < 16; i++) { + challenge[5 + i] = rand() & 0xFF; + } + + // add user as name field + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + challenge[21 + i] = name[i]; + } + + // Generate response + uint8_t response[len]; + bzero(response, len*sizeof(uint8_t)); + response[0] = 0x02; // response code + response[1] = chap_id; + response[2] = (len >> 8) & 0xff; + response[3] = len & 0xff; + response[4] = 16; + + // Generate response value + uint16_t resp_val_len = 16 /* MD5 len */ + + 1 /* ID */ + + cfg.pass.length(); + uint8_t resp_val[resp_val_len]; + resp_val[0] = chap_id; + + // add secret + for (size_t i = 0; i < cfg.pass.length(); i++) { + const char* pass = cfg.pass.c_str(); + resp_val[1 + i] = pass[i]; + } + + // copy original challenge behind secret + uint8_t *chal_val = &challenge[5]; + memcpy(&resp_val[1+cfg.pass.length()], chal_val, 16); + + // Compute MD5 of resp_val and add to response + security_md5(resp_val, resp_val_len, &response[5]); + + // add user as name field again + for (size_t i = 0; i < cfg.user.length(); i++) { + const char *name = cfg.user.c_str(); + response[21 + i] = name[i]; + } + + // Add challenge and resposne to ESM info response + esm_info_resp.protocol_cnfg_opts_present = true; + esm_info_resp.protocol_cnfg_opts.opt[0].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[0].contents, challenge, sizeof(challenge)); + esm_info_resp.protocol_cnfg_opts.opt[0].len = sizeof(challenge); + + esm_info_resp.protocol_cnfg_opts.opt[1].id = LIBLTE_MME_CONFIGURATION_PROTOCOL_OPTIONS_CHAP; + memcpy(esm_info_resp.protocol_cnfg_opts.opt[1].contents, response, sizeof(response)); + esm_info_resp.protocol_cnfg_opts.opt[1].len = sizeof(response); + esm_info_resp.protocol_cnfg_opts.N_opts = 2; + } else { + esm_info_resp.protocol_cnfg_opts_present = false; + } + + byte_buffer_t *pdu = pool_allocate; + if (!pdu) { + nas_log->error("Fatal Error: Couldn't allocate PDU in send_attach_request().\n"); + return; + } + + if (liblte_mme_pack_esm_information_response_msg(&esm_info_resp, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, + ctxt.tx_count, + (LIBLTE_BYTE_MSG_STRUCT *)pdu)) { + nas_log->error("Error packing ESM information response.\n"); + return; + } + + if(pcap != NULL) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + cipher_encrypt(pdu); + if (pdu->N_bytes > 5) { + integrity_generate(&k_nas_int[16], + ctxt.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); + } else { + nas_log->error("Invalid PDU size %d\n", pdu->N_bytes); + return; + } + + nas_log->info_hex(pdu->msg, pdu->N_bytes, "Sending ESM information response\n"); + rrc->write_sdu(cfg.lcid, pdu); + + ctxt.tx_count++; + chap_id++; +} + + +/******************************************************************************* + * Security context persistence file + ******************************************************************************/ + +bool nas::read_ctxt_file(nas_sec_ctxt *ctxt) +{ + std::ifstream file; + if(!ctxt) { + return false; + } + + if (cfg.force_imsi_attach) { + nas_log->info("Skip reading context from file.\n"); + return false; + } + + file.open(".ctxt", std::ios::in); + if(file.is_open()) { + if(!readvar(file, "m_tmsi=", &ctxt->guti.m_tmsi)) {return false;} + if(!readvar(file, "mcc=", &ctxt->guti.mcc)) {return false;} + if(!readvar(file, "mnc=", &ctxt->guti.mnc)) {return false;} + if(!readvar(file, "mme_group_id=", &ctxt->guti.mme_group_id)) {return false;} + if(!readvar(file, "mme_code=", &ctxt->guti.mme_code)) {return false;} + if(!readvar(file, "tx_count=", &ctxt->tx_count)) {return false;} + if(!readvar(file, "rx_count=", &ctxt->rx_count)) {return false;} + if(!readvar(file, "int_alg=", &ctxt->integ_algo)) {return false;} + if(!readvar(file, "enc_alg=", &ctxt->cipher_algo)) {return false;} + if(!readvar(file, "ksi=", &ctxt->ksi)) {return false;} + + if(!readvar(file, "k_asme=", ctxt->k_asme, 32)) {return false;} + + file.close(); + have_guti = true; + nas_log->info("Read GUTI from file " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + ctxt->guti.m_tmsi, + ctxt->guti.mcc, + ctxt->guti.mnc, + ctxt->guti.mme_group_id, + ctxt->guti.mme_code); + have_ctxt = true; + nas_log->info("Read security ctxt from file .ctxt. " + "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d\n", + ctxt->ksi, + hex_to_string(ctxt->k_asme,32).c_str(), + ctxt->tx_count, + ctxt->rx_count, + ctxt->integ_algo, + ctxt->cipher_algo); + return true; + + } else { + return false; + } +} + +bool nas::write_ctxt_file(nas_sec_ctxt ctxt) +{ + if (!have_guti || !have_ctxt) { + return false; + } + std::ofstream file; + file.open(".ctxt", std::ios::out | std::ios::trunc); + if (file.is_open()) { + file << "m_tmsi=" << (int) ctxt.guti.m_tmsi << std::endl; + file << "mcc=" << (int) ctxt.guti.mcc << std::endl; + file << "mnc=" << (int) ctxt.guti.mnc << std::endl; + file << "mme_group_id=" << (int) ctxt.guti.mme_group_id << std::endl; + file << "mme_code=" << (int) ctxt.guti.mme_code << std::endl; + file << "tx_count=" << (int) ctxt.tx_count << std::endl; + file << "rx_count=" << (int) ctxt.rx_count << std::endl; + file << "int_alg=" << (int) ctxt.integ_algo << std::endl; + file << "enc_alg=" << (int) ctxt.cipher_algo << std::endl; + file << "ksi=" << (int) ctxt.ksi << std::endl; + + file << "k_asme=" << hex_to_string(ctxt.k_asme, 32) << std::endl; + + nas_log->info("Saved GUTI to file " + "m_tmsi: %x, mcc: %x, mnc: %x, mme_group_id: %x, mme_code: %x\n", + ctxt.guti.m_tmsi, + ctxt.guti.mcc, + ctxt.guti.mnc, + ctxt.guti.mme_group_id, + ctxt.guti.mme_code); + nas_log->info("Saved security ctxt to file .ctxt. " + "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d\n", + ctxt.ksi, + hex_to_string(ctxt.k_asme,32).c_str(), + ctxt.tx_count, + ctxt.rx_count, + ctxt.integ_algo, + ctxt.cipher_algo); + file.close(); + return true; + } else { + return false; + } +} + +/********************************************************************* + * Conversion helpers + ********************************************************************/ +std::string nas::hex_to_string(uint8_t *hex, int size) +{ + std::stringstream ss; + + ss << std::hex << std::setfill('0'); + for(int i=0; i(hex[i]); + } + return ss.str(); +} + +bool nas::string_to_hex(std::string hex_str, uint8_t *hex, uint32_t len) +{ + static const char* const lut = "0123456789abcdef"; + uint32_t str_len = hex_str.length(); + if(str_len & 1) { + return false; // uneven hex_str length + } + if(str_len > len*2) { + return false; // not enough space in hex buffer + } + for(uint32_t i=0; ifull_net_name_present) { + ss << info->full_net_name.name; + } + if(info->short_net_name_present) { + ss << " (" << info->short_net_name.name << ")"; + } + if(info->utc_and_local_time_zone_present) { + ss << " " << (int)info->utc_and_local_time_zone.day; + ss << "/" << (int)info->utc_and_local_time_zone.month; + ss << "/" << (int)info->utc_and_local_time_zone.year; + ss << " " << (int)info->utc_and_local_time_zone.hour; + ss << ":" << (int)info->utc_and_local_time_zone.minute; + ss << ":" << (int)info->utc_and_local_time_zone.second; + ss << " TZ:" << (int)info->utc_and_local_time_zone.tz; + } + return ss.str(); +} + + +} // namespace srsue diff --git a/srsue/src/upper/pcsc_usim.cc b/srsue/src/upper/pcsc_usim.cc new file mode 100644 index 0000000..0d423c7 --- /dev/null +++ b/srsue/src/upper/pcsc_usim.cc @@ -0,0 +1,1370 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/common/bcd_helpers.h" +#include "string.h" + +#define CHECK_SIM_PIN 1 + +using namespace srslte; + +namespace srsue{ + +pcsc_usim::pcsc_usim() : initiated(false) +{ + bzero(ck, CK_LEN); + bzero(ik, IK_LEN); + bzero(auts, IK_LEN); +} + +pcsc_usim::~pcsc_usim() +{ + sc.deinit(); +} + +int pcsc_usim::init(usim_args_t *args, srslte::log *log_) +{ + int ret = SRSLTE_ERROR; + log = log_; + + if (sc.init(args, log_) != SRSLTE_SUCCESS) { + return ret; + } + + // Read IMSI from SIM card + char tmp[15]; + size_t tmp_len = 15; // set to max IMSI length + if (sc.get_imsi(tmp, &tmp_len)) { + log->error("Error reading IMSI from SIM.\n"); + return ret; + } + imsi_str = tmp; + + // Check extracted IMSI and convert + if(15 == imsi_str.length()) { + const char *imsi_c = imsi_str.c_str(); + imsi = 0; + for(int i = 0; i < 15; i++) + { + imsi *= 10; + imsi += imsi_c[i] - '0'; + } + } else { + log->error("Invalid length for IMSI: %zu should be %d\n", imsi_str.length(), 15); + log->console("Invalid length for IMSI: %zu should be %d\n", imsi_str.length(), 15); + return ret; + } + + // Check IMEI + if(15 == args->imei.length()) { + const char *imei_c = args->imei.c_str(); + imei = 0; + for(int i = 0; i < 15; i++) + { + imei *= 10; + imei += imei_c[i] - '0'; + } + } else { + log->error("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + log->console("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + return ret; + } + + // Get MNC length + mnc_length = sc.get_mnc_len(); + log->debug("MNC length %d\n", mnc_length); + + initiated = true; + ret = SRSLTE_SUCCESS; + + return ret; +} + +void pcsc_usim::stop() +{} + + +/******************************************************************************* + NAS interface +*******************************************************************************/ + +std::string pcsc_usim::get_imsi_str() +{ + return imsi_str; +} +std::string pcsc_usim::get_imei_str() +{ + return imei_str; +} + +bool pcsc_usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imsi_ || n < 15) { + log->error("Invalid parameters to get_imsi_vec"); + return false; + } + + uint64_t temp = imsi; + for(int i=14;i>=0;i--) { + imsi_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool pcsc_usim::get_imei_vec(uint8_t* imei_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imei_ || n < 15) { + log->error("Invalid parameters to get_imei_vec"); + return false; + } + + uint64 temp = imei; + for(int i=14;i>=0;i--) + { + imei_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool pcsc_usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::ostringstream mcc_str, mnc_str; + + int mcc_len = 3; + for (int i=0;imcc); + string_to_mnc(mnc_str.str(), &home_plmn_id->mnc); + + log->info("Read Home PLMN Id=%s\n", + plmn_id_to_string(*home_plmn_id).c_str()); + + return true; +} + +auth_result_t pcsc_usim::generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) +{ + auth_result_t ret = AUTH_FAILED; + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return ret; + } + + // Use RAND and AUTN to compute RES, CK, IK using SIM card + switch (sc.umts_auth(rand, autn_enb, res, res_len, ik, ck, auts)) { + case 0: + log->info("SCARD: USIM authentication successful.\n"); + break; + case -1: + log->error("SCARD: Failure during USIM UMTS authentication\n"); + return ret; + case -2: + log->info("SCARD: USIM synchronization failure, AUTS generated\n"); + log->debug_hex(auts, AKA_AUTS_LEN, "AUTS\n"); + memcpy(res, auts, AKA_AUTS_LEN); + *res_len = AKA_AUTS_LEN; + return AUTH_SYNCH_FAILURE; + default: + log->warning("SCARD: Unknown USIM failure.\n"); + return ret; + } + + // FIXME: Extract ak and seq from auts + memset(ak, 0x00, AK_LEN); + + // Extract sqn from autn + uint8_t sqn[SQN_LEN]; + for(int i=0;i<6;i++) { + sqn[i] = autn_enb[i] ^ ak[i]; + } + + // Generate K_asme + log->debug_hex(ck, CK_LEN, "CK:\n"); + log->debug_hex(ik, IK_LEN, "IK:\n"); + log->debug_hex(ak, AK_LEN, "AK:\n"); + log->debug_hex(sqn, SQN_LEN, "SQN:\n"); + log->debug("mcc=%d, mnc=%d\n", mcc, mnc); + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + log->info_hex(k_asme, KEY_LEN, "K_ASME:\n"); + + ret = AUTH_OK; + + return ret; +} + +void pcsc_usim::generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + // Generate K_nas_enc and K_nas_int + security_generate_k_nas( k_asme, + cipher_algo, + integ_algo, + k_nas_enc, + k_nas_int); +} + +/******************************************************************************* + RRC interface +*******************************************************************************/ + +void pcsc_usim::generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + // Generate K_enb + security_generate_k_enb( k_asme, + count_ul, + k_enb); + + memcpy(this->k_asme, k_asme, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); + + current_ncc = 0; +} + +void pcsc_usim::generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return; + } + + uint8_t *enb_star_key = k_enb; + + if (ncc < 0) { + ncc = current_ncc; + } + + // Generate successive NH + while(current_ncc != (uint32_t) ncc) { + uint8_t *sync = NULL; + if (current_ncc) { + sync = nh; + } else { + sync = k_enb; + } + // Generate NH + security_generate_nh(k_asme, + sync, + nh); + + current_ncc++; + if (current_ncc == 7) { + current_ncc = 0; + } + enb_star_key = nh; + } + + // Generate K_enb + security_generate_k_enb_star( enb_star_key, + pci, + earfcn, + k_enb_star); + + // K_enb becomes K_enb* + memcpy(k_enb, k_enb_star, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); +} + +/******************************************************************************* + Helpers +*******************************************************************************/ + + +/********************************* + * PC/SC class + ********************************/ + +// return 0 if initialization was successfull, -1 otherwies +int pcsc_usim::scard::init(usim_args_t *args, srslte::log *log_) +{ + int ret_value = SRSLTE_ERROR; + uint pos = 0; // SC reader + bool reader_found = false; + //int transaction = 1; + size_t blen; + log = log_; + + long ret; + ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_context); + if (ret != SCARD_S_SUCCESS) { + log->error("SCardEstablishContext(): %s\n", pcsc_stringify_error(ret)); + return ret_value; + } + + unsigned long len; + ret = SCardListReaders(scard_context, NULL, NULL, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("SCardListReaders(): %s\n", pcsc_stringify_error(ret)); + return ret_value; + } + + char *readers = (char *)malloc(len); + if (readers == NULL) { + log->error("Malloc failed\n"); + return ret_value; + } + + ret = SCardListReaders(scard_context, NULL, readers, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("SCardListReaders() 2: %s\n", pcsc_stringify_error(ret)); + goto clean_exit; + } + if (len < 3) { + log->info("No smart card readers available.\n"); + return ret_value; + } + + + /* readers: NULL-separated list of reader names, and terminating NULL */ + pos = 0; + while (pos < len-1) { + log->info("Available Card Reader: %s\n", &readers[pos]); + while (readers[pos] != '\0' && pos < len) { + pos++; + } + pos++; // skip separator + } + + reader_found = false; + pos = 0; + + // If no reader specified, test all available readers for SIM cards. Otherwise consider specified reader only. + if (args->reader.length() == 0) { + while (pos < len && !reader_found) { + log->info("Trying Card Reader: %s\n", &readers[pos]); + // Connect to card + ret = SCardConnect(scard_context, &readers[pos], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard_handle, &scard_protocol); + if (ret == SCARD_S_SUCCESS) { + reader_found = true; + } else { + if (ret == (long)SCARD_E_NO_SMARTCARD) { + log->error("No smart card inserted.\n"); + } else { + log->error("%s\n", pcsc_stringify_error(ret)); + } + log->info("Failed to use Card Reader: %s\n", &readers[pos]); + + // proceed to next reader + while (pos < len && readers[pos] != '\0' ) { + pos++; + } + pos++; // skip separator + } + } + } else { + // card reader specified in config. search for this reader. + while (pos < len && !reader_found) { + if (strcmp(&readers[pos], args->reader.c_str()) == 0) { + reader_found = true; + log->info("Card Reader found: %s\n", args->reader.c_str()); + } else { + // next reader + while (pos < len && readers[pos] != '\0' ) { + pos++; + } + pos++; // skip separator + } + } + if (!reader_found) { + log->error("Cannot find reader: %s\n", args->reader.c_str()); + } else { + ret = SCardConnect(scard_context, &readers[pos], SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &scard_handle, &scard_protocol); + if (ret == SCARD_S_SUCCESS) { + // successfully connected to card + } else { + if (ret == (long)SCARD_E_NO_SMARTCARD) { + log->error("No smart card inserted.\n"); + } else { + log->error("%s\n", pcsc_stringify_error(ret)); + } + + log->info("Failed to use Card Reader: %s\n", args->reader.c_str()); + } + } + } + + free(readers); + readers = NULL; + + log->info("Card=0x%x active_protocol=%lu (%s)\n", + (unsigned int)scard_handle, (unsigned long)scard_protocol, scard_protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); + + ret = SCardBeginTransaction(scard_handle); + if (ret != SCARD_S_SUCCESS) { + log->error("%s\n", pcsc_stringify_error(ret)); + goto clean_exit; + } + + // Verify USIM support + unsigned char buf[100]; + blen = sizeof(buf); + if (_select_file(SCARD_FILE_MF, buf, &blen, SCARD_USIM, NULL, 0)) { + log->info("USIM is not supported. Trying to use GSM SIM"); + sim_type = SCARD_GSM_SIM; + } else { + log->info("USIM is supported\n"); + sim_type = SCARD_USIM; + } + + if (sim_type == SCARD_GSM_SIM) { + blen = sizeof(buf); + if (select_file(SCARD_FILE_MF, buf, &blen)) { + log->debug("SCARD: Failed to read MF\n"); + goto clean_exit; + } + + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_DF, buf, &blen)) { + log->debug("SCARD: Failed to read GSM DF\n"); + goto clean_exit; + } + } else { + unsigned char aid[32]; + int aid_len; + + aid_len = get_aid(aid, sizeof(aid)); + if (aid_len < 0) { + log->debug("SCARD: Failed to find AID for 3G USIM app - try to use standard 3G RID\n"); + memcpy(aid, "\xa0\x00\x00\x00\x87", 5); + aid_len = 5; + } + + log->debug_hex(aid, aid_len, "SCARD: 3G USIM AID\n"); + + /* Select based on AID = 3G RID from EF_DIR. This is usually + * starting with A0 00 00 00 87. */ + blen = sizeof(buf); + if (_select_file(0, buf, &blen, sim_type, aid, aid_len)) { + log->error("SCARD: Failed to read 3G USIM app\n"); + log->error_hex(aid, aid_len, "SCARD: 3G USIM AID\n"); + goto clean_exit; + } + } + +#if CHECK_SIM_PIN + // Verify whether CHV1 (PIN1) is needed to access the card. + ret = pin_needed(buf, blen); + if (ret < 0) { + log->debug("SCARD: Failed to determine whether PIN is needed\n"); + goto clean_exit; + } + if (ret) { + log->debug("PIN1 needed for SIM access (retry counter=%d)\n", get_pin_retry_counter()); + pin1_needed = true; + } else { + pin1_needed = false; + } + + // stop before pin retry counter reaches zero + if (pin1_needed && get_pin_retry_counter() <= 1) { + log->error("PIN1 needed for SIM access (retry counter=%d), emergency stop.\n", get_pin_retry_counter()); + goto clean_exit; + } + + // Set pin + if (pin1_needed) { + // verify PIN + ret = verify_pin(args->pin.c_str()); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Could not verify PIN\n"); + goto clean_exit; + } + } +#else + pin1_needed = false; +#endif + + ret = SCardEndTransaction(scard_handle, SCARD_LEAVE_CARD); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Could not end transaction: 0x%x\n", (unsigned int) ret); + goto clean_exit; + } + + ret_value = SRSLTE_SUCCESS; + +clean_exit: + if (readers) { + free(readers); + } + + return ret_value; +} + + +int pcsc_usim::scard::_select_file(unsigned short file_id, unsigned char *buf, size_t *buf_len, sim_types_t sim_type, unsigned char *aid, size_t aidlen) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[50] = { SIM_CMD_SELECT }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + size_t len, rlen; + + if (sim_type == SCARD_USIM) { + cmd[0] = USIM_CLA; + cmd[3] = 0x04; + get_resp[0] = USIM_CLA; + } + + log->debug("SCARD: select file %04x\n", file_id); + if (aid) { + log->debug_hex(aid, aidlen, "SCARD: select file by AID"); + if (5 + aidlen > sizeof(cmd)) + return -1; + cmd[2] = 0x04; /* Select by AID */ + cmd[4] = aidlen; /* len */ + memcpy(cmd + 5, aid, aidlen); + cmdlen = 5 + aidlen; + } else { + cmd[5] = file_id >> 8; + cmd[6] = file_id & 0xff; + cmdlen = 7; + } + len = sizeof(resp); + ret = transmit(cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) { + log->error("SCARD: SCardTransmit failed %s\n", pcsc_stringify_error(ret)); + return -1; + } + + if (len != 2) { + log->error("SCARD: unexpected resp len %d (expected 2)\n", (int)len); + return -1; + } + + if (resp[0] == 0x98 && resp[1] == 0x04) { + /* Security status not satisfied (PIN_WLAN) */ + log->warning("SCARD: Security status not satisfied.\n"); + return -1; + } + + if (resp[0] == 0x6e) { + log->debug("SCARD: used CLA not supported.\n"); + return -1; + } + + if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { + log->warning("SCARD: unexpected response 0x%02x (expected 0x61, 0x6c, or 0x9f)\n", resp[0]); + return -1; + } + + /* Normal ending of command; resp[1] bytes available */ + get_resp[4] = resp[1]; + log->debug("SCARD: trying to get response (%d bytes)\n", resp[1]); + + rlen = *buf_len; + ret = transmit(get_resp, sizeof(get_resp), buf, &rlen); + if (ret == SCARD_S_SUCCESS) { + *buf_len = resp[1] < rlen ? resp[1] : rlen; + return 0; + } + + log->warning("SCARD: SCardTransmit err=0x%lx\n", ret); + return -1; +} + + +int pcsc_usim::scard::select_file(unsigned short file_id,unsigned char *buf, size_t *buf_len) +{ + return _select_file(file_id, buf, buf_len, sim_type, NULL, 0); +} + + +long pcsc_usim::scard::transmit(unsigned char *_send, size_t send_len, unsigned char *_recv, size_t *recv_len) +{ + long ret; + unsigned long rlen; + + log->debug_hex(_send, send_len, "SCARD: scard_transmit: send\n"); + rlen = *recv_len; + ret = SCardTransmit(scard_handle, + scard_protocol == SCARD_PROTOCOL_T1 ? + SCARD_PCI_T1 : SCARD_PCI_T0, + _send, (unsigned long) send_len, + NULL, _recv, &rlen); + *recv_len = rlen; + if (ret == SCARD_S_SUCCESS) { + log->debug_hex(_recv, rlen, "SCARD: SCardTransmit: recv\n"); + } else { + log->error("SCARD: SCardTransmit failed %s\n", pcsc_stringify_error(ret)); + } + return ret; +} + +int pcsc_usim::scard::pin_needed(unsigned char *hdr, size_t hlen) +{ + if (sim_type == SCARD_GSM_SIM) { + if (hlen > SCARD_CHV1_OFFSET && !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) + return 1; + return 0; + } + + if (sim_type == SCARD_USIM) { + int ps_do; + if (parse_fsp_templ(hdr, hlen, &ps_do, NULL)) + return -1; + /* TODO: there could be more than one PS_DO entry because of + * multiple PINs in key reference.. */ + if (ps_do > 0 && (ps_do & 0x80)) + return 1; + return 0; + } + + return -1; +} + + + +int pcsc_usim::scard::get_pin_retry_counter() +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + uint16_t val; + + log->info("SCARD: fetching PIN retry counter\n"); + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[4] = 0; /* Empty data */ + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2) { + log->error("SCARD: failed to fetch PIN retry counter\n"); + return -1; + } + + val = to_uint16(resp); + if (val == 0x63c0 || val == 0x6983) { + log->debug("SCARD: PIN has been blocked\n"); + return 0; + } + + if (val >= 0x63c0 && val <= 0x63cf) + return val & 0x000f; + + log->info("SCARD: Unexpected PIN retry counter response value 0x%x\n", val); + return 0; +} + + + +int pcsc_usim::scard::get_aid(unsigned char *aid, size_t maxlen) +{ + int rlen, rec; + struct efdir { + unsigned char appl_template_tag; /* 0x61 */ + unsigned char appl_template_len; + unsigned char appl_id_tag; /* 0x4f */ + unsigned char aid_len; + unsigned char rid[5]; + unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ + } *efdir; + unsigned char buf[127], *aid_pos; + size_t blen; + unsigned int aid_len = 0; + + efdir = (struct efdir *) buf; + aid_pos = &buf[4]; + blen = sizeof(buf); + if (select_file(SCARD_FILE_EF_DIR, buf, &blen)) { + log->debug("SCARD: Failed to read EF_DIR\n"); + return -1; + } + log->debug_hex(buf, blen, "SCARD: EF_DIR select\n"); + + for (rec = 1; rec < 10; rec++) { + rlen = get_record_len(rec, SIM_RECORD_MODE_ABSOLUTE); + if (rlen < 0) { + log->debug("SCARD: Failed to get EF_DIR record length\n"); + return -1; + } + blen = sizeof(buf); + if (rlen > (int) blen) { + log->debug("SCARD: Too long EF_DIR record\n"); + return -1; + } + if (read_record(buf, rlen, rec, SIM_RECORD_MODE_ABSOLUTE) < 0) { + log->debug("SCARD: Failed to read EF_DIR record %d", rec); + return -1; + } + log->debug_hex(buf, rlen, "SCARD: EF_DIR record\n"); + + if (efdir->appl_template_tag != 0x61) { + log->debug("SCARD: Unexpected application template tag 0x%x", efdir->appl_template_tag); + continue; + } + + if (efdir->appl_template_len > rlen - 2) { + log->debug("SCARD: Too long application template (len=%d rlen=%d)", efdir->appl_template_len, rlen); + continue; + } + + if (efdir->appl_id_tag != 0x4f) { + log->debug("SCARD: Unexpected application identifier tag 0x%x", efdir->appl_id_tag); + continue; + } + + aid_len = efdir->aid_len; + if (aid_len < 1 || aid_len > 16) { + log->debug("SCARD: Invalid AID length %u\n", aid_len); + continue; + } + + log->debug_hex(aid_pos, aid_len, "SCARD: AID from EF_DIR record\n"); + + if (efdir->appl_code[0] == 0x10 && efdir->appl_code[1] == 0x02) { + log->debug("SCARD: 3G USIM app found from EF_DIR record %d\n", rec); + break; + } + } + + if (rec >= 10) { + log->debug("SCARD: 3G USIM app not found from EF_DIR records\n"); + return -1; + } + + if (aid_len > maxlen) { + log->debug("SCARD: Too long AID\n"); + return -1; + } + + memcpy(aid, aid_pos, aid_len); + + return aid_len; +} + +int pcsc_usim::scard::get_record_len(unsigned char recnum, unsigned char mode) +{ + unsigned char buf[255]; + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen; + long ret; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = sizeof(buf); + + blen = sizeof(buf); + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: failed to determine file length for record %d\n", recnum); + return -1; + } + + log->debug_hex(buf, blen, "SCARD: file length determination response\n"); + + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { + log->error("SCARD: unexpected response to file length determination\n"); + return -1; + } + + return buf[1]; +} + + +int pcsc_usim::scard::read_record(unsigned char *data, size_t len, unsigned char recnum, unsigned char mode) +{ + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = len; + + buf = (unsigned char*)malloc(blen); + if (buf == NULL) + return -1; + + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + free(buf); + return -2; + } + if (blen != len + 2) { + log->debug("SCARD: record read returned unexpected length %ld (expected %ld)\n", (long) blen, (long) len + 2); + free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + log->debug("SCARD: record read returned unexpected status %02x %02x (expected 90 00)\n", buf[len], buf[len + 1]); + free(buf); + return -4; + } + + memcpy(data, buf, len); + free(buf); + + return 0; +} + + +/** + * scard_get_imsi - Read IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * @imsi: Buffer for IMSI + * @len: Length of imsi buffer; set to IMSI length on success + * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file + * selection returns invalid result code, -3 if parsing FSP template file fails + * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set + * to needed length), -5 if reading IMSI file fails. + * + * This function can be used to read IMSI from the SIM/USIM card. If the IMSI + * file is PIN protected, scard_set_pin() must have been used to set the + * correct PIN code before calling scard_get_imsi(). + */ +int pcsc_usim::scard::get_imsi(char *imsi, size_t *len) +{ + unsigned char buf[100]; + size_t blen, imsilen, i; + char *pos; + + log->debug("SCARD: reading IMSI from (GSM) EF-IMSI\n"); + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_EF_IMSI, buf, &blen)) + return -1; + if (blen < 4) { + log->warning("SCARD: too short (GSM) EF-IMSI header (len=%ld)\n", (long) blen); + return -2; + } + + if (sim_type == SCARD_GSM_SIM) { + blen = to_uint16(&buf[2]); + } else { + int file_size; + if (parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + blen = file_size; + } + if (blen < 2 || blen > sizeof(buf)) { + log->debug("SCARD: invalid IMSI file length=%ld\n", (long) blen); + return -3; + } + + imsilen = (blen - 2) * 2 + 1; + log->debug("SCARD: IMSI file length=%ld imsilen=%ld\n", (long) blen, (long) imsilen); + if (blen < 2 || imsilen > *len) { + *len = imsilen; + return -4; + } + + if (read_file(buf, blen)) + return -5; + + pos = imsi; + *pos++ = '0' + (buf[1] >> 4 & 0x0f); + for (i = 2; i < blen; i++) { + unsigned char digit; + + digit = buf[i] & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + + digit = buf[i] >> 4 & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + } + *len = imsilen; + + return 0; +} + +int pcsc_usim::scard::read_file(unsigned char *data, size_t len) +{ + unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + cmd[4] = len; + + buf = (unsigned char*)malloc(blen); + if (buf == NULL) + return -1; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + ret = transmit(cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + free(buf); + return -2; + } + if (blen != len + 2) { + log->error("SCARD: file read returned unexpected length %ld (expected %ld)\n", (long) blen, (long) len + 2); + free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + log->error("SCARD: file read returned unexpected status %02x %02x (expected 90 00)\n", buf[len], buf[len + 1]); + free(buf); + return -4; + } + + memcpy(data, buf, len); + free(buf); + + return 0; +} + + +int pcsc_usim::scard::parse_fsp_templ(unsigned char *buf, size_t buf_len, int *ps_do, int *file_len) +{ + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + log->error("SCARD: file header did not start with FSP template tag\n"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if (pos[0] < end - pos) + end = pos + 1 + pos[0]; + pos++; + log->debug_hex(pos, end - pos, "SCARD: file header FSP template\n"); + + while (end - pos >= 2) { + unsigned char type, len; + + type = pos[0]; + len = pos[1]; + log->debug("SCARD: file header TLV 0x%02x len=%d\n", type, len); + pos += 2; + + if (len > (unsigned int) (end - pos)) + break; + + switch (type) { + case USIM_TLV_FILE_DESC: + log->debug_hex(pos, len, "SCARD: File Descriptor TLV\n"); + break; + case USIM_TLV_FILE_ID: + log->debug_hex(pos, len, "SCARD: File Identifier TLV\n"); + break; + case USIM_TLV_DF_NAME: + log->debug_hex(pos, len, "SCARD: DF name (AID) TLV\n"); + break; + case USIM_TLV_PROPR_INFO: + log->debug_hex(pos, len,"SCARD: Proprietary information TLV\n"); + break; + case USIM_TLV_LIFE_CYCLE_STATUS: + log->debug_hex(pos, len, "SCARD: Life Cycle Status Integer TLV\n"); + break; + case USIM_TLV_FILE_SIZE: + log->debug_hex(pos, len, "SCARD: File size TLV\n"); + if ((len == 1 || len == 2) && file_len) { + if (len == 1) { + *file_len = (int) pos[0]; + } else { + *file_len = to_uint16(pos); + } + log->debug("SCARD: file_size=%d\n", *file_len); + } + break; + case USIM_TLV_TOTAL_FILE_SIZE: + log->debug_hex(pos, len, "SCARD: Total file size TLV\n"); + break; + case USIM_TLV_PIN_STATUS_TEMPLATE: + log->debug_hex(pos, len, "SCARD: PIN Status Template DO TLV\n"); + if (len >= 2 && pos[0] == USIM_PS_DO_TAG && + pos[1] >= 1 && ps_do) { + log->debug("SCARD: PS_DO=0x%02x\n", pos[2]); + *ps_do = (int) pos[2]; + } + break; + case USIM_TLV_SHORT_FILE_ID: + log->debug_hex(pos, len, "SCARD: Short File Identifier (SFI) TLV\n"); + break; + case USIM_TLV_SECURITY_ATTR_8B: + case USIM_TLV_SECURITY_ATTR_8C: + case USIM_TLV_SECURITY_ATTR_AB: + log->debug_hex(pos, len, "SCARD: Security attribute TLV\n"); + break; + default: + log->debug_hex(pos, len,"SCARD: Unrecognized TLV\n"); + break; + } + + pos += len; + + if (pos == end) + return 0; + } + return -1; +} + + + +/** + * scard_deinit - Deinitialize SIM/USIM connection + * @scard: Pointer to private data from scard_init() + * + * This function closes the SIM/USIM connect opened with scard_init(). + */ +void pcsc_usim::scard::deinit() +{ + long ret; + + log->debug("SCARD: deinitializing smart card interface\n"); + + ret = SCardDisconnect(scard_handle, SCARD_UNPOWER_CARD); + if (ret != SCARD_S_SUCCESS) { + log->debug("SCARD: Failed to disconnect smart card (err=%ld)\n", ret); + } + + ret = SCardReleaseContext(scard_context); + if (ret != SCARD_S_SUCCESS) { + log->debug("Failed to release smart card context (err=%ld)\n", ret); + } +} + + + +/** + * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * Returns: length (>0) on success, -1 if administrative data file cannot be + * selected, -2 if administrative data file selection returns invalid result + * code, -3 if parsing FSP template file fails (USIM only), -4 if length of + * the file is unexpected, -5 if reading file fails, -6 if MNC length is not + * in range (i.e. 2 or 3), -7 if MNC length is not available. + * + */ +int pcsc_usim::scard::get_mnc_len() +{ + unsigned char buf[100]; + size_t blen; + int file_size; + + log->debug("SCARD: reading MNC len from (GSM) EF-AD\n"); + blen = sizeof(buf); + if (select_file(SCARD_FILE_GSM_EF_AD, buf, &blen)) + return -1; + if (blen < 4) { + log->debug("SCARD: too short (GSM) EF-AD header (len=%ld)\n", (long) blen); + return -2; + } + + if (sim_type == SCARD_GSM_SIM) { + file_size = to_uint16(&buf[2]); + } else { + if (parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + } + if (file_size == 3) { + log->debug("SCARD: MNC length not available\n"); + return -7; + } + if (file_size < 4 || file_size > (int) sizeof(buf)) { + log->debug("SCARD: invalid file length=%ld\n", (long) file_size); + return -4; + } + + if (read_file(buf, file_size)) + return -5; + buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ + if (buf[3] < 2 || buf[3] > 3) { + log->debug("SCARD: invalid MNC length=%ld\n", (long) buf[3]); + return -6; + } + log->debug("SCARD: MNC length=%ld\n", (long) buf[3]); + return buf[3]; +} + + +/** + * scard_umts_auth - Run UMTS authentication command on USIM card + * @scard: Pointer to private data from scard_init() + * @_rand: 16-byte RAND value from HLR/AuC + * @autn: 16-byte AUTN value from HLR/AuC + * @res: 16-byte buffer for RES + * @res_len: Variable that will be set to RES length + * @ik: 16-byte buffer for IK + * @ck: 16-byte buffer for CK + * @auts: 14-byte buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization + * failure + * + * This function performs AKA authentication using USIM card and the provided + * RAND and AUTN values from HLR/AuC. If authentication command can be + * completed successfully, RES, IK, and CK values will be written into provided + * buffers and res_len is set to length of received RES value. If USIM reports + * synchronization failure, the received AUTS value will be written into auts + * buffer. In this case, RES, IK, and CK are not valid. + */ +int pcsc_usim::scard::umts_auth(const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, int *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts) +{ + unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = { USIM_CMD_RUN_UMTS_ALG }; + unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[64], *pos, *end; + size_t len; + long ret; + + + if (sim_type == SCARD_GSM_SIM) { + log->debug("SCARD: Non-USIM card - cannot do UMTS auth\n"); + return -1; + } + + log->debug_hex(_rand, AKA_RAND_LEN, "SCARD: UMTS auth - RAND\n"); + log->debug_hex(autn, AKA_AUTN_LEN, "SCARD: UMTS auth - AUTN\n"); + cmd[5] = AKA_RAND_LEN; + memcpy(cmd + 6, _rand, AKA_RAND_LEN); + cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; + memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -1; + + if (len <= sizeof(resp)) + log->debug_hex(resp, len, "SCARD: UMTS alg response\n"); + + if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { + // Authentication error, application specific + log->warning("SCARD: UMTS auth failed - MAC != XMAC\n"); + return -1; + } else if (len != 2 || resp[0] != 0x61) { + log->warning("SCARD: unexpected response for UMTS auth request (len=%ld resp=%02x %02x)\n", + (long) len, resp[0], resp[1]); + return -1; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = transmit(get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) + return -1; + + log->debug_hex(buf, len, "SCARD: UMTS get response result\n"); + if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && buf[1] == AKA_AUTS_LEN) { + log->debug("SCARD: UMTS Synchronization-Failure\n"); + memcpy(auts, buf + 2, AKA_AUTS_LEN); + log->debug_hex(auts, AKA_AUTS_LEN, "SCARD: AUTS\n"); + *res_len = AKA_AUTS_LEN; + return -2; + } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { + pos = buf + 1; + end = buf + len; + + /* RES */ + if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) { + log->debug("SCARD: Invalid RES\n"); + return -1; + } + *res_len = *pos++; + memcpy(res, pos, *res_len); + pos += *res_len; + log->debug_hex(res, *res_len, "SCARD: RES\n"); + + /* CK */ + if (pos[0] != CK_LEN || CK_LEN > end - pos) { + log->debug("SCARD: Invalid CK\n"); + return -1; + } + pos++; + memcpy(ck, pos, CK_LEN); + pos += CK_LEN; + log->debug_hex(ck, CK_LEN, "SCARD: CK\n"); + + /* IK */ + if (pos[0] != IK_LEN || IK_LEN > end - pos) { + log->debug("SCARD: Invalid IK\n"); + return -1; + } + pos++; + memcpy(ik, pos, IK_LEN); + pos += IK_LEN; + log->debug_hex(ik, IK_LEN, "SCARD: IK\n"); + + if (end > pos) { + log->debug_hex(pos, end - pos, "SCARD: Ignore extra data in end\n"); + } + + return 0; + } + + log->debug("SCARD: Unrecognized response\n"); + return -1; +} + + +int pcsc_usim::scard::verify_pin(const char *pin) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + + log->debug("SCARD: verifying PIN\n"); + + if (pin == NULL || strlen(pin) > 8) + return -1; + + if (sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + memcpy(cmd + 5, pin, strlen(pin)); + memset(cmd + 5 + strlen(pin), 0xff, 8 - strlen(pin)); + + len = sizeof(resp); + ret = transmit(cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { + log->debug("SCARD: PIN verification failed\n"); + return -1; + } + + log->debug("SCARD: PIN verified successfully\n"); + return SCARD_S_SUCCESS; +} + + +} // namespace srsue diff --git a/srsue/src/upper/rrc.cc b/srsue/src/upper/rrc.cc new file mode 100644 index 0000000..440b4a3 --- /dev/null +++ b/srsue/src/upper/rrc.cc @@ -0,0 +1,3400 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include // for printing uint64_t +#include +#include "srsue/hdr/upper/rrc.h" +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/common/security.h" +#include "srslte/common/bcd_helpers.h" + +using namespace srslte; + +namespace srsue { + +const static uint32_t NOF_REQUIRED_SIBS = 4; +const static uint32_t required_sibs[NOF_REQUIRED_SIBS] = {0,1,2,12}; // SIB1, SIB2, SIB3 and SIB13 (eMBMS) + +/******************************************************************************* + Base functions +*******************************************************************************/ + +rrc::rrc() + :state(RRC_STATE_IDLE) + ,drb_up(false) + ,serving_cell(NULL) +{ + n310_cnt = 0; + n311_cnt = 0; + serving_cell = new cell_t(); + neighbour_cells.reserve(NOF_NEIGHBOUR_CELLS); + initiated = false; + running = false; + go_idle = false; + go_rlf = false; +} + +rrc::~rrc() +{ + if (serving_cell) { + delete(serving_cell); + } + + std::vector::iterator it; + for (it = neighbour_cells.begin(); it != neighbour_cells.end(); ++it) { + delete(*it); + } +} + +static void liblte_rrc_handler(void *ctx, char *str) { + rrc *r = (rrc *) ctx; + r->liblte_rrc_log(str); +} + +void rrc::liblte_rrc_log(char *str) { + if (rrc_log) { + rrc_log->warning("[ASN]: %s\n", str); + } else { + printf("[ASN]: %s\n", str); + } +} +void rrc::print_mbms() +{ + if(rrc_log) { + if(serving_cell->has_mcch) { + LIBLTE_RRC_MCCH_MSG_STRUCT msg; + memcpy(&msg, &serving_cell->mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + std::stringstream ss; + for(uint32_t i=0;imbms_sessioninfolist_r9_size; j++) { + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *sess = &pmch->mbms_sessioninfolist_r9[j]; + ss << " Service ID: " << sess->tmgi_r9.serviceid_r9; + if(sess->sessionid_r9_present) { + ss << ", Session ID: " << (uint32_t)sess->sessionid_r9; + } + if(sess->tmgi_r9.plmn_id_explicit) { + std::string tmp; + if(mcc_to_string(sess->tmgi_r9.plmn_id_r9.mcc, &tmp)) { + ss << ", MCC: " << tmp; + } + if(mnc_to_string(sess->tmgi_r9.plmn_id_r9.mnc, &tmp)) { + ss << ", MNC: " << tmp; + } + } else { + ss << ", PLMN index: " << (uint32_t)sess->tmgi_r9.plmn_index_r9; + } + ss << ", LCID: " << (uint32_t)sess->logicalchannelid_r9; + ss << std::endl; + } + } + //rrc_log->console(ss.str()); + std::cout << ss.str(); + } else { + rrc_log->console("MCCH not available for current cell\n"); + } + } +} + +bool rrc::mbms_service_start(uint32_t serv, uint32_t port) +{ + bool ret = false; + + if(serving_cell->has_mcch) { + LIBLTE_RRC_MCCH_MSG_STRUCT msg; + memcpy(&msg, &serving_cell->mcch, sizeof(LIBLTE_RRC_MCCH_MSG_STRUCT)); + for(uint32_t i=0;imbms_sessioninfolist_r9_size; j++) { + LIBLTE_RRC_MBMS_SESSION_INFO_R9_STRUCT *sess = &pmch->mbms_sessioninfolist_r9[j]; + if(serv == sess->tmgi_r9.serviceid_r9) { + rrc_log->console("MBMS service started. Service id:%d, port: %d\n", serv, port); + ret = true; + add_mrb(sess->logicalchannelid_r9, port); + } + } + } + } + return ret; +} + + +void rrc::init(phy_interface_rrc *phy_, + mac_interface_rrc *mac_, + rlc_interface_rrc *rlc_, + pdcp_interface_rrc *pdcp_, + nas_interface_rrc *nas_, + usim_interface_rrc *usim_, + gw_interface_rrc *gw_, + mac_interface_timers *mac_timers_, + srslte::log *rrc_log_) { + pool = byte_buffer_pool::get_instance(); + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + nas = nas_; + usim = usim_; + gw = gw_; + rrc_log = rrc_log_; + + // Use MAC timers + mac_timers = mac_timers_; + state = RRC_STATE_IDLE; + plmn_is_selected = false; + + security_is_activated = false; + + pthread_mutex_init(&mutex, NULL); + + args.ue_category = SRSLTE_UE_CATEGORY; + args.supported_bands[0] = 7; + args.nof_supported_bands = 1; + args.feature_group = 0xe6041000; + + t300 = mac_timers->timer_get_unique_id(); + t301 = mac_timers->timer_get_unique_id(); + t302 = mac_timers->timer_get_unique_id(); + t310 = mac_timers->timer_get_unique_id(); + t311 = mac_timers->timer_get_unique_id(); + t304 = mac_timers->timer_get_unique_id(); + + dedicatedInfoNAS = NULL; + ueIdentity_configured = false; + + transaction_id = 0; + + // Register logging handler with liblte_rrc + liblte_rrc_log_register_handler(this, liblte_rrc_handler); + + cell_clean_cnt = 0; + + ho_start = false; + + pending_mob_reconf = false; + + // Set default values for all layers + set_rrc_default(); + set_phy_default(); + set_mac_default(); + + measurements.init(this); + // set seed for rand (used in attach) + srand(time(NULL)); + + running = true; + start(); + initiated = true; +} + +void rrc::stop() { + running = false; + cmd_msg_t msg; + msg.command = cmd_msg_t::STOP; + cmd_q.push(msg); + wait_thread_finish(); +} + +rrc_state_t rrc::get_state() { + return state; +} + +bool rrc::is_connected() { + return (RRC_STATE_CONNECTED == state); +} + +bool rrc::have_drb() { + return drb_up; +} + +void rrc::set_args(rrc_args_t *args) { + memcpy(&this->args, args, sizeof(rrc_args_t)); +} + +/* + * Low priority thread to run functions that can not be executed from main thread + */ +void rrc::run_thread() { + while(running) { + cmd_msg_t msg = cmd_q.wait_pop(); + switch(msg.command) { + case cmd_msg_t::STOP: + return; + case cmd_msg_t::PCCH: + process_pcch(msg.pdu); + break; + } + } +} + + +/* + * + * RRC State Machine + * + */ +void rrc::run_tti(uint32_t tti) { + + if (!initiated) { + return; + } + + /* We can not block in this thread because it is called from + * the MAC TTI timer and needs to return immediatly to perform other + * tasks. Therefore in this function we use trylock() instead of lock() and + * skip function if currently locked, since none of the functions here is urgent + */ + if (!pthread_mutex_trylock(&mutex)) { + + // Process pending PHY measurements in IDLE/CONNECTED + process_phy_meas(); + + // Run state machine + rrc_log->debug("State %s\n", rrc_state_text[state]); + switch (state) { + case RRC_STATE_IDLE: + + /* CAUTION: The execution of cell_search() and cell_selection() take more than 1 ms + * and will slow down MAC TTI ticks. This has no major effect at the moment because + * the UE is in IDLE but we could consider splitting MAC and RRC threads to avoid this + */ + + // If attached but not camping on the cell, perform cell reselection + if (nas->is_attached()) { + rrc_log->debug("Running cell selection and reselection in IDLE\n"); + switch(cell_selection()) { + case rrc::CHANGED_CELL: + // New cell has been selected, start receiving PCCH + mac->pcch_start_rx(); + break; + case rrc::NO_CELL: + rrc_log->warning("Could not find any cell to camp on\n"); + break; + case rrc::SAME_CELL: + if (!phy->cell_is_camping()) { + rrc_log->warning("Did not reselect cell but serving cell is out-of-sync.\n"); + serving_cell->in_sync = false; + } + break; + } + } + break; + case RRC_STATE_CONNECTED: + if (ho_start) { + ho_start = false; + if (!ho_prepare()) { + con_reconfig_failed(); + } + } + measurements.run_tti(tti); + if (go_idle) { + go_idle = false; + leave_connected(); + } + if (go_rlf) { + go_rlf = false; + // Initiate connection re-establishment procedure after RLF + send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_OTHER_FAILURE); + } + break; + default:break; + } + + // Clean old neighbours + cell_clean_cnt++; + if (cell_clean_cnt == 1000) { + clean_neighbours(); + cell_clean_cnt = 0; + } + pthread_mutex_unlock(&mutex); + } // Skip TTI if mutex is locked +} + + + + + + + + + +/******************************************************************************* +* +* +* +* NAS interface: PLMN search and RRC connection establishment +* +* +* +*******************************************************************************/ + +uint16_t rrc::get_mcc() { + return serving_cell->get_mcc(); +} + +uint16_t rrc::get_mnc() { + return serving_cell->get_mnc(); +} + +/* NAS interface to search for available PLMNs. + * It goes through all known frequencies, synchronizes and receives SIB1 for each to extract PLMN. + * The function is blocking and waits until all frequencies have been + * searched and PLMNs are obtained. + * + * This function is thread-safe with connection_request() + */ +int rrc::plmn_search(found_plmn_t found_plmns[MAX_FOUND_PLMNS]) +{ + // Mutex with connect + pthread_mutex_lock(&mutex); + + rrc_log->info("Starting PLMN search\n"); + uint32_t nof_plmns = 0; + phy_interface_rrc::cell_search_ret_t ret; + do { + ret = cell_search(); + if (ret.found == phy_interface_rrc::cell_search_ret_t::CELL_FOUND) { + if (serving_cell->has_sib1()) { + // Save PLMN and TAC to NAS + for (uint32_t i = 0; i < serving_cell->nof_plmns(); i++) { + if (nof_plmns < MAX_FOUND_PLMNS) { + found_plmns[nof_plmns].plmn_id = serving_cell->get_plmn(i); + found_plmns[nof_plmns].tac = serving_cell->get_tac(); + nof_plmns++; + } else { + rrc_log->error("No more space for plmns (%d)\n", nof_plmns); + } + } + } else { + rrc_log->error("SIB1 not acquired\n"); + } + } + } while (ret.last_freq == phy_interface_rrc::cell_search_ret_t::MORE_FREQS && + ret.found != phy_interface_rrc::cell_search_ret_t::ERROR); + + // Process all pending measurements before returning + process_phy_meas(); + + pthread_mutex_unlock(&mutex); + + if (ret.found == phy_interface_rrc::cell_search_ret_t::ERROR) { + return -1; + } else { + return nof_plmns; + } +} + +/* This is the NAS interface. When NAS requests to select a PLMN we have to + * connect to either register or because there is pending higher layer traffic. + */ +void rrc::plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) { + plmn_is_selected = true; + selected_plmn_id = plmn_id; + + rrc_log->info("PLMN Selected %s\n", plmn_id_to_string(plmn_id).c_str()); +} + +/* 5.3.3.2 Initiation of RRC Connection Establishment procedure + * + * Higher layers request establishment of RRC connection while UE is in RRC_IDLE + * + * This procedure selects a suitable cell for transmission of RRCConnectionRequest and configures + * it. Sends connectionRequest message and returns if message transmitted successfully. + * It does not wait until completition of Connection Establishment procedure + */ +bool rrc::connection_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause, + srslte::byte_buffer_t *dedicatedInfoNAS) +{ + + if (!plmn_is_selected) { + rrc_log->error("Trying to connect but PLMN not selected.\n"); + return false; + } + + if (state != RRC_STATE_IDLE) { + rrc_log->warning("Requested RRC connection establishment while not in IDLE\n"); + return false; + } + + if (mac_timers->timer_get(t302)->is_running()) { + rrc_log->info("Requested RRC connection establishment while T302 is running\n"); + nas->set_barring(nas_interface_rrc::BARRING_MO_DATA); + return false; + } + + bool ret = false; + + pthread_mutex_lock(&mutex); + + rrc_log->info("Initiation of Connection establishment procedure\n"); + + // Perform cell selection & reselection for the selected PLMN + cs_ret_t cs_ret = cell_selection(); + + // .. and SI acquisition + if (phy->cell_is_camping()) { + + // Set default configurations + set_phy_default(); + set_mac_default(); + + // CCCH configuration applied already at start + // timeAlignmentCommon applied in configure_serving_cell + + rrc_log->info("Configuring serving cell...\n"); + if (configure_serving_cell()) { + + mac_timers->timer_get(t300)->reset(); + mac_timers->timer_get(t300)->run(); + + // Send connectionRequest message to lower layers + send_con_request(cause); + + // Save dedicatedInfoNAS SDU + if (this->dedicatedInfoNAS) { + rrc_log->warning("Received a new dedicatedInfoNAS SDU but there was one still in queue. Removing it\n"); + pool->deallocate(this->dedicatedInfoNAS); + } + this->dedicatedInfoNAS = dedicatedInfoNAS; + + // Wait until t300 stops due to RRCConnectionSetup/Reject or expiry + while (mac_timers->timer_get(t300)->is_running()) { + usleep(1000); + } + + if (state == RRC_STATE_CONNECTED) { + // Received ConnectionSetup + ret = true; + } else if (mac_timers->timer_get(t300)->is_expired()) { + // T300 is expired: 5.3.3.6 + rrc_log->info("Timer T300 expired: ConnectionRequest timed out\n"); + mac->reset(); + set_mac_default(); + rlc->reestablish(); + } else { + // T300 is stopped but RRC not Connected is because received Reject: Section 5.3.3.8 + rrc_log->info("Timer T300 stopped: Received ConnectionReject\n"); + mac->reset(); + set_mac_default(); + } + + } else { + rrc_log->error("Configuring serving cell\n"); + } + } else { + switch(cs_ret) { + case SAME_CELL: + rrc_log->warning("Did not reselect cell but serving cell is out-of-sync.\n"); + serving_cell->in_sync = false; + break; + case CHANGED_CELL: + rrc_log->warning("Selected a new cell but could not camp on. Setting out-of-sync.\n"); + serving_cell->in_sync = false; + break; + default: + rrc_log->warning("Could not find any suitable cell to connect\n"); + } + } + + if (!ret) { + rrc_log->warning("Could not estblish connection. Deallocating dedicatedInfoNAS PDU\n"); + pool->deallocate(this->dedicatedInfoNAS); + this->dedicatedInfoNAS = NULL; + } + + pthread_mutex_unlock(&mutex); + return ret; +} + +void rrc::set_ue_idenity(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi) { + ueIdentity_configured = true; + ueIdentity = s_tmsi; + rrc_log->info("Set ue-Identity to 0x%x:0x%x\n", ueIdentity.mmec, ueIdentity.m_tmsi); +} + +/* Retrieves all required SIB or configures them if already retrieved before + */ +bool rrc::configure_serving_cell() { + + if (!phy->cell_is_camping()) { + rrc_log->error("Trying to configure Cell while not camping on it\n"); + return false; + } + serving_cell->has_mcch = false; + // Obtain the SIBs if not available or apply the configuration if available + for (uint32_t i = 0; i < NOF_REQUIRED_SIBS; i++) { + if (!serving_cell->has_sib(required_sibs[i])) { + rrc_log->info("Cell has no SIB%d. Obtaining SIB%d\n", required_sibs[i]+1, required_sibs[i]+1); + if (!si_acquire(required_sibs[i])) { + rrc_log->info("Timeout while acquiring SIB%d\n", required_sibs[i]+1); + if (required_sibs[i] < 2) { + return false; + } + } + } else { + rrc_log->info("Cell has SIB%d\n", required_sibs[i]+1); + switch(required_sibs[i]) { + case 1: + apply_sib2_configs(serving_cell->sib2ptr()); + break; + case 12: + apply_sib13_configs(serving_cell->sib13ptr()); + break; + } + } + } + return true; +} + + + + + + +/******************************************************************************* +* +* +* +* PHY interface: neighbour and serving cell measurements and out-of-sync/in-sync +* +* +* +*******************************************************************************/ + +/* This function is called from a PHY worker thus must return very quickly. + * Queue the values of the measurements and process them from the RRC thread + */ +void rrc::new_phy_meas(float rsrp, float rsrq, uint32_t tti, int earfcn_i, int pci_i) { + uint32_t pci = 0; + uint32_t earfcn = 0; + if (earfcn_i < 0) { + earfcn = (uint32_t) serving_cell->get_earfcn(); + } else { + earfcn = (uint32_t) earfcn_i; + } + if (pci_i < 0) { + pci = (uint32_t) serving_cell->get_pci(); + } else { + pci = (uint32_t) pci_i; + } + phy_meas_t new_meas = {rsrp, rsrq, tti, earfcn, pci}; + phy_meas_q.push(new_meas); + rrc_log->info("MEAS: New measurement pci=%d, rsrp=%.1f dBm.\n", pci, rsrp); +} + +/* Processes all pending PHY measurements in queue. Must be called from a mutexed function + */ +void rrc::process_phy_meas() { + phy_meas_t m; + while(phy_meas_q.try_pop(&m)) { + rrc_log->debug("MEAS: Processing measurement. %lu measurements in queue\n", phy_meas_q.size()); + process_new_phy_meas(m); + } +} + +void rrc::process_new_phy_meas(phy_meas_t meas) +{ + float rsrp = meas.rsrp; + float rsrq = meas.rsrq; + uint32_t tti = meas.tti; + uint32_t earfcn = meas.earfcn; + uint32_t pci = meas.pci; + + // Measurements in RRC_CONNECTED go through measurement class to log reports etc. + if (state != RRC_STATE_IDLE) { + measurements.new_phy_meas(earfcn, pci, rsrp, rsrq, tti); + + // Measurements in RRC_IDLE update serving cell + } else { + + // Update serving cell + if (serving_cell->equals(earfcn, pci)) { + serving_cell->set_rsrp(rsrp); + // Or update/add neighbour cell + } else { + add_neighbour_cell(earfcn, pci, rsrp); + } + } +} + +// Detection of physical layer problems in RRC_CONNECTED (5.3.11.1) +void rrc::out_of_sync() +{ + + // CAUTION: We do not lock in this function since they are called from real-time threads + + serving_cell->in_sync = false; + rrc_log->info("Received out-of-sync while in state %s. n310=%d, t311=%s, t310=%s\n", + rrc_state_text[state], n310_cnt, + mac_timers->timer_get(t311)->is_running()?"running":"stop", + mac_timers->timer_get(t310)->is_running()?"running":"stop"); + if (state == RRC_STATE_CONNECTED) { + if (!mac_timers->timer_get(t311)->is_running() && !mac_timers->timer_get(t310)->is_running()) { + n310_cnt++; + if (n310_cnt == N310) { + rrc_log->info("Detected %d out-of-sync from PHY. Trying to resync. Starting T310 timer %d ms\n", + N310, mac_timers->timer_get(t310)->get_timeout()); + mac_timers->timer_get(t310)->reset(); + mac_timers->timer_get(t310)->run(); + n310_cnt = 0; + } + } + } +} + +// Recovery of physical layer problems (5.3.11.2) +void rrc::in_sync() +{ + + // CAUTION: We do not lock in this function since they are called from real-time threads + + serving_cell->in_sync = true; + if (mac_timers->timer_get(t310)->is_running()) { + n311_cnt++; + if (n311_cnt == N311) { + mac_timers->timer_get(t310)->stop(); + n311_cnt = 0; + rrc_log->info("Detected %d in-sync from PHY. Stopping T310 timer\n", N311); + } + } +} + + + + + + + + + + + + + + + + +/******************************************************************************* +* +* +* +* System Information Acquisition procedure +* +* +* +*******************************************************************************/ + + +// Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message +uint32_t rrc::sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf) { + return (period*10*(1+tti/(period*10))+(offset*10)+sf)%10240; // the 1 means next opportunity +} + +/* Implemnets the SI acquisition procedure + * Configures the MAC/PHY scheduling to retrieve SI messages. The function is blocking and will not + * return until SIB is correctly received or timeout + */ +bool rrc::si_acquire(uint32_t sib_index) +{ + uint32_t tti; + uint32_t si_win_start=0, si_win_len=0; + uint16_t period; + uint32_t sched_index; + uint32_t x, sf, offset; + + uint32_t last_win_start = 0; + uint32_t timeout = 0; + + while(timeout < SIB_SEARCH_TIMEOUT_MS && !serving_cell->has_sib(sib_index)) { + + bool instruct_phy = false; + + if (sib_index == 0) { + + // Instruct MAC to look for SIB1 + tti = mac->get_current_tti(); + si_win_start = sib_start_tti(tti, 2, 0, 5); + if (last_win_start == 0 || + (srslte_tti_interval(tti, last_win_start) >= 20 && srslte_tti_interval(tti, last_win_start) < 1000)) { + + last_win_start = si_win_start; + si_win_len = 1; + instruct_phy = true; + } + period = 20; + sched_index = 0; + } else { + // Instruct MAC to look for SIB2..13 + if (serving_cell->has_sib1()) { + + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = serving_cell->sib1ptr(); + + // SIB2 scheduling + if (sib_index == 1) { + period = liblte_rrc_si_periodicity_num[sib1->sched_info[0].si_periodicity]; + sched_index = 0; + } else { + // SIB3+ scheduling Section 5.2.3 + if (sib_index >= 2) { + bool found = false; + for (uint32_t i=0;iN_sched_info && !found;i++) { + for (uint32_t j=0;jsched_info[i].N_sib_mapping_info && !found;j++) { + if ((uint32_t) sib1->sched_info[i].sib_mapping_info[j].sib_type == sib_index - 2) { + period = liblte_rrc_si_periodicity_num[sib1->sched_info[i].si_periodicity]; + sched_index = i; + found = true; + } + } + } + if (!found) { + rrc_log->info("Could not find SIB%d scheduling in SIB1\n", sib_index+1); + return false; + } + } + } + si_win_len = liblte_rrc_si_window_length_num[sib1->si_window_length]; + x = sched_index*si_win_len; + sf = x%10; + offset = x/10; + + tti = mac->get_current_tti(); + si_win_start = sib_start_tti(tti, period, offset, sf); + si_win_len = liblte_rrc_si_window_length_num[sib1->si_window_length]; + + if (last_win_start == 0 || + (srslte_tti_interval(tti, last_win_start) > period*5 && srslte_tti_interval(tti, last_win_start) < 1000)) + { + last_win_start = si_win_start; + instruct_phy = true; + } + } else { + rrc_log->error("Trying to receive SIB%d but SIB1 not received\n", sib_index+1); + } + } + + // Instruct MAC to decode SIB + if (instruct_phy && !serving_cell->has_sib(sib_index)) { + mac->bcch_start_rx(si_win_start, si_win_len); + rrc_log->info("Instructed MAC to search for SIB%d, win_start=%d, win_len=%d, period=%d, sched_index=%d\n", + sib_index+1, si_win_start, si_win_len, period, sched_index); + } + usleep(1000); + timeout++; + } + return serving_cell->has_sib(sib_index); +} + + + + + + + + + +/******************************************************************************* +* +* +* +* Cell selection, reselection and neighbour cell database management +* +* +* +*******************************************************************************/ + +/* Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet + */ +phy_interface_rrc::cell_search_ret_t rrc::cell_search() +{ + phy_interface_rrc::phy_cell_t new_cell; + + phy_interface_rrc::cell_search_ret_t ret = phy->cell_search(&new_cell); + + switch(ret.found) { + case phy_interface_rrc::cell_search_ret_t::CELL_FOUND: + rrc_log->info("Cell found in this frequency. Setting new serving cell...\n"); + + // Create cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search. + if (!add_neighbour_cell(new_cell, NAN)) { + rrc_log->info("No more space for neighbour cells\n"); + break; + } + set_serving_cell(new_cell); + + if (phy->cell_is_camping()) { + if (!serving_cell->has_sib1()) { + rrc_log->info("Cell has no SIB1. Obtaining SIB1\n"); + if (!si_acquire(0)) { + rrc_log->error("Timeout while acquiring SIB1\n"); + } + } else { + rrc_log->info("Cell has SIB1\n"); + } + } else { + rrc_log->warning("Could not camp on found cell. Trying next one...\n"); + } + break; + case phy_interface_rrc::cell_search_ret_t::CELL_NOT_FOUND: + rrc_log->info("No cells found.\n"); + break; + case phy_interface_rrc::cell_search_ret_t::ERROR: + rrc_log->error("In cell search. Finishing PLMN search\n"); + break; + } + return ret; +} + +/* Cell selection procedure 36.304 5.2.3 + * Select the best cell to camp on among the list of known cells + */ +rrc::cs_ret_t rrc::cell_selection() +{ + // Neighbour cells are sorted in descending order of RSRP + for (uint32_t i = 0; i < neighbour_cells.size(); i++) { + if (/*TODO: CHECK that PLMN matches. Currently we don't receive SIB1 of neighbour cells + * neighbour_cells[i]->plmn_equals(selected_plmn_id) && */ + neighbour_cells[i]->in_sync) // matches S criteria + { + // If currently connected, verify cell selection criteria + if (!serving_cell->in_sync || + (cell_selection_criteria(neighbour_cells[i]->get_rsrp()) && + neighbour_cells[i]->get_rsrp() > serving_cell->get_rsrp() + 5)) + { + // Try to select Cell + set_serving_cell(i); + rrc_log->info("Selected cell idx=%d, PCI=%d, EARFCN=%d\n", + i, serving_cell->get_pci(), serving_cell->get_earfcn()); + rrc_log->console("Selected cell PCI=%d, EARFCN=%d\n", + serving_cell->get_pci(), serving_cell->get_earfcn()); + + if (phy->cell_select(&serving_cell->phy_cell)) { + if (configure_serving_cell()) { + rrc_log->info("Selected and configured cell successfully\n"); + return CHANGED_CELL; + } else { + rrc_log->error("While configuring serving cell\n"); + } + } else { + serving_cell->in_sync = false; + rrc_log->warning("Could not camp on selected cell\n"); + } + } + } + } + if (serving_cell->in_sync) { + if (!phy->cell_is_camping()) { + rrc_log->info("Serving cell is in-sync but not camping. Selecting it...\n"); + if (phy->cell_select(&serving_cell->phy_cell)) { + rrc_log->info("Selected serving cell OK.\n"); + } else { + serving_cell->in_sync = false; + rrc_log->error("Could not camp on serving cell.\n"); + } + } + return SAME_CELL; + } + // If can not find any suitable cell, search again + rrc_log->info("Cell selection and reselection in IDLE did not find any suitable cell. Searching again\n"); + // If can not camp on any cell, search again for new cells + phy_interface_rrc::cell_search_ret_t ret = cell_search(); + + return (ret.found == phy_interface_rrc::cell_search_ret_t::CELL_FOUND)?CHANGED_CELL:NO_CELL; +} + +// Cell selection criteria Section 5.2.3.2 of 36.304 +bool rrc::cell_selection_criteria(float rsrp, float rsrq) +{ + if (get_srxlev(rsrp) > 0 || !serving_cell->has_sib3()) { + return true; + } else { + return false; + } +} + +float rrc::get_srxlev(float Qrxlevmeas) { + // TODO: Do max power limitation + float Pcompensation = 0; + return Qrxlevmeas - (cell_resel_cfg.Qrxlevmin + cell_resel_cfg.Qrxlevminoffset) - Pcompensation; +} + +float rrc::get_squal(float Qqualmeas) { + return Qqualmeas - (cell_resel_cfg.Qqualmin + cell_resel_cfg.Qqualminoffset); +} + +// Cell reselection in IDLE Section 5.2.4 of 36.304 +void rrc::cell_reselection(float rsrp, float rsrq) +{ + // Intra-frequency cell-reselection criteria + + if (get_srxlev(rsrp) > cell_resel_cfg.s_intrasearchP && rsrp > -95.0) { + // UE may not perform intra-frequency measurements. + phy->meas_reset(); + // keep measuring serving cell + phy->meas_start(phy->get_current_earfcn(), phy->get_current_pci()); + } else { + // UE must start intra-frequency measurements + phy->meas_start(phy->get_current_earfcn(), -1); + } + + // TODO: Inter-frequency cell reselection +} + +// Set new serving cell +void rrc::set_serving_cell(phy_interface_rrc::phy_cell_t phy_cell) { + int cell_idx = find_neighbour_cell(phy_cell.earfcn, phy_cell.cell.id); + if (cell_idx >= 0) { + set_serving_cell(cell_idx); + } else { + rrc_log->error("Setting serving cell: Unkonwn cell with earfcn=%d, PCI=%d\n", phy_cell.earfcn, phy_cell.cell.id); + } +} + +// Set new serving cell +void rrc::set_serving_cell(uint32_t cell_idx) { + + if (cell_idx < neighbour_cells.size()) + { + // Remove future serving cell from neighbours to make space for current serving cell + cell_t *new_serving_cell = neighbour_cells[cell_idx]; + if (!new_serving_cell) { + rrc_log->error("Setting serving cell. Index %d is empty\n", cell_idx); + return; + } + neighbour_cells.erase(std::remove(neighbour_cells.begin(), neighbour_cells.end(), neighbour_cells[cell_idx]), neighbour_cells.end()); + + // Move serving cell to neighbours list + if (serving_cell->is_valid()) { + // Make sure it does not exist already + int serving_idx = find_neighbour_cell(serving_cell->get_earfcn(), serving_cell->get_pci()); + if (serving_idx >= 0 && (uint32_t) serving_idx < neighbour_cells.size()) { + printf("Error serving cell is already in the neighbour list. Removing it\n"); + neighbour_cells.erase(std::remove(neighbour_cells.begin(), neighbour_cells.end(), neighbour_cells[serving_idx]), neighbour_cells.end()); + } + // If not in the list, add it to the list of neighbours (sorted inside the function) + if (!add_neighbour_cell(serving_cell)) { + rrc_log->info("Serving cell not added to list of neighbours. Worse than current neighbours\n"); + } + } + + // Set new serving cell + serving_cell = new_serving_cell; + + rrc_log->info("Setting serving cell idx=%d, earfcn=%d, PCI=%d, nof_neighbours=%lu\n", + cell_idx, serving_cell->get_earfcn(), serving_cell->get_pci(), neighbour_cells.size()); + + } else { + rrc_log->error("Setting invalid serving cell idx %d\n", cell_idx); + } +} + +bool sort_rsrp(cell_t *u1, cell_t *u2) { + return u1->greater(u2); +} + +void rrc::delete_neighbour(uint32_t cell_idx) { + measurements.delete_report(neighbour_cells[cell_idx]->get_earfcn(), neighbour_cells[cell_idx]->get_pci()); + delete neighbour_cells[cell_idx]; + neighbour_cells.erase(std::remove(neighbour_cells.begin(), neighbour_cells.end(), neighbour_cells[cell_idx]), neighbour_cells.end()); +} + +std::vector::iterator rrc::delete_neighbour(std::vector::iterator it) { + measurements.delete_report((*it)->get_earfcn(), (*it)->get_pci()); + delete (*it); + return neighbour_cells.erase(it); +} + +/* Called by main RRC thread to remove neighbours from which measurements have not been received in a while + */ +void rrc::clean_neighbours() +{ + struct timeval now; + gettimeofday(&now, NULL); + + std::vector::iterator it = neighbour_cells.begin(); + while(it != neighbour_cells.end()) { + if ((*it)->timeout_secs(now) > NEIGHBOUR_TIMEOUT) { + rrc_log->info("Neighbour PCI=%d timed out. Deleting\n", (*it)->get_pci()); + it = delete_neighbour(it); + } else { + ++it; + } + } +} + +// Sort neighbour cells by decreasing order of RSRP +void rrc::sort_neighbour_cells() +{ + // Remove out-of-sync cells + std::vector::iterator it = neighbour_cells.begin(); + while(it != neighbour_cells.end()) { + if ((*it)->in_sync == false) { + rrc_log->info("Neighbour PCI=%d is out-of-sync. Deleting\n", (*it)->get_pci()); + it = delete_neighbour(it); + } else { + ++it; + } + } + + std::sort(neighbour_cells.begin(), neighbour_cells.end(), sort_rsrp); + + if (neighbour_cells.size() > 0) { + char ordered[512]; + int n=0; + n += snprintf(ordered, 512, "[pci=%d, rsrp=%.2f", neighbour_cells[0]->phy_cell.cell.id, neighbour_cells[0]->get_rsrp()); + for (uint32_t i=1;iget_pci(), neighbour_cells[i]->get_rsrp()); + } + rrc_log->info("Neighbours: %s]\n", ordered); + } else { + rrc_log->info("Neighbours: Empty\n"); + } +} + +bool rrc::add_neighbour_cell(cell_t *new_cell) { + bool ret = false; + if (neighbour_cells.size() < NOF_NEIGHBOUR_CELLS) { + ret = true; + } else if (new_cell->greater(neighbour_cells[neighbour_cells.size()-1])) { + // Replace old one by new one + delete_neighbour(neighbour_cells.size()-1); + ret = true; + } + if (ret) { + neighbour_cells.push_back(new_cell); + } + rrc_log->info("Added neighbour cell EARFCN=%d, PCI=%d, nof_neighbours=%zd\n", + new_cell->get_earfcn(), new_cell->get_pci(), neighbour_cells.size()); + sort_neighbour_cells(); + return ret; +} + +// If only neighbour PCI is provided, copy full cell from serving cell +bool rrc::add_neighbour_cell(uint32_t earfcn, uint32_t pci, float rsrp) { + phy_interface_rrc::phy_cell_t phy_cell; + phy_cell = serving_cell->phy_cell; + phy_cell.earfcn = earfcn; + phy_cell.cell.id = pci; + return add_neighbour_cell(phy_cell, rsrp); +} + +bool rrc::add_neighbour_cell(phy_interface_rrc::phy_cell_t phy_cell, float rsrp) { + if (phy_cell.earfcn == 0) { + phy_cell.earfcn = serving_cell->get_earfcn(); + } + + // First check if already exists + int cell_idx = find_neighbour_cell(phy_cell.earfcn, phy_cell.cell.id); + + rrc_log->info("Adding PCI=%d, earfcn=%d, cell_idx=%d\n", phy_cell.cell.id, phy_cell.earfcn, cell_idx); + + // If exists, update RSRP if provided, sort again and return + if (cell_idx >= 0 && isnormal(rsrp)) { + neighbour_cells[cell_idx]->set_rsrp(rsrp); + sort_neighbour_cells(); + return true; + } + + // If not, create a new one + cell_t *new_cell = new cell_t(phy_cell, rsrp); + + return add_neighbour_cell(new_cell); +} + +int rrc::find_neighbour_cell(uint32_t earfcn, uint32_t pci) { + for (uint32_t i = 0; i < neighbour_cells.size(); i++) { + if (neighbour_cells[i]->equals(earfcn, pci)) { + return (int) i; + } + } + return -1; +} + + +/******************************************************************************* +* +* +* +* Other functions +* +* +* +*******************************************************************************/ + +/* Detection of radio link failure (5.3.11.3) + * Upon T310 expiry, RA problem or RLC max retx + */ +void rrc::radio_link_failure() { + // TODO: Generate and store failure report + rrc_log->warning("Detected Radio-Link Failure\n"); + rrc_log->console("Warning: Detected Radio-Link Failure\n"); + if (state == RRC_STATE_CONNECTED) { + go_rlf = true; + } +} + +/* Reception of PUCCH/SRS release procedure (Section 5.3.13) */ +void rrc::release_pucch_srs() { + // Apply default configuration for PUCCH (CQI and SR) and SRS (release) + set_phy_default_pucch_srs(); + + // Configure RX signals without pregeneration because default option is release + phy->configure_ul_params(true); +} + +void rrc::ra_problem() { + radio_link_failure(); +} + +void rrc::max_retx_attempted() { + //TODO: Handle the radio link failure + rrc_log->warning("Max RLC reTx attempted\n"); + radio_link_failure(); +} + +void rrc::timer_expired(uint32_t timeout_id) { + if (timeout_id == t310) { + rrc_log->info("Timer T310 expired: Radio Link Failure\n"); + radio_link_failure(); + } else if (timeout_id == t311) { + rrc_log->info("Timer T311 expired: Going to RRC IDLE\n"); + go_idle = true; + } else if (timeout_id == t301) { + if (state == RRC_STATE_IDLE) { + rrc_log->info("Timer T301 expired: Already in IDLE.\n"); + } else { + rrc_log->info("Timer T301 expired: Going to RRC IDLE\n"); + go_idle = true; + } + } else if (timeout_id == t302) { + rrc_log->info("Timer T302 expired. Informing NAS about barrier alleviation\n"); + nas->set_barring(nas_interface_rrc::BARRING_NONE); + } else if (timeout_id == t300) { + // Do nothing, handled in connection_request() + } else if (timeout_id == t304) { + rrc_log->console("Timer T304 expired: Handover failed\n"); + ho_failed(); + // fw to measurement + } else if (!measurements.timer_expired(timeout_id)) { + rrc_log->error("Timeout from unknown timer id %d\n", timeout_id); + } +} + + + + + + + + +/******************************************************************************* +* +* +* +* Connection Control: Establishment, Reconfiguration, Reestablishment and Release +* +* +* +*******************************************************************************/ + +void rrc::send_con_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause) { + rrc_log->debug("Preparing RRC Connection Request\n"); + bzero(&ul_ccch_msg, sizeof(LIBLTE_RRC_UL_CCCH_MSG_STRUCT)); + + // Prepare ConnectionRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ; + + if (ueIdentity_configured) { + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI; + ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi.m_tmsi = ueIdentity.m_tmsi; + ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi.mmec = ueIdentity.mmec; + } else { + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE; + // TODO use proper RNG + uint64_t random_id = 0; + for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes + random_id |= ( (uint64_t)rand() & 0xFF ) << i*8; + } + ul_ccch_msg.msg.rrc_con_req.ue_id.random = random_id; + } + + ul_ccch_msg.msg.rrc_con_req.cause = cause; + + send_ul_ccch_msg(); +} + +/* RRC connection re-establishment procedure (5.3.7) */ +void rrc::send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM cause) +{ + bzero(&ul_ccch_msg, sizeof(LIBLTE_RRC_UL_CCCH_MSG_STRUCT)); + + uint16_t crnti; + uint16_t pci; + uint32_t cellid; + if (cause == LIBLTE_RRC_CON_REEST_REQ_CAUSE_HANDOVER_FAILURE) { + crnti = ho_src_rnti; + pci = ho_src_cell.get_pci(); + cellid = ho_src_cell.get_cell_id(); + } else { + mac_interface_rrc::ue_rnti_t uernti; + mac->get_rntis(&uernti); + crnti = uernti.crnti; + pci = serving_cell->get_pci(); + cellid = serving_cell->get_cell_id(); + } + + // Compute shortMAC-I + uint8_t varShortMAC[128], varShortMAC_packed[16]; + bzero(varShortMAC, 128); + bzero(varShortMAC_packed, 16); + uint8_t *msg_ptr = varShortMAC; + + // ASN.1 encode VarShortMAC-Input + liblte_rrc_pack_cell_identity_ie(cellid, &msg_ptr); + liblte_rrc_pack_phys_cell_id_ie(pci, &msg_ptr); + liblte_rrc_pack_c_rnti_ie(crnti, &msg_ptr); + + // byte align (already zero-padded) + uint32_t N_bits = (uint32_t) (msg_ptr-varShortMAC); + uint32_t N_bytes = ((N_bits-1)/8+1); + srslte_bit_pack_vector(varShortMAC, varShortMAC_packed, N_bytes*8); + + rrc_log->info("Encoded varShortMAC: cellId=0x%x, PCI=%d, rnti=0x%x (%d bytes, %d bits)\n", + cellid, pci, crnti, N_bytes, N_bits); + + // Compute MAC-I + uint8_t mac_key[4]; + switch(integ_algo) { + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(&k_rrc_int[16], + 0xffffffff, // 32-bit all to ones + 0x1f, // 5-bit all to ones + 1, // 1-bit to one + varShortMAC_packed, + N_bytes, + mac_key); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(&k_rrc_int[16], + 0xffffffff, // 32-bit all to ones + 0x1f, // 5-bit all to ones + 1, // 1-bit to one + varShortMAC_packed, + N_bytes, + mac_key); + break; + default: + rrc_log->info("Unsupported integrity algorithm during reestablishment\n"); + } + + // Prepare ConnectionRestalishmentRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti = crnti; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.phys_cell_id = pci; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.short_mac_i = mac_key[2] << 8 | mac_key[3]; + ul_ccch_msg.msg.rrc_con_reest_req.cause = cause; + + rrc_log->info("Initiating RRC Connection Reestablishment Procedure\n"); + rrc_log->console("RRC Connection Reestablishment\n"); + mac_timers->timer_get(t310)->stop(); + mac_timers->timer_get(t311)->reset(); + mac_timers->timer_get(t311)->run(); + + phy->reset(); + set_phy_default(); + mac->reset(); + set_mac_default(); + + // Perform cell selection in accordance to 36.304 + if (cell_selection_criteria(serving_cell->get_rsrp()) && serving_cell->in_sync) { + if (phy->cell_select(&serving_cell->phy_cell)) { + + if (mac_timers->timer_get(t311)->is_running()) { + // Actions following cell reselection while T311 is running 5.3.7.3 + rrc_log->info("Cell Selection finished. Initiating transmission of RRC Connection Reestablishment Request\n"); + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); + + mac_timers->timer_get(t301)->reset(); + mac_timers->timer_get(t301)->run(); + mac_timers->timer_get(t311)->stop(); + send_ul_ccch_msg(); + } else { + rrc_log->info("T311 expired while selecting cell. Going to IDLE\n"); + go_idle = true; + } + } else { + rrc_log->warning("Could not re-synchronize with cell.\n"); + go_idle = true; + } + } else { + rrc_log->info("Selected cell no longer suitable for camping (in_sync=%s). Going to IDLE\n", serving_cell->in_sync?"yes":"no"); + go_idle = true; + } +} + +void rrc::send_con_restablish_complete() { + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + + rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n"); + + rrc_log->console("RRC Connected\n"); + + // Prepare ConnectionSetupComplete packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE; + ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id; + + send_ul_dcch_msg(); +} + +void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) { + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + rrc_log->debug("Preparing RRC Connection Setup Complete\n"); + + // Prepare ConnectionSetupComplete packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE; + ul_dcch_msg.msg.rrc_con_setup_complete.registered_mme_present = false; + ul_dcch_msg.msg.rrc_con_setup_complete.rrc_transaction_id = transaction_id; + ul_dcch_msg.msg.rrc_con_setup_complete.selected_plmn_id = 1; + memcpy(ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.msg, nas_msg->msg, nas_msg->N_bytes); + ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.N_bytes = nas_msg->N_bytes; + + pool->deallocate(nas_msg); + + send_ul_dcch_msg(); +} + +void rrc::send_ul_info_transfer(byte_buffer_t *nas_msg) { + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + + rrc_log->debug("Preparing RX Info Transfer\n"); + + // Prepare RX INFO packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER; + ul_dcch_msg.msg.ul_info_transfer.dedicated_info_type = LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS; + memcpy(ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, nas_msg->msg, nas_msg->N_bytes); + ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes = nas_msg->N_bytes; + + pool->deallocate(nas_msg); + + send_ul_dcch_msg(); +} + +void rrc::send_security_mode_complete() { + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + rrc_log->debug("Preparing Security Mode Complete\n"); + + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE; + ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id; + + send_ul_dcch_msg(); +} + +void rrc::send_rrc_con_reconfig_complete() { + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + rrc_log->debug("Preparing RRC Connection Reconfig Complete\n"); + + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE; + ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id; + + send_ul_dcch_msg(); +} + +bool rrc::ho_prepare() { + if (pending_mob_reconf) { + rrc_log->info("Processing HO command to target PCell=%d\n", mob_reconf.mob_ctrl_info.target_pci); + + int target_cell_idx = find_neighbour_cell(serving_cell->get_earfcn(), mob_reconf.mob_ctrl_info.target_pci); + if (target_cell_idx < 0) { + rrc_log->console("Received HO command to unknown PCI=%d\n", mob_reconf.mob_ctrl_info.target_pci); + rrc_log->error("Could not find target cell earfcn=%d, pci=%d\n", + serving_cell->get_earfcn(), + mob_reconf.mob_ctrl_info.target_pci); + return false; + } + + // Section 5.3.5.4 + mac_timers->timer_get(t310)->stop(); + mac_timers->timer_get(t304)->set(this, liblte_rrc_t304_num[mob_reconf.mob_ctrl_info.t304]); + if (mob_reconf.mob_ctrl_info.carrier_freq_eutra_present && + mob_reconf.mob_ctrl_info.carrier_freq_eutra.dl_carrier_freq != serving_cell->get_earfcn()) { + rrc_log->error("Received mobilityControlInfo for inter-frequency handover\n"); + return false; + } + + // Save serving cell and current configuration + ho_src_cell = *serving_cell; + mac_interface_rrc::ue_rnti_t uernti; + mac->get_rntis(&uernti); + ho_src_rnti = uernti.crnti; + + // Reset/Reestablish stack + mac->clear_rntis(); + phy->meas_reset(); + mac->wait_uplink(); + pdcp->reestablish(); + rlc->reestablish(); + mac->reset(); + phy->reset(); + + mac->set_ho_rnti(mob_reconf.mob_ctrl_info.new_ue_id, mob_reconf.mob_ctrl_info.target_pci); + apply_rr_config_common_dl(&mob_reconf.mob_ctrl_info.rr_cnfg_common); + + if (!phy->cell_select(&neighbour_cells[target_cell_idx]->phy_cell)) { + rrc_log->error("Could not synchronize with target cell pci=%d. Trying to return to source PCI\n", + neighbour_cells[target_cell_idx]->get_pci()); + return false; + } + + set_serving_cell(target_cell_idx); + + if (mob_reconf.mob_ctrl_info.rach_cnfg_ded_present) { + rrc_log->info("Starting non-contention based RA with preamble_idx=%d, mask_idx=%d\n", + mob_reconf.mob_ctrl_info.rach_cnfg_ded.preamble_index, + mob_reconf.mob_ctrl_info.rach_cnfg_ded.prach_mask_index); + mac->start_noncont_ho(mob_reconf.mob_ctrl_info.rach_cnfg_ded.preamble_index, + mob_reconf.mob_ctrl_info.rach_cnfg_ded.prach_mask_index); + } else { + rrc_log->info("Starting contention-based RA\n"); + mac->start_cont_ho(); + } + + int ncc = -1; + if (mob_reconf.sec_cnfg_ho_present) { + ncc = mob_reconf.sec_cnfg_ho.intra_lte.next_hop_chaining_count; + if (mob_reconf.sec_cnfg_ho.intra_lte.key_change_ind) { + rrc_log->console("keyChangeIndicator in securityConfigHO not supported\n"); + return false; + } + if (mob_reconf.sec_cnfg_ho.intra_lte.sec_alg_cnfg_present) { + cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM) mob_reconf.sec_cnfg_ho.intra_lte.sec_alg_cnfg.cipher_alg; + integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) mob_reconf.sec_cnfg_ho.intra_lte.sec_alg_cnfg.int_alg; + rrc_log->info("Changed Ciphering to %s and Integrity to %s\n", + ciphering_algorithm_id_text[cipher_algo], + integrity_algorithm_id_text[integ_algo]); + } + } + + usim->generate_as_keys_ho(mob_reconf.mob_ctrl_info.target_pci, phy->get_current_earfcn(), + ncc, + k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); + + pdcp->config_security_all(k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); + send_rrc_con_reconfig_complete(); + } + return true; +} + +void rrc::ho_ra_completed(bool ra_successful) { + if (pending_mob_reconf) { + + if (ra_successful) { + measurements.ho_finish(); + + if (mob_reconf.meas_cnfg_present) { + measurements.parse_meas_config(&mob_reconf.meas_cnfg); + } + + mac_timers->timer_get(t304)->stop(); + + apply_rr_config_common_ul(&mob_reconf.mob_ctrl_info.rr_cnfg_common); + if (mob_reconf.rr_cnfg_ded_present) { + apply_rr_config_dedicated(&mob_reconf.rr_cnfg_ded); + } + } + // T304 will expiry and send ho_failure + + rrc_log->info("HO %ssuccessful\n", ra_successful?"":"un"); + rrc_log->console("HO %ssuccessful\n", ra_successful?"":"un"); + + pending_mob_reconf = false; + } else { + rrc_log->error("Received HO random access completed but no pending mobility reconfiguration info\n"); + } +} + +bool rrc::con_reconfig_ho(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig) +{ + if (reconfig->mob_ctrl_info.target_pci == phy->get_current_pci()) { + rrc_log->console("Warning: Received HO command to own cell\n"); + rrc_log->warning("Received HO command to own cell\n"); + return false; + } + + rrc_log->info("Received HO command to target PCell=%d\n", reconfig->mob_ctrl_info.target_pci); + rrc_log->console("Received HO command to target PCell=%d, NCC=%d\n", + reconfig->mob_ctrl_info.target_pci, reconfig->sec_cnfg_ho.intra_lte.next_hop_chaining_count); + + // store mobilityControlInfo + memcpy(&mob_reconf, reconfig, sizeof(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT)); + pending_mob_reconf = true; + + ho_start = true; + + return true; +} + +// Handle RRC Reconfiguration without MobilityInformation Section 5.3.5.3 +bool rrc::con_reconfig(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig) { + if (reconfig->rr_cnfg_ded_present) { + if (!apply_rr_config_dedicated(&reconfig->rr_cnfg_ded)) { + return false; + } + } + if (reconfig->meas_cnfg_present) { + if (!measurements.parse_meas_config(&reconfig->meas_cnfg)) { + return false; + } + } + + send_rrc_con_reconfig_complete(); + + byte_buffer_t *nas_sdu; + for (uint32_t i = 0; i < reconfig->N_ded_info_nas; i++) { + nas_sdu = pool_allocate; + if (nas_sdu) { + memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes); + nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes; + nas->write_pdu(RB_ID_SRB1, nas_sdu); + } else { + rrc_log->error("Fatal Error: Couldn't allocate PDU in handle_rrc_con_reconfig().\n"); + return false; + } + } + return true; +} + +// HO failure from T304 expiry 5.3.5.6 +void rrc::ho_failed() { + send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_HANDOVER_FAILURE); +} + +// Reconfiguration failure or Section 5.3.5.5 +void rrc::con_reconfig_failed() +{ + // Set previous PHY/MAC configuration + phy->set_config(&previous_phy_cfg); + mac->set_config(&previous_mac_cfg); + + if (security_is_activated) { + // Start the Reestablishment Procedure + send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_RECONFIG_FAILURE); + } else { + go_idle = true; + } +} + +void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig) +{ + phy->get_config(&previous_phy_cfg); + mac->get_config(&previous_mac_cfg); + + if (reconfig->mob_ctrl_info_present) { + if (!con_reconfig_ho(reconfig)) { + con_reconfig_failed(); + } + } else { + if (!con_reconfig(reconfig)) { + con_reconfig_failed(); + } + } +} + +/* Actions upon reception of RRCConnectionRelease 5.3.8.3 */ +void rrc::rrc_connection_release() { + // Save idleModeMobilityControlInfo, etc. + rrc_log->console("Received RRC Connection Release\n"); + go_idle = true; +} + +/* Actions upon leaving RRC_CONNECTED 5.3.12 */ +void rrc::leave_connected() +{ + rrc_log->console("RRC IDLE\n"); + rrc_log->info("Leaving RRC_CONNECTED state\n"); + state = RRC_STATE_IDLE; + drb_up = false; + security_is_activated = false; + measurements.reset(); + pdcp->reset(); + rlc->reset(); + phy->reset(); + mac->reset(); + set_phy_default(); + set_mac_default(); + mac_timers->timer_get(t301)->stop(); + mac_timers->timer_get(t310)->stop(); + mac_timers->timer_get(t311)->stop(); + mac_timers->timer_get(t304)->stop(); + rrc_log->info("Going RRC_IDLE\n"); + if (phy->cell_is_camping()) { + // Receive paging + mac->pcch_start_rx(); + // Instruct PHY to measure serving cell for cell reselection + phy->meas_start(phy->get_current_earfcn(), phy->get_current_pci()); + } +} + + + + + + +/******************************************************************************* +* +* +* +* Reception of Broadcast messages (MIB and SIBs) +* +* +* +*******************************************************************************/ +void rrc::write_pdu_bcch_bch(byte_buffer_t *pdu) { + // Do we need to do something with BCH? + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "BCCH BCH message received."); + pool->deallocate(pdu); +} + +void rrc::write_pdu_bcch_dlsch(byte_buffer_t *pdu) { + mac->clear_rntis(); + + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "BCCH DLSCH message received."); + rrc_log->info("BCCH DLSCH message Stack latency: %ld us\n", pdu->get_latency_us()); + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + ZERO_OBJECT(dlsch_msg); + + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + pool->deallocate(pdu); + liblte_rrc_unpack_bcch_dlsch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &dlsch_msg); + + for(uint32_t i=0; iinfo("Processing SIB%d (%d/%d)\n", liblte_rrc_sys_info_block_type_num[dlsch_msg.sibs[i].sib_type], i, dlsch_msg.N_sibs); + + if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 == dlsch_msg.sibs[i].sib_type) { + serving_cell->set_sib1(&dlsch_msg.sibs[i].sib.sib1); + handle_sib1(); + } else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2 == dlsch_msg.sibs[i].sib_type && !serving_cell->has_sib2()) { + serving_cell->set_sib2(&dlsch_msg.sibs[i].sib.sib2); + handle_sib2(); + } else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3 == dlsch_msg.sibs[i].sib_type && !serving_cell->has_sib3()) { + serving_cell->set_sib3(&dlsch_msg.sibs[i].sib.sib3); + handle_sib3(); + }else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13 == dlsch_msg.sibs[i].sib_type && !serving_cell->has_sib13()) { + serving_cell->set_sib13(&dlsch_msg.sibs[i].sib.sib13); + handle_sib13(); + } + } +} + +void rrc::handle_sib1() +{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = serving_cell->sib1ptr(); + rrc_log->info("SIB1 received, CellID=%d, si_window=%d, sib2_period=%d\n", + serving_cell->get_cell_id()&0xfff, + liblte_rrc_si_window_length_num[sib1->si_window_length], + liblte_rrc_si_periodicity_num[sib1->sched_info[0].si_periodicity]); + + // Print SIB scheduling info + uint32_t i,j; + for(i=0;iN_sched_info;i++){ + for(j=0;jsched_info[i].N_sib_mapping_info;j++){ + LIBLTE_RRC_SIB_TYPE_ENUM t = sib1->sched_info[i].sib_mapping_info[j].sib_type; + LIBLTE_RRC_SI_PERIODICITY_ENUM p = sib1->sched_info[i].si_periodicity; + rrc_log->debug("SIB scheduling info, sib_type=%d, si_periodicity=%d\n", + liblte_rrc_sib_type_num[t], + liblte_rrc_si_periodicity_num[p]); + } + } + + // Set TDD Config + if(sib1->tdd) { + phy->set_config_tdd(&sib1->tdd_cnfg); + } +} + +void rrc::handle_sib2() +{ + rrc_log->info("SIB2 received\n"); + + apply_sib2_configs(serving_cell->sib2ptr()); + +} + +void rrc::handle_sib3() +{ + rrc_log->info("SIB3 received\n"); + + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = serving_cell->sib3ptr(); + + // cellReselectionInfoCommon + cell_resel_cfg.q_hyst = liblte_rrc_q_hyst_num[sib3->q_hyst]; + + // cellReselectionServingFreqInfo + cell_resel_cfg.threshservinglow = sib3->thresh_serving_low; + + // intraFreqCellReselectionInfo + cell_resel_cfg.Qrxlevmin = sib3->q_rx_lev_min; + if (sib3->s_intra_search_present) { + cell_resel_cfg.s_intrasearchP = sib3->s_intra_search; + } else { + cell_resel_cfg.s_intrasearchP = INFINITY; + } + +} + +void rrc::handle_sib13() +{ + rrc_log->info("SIB13 received\n"); + +// mac->set_config_mbsfn_sib13(&serving_cell->sib13.mbsfn_area_info_list_r9[0], +// serving_cell->sib13.mbsfn_area_info_list_r9_size, +// &serving_cell->sib13.mbsfn_notification_config); +} + + + + +/******************************************************************************* +* +* +* +* Reception of Paging messages +* +* +* +*******************************************************************************/ +void rrc::write_pdu_pcch(byte_buffer_t *pdu) { + cmd_msg_t msg; + msg.pdu = pdu; + msg.command = cmd_msg_t::PCCH; + cmd_q.push(msg); +} + +void rrc::process_pcch(byte_buffer_t *pdu) { + if (pdu->N_bytes > 0 && pdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BITS) { + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "PCCH message received %d bytes\n", pdu->N_bytes); + rrc_log->info("PCCH message Stack latency: %ld us\n", pdu->get_latency_us()); + + LIBLTE_RRC_PCCH_MSG_STRUCT pcch_msg; + ZERO_OBJECT(pcch_msg); + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + pool->deallocate(pdu); + liblte_rrc_unpack_pcch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &pcch_msg); + + if (pcch_msg.paging_record_list_size > LIBLTE_RRC_MAX_PAGE_REC) { + pcch_msg.paging_record_list_size = LIBLTE_RRC_MAX_PAGE_REC; + } + + if (!ueIdentity_configured) { + rrc_log->warning("Received paging message but no ue-Identity is configured\n"); + return; + } + LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi_paged; + for (uint32_t i = 0; i < pcch_msg.paging_record_list_size; i++) { + s_tmsi_paged = &pcch_msg.paging_record_list[i].ue_identity.s_tmsi; + rrc_log->info("Received paging (%d/%d) for UE %x:%x\n", i + 1, pcch_msg.paging_record_list_size, + pcch_msg.paging_record_list[i].ue_identity.s_tmsi.mmec, + pcch_msg.paging_record_list[i].ue_identity.s_tmsi.m_tmsi); + if (ueIdentity.mmec == s_tmsi_paged->mmec && ueIdentity.m_tmsi == s_tmsi_paged->m_tmsi) { + if (RRC_STATE_IDLE == state) { + rrc_log->info("S-TMSI match in paging message\n"); + rrc_log->console("S-TMSI match in paging message\n"); + nas->paging(s_tmsi_paged); + } else { + rrc_log->warning("Received paging while in CONNECT\n"); + } + } else { + rrc_log->info("Received paging for unknown identity\n"); + } + } + } +} + + +void rrc::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + if (pdu->N_bytes > 0 && pdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BITS) { + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "MCH message received %d bytes on lcid:%d\n", pdu->N_bytes, lcid); + rrc_log->info("MCH message Stack latency: %ld us\n", pdu->get_latency_us()); + //TODO: handle MCCH notifications and update MCCH + if(0 == lcid && !serving_cell->has_mcch) { + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + liblte_rrc_unpack_mcch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &serving_cell->mcch); + serving_cell->has_mcch = true; + phy->set_config_mbsfn_mcch(&serving_cell->mcch); + } + + pool->deallocate(pdu); + } +} + + + + + + + + +/******************************************************************************* +* +* +* +* Packet processing +* +* +*******************************************************************************/ +byte_buffer_t* rrc::byte_align_and_pack() +{ + // Byte align and pack the message bits for PDCP + if ((bit_buf.N_bits % 8) != 0) { + for (uint32_t i = 0; i < 8 - (bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + + // Reset and reuse sdu buffer if provided + byte_buffer_t *pdcp_buf = pool_allocate; + if (pdcp_buf) { + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits / 8; + pdcp_buf->set_timestamp(); + } else { + rrc_log->error("Fatal Error: Couldn't allocate PDU in byte_align_and_pack().\n"); + } + return pdcp_buf; +} + +void rrc::send_ul_ccch_msg() +{ + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); + byte_buffer_t *pdu = byte_align_and_pack(); + if (pdu) { + // Set UE contention resolution ID in MAC + uint64_t uecri = 0; + uint8_t *ue_cri_ptr = (uint8_t *) &uecri; + uint32_t nbytes = 6; + for (uint32_t i = 0; i < nbytes; i++) { + ue_cri_ptr[nbytes - i - 1] = pdu->msg[i]; + } + + rrc_log->debug("Setting UE contention resolution ID: %" PRIu64 "\n", uecri); + mac->set_contention_id(uecri); + + rrc_log->info("Sending %s\n", liblte_rrc_ul_ccch_msg_type_text[ul_ccch_msg.msg_type]); + pdcp->write_sdu(RB_ID_SRB0, pdu); + } +} + +void rrc::send_ul_dcch_msg() +{ + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); + byte_buffer_t *pdu = byte_align_and_pack(); + if (pdu) { + rrc_log->info("Sending %s\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + pdcp->write_sdu(RB_ID_SRB1, pdu); + } +} + +void rrc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) { + + if (state == RRC_STATE_IDLE) { + rrc_log->warning("Received ULInformationTransfer SDU when in IDLE\n"); + return; + } + rrc_log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU", get_rb_name(lcid).c_str()); + send_ul_info_transfer(sdu); +} + +void rrc::write_pdu(uint32_t lcid, byte_buffer_t *pdu) { + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", get_rb_name(lcid).c_str()); + + switch (lcid) { + case RB_ID_SRB0: + parse_dl_ccch(pdu); + break; + case RB_ID_SRB1: + case RB_ID_SRB2: + parse_dl_dcch(lcid, pdu); + break; + default: + rrc_log->error("RX PDU with invalid bearer id: %d", lcid); + break; + } +} + +void rrc::parse_dl_ccch(byte_buffer_t *pdu) { + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + pool->deallocate(pdu); + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + liblte_rrc_unpack_dl_ccch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &dl_ccch_msg); + + rrc_log->info("SRB0 - Received %s\n", + liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg.msg_type]); + + switch (dl_ccch_msg.msg_type) { + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ: + // 5.3.3.8 + rrc_log->info("Received ConnectionReject. Wait time: %d\n", + dl_ccch_msg.msg.rrc_con_rej.wait_time); + rrc_log->console("Received ConnectionReject. Wait time: %d\n", + dl_ccch_msg.msg.rrc_con_rej.wait_time); + + mac_timers->timer_get(t300)->stop(); + + if (dl_ccch_msg.msg.rrc_con_rej.wait_time) { + nas->set_barring(nas_interface_rrc::BARRING_ALL); + mac_timers->timer_get(t302)->set(this, dl_ccch_msg.msg.rrc_con_rej.wait_time*1000); + mac_timers->timer_get(t302)->run(); + } else { + // Perform the actions upon expiry of T302 if wait time is zero + nas->set_barring(nas_interface_rrc::BARRING_NONE); + go_idle = true; + } + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP: + rrc_log->info("ConnectionSetup received\n"); + transaction_id = dl_ccch_msg.msg.rrc_con_setup.rrc_transaction_id; + handle_con_setup(&dl_ccch_msg.msg.rrc_con_setup); + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST: + rrc_log->info("ConnectionReestablishment received\n"); + rrc_log->console("Reestablishment OK\n"); + transaction_id = dl_ccch_msg.msg.rrc_con_reest.rrc_transaction_id; + handle_con_reest(&dl_ccch_msg.msg.rrc_con_reest); + break; + /* Reception of RRCConnectionReestablishmentReject 5.3.7.8 */ + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ: + rrc_log->info("ConnectionReestablishmentReject received\n"); + rrc_log->console("Reestablishment Reject\n"); + go_idle = true; + break; + default: + break; + } +} + +void rrc::parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu) { + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes * 8); + bit_buf.N_bits = pdu->N_bytes * 8; + liblte_rrc_unpack_dl_dcch_msg((LIBLTE_BIT_MSG_STRUCT *) &bit_buf, &dl_dcch_msg); + + rrc_log->info("%s - Received %s\n", + get_rb_name(lcid).c_str(), + liblte_rrc_dl_dcch_msg_type_text[dl_dcch_msg.msg_type]); + + pool->deallocate(pdu); + + switch (dl_dcch_msg.msg_type) { + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER: + pdu = pool_allocate; + if (!pdu) { + rrc_log->error("Fatal error: out of buffers in pool\n"); + return; + } + memcpy(pdu->msg, dl_dcch_msg.msg.dl_info_transfer.dedicated_info.msg, + dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes); + pdu->N_bytes = dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes; + nas->write_pdu(lcid, pdu); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND: + transaction_id = dl_dcch_msg.msg.security_mode_cmd.rrc_transaction_id; + + cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM) dl_dcch_msg.msg.security_mode_cmd.sec_algs.cipher_alg; + integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) dl_dcch_msg.msg.security_mode_cmd.sec_algs.int_alg; + + rrc_log->info("Received Security Mode Command eea: %s, eia: %s\n", + ciphering_algorithm_id_text[cipher_algo], + integrity_algorithm_id_text[integ_algo]); + + // Generate AS security keys + uint8_t k_asme[32]; + nas->get_k_asme(k_asme, 32); + usim->generate_as_keys(k_asme, nas->get_ul_count(), k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); + rrc_log->info_hex(k_rrc_enc, 32, "RRC encryption key - k_rrc_enc"); + rrc_log->info_hex(k_rrc_int, 32, "RRC integrity key - k_rrc_int"); + rrc_log->info_hex(k_up_enc, 32, "UP encryption key - k_up_enc"); + + security_is_activated = true; + + // Configure PDCP for security + pdcp->config_security(lcid, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); + pdcp->enable_integrity(lcid); + send_security_mode_complete(); + pdcp->enable_encryption(lcid); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG: + transaction_id = dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id; + handle_rrc_con_reconfig(lcid, &dl_dcch_msg.msg.rrc_con_reconfig); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY: + transaction_id = dl_dcch_msg.msg.ue_cap_enquiry.rrc_transaction_id; + for (uint32_t i = 0; i < dl_dcch_msg.msg.ue_cap_enquiry.N_ue_cap_reqs; i++) { + if (LIBLTE_RRC_RAT_TYPE_EUTRA == dl_dcch_msg.msg.ue_cap_enquiry.ue_capability_request[i]) { + send_rrc_ue_cap_info(); + break; + } + } + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE: + rrc_connection_release(); + break; + default: + break; + } +} + + + + + + + + + +/******************************************************************************* +* +* +* +* Capabilities Message +* +* +* +*******************************************************************************/ +void rrc::enable_capabilities() { + bool enable_ul_64 = args.ue_category >= 5 && serving_cell->sib2ptr()->rr_config_common_sib.pusch_cnfg.enable_64_qam; + rrc_log->info("%s 64QAM PUSCH\n", enable_ul_64 ? "Enabling" : "Disabling"); + phy->set_config_64qam_en(enable_ul_64); +} + +void rrc::send_rrc_ue_cap_info() { + rrc_log->debug("Preparing UE Capability Info\n"); + + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO; + ul_dcch_msg.msg.ue_capability_info.rrc_transaction_id = transaction_id; + + LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *info = &ul_dcch_msg.msg.ue_capability_info; + info->N_ue_caps = 1; + info->ue_capability_rat[0].rat_type = LIBLTE_RRC_RAT_TYPE_EUTRA; + + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *cap = &info->ue_capability_rat[0].eutra_capability; + cap->access_stratum_release = LIBLTE_RRC_ACCESS_STRATUM_RELEASE_REL8; + cap->ue_category = args.ue_category; + + cap->pdcp_params.max_rohc_ctxts_present = false; + cap->pdcp_params.supported_rohc_profiles[0] = false; + cap->pdcp_params.supported_rohc_profiles[1] = false; + cap->pdcp_params.supported_rohc_profiles[2] = false; + cap->pdcp_params.supported_rohc_profiles[3] = false; + cap->pdcp_params.supported_rohc_profiles[4] = false; + cap->pdcp_params.supported_rohc_profiles[5] = false; + cap->pdcp_params.supported_rohc_profiles[6] = false; + cap->pdcp_params.supported_rohc_profiles[7] = false; + cap->pdcp_params.supported_rohc_profiles[8] = false; + + cap->phy_params.specific_ref_sigs_supported = false; + cap->phy_params.tx_antenna_selection_supported = false; + + cap->rf_params.N_supported_band_eutras = args.nof_supported_bands; + cap->meas_params.N_band_list_eutra = args.nof_supported_bands; + for (uint32_t i=0;irf_params.supported_band_eutra[i].band_eutra = args.supported_bands[i]; + cap->rf_params.supported_band_eutra[i].half_duplex = false; + cap->meas_params.band_list_eutra[i].N_inter_freq_need_for_gaps = 1; + cap->meas_params.band_list_eutra[i].inter_freq_need_for_gaps[0] = true; + } + + cap->feature_group_indicator_present = true; + cap->feature_group_indicator = args.feature_group; + cap->inter_rat_params.utra_fdd_present = false; + cap->inter_rat_params.utra_tdd128_present = false; + cap->inter_rat_params.utra_tdd384_present = false; + cap->inter_rat_params.utra_tdd768_present = false; + cap->inter_rat_params.geran_present = false; + cap->inter_rat_params.cdma2000_hrpd_present = false; + cap->inter_rat_params.cdma2000_1xrtt_present = false; + + send_ul_dcch_msg(); +} + + + + + + + + + + +/******************************************************************************* +* +* +* +* PHY and MAC Radio Resource configuration +* +* +* +*******************************************************************************/ + +void rrc::apply_rr_config_common_dl(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *config) { + mac_interface_rrc::mac_cfg_t mac_cfg; + mac->get_config(&mac_cfg); + if (config->rach_cnfg_present) { + memcpy(&mac_cfg.rach, &config->rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + mac_cfg.ul_harq_params.max_harq_msg3_tx = config->rach_cnfg.max_harq_msg3_tx; + } + mac_cfg.prach_config_index = config->prach_cnfg.root_sequence_index; + + mac->set_config(&mac_cfg); + + phy_interface_rrc::phy_cfg_t phy_cfg; + phy->get_config(&phy_cfg); + phy_interface_rrc::phy_cfg_common_t *common = &phy_cfg.common; + + if (config->pdsch_cnfg_present) { + memcpy(&common->pdsch_cnfg, &config->pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + } + common->prach_cnfg.root_sequence_index = config->prach_cnfg.root_sequence_index; + if (config->prach_cnfg.prach_cnfg_info_present) { + memcpy(&common->prach_cnfg.prach_cnfg_info, &config->prach_cnfg.prach_cnfg_info, sizeof(LIBLTE_RRC_PRACH_CONFIG_INFO_STRUCT)); + } + + phy->set_config_common(common); +} + +void rrc::apply_rr_config_common_ul(LIBLTE_RRC_RR_CONFIG_COMMON_STRUCT *config) { + phy_interface_rrc::phy_cfg_t phy_cfg; + phy->get_config(&phy_cfg); + phy_interface_rrc::phy_cfg_common_t *common = &phy_cfg.common; + + memcpy(&common->pusch_cnfg, &config->pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + if (config->pucch_cnfg_present) { + memcpy(&common->pucch_cnfg, &config->pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + } + if (config->ul_pwr_ctrl_present) { + memcpy(&common->ul_pwr_ctrl, &config->ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + } + if (config->srs_ul_cnfg.present) { + memcpy(&common->srs_ul_cnfg, &config->srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common->srs_ul_cnfg.present = false; + } + phy->set_config_common(common); + phy->configure_ul_params(); +} + +void rrc::apply_sib2_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2) { + + // Apply RACH timeAlginmentTimer configuration + mac_interface_rrc::mac_cfg_t cfg; + mac->get_config(&cfg); + + cfg.main.time_alignment_timer = sib2->time_alignment_timer; + memcpy(&cfg.rach, &sib2->rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + cfg.prach_config_index = sib2->rr_config_common_sib.prach_cnfg.root_sequence_index; + cfg.ul_harq_params.max_harq_msg3_tx = cfg.rach.max_harq_msg3_tx; + // Apply MBSFN configuration +// cfg.mbsfn_subfr_cnfg_list_size = sib2->mbsfn_subfr_cnfg_list_size; +// for(uint8_t i=0;imbsfn_subfr_cnfg_list_size;i++) { +// memcpy(&cfg.mbsfn_subfr_cnfg_list[i], &sib2->mbsfn_subfr_cnfg_list[i], sizeof(LIBLTE_RRC_MBSFN_SUBFRAME_CONFIG_STRUCT)); +// } + + // Set MBSFN configs + phy->set_config_mbsfn_sib2(sib2); + + mac->set_config(&cfg); + + rrc_log->info("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms\n", + liblte_rrc_number_of_ra_preambles_num[sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer]); + + // Apply PHY RR Config Common + phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2->rr_config_common_sib.ul_pwr_ctrl, + sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + if (sib2->rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, + sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + + phy->configure_ul_params(); + + rrc_log->info("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2->rr_config_common_sib.pusch_cnfg.n_sb); + + rrc_log->info("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2->rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + rrc_log->info("Set PRACH ConfigCommon: SeqIdx=%d, HS=%s, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2->rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag ? "yes" : "no", + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + rrc_log->info("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%s\n", + liblte_rrc_srs_bw_config_num[sib2->rr_config_common_sib.srs_ul_cnfg.bw_cnfg], + liblte_rrc_srs_subfr_config_num[sib2->rr_config_common_sib.srs_ul_cnfg.subfr_cnfg], + sib2->rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx ? "yes" : "no"); + + mac_timers->timer_get(t300)->set(this, liblte_rrc_t300_num[sib2->ue_timers_and_constants.t300]); + mac_timers->timer_get(t301)->set(this, liblte_rrc_t301_num[sib2->ue_timers_and_constants.t301]); + mac_timers->timer_get(t310)->set(this, liblte_rrc_t310_num[sib2->ue_timers_and_constants.t310]); + mac_timers->timer_get(t311)->set(this, liblte_rrc_t311_num[sib2->ue_timers_and_constants.t311]); + N310 = liblte_rrc_n310_num[sib2->ue_timers_and_constants.n310]; + N311 = liblte_rrc_n311_num[sib2->ue_timers_and_constants.n311]; + + rrc_log->info("Set Constants and Timers: N310=%d, N311=%d, t300=%d, t301=%d, t310=%d, t311=%d\n", + N310, N311, mac_timers->timer_get(t300)->get_timeout(), mac_timers->timer_get(t301)->get_timeout(), + mac_timers->timer_get(t310)->get_timeout(), mac_timers->timer_get(t311)->get_timeout()); + +} + +void rrc::apply_sib13_configs(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_13_STRUCT *sib13) +{ + phy->set_config_mbsfn_sib13(&serving_cell->sib13); + add_mrb(0, 0); // Add MRB0 +} + +// Go through all information elements and apply defaults (9.2.4) if not defined +void rrc::apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults) { + // Get current configuration + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *current_cfg; + phy_interface_rrc::phy_cfg_t c; + phy->get_config(&c); + current_cfg = &c.dedicated; + + if (phy_cnfg->pucch_cnfg_ded_present) { + memcpy(¤t_cfg->pucch_cnfg_ded, &phy_cnfg->pucch_cnfg_ded, sizeof(LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->pucch_cnfg_ded.tdd_ack_nack_feedback_mode = LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_BUNDLING; + current_cfg->pucch_cnfg_ded.ack_nack_repetition_setup_present = false; + } + if (phy_cnfg->pusch_cnfg_ded_present) { + memcpy(¤t_cfg->pusch_cnfg_ded, &phy_cnfg->pusch_cnfg_ded, sizeof(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->pusch_cnfg_ded.beta_offset_ack_idx = 10; + current_cfg->pusch_cnfg_ded.beta_offset_ri_idx = 12; + current_cfg->pusch_cnfg_ded.beta_offset_cqi_idx = 15; + } + if (phy_cnfg->ul_pwr_ctrl_ded_present) { + memcpy(¤t_cfg->ul_pwr_ctrl_ded, &phy_cnfg->ul_pwr_ctrl_ded, + sizeof(LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->ul_pwr_ctrl_ded.p0_ue_pusch = 0; + current_cfg->ul_pwr_ctrl_ded.delta_mcs_en = LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + current_cfg->ul_pwr_ctrl_ded.accumulation_en = true; + current_cfg->ul_pwr_ctrl_ded.p0_ue_pucch = 0; + current_cfg->ul_pwr_ctrl_ded.p_srs_offset = 7; + } + if (phy_cnfg->ul_pwr_ctrl_ded.filter_coeff_present) { + current_cfg->ul_pwr_ctrl_ded.filter_coeff = phy_cnfg->ul_pwr_ctrl_ded.filter_coeff; + } else { + current_cfg->ul_pwr_ctrl_ded.filter_coeff = LIBLTE_RRC_FILTER_COEFFICIENT_FC4; + } + if (phy_cnfg->tpc_pdcch_cnfg_pucch_present) { + memcpy(¤t_cfg->tpc_pdcch_cnfg_pucch, &phy_cnfg->tpc_pdcch_cnfg_pucch, + sizeof(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT)); + } else if (apply_defaults) { + current_cfg->tpc_pdcch_cnfg_pucch.setup_present = false; + } + if (phy_cnfg->tpc_pdcch_cnfg_pusch_present) { + memcpy(¤t_cfg->tpc_pdcch_cnfg_pusch, &phy_cnfg->tpc_pdcch_cnfg_pusch, + sizeof(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT)); + } else { + current_cfg->tpc_pdcch_cnfg_pusch.setup_present = false; + } + if (phy_cnfg->cqi_report_cnfg_present) { + if (phy_cnfg->cqi_report_cnfg.report_periodic_present) { + memcpy(¤t_cfg->cqi_report_cnfg.report_periodic, &phy_cnfg->cqi_report_cnfg.report_periodic, + sizeof(LIBLTE_RRC_CQI_REPORT_PERIODIC_STRUCT)); + current_cfg->cqi_report_cnfg.report_periodic_setup_present = phy_cnfg->cqi_report_cnfg.report_periodic_setup_present; + } else if (apply_defaults) { + current_cfg->cqi_report_cnfg.report_periodic_setup_present = false; + } + if (phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present) { + current_cfg->cqi_report_cnfg.report_mode_aperiodic = phy_cnfg->cqi_report_cnfg.report_mode_aperiodic; + current_cfg->cqi_report_cnfg.report_mode_aperiodic_present = phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present; + } else if (apply_defaults) { + current_cfg->cqi_report_cnfg.report_mode_aperiodic_present = false; + } + current_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = phy_cnfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset; + } + if (phy_cnfg->srs_ul_cnfg_ded_present && phy_cnfg->srs_ul_cnfg_ded.setup_present) { + memcpy(¤t_cfg->srs_ul_cnfg_ded, &phy_cnfg->srs_ul_cnfg_ded, + sizeof(LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->srs_ul_cnfg_ded.setup_present = false; + } + if (phy_cnfg->antenna_info_present) { + if (!phy_cnfg->antenna_info_default_value) { + if (phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_1 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_2 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_3 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_4) { + rrc_log->error("Transmission mode TM%s not currently supported by srsUE\n", + liblte_rrc_transmission_mode_text[phy_cnfg->antenna_info_explicit_value.tx_mode]); + } + memcpy(¤t_cfg->antenna_info_explicit_value, &phy_cnfg->antenna_info_explicit_value, + sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_2; + current_cfg->antenna_info_explicit_value.codebook_subset_restriction_present = false; + current_cfg->antenna_info_explicit_value.ue_tx_antenna_selection_setup_present = false; + } + } else if (apply_defaults) { + current_cfg->antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_2; + current_cfg->antenna_info_explicit_value.codebook_subset_restriction_present = false; + current_cfg->antenna_info_explicit_value.ue_tx_antenna_selection_setup_present = false; + } + if (phy_cnfg->sched_request_cnfg_present) { + memcpy(¤t_cfg->sched_request_cnfg, &phy_cnfg->sched_request_cnfg, + sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + } else if (apply_defaults) { + current_cfg->sched_request_cnfg.setup_present = false; + } + if (phy_cnfg->pdsch_cnfg_ded_present) { + current_cfg->pdsch_cnfg_ded = phy_cnfg->pdsch_cnfg_ded; + rrc_log->info("Set PDSCH-Config=%s (present)\n", liblte_rrc_pdsch_config_p_a_text[(int) current_cfg->pdsch_cnfg_ded]); + } else if (apply_defaults) { + current_cfg->pdsch_cnfg_ded = LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_0; + rrc_log->info("Set PDSCH-Config=%s (default)\n", liblte_rrc_pdsch_config_p_a_text[(int) current_cfg->pdsch_cnfg_ded]); + } + + if (phy_cnfg->cqi_report_cnfg_present) { + if (phy_cnfg->cqi_report_cnfg.report_periodic_present) { + rrc_log->info("Set cqi-PUCCH-ResourceIndex=%d, cqi-pmi-ConfigIndex=%d, cqi-FormatIndicatorPeriodic=%s\n", + current_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx, + current_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + liblte_rrc_cqi_format_indicator_periodic_text[current_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic]); + } + if (phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present) { + rrc_log->info("Set cqi-ReportModeAperiodic=%s\n", + liblte_rrc_cqi_report_mode_aperiodic_text[current_cfg->cqi_report_cnfg.report_mode_aperiodic]); + } + + } + + if (phy_cnfg->sched_request_cnfg_present) { + rrc_log->info("Set PHY config ded: SR-n_pucch=%d, SR-ConfigIndex=%d, SR-TransMax=%d\n", + current_cfg->sched_request_cnfg.sr_pucch_resource_idx, + current_cfg->sched_request_cnfg.sr_cnfg_idx, + liblte_rrc_dsr_trans_max_num[current_cfg->sched_request_cnfg.dsr_trans_max]); + } + + if (current_cfg->srs_ul_cnfg_ded_present) { + rrc_log->info("Set PHY config ded: SRS-ConfigIndex=%d, SRS-bw=%s, SRS-Nrcc=%d, SRS-hop=%s, SRS-Ncs=%s\n", + current_cfg->srs_ul_cnfg_ded.srs_cnfg_idx, + liblte_rrc_srs_bandwidth_text[current_cfg->srs_ul_cnfg_ded.srs_bandwidth], + current_cfg->srs_ul_cnfg_ded.freq_domain_pos, + liblte_rrc_srs_hopping_bandwidth_text[current_cfg->srs_ul_cnfg_ded.srs_hopping_bandwidth], + liblte_rrc_cyclic_shift_text[current_cfg->srs_ul_cnfg_ded.cyclic_shift]); + } + + phy->set_config_dedicated(current_cfg); + + // Apply changes to PHY + phy->configure_ul_params(); +} + +void rrc::apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cnfg, bool apply_defaults) { + // Set Default MAC main configuration (9.2.2) + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT default_cfg; + bzero(&default_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + default_cfg.ulsch_cnfg.max_harq_tx = LIBLTE_RRC_MAX_HARQ_TX_N5; + default_cfg.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_INFINITY; + default_cfg.ulsch_cnfg.retx_bsr_timer = LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF2560; + default_cfg.ulsch_cnfg.tti_bundling = false; + default_cfg.drx_cnfg.setup_present = false; + default_cfg.phr_cnfg.setup_present = false; + default_cfg.time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + + + if (!apply_defaults) { + if (mac_cnfg->ulsch_cnfg_present) { + if (mac_cnfg->ulsch_cnfg.max_harq_tx_present) { + default_cfg.ulsch_cnfg.max_harq_tx = mac_cnfg->ulsch_cnfg.max_harq_tx; + default_cfg.ulsch_cnfg.max_harq_tx_present = true; + } + if (mac_cnfg->ulsch_cnfg.periodic_bsr_timer_present) { + default_cfg.ulsch_cnfg.periodic_bsr_timer = mac_cnfg->ulsch_cnfg.periodic_bsr_timer; + default_cfg.ulsch_cnfg.periodic_bsr_timer_present = true; + } + default_cfg.ulsch_cnfg.retx_bsr_timer = mac_cnfg->ulsch_cnfg.retx_bsr_timer; + default_cfg.ulsch_cnfg.tti_bundling = mac_cnfg->ulsch_cnfg.tti_bundling; + } + if (mac_cnfg->drx_cnfg_present) { + memcpy(&default_cfg.drx_cnfg, &mac_cnfg->drx_cnfg, sizeof(LIBLTE_RRC_DRX_CONFIG_STRUCT)); + default_cfg.drx_cnfg_present = true; + } + if (mac_cnfg->phr_cnfg_present) { + memcpy(&default_cfg.phr_cnfg, &mac_cnfg->phr_cnfg, sizeof(LIBLTE_RRC_PHR_CONFIG_STRUCT)); + default_cfg.phr_cnfg_present = true; + } + default_cfg.time_alignment_timer = mac_cnfg->time_alignment_timer; + } + + // Setup MAC configuration + mac->set_config_main(&default_cfg); + + // Update UL HARQ config + mac_interface_rrc::mac_cfg_t cfg; + mac->get_config(&cfg); + cfg.ul_harq_params.max_harq_tx = liblte_rrc_max_harq_tx_num[default_cfg.ulsch_cnfg.max_harq_tx]; + mac->set_config(&cfg); + + rrc_log->info("Set MAC main config: harq-MaxReTX=%d, bsr-TimerReTX=%d, bsr-TimerPeriodic=%d\n", + liblte_rrc_max_harq_tx_num[default_cfg.ulsch_cnfg.max_harq_tx], + liblte_rrc_retransmission_bsr_timer_num[default_cfg.ulsch_cnfg.retx_bsr_timer], + liblte_rrc_periodic_bsr_timer_num[default_cfg.ulsch_cnfg.periodic_bsr_timer]); + if (default_cfg.phr_cnfg_present) { + rrc_log->info("Set MAC PHR config: periodicPHR-Timer=%d, prohibitPHR-Timer=%d, dl-PathlossChange=%d\n", + liblte_rrc_periodic_phr_timer_num[default_cfg.phr_cnfg.periodic_phr_timer], + liblte_rrc_prohibit_phr_timer_num[default_cfg.phr_cnfg.prohibit_phr_timer], + liblte_rrc_dl_pathloss_change_num[default_cfg.phr_cnfg.dl_pathloss_change]); + } +} + +bool rrc::apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg) { + if (cnfg->phy_cnfg_ded_present) { + apply_phy_config_dedicated(&cnfg->phy_cnfg_ded, false); + // Apply SR configuration to MAC + if (cnfg->phy_cnfg_ded.sched_request_cnfg_present) { + mac->set_config_sr(&cnfg->phy_cnfg_ded.sched_request_cnfg); + } + } + + if (cnfg->mac_main_cnfg_present) { + apply_mac_config_dedicated(&cnfg->mac_main_cnfg.explicit_value, cnfg->mac_main_cnfg.default_value); + } + + if (cnfg->sps_cnfg_present) { + //TODO + } + if (cnfg->rlf_timers_and_constants_present) { + mac_timers->timer_get(t301)->set(this, liblte_rrc_t301_num[cnfg->rlf_timers_and_constants.t301]); + mac_timers->timer_get(t310)->set(this, liblte_rrc_t310_num[cnfg->rlf_timers_and_constants.t310]); + mac_timers->timer_get(t311)->set(this, liblte_rrc_t311_num[cnfg->rlf_timers_and_constants.t311]); + N310 = liblte_rrc_n310_num[cnfg->rlf_timers_and_constants.n310]; + N311 = liblte_rrc_n311_num[cnfg->rlf_timers_and_constants.n311]; + + rrc_log->info("Updated Constants and Timers: N310=%d, N311=%d, t300=%u, t301=%u, t310=%u, t311=%u\n", + N310, N311, mac_timers->timer_get(t300)->get_timeout(), mac_timers->timer_get(t301)->get_timeout(), + mac_timers->timer_get(t310)->get_timeout(), mac_timers->timer_get(t311)->get_timeout()); + } + for (uint32_t i = 0; i < cnfg->srb_to_add_mod_list_size; i++) { + // TODO: handle SRB modification + add_srb(&cnfg->srb_to_add_mod_list[i]); + } + for (uint32_t i = 0; i < cnfg->drb_to_release_list_size; i++) { + release_drb(cnfg->drb_to_release_list[i]); + } + for (uint32_t i = 0; i < cnfg->drb_to_add_mod_list_size; i++) { + // TODO: handle DRB modification + add_drb(&cnfg->drb_to_add_mod_list[i]); + } + return true; +} + +void rrc::handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup) { + // Apply the Radio Resource configuration + apply_rr_config_dedicated(&setup->rr_cnfg); + + // Must enter CONNECT before stopping T300 + state = RRC_STATE_CONNECTED; + + rrc_log->console("RRC Connected\n"); + mac_timers->timer_get(t300)->stop(); + mac_timers->timer_get(t302)->stop(); + nas->set_barring(nas_interface_rrc::BARRING_NONE); + + if (dedicatedInfoNAS) { + send_con_setup_complete(dedicatedInfoNAS); + dedicatedInfoNAS = NULL; // deallocated Inside! + } else { + rrc_log->error("Pending to transmit a ConnectionSetupComplete but no dedicatedInfoNAS was in queue\n"); + } +} + +/* Reception of RRCConnectionReestablishment by the UE 5.3.7.5 */ +void rrc::handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup) { + + mac_timers->timer_get(t301)->stop(); + + pdcp->reestablish(); + rlc->reestablish(); + + // Apply the Radio Resource configuration + apply_rr_config_dedicated(&setup->rr_cnfg); + + // Send ConnectionSetupComplete message + send_con_restablish_complete(); +} + + +void rrc::add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg) { + // Setup PDCP + pdcp->add_bearer(srb_cnfg->srb_id, srslte_pdcp_config_t(true)); // Set PDCP config control flag + if(RB_ID_SRB2 == srb_cnfg->srb_id) { + pdcp->config_security(srb_cnfg->srb_id, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); + pdcp->enable_integrity(srb_cnfg->srb_id); + pdcp->enable_encryption(srb_cnfg->srb_id); + } + + // Setup RLC + if (srb_cnfg->rlc_cnfg_present) { + if (srb_cnfg->rlc_default_cnfg_present) { + rlc->add_bearer(srb_cnfg->srb_id); + }else{ + rlc->add_bearer(srb_cnfg->srb_id, srslte_rlc_config_t(&srb_cnfg->rlc_explicit_cnfg)); + } + } + + // Setup MAC + uint8_t log_chan_group = 0; + uint8_t priority = 1; + int prioritized_bit_rate = -1; + int bucket_size_duration = -1; + + if (srb_cnfg->lc_cnfg_present) { + if (srb_cnfg->lc_default_cnfg_present) { + if (RB_ID_SRB2 == srb_cnfg->srb_id) + priority = 3; + } else { + if (srb_cnfg->lc_explicit_cnfg.log_chan_sr_mask_present) { + //TODO + } + if (srb_cnfg->lc_explicit_cnfg.ul_specific_params_present) { + if (srb_cnfg->lc_explicit_cnfg.ul_specific_params.log_chan_group_present) + log_chan_group = srb_cnfg->lc_explicit_cnfg.ul_specific_params.log_chan_group; + + priority = srb_cnfg->lc_explicit_cnfg.ul_specific_params.priority; + prioritized_bit_rate = liblte_rrc_prioritized_bit_rate_num[srb_cnfg->lc_explicit_cnfg.ul_specific_params.prioritized_bit_rate]; + bucket_size_duration = liblte_rrc_bucket_size_duration_num[srb_cnfg->lc_explicit_cnfg.ul_specific_params.bucket_size_duration]; + } + } + mac->setup_lcid(srb_cnfg->srb_id, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration); + } + + srbs[srb_cnfg->srb_id] = *srb_cnfg; + rrc_log->info("Added radio bearer %s\n", get_rb_name(srb_cnfg->srb_id).c_str()); +} + +void rrc::add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg) { + + if (!drb_cnfg->pdcp_cnfg_present || + !drb_cnfg->rlc_cnfg_present || + !drb_cnfg->lc_cnfg_present) { + rrc_log->error("Cannot add DRB - incomplete configuration\n"); + return; + } + uint32_t lcid = 0; + if (drb_cnfg->lc_id_present) { + lcid = drb_cnfg->lc_id; + } else { + lcid = RB_ID_SRB2 + drb_cnfg->drb_id; + rrc_log->warning("LCID not present, using %d\n", lcid); + } + + // Setup PDCP + srslte_pdcp_config_t pdcp_cfg; + pdcp_cfg.is_data = true; + if (drb_cnfg->pdcp_cnfg.rlc_um_pdcp_sn_size_present) { + if (LIBLTE_RRC_PDCP_SN_SIZE_7_BITS == drb_cnfg->pdcp_cnfg.rlc_um_pdcp_sn_size) { + pdcp_cfg.sn_len = 7; + } + } + pdcp->add_bearer(lcid, pdcp_cfg); + pdcp->config_security(lcid, k_up_enc, k_up_int, cipher_algo, integ_algo); + pdcp->enable_encryption(lcid); + + // Setup RLC + rlc->add_bearer(lcid, srslte_rlc_config_t(&drb_cnfg->rlc_cnfg)); + + // Setup MAC + uint8_t log_chan_group = 0; + uint8_t priority = 1; + int prioritized_bit_rate = -1; + int bucket_size_duration = -1; + if (drb_cnfg->lc_cnfg.ul_specific_params_present) { + if (drb_cnfg->lc_cnfg.ul_specific_params.log_chan_group_present) { + log_chan_group = drb_cnfg->lc_cnfg.ul_specific_params.log_chan_group; + } else { + rrc_log->warning("LCG not present, setting to 0\n"); + } + priority = drb_cnfg->lc_cnfg.ul_specific_params.priority; + prioritized_bit_rate = liblte_rrc_prioritized_bit_rate_num[drb_cnfg->lc_cnfg.ul_specific_params.prioritized_bit_rate]; + + if (prioritized_bit_rate > 0) { + rrc_log->warning("PBR>0 currently not supported. Setting it to Inifinty\n"); + prioritized_bit_rate = -1; + } + + bucket_size_duration = liblte_rrc_bucket_size_duration_num[drb_cnfg->lc_cnfg.ul_specific_params.bucket_size_duration]; + } + mac->setup_lcid(lcid, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration); + + drbs[lcid] = *drb_cnfg; + drb_up = true; + rrc_log->info("Added radio bearer %s\n", get_rb_name(lcid).c_str()); +} + +void rrc::release_drb(uint8_t lcid) { + // TODO +} + +void rrc::add_mrb(uint32_t lcid, uint32_t port) +{ + gw->add_mch_port(lcid, port); + rlc->add_bearer_mrb(lcid); + mac->mch_start_rx(lcid); + rrc_log->info("Added MRB bearer for lcid:%d\n", lcid); +} + +// PHY CONFIG DEDICATED Defaults (3GPP 36.331 v10 9.2.4) +void rrc::set_phy_default_pucch_srs() { + + phy_interface_rrc::phy_cfg_t current_cfg; + phy->get_config(¤t_cfg); + + // Set defaults to CQI, SRS and SR + current_cfg.dedicated.cqi_report_cnfg_present = false; + current_cfg.dedicated.srs_ul_cnfg_ded_present = false; + current_cfg.dedicated.sched_request_cnfg_present = false; + + apply_phy_config_dedicated(¤t_cfg.dedicated, true); + + // Release SR configuration from MAC + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac->set_config_sr(&cfg); +} + +void rrc::set_phy_default() { + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT defaults; + bzero(&defaults, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + apply_phy_config_dedicated(&defaults, true); +} + +void rrc::set_mac_default() { + apply_mac_config_dedicated(NULL, true); + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT sr_cfg; + bzero(&sr_cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + sr_cfg.setup_present = false; + mac->set_config_sr(&sr_cfg); +} + +void rrc::set_rrc_default() { + N310 = 1; + N311 = 1; + mac_timers->timer_get(t310)->set(this, 1000); + mac_timers->timer_get(t311)->set(this, 1000); +} + + + + + + + + + + + + + + + + + + + +/************************************************************************ + * + * + * RRC Measurements + * + * + ************************************************************************/ + +void rrc::rrc_meas::init(rrc *parent) { + this->parent = parent; + this->log_h = parent->rrc_log; + this->phy = parent->phy; + this->mac_timers = parent->mac_timers; + s_measure_enabled = false; + reset(); +} + +void rrc::rrc_meas::reset() +{ + filter_k_rsrp = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + filter_k_rsrq = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + + // FIXME: Turn struct into a class and use destructor + std::map::iterator iter = active.begin(); + while (iter != active.end()) { + remove_meas_id(iter++); + } + + // These objects do not need destructor + objects.clear(); + reports_cfg.clear(); + phy->meas_reset(); + bzero(&pcell_measurement, sizeof(meas_value_t)); +} + +/* L3 filtering 5.5.3.2 */ +void rrc::rrc_meas::L3_filter(meas_value_t *value, float values[NOF_MEASUREMENTS]) +{ + for (int i=0;ims[i]) { + value->ms[i] = SRSLTE_VEC_EMA(values[i], value->ms[i], filter_a[i]); + } else { + value->ms[i] = values[i]; + } + } +} + +void rrc::rrc_meas::new_phy_meas(uint32_t earfcn, uint32_t pci, float rsrp, float rsrq, uint32_t tti) +{ + float values[NOF_MEASUREMENTS] = {rsrp, rsrq}; + + // This indicates serving cell + if (parent->serving_cell->equals(earfcn, pci)) { + + log_h->debug("MEAS: New measurement serving cell, rsrp=%f, rsrq=%f, tti=%d\n", rsrp, rsrq, tti); + + L3_filter(&pcell_measurement, values); + + // Update serving cell measurement + parent->serving_cell->set_rsrp(rsrp); + + } else { + + // Add to list of neighbour cells + bool added = parent->add_neighbour_cell(earfcn, pci, rsrp); + + log_h->debug("MEAS: New measurement %s earfcn=%d, pci=%d, rsrp=%f, rsrq=%f, tti=%d\n", + added?"added":"not added", earfcn, pci, rsrp, rsrq, tti); + + // Only report measurements of 8th strongest cells + if (added) { + // Save PHY measurement for all active measurements whose earfcn/pci matches + for(std::map::iterator iter=active.begin(); iter!=active.end(); ++iter) { + meas_t *m = &iter->second; + if (objects[m->object_id].earfcn == earfcn) { + // If it's a newly discovered cell, add it to objects + if (!m->cell_values.count(pci)) { + uint32_t cell_idx = objects[m->object_id].cells.size(); + objects[m->object_id].cells[cell_idx].pci = pci; + objects[m->object_id].cells[cell_idx].q_offset = 0; + } + // Update or add cell + L3_filter(&m->cell_values[pci], values); + return; + } + } + } + } +} + +// Remove all stored measurements for a given cell +void rrc::rrc_meas::delete_report(uint32_t earfcn, uint32_t pci) { + for(std::map::iterator iter=active.begin(); iter!=active.end(); ++iter) { + meas_t *m = &iter->second; + if (objects[m->object_id].earfcn == earfcn) { + if (m->cell_values.count(pci)) { + m->cell_values.erase(pci); + log_h->info("Deleting report PCI=%d from cell_values\n", pci); + } + } + } +} + +void rrc::rrc_meas::run_tti(uint32_t tti) { + // Measurement Report Triggering Section 5.5.4 + calculate_triggers(tti); +} + +bool rrc::rrc_meas::find_earfcn_cell(uint32_t earfcn, uint32_t pci, meas_obj_t **object, int *cell_idx) { + if (object) { + *object = NULL; + } + for (std::map::iterator obj = objects.begin(); obj != objects.end(); ++obj) { + if (obj->second.earfcn == earfcn) { + if (object) { + *object = &obj->second; + } + for (std::map::iterator c = obj->second.cells.begin(); c != obj->second.cells.end(); ++c) { + if (c->second.pci == pci) { + if (cell_idx) { + *cell_idx = c->first; + return true; + } + } + } + // return true if cell idx not found but frequency is found + if (cell_idx) { + *cell_idx = -1; + } + return true; + } + } + return false; +} + +/* Generate report procedure 5.5.5 */ +void rrc::rrc_meas::generate_report(uint32_t meas_id) +{ + parent->ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_MEASUREMENT_REPORT; + LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT *report = &parent->ul_dcch_msg.msg.measurement_report; + + bzero(report, sizeof(LIBLTE_RRC_MEASUREMENT_REPORT_STRUCT)); + + meas_t *m = &active[meas_id]; + report_cfg_t *cfg = &reports_cfg[m->report_id]; + + report->meas_id = meas_id; + report->pcell_rsrp_result = value_to_range(RSRP, pcell_measurement.ms[RSRP]); + report->pcell_rsrq_result = value_to_range(RSRQ, pcell_measurement.ms[RSRQ]); + + log_h->info("MEAS: Generate report MeasId=%d, nof_reports_send=%d, Pcell rsrp=%f rsrq=%f\n", + report->meas_id, m->nof_reports_sent, pcell_measurement.ms[RSRP], pcell_measurement.ms[RSRQ]); + + // TODO: report up to 8 best cells + for (std::map::iterator cell = m->cell_values.begin(); cell != m->cell_values.end(); ++cell) + { + if (cell->second.triggered && report->meas_result_neigh_cells.eutra.n_result < 8) + { + LIBLTE_RRC_MEAS_RESULT_EUTRA_STRUCT *rc = &report->meas_result_neigh_cells.eutra.result_eutra_list[report->meas_result_neigh_cells.eutra.n_result]; + + rc->phys_cell_id = cell->first; + rc->meas_result.have_rsrp = cfg->report_quantity==RSRP || cfg->report_quantity==BOTH; + rc->meas_result.have_rsrq = cfg->report_quantity==RSRQ || cfg->report_quantity==BOTH; + rc->meas_result.rsrp_result = value_to_range(RSRP, cell->second.ms[RSRP]); + rc->meas_result.rsrq_result = value_to_range(RSRQ, cell->second.ms[RSRQ]); + + log_h->info("MEAS: Adding to report neighbour=%d, pci=%d, rsrp=%f, rsrq=%f\n", + report->meas_result_neigh_cells.eutra.n_result, rc->phys_cell_id, + cell->second.ms[RSRP], cell->second.ms[RSRQ]); + + report->meas_result_neigh_cells.eutra.n_result++; + } + } + report->have_meas_result_neigh_cells = report->meas_result_neigh_cells.eutra.n_result > 0; + + m->nof_reports_sent++; + mac_timers->timer_get(m->periodic_timer)->stop(); + + if (m->nof_reports_sent < cfg->amount) { + mac_timers->timer_get(m->periodic_timer)->reset(); + mac_timers->timer_get(m->periodic_timer)->run(); + } else { + if (cfg->trigger_type == report_cfg_t::PERIODIC) { + m->triggered = false; + } + } + + // Send to lower layers + parent->send_ul_dcch_msg(); +} + +/* Handle entering/leaving event conditions 5.5.4.1 */ +bool rrc::rrc_meas::process_event(LIBLTE_RRC_EVENT_EUTRA_STRUCT *event, uint32_t tti, + bool enter_condition, bool exit_condition, + meas_t *m, meas_value_t *cell) +{ + bool generate_report = false; + if (enter_condition && (!m->triggered || !cell->triggered)) { + if (!cell->timer_enter_triggered) { + cell->timer_enter_triggered = true; + cell->enter_tti = tti; + } else if (srslte_tti_interval(tti, cell->enter_tti) >= event->time_to_trigger) { + m->triggered = true; + cell->triggered = true; + m->nof_reports_sent = 0; + generate_report = true; + } + } else if (exit_condition) { + if (!cell->timer_exit_triggered) { + cell->timer_exit_triggered = true; + cell->exit_tti = tti; + } else if (srslte_tti_interval(tti, cell->exit_tti) >= event->time_to_trigger) { + m->triggered = false; + cell->triggered = false; + mac_timers->timer_get(m->periodic_timer)->stop(); + if (event) { + if (event->event_id == LIBLTE_RRC_EVENT_ID_EUTRA_A3 && event->event_a3.report_on_leave) { + generate_report = true; + } + } + } + } + if (!enter_condition) { + cell->timer_enter_triggered = false; + } + if (!enter_condition) { + cell->timer_exit_triggered = false; + } + return generate_report; +} + +/* Calculate trigger conditions for each cell 5.5.4 */ +void rrc::rrc_meas::calculate_triggers(uint32_t tti) +{ + float Ofp = 0, Ocp = 0; + meas_obj_t *serving_object = NULL; + int serving_cell_idx = 0; + + // Get serving cell + if (active.size()) { + if (find_earfcn_cell(phy->get_current_earfcn(), phy->get_current_pci(), &serving_object, &serving_cell_idx)) { + Ofp = serving_object->q_offset; + if (serving_cell_idx >= 0) { + Ocp = serving_object->cells[serving_cell_idx].q_offset; + } + } else { + log_h->warning("Can't find current eafcn=%d, pci=%d in objects list. Using Ofp=0, Ocp=0\n", + phy->get_current_earfcn(), phy->get_current_pci()); + } + } + + + + for (std::map::iterator m = active.begin(); m != active.end(); ++m) { + report_cfg_t *cfg = &reports_cfg[m->second.report_id]; + float hyst = 0.5*cfg->event.hysteresis; + float Mp = pcell_measurement.ms[cfg->trigger_quantity]; + + LIBLTE_RRC_EVENT_ID_EUTRA_ENUM event_id = cfg->event.event_id; + const char *event_str = liblte_rrc_event_id_eutra_text[event_id]; + + bool gen_report = false; + + if (cfg->trigger_type == report_cfg_t::EVENT) { + + // A1 & A2 are for serving cell only + if (event_id < LIBLTE_RRC_EVENT_ID_EUTRA_A3) { + + bool enter_condition; + bool exit_condition; + if (event_id == LIBLTE_RRC_EVENT_ID_EUTRA_A1) { + enter_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range); + exit_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range); + } else { + enter_condition = Mp + hyst < range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range); + exit_condition = Mp - hyst > range_to_value(cfg->trigger_quantity, cfg->event.event_a1.eutra.range); + } + + // check only if + gen_report |= process_event(&cfg->event, tti, enter_condition, exit_condition, + &m->second, &pcell_measurement); + + if (gen_report) { + log_h->info("Triggered by A1/A2 event\n"); + } + // Rest are evaluated for every cell in frequency + } else { + meas_obj_t *obj = &objects[m->second.object_id]; + for (std::map::iterator cell = obj->cells.begin(); cell != obj->cells.end(); ++cell) { + if (m->second.cell_values.count(cell->second.pci)) { + float Ofn = obj->q_offset; + float Ocn = cell->second.q_offset; + float Mn = m->second.cell_values[cell->second.pci].ms[cfg->trigger_quantity]; + float Off=0, th=0, th1=0, th2=0; + bool enter_condition = false; + bool exit_condition = false; + switch (event_id) { + case LIBLTE_RRC_EVENT_ID_EUTRA_A3: + Off = 0.5*cfg->event.event_a3.offset; + enter_condition = Mn + Ofn + Ocn - hyst > Mp + Ofp + Ocp + Off; + exit_condition = Mn + Ofn + Ocn + hyst < Mp + Ofp + Ocp + Off; + break; + case LIBLTE_RRC_EVENT_ID_EUTRA_A4: + th = range_to_value(cfg->trigger_quantity, cfg->event.event_a4.eutra.range); + enter_condition = Mn + Ofn + Ocn - hyst > th; + exit_condition = Mn + Ofn + Ocn + hyst < th; + break; + case LIBLTE_RRC_EVENT_ID_EUTRA_A5: + th1 = range_to_value(cfg->trigger_quantity, cfg->event.event_a5.eutra1.range); + th2 = range_to_value(cfg->trigger_quantity, cfg->event.event_a5.eutra2.range); + enter_condition = (Mp + hyst < th1) && (Mn + Ofn + Ocn - hyst > th2); + exit_condition = (Mp - hyst > th1) && (Mn + Ofn + Ocn + hyst < th2); + break; + default: + log_h->error("Error event %s not implemented\n", event_str); + } + gen_report |= process_event(&cfg->event, tti, enter_condition, exit_condition, + &m->second, &m->second.cell_values[cell->second.pci]); + } + } + } + } + if (gen_report) { + log_h->info("Generate report MeasId=%d, from event\n", m->first); + generate_report(m->first); + } + } +} + +// Procedure upon handover or reestablishment 5.5.6.1 +void rrc::rrc_meas::ho_finish() { + // Remove all measId with trigger periodic + std::map::iterator iter = active.begin(); + while (iter != active.end()) { + if (reports_cfg[iter->second.report_id].trigger_type == report_cfg_t::PERIODIC) { + remove_meas_id(iter++); + } else { + ++iter; + } + } + + //TODO: Inter-frequency handover + + // Stop all reports + for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { + stop_reports(&iter->second); + } +} + +// 5.5.4.1 expiry of periodical reporting timer +bool rrc::rrc_meas::timer_expired(uint32_t timer_id) { + for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { + if (iter->second.periodic_timer == timer_id) { + log_h->info("Generate report MeasId=%d, from timerId=%d\n", iter->first, timer_id); + generate_report(iter->first); + return true; + } + } + return false; +} + +void rrc::rrc_meas::stop_reports(meas_t *m) { + mac_timers->timer_get(m->periodic_timer)->stop(); + m->triggered = false; +} + +void rrc::rrc_meas::stop_reports_object(uint32_t object_id) { + for (std::map::iterator iter = active.begin(); iter != active.end(); ++iter) { + if (iter->second.object_id == object_id) { + stop_reports(&iter->second); + } + } +} + +void rrc::rrc_meas::remove_meas_object(uint32_t object_id) { + std::map::iterator iter = active.begin(); + while (iter != active.end()) { + if (iter->second.object_id == object_id) { + remove_meas_id(iter++); + } else { + ++iter; + } + } +} + +void rrc::rrc_meas::remove_meas_report(uint32_t report_id) { + std::map::iterator iter = active.begin(); + while (iter != active.end()) { + if (iter->second.report_id == report_id) { + remove_meas_id(iter++); + } else { + ++iter; + } + } +} + +void rrc::rrc_meas::remove_meas_id(uint32_t measId) { + if (active.count(measId)) { + mac_timers->timer_get(active[measId].periodic_timer)->stop(); + mac_timers->timer_release_id(active[measId].periodic_timer); + log_h->info("MEAS: Removed measId=%d\n", measId); + active.erase(measId); + } else { + log_h->warning("MEAS: Removing unexistent measId=%d\n", measId); + } +} + +void rrc::rrc_meas::remove_meas_id(std::map::iterator it) { + mac_timers->timer_get(it->second.periodic_timer)->stop(); + mac_timers->timer_release_id(it->second.periodic_timer); + log_h->info("MEAS: Removed measId=%d\n", it->first); + active.erase(it); +} + +/* Parses MeasConfig object from RRCConnectionReconfiguration message and applies configuration + * as per section 5.5.2 + */ +bool rrc::rrc_meas::parse_meas_config(LIBLTE_RRC_MEAS_CONFIG_STRUCT *cfg) +{ + + // Measurement object removal 5.5.2.4 + for (uint32_t i=0;iN_meas_obj_to_remove;i++) { + objects.erase(cfg->meas_obj_to_remove_list[i]); + remove_meas_object(cfg->meas_obj_to_remove_list[i]); + log_h->info("MEAS: Removed measObjectId=%d\n", cfg->meas_obj_to_remove_list[i]); + } + + // Measurement object addition/modification Section 5.5.2.5 + if (cfg->meas_obj_to_add_mod_list_present) { + for (uint32_t i=0;imeas_obj_to_add_mod_list.N_meas_obj;i++) { + if (cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_type == LIBLTE_RRC_MEAS_OBJECT_TYPE_EUTRA) { + LIBLTE_RRC_MEAS_OBJECT_EUTRA_STRUCT *src_obj = &cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_eutra; + + // Access the object if exists or create it + meas_obj_t *dst_obj = &objects[cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id]; + + dst_obj->earfcn = src_obj->carrier_freq;; + if (src_obj->offset_freq_not_default) { + dst_obj->q_offset = liblte_rrc_q_offset_range_num[src_obj->offset_freq]; + } else { + dst_obj->q_offset = 0; + } + + if (src_obj->black_cells_to_remove_list_present) { + for (uint32_t j=0;jblack_cells_to_remove_list.N_cell_idx;j++) { + dst_obj->cells.erase(src_obj->black_cells_to_remove_list.cell_idx[j]); + } + } + + for (uint32_t j=0;jN_cells_to_add_mod;j++) { + dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset = liblte_rrc_q_offset_range_num[src_obj->cells_to_add_mod_list[j].cell_offset]; + dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci = src_obj->cells_to_add_mod_list[j].pci; + + log_h->info("MEAS: Added measObjectId=%d, earfcn=%d, q_offset=%f, pci=%d, offset_cell=%f\n", + cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id, dst_obj->earfcn, dst_obj->q_offset, + dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].pci, + dst_obj->cells[src_obj->cells_to_add_mod_list[j].cell_idx].q_offset); + + } + + // Untrigger reports and stop timers + stop_reports_object(cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_id); + + // TODO: Blackcells + // TODO: meassubframepattern + + } else { + log_h->warning("MEAS: Unsupported MeasObject type %s\n", + liblte_rrc_meas_object_type_text[cfg->meas_obj_to_add_mod_list.meas_obj_list[i].meas_obj_type]); + } + } + } + + // Reporting configuration removal 5.5.2.6 + for (uint32_t i=0;iN_rep_cnfg_to_remove;i++) { + reports_cfg.erase(cfg->rep_cnfg_to_remove_list[i]); + remove_meas_report(cfg->rep_cnfg_to_remove_list[i]); + log_h->info("MEAS: Removed reportConfigId=%d\n", cfg->rep_cnfg_to_remove_list[i]); + } + + // Reporting configuration addition/modification 5.5.2.7 + if (cfg->rep_cnfg_to_add_mod_list_present) { + for (uint32_t i=0;irep_cnfg_to_add_mod_list.N_rep_cnfg;i++) { + if (cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_type == LIBLTE_RRC_REPORT_CONFIG_TYPE_EUTRA) { + LIBLTE_RRC_REPORT_CONFIG_EUTRA_STRUCT *src_rep = &cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_eutra; + // Access the object if exists or create it + report_cfg_t *dst_rep = &reports_cfg[cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id]; + + dst_rep->trigger_type = src_rep->trigger_type==LIBLTE_RRC_TRIGGER_TYPE_EUTRA_EVENT?report_cfg_t::EVENT:report_cfg_t::PERIODIC; + dst_rep->event = src_rep->event; + dst_rep->amount = liblte_rrc_report_amount_num[src_rep->report_amount]; + dst_rep->interval = liblte_rrc_report_interval_num[src_rep->report_interval]; + dst_rep->max_cell = src_rep->max_report_cells; + dst_rep->trigger_quantity = (quantity_t) src_rep->trigger_quantity; + dst_rep->report_quantity = src_rep->report_quantity==LIBLTE_RRC_REPORT_QUANTITY_SAME_AS_TRIGGER_QUANTITY?dst_rep->trigger_quantity:BOTH; + + log_h->info("MEAS: Added reportConfigId=%d, event=%s, amount=%d, interval=%d\n", + cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id, + liblte_rrc_event_id_eutra_text[dst_rep->event.event_id], + dst_rep->amount, dst_rep->interval); + + // Reset reports counter + for(std::map::iterator iter=active.begin(); iter!=active.end(); ++iter) { + if (iter->second.report_id == cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_id) { + iter->second.nof_reports_sent = 0; + stop_reports(&iter->second); + } + } + } else { + log_h->warning("MEAS: Unsupported reportConfigType %s\n", liblte_rrc_report_config_type_text[cfg->rep_cnfg_to_add_mod_list.rep_cnfg_list[i].rep_cnfg_type]); + } + } + } + + // Quantity configuration 5.5.2.8 + if (cfg->quantity_cnfg_present && cfg->quantity_cnfg.qc_eutra_present) { + if (cfg->quantity_cnfg.qc_eutra.fc_rsrp_not_default) { + filter_k_rsrp = liblte_rrc_filter_coefficient_num[cfg->quantity_cnfg.qc_eutra.fc_rsrp]; + } else { + filter_k_rsrp = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + } + if (cfg->quantity_cnfg.qc_eutra.fc_rsrq_not_default) { + filter_k_rsrq = liblte_rrc_filter_coefficient_num[cfg->quantity_cnfg.qc_eutra.fc_rsrq]; + } else { + filter_k_rsrq = liblte_rrc_filter_coefficient_num[LIBLTE_RRC_FILTER_COEFFICIENT_FC4]; + } + filter_a[RSRP] = pow(0.5, (float) filter_k_rsrp/4); + filter_a[RSRQ] = pow(0.5, (float) filter_k_rsrq/4); + + log_h->info("MEAS: Quantity configuration k_rsrp=%d, k_rsrq=%d\n", filter_k_rsrp, filter_k_rsrq); + } + + // Measurement identity removal 5.5.2.2 + for (uint32_t i=0;iN_meas_id_to_remove;i++) { + remove_meas_id(cfg->meas_id_to_remove_list[i]); + } + + log_h->info("nof active measId=%zd\n", active.size()); + + // Measurement identity addition/modification 5.5.2.3 + if (cfg->meas_id_to_add_mod_list_present) { + for (uint32_t i=0;imeas_id_to_add_mod_list.N_meas_id;i++) { + LIBLTE_RRC_MEAS_ID_TO_ADD_MOD_STRUCT *measId = &cfg->meas_id_to_add_mod_list.meas_id_list[i]; + // Stop the timer if the entry exists or create the timer if not + bool is_new = false; + if (active.count(measId->meas_id)) { + mac_timers->timer_get(active[measId->meas_id].periodic_timer)->stop(); + } else { + is_new = true; + active[measId->meas_id].periodic_timer = mac_timers->timer_get_unique_id(); + } + active[measId->meas_id].object_id = measId->meas_obj_id; + active[measId->meas_id].report_id = measId->rep_cnfg_id; + log_h->info("MEAS: %s measId=%d, measObjectId=%d, reportConfigId=%d, timer_id=%u, nof_values=%lu\n", + is_new?"Added":"Updated", measId->meas_id, measId->meas_obj_id, measId->rep_cnfg_id, + active[measId->meas_id].periodic_timer, + active[measId->meas_id].cell_values.size()); + } + } + + // S-Measure + if (cfg->s_meas_present) { + if (cfg->s_meas) { + s_measure_enabled = true; + s_measure_value = range_to_value(RSRP, cfg->s_meas); + } else { + s_measure_enabled = false; + } + } + + update_phy(); + + return true; +} + +/* Instruct PHY to start measurement */ +void rrc::rrc_meas::update_phy() +{ + phy->meas_reset(); + for(std::map::iterator iter=active.begin(); iter!=active.end(); ++iter) { + meas_t m = iter->second; + meas_obj_t o = objects[m.object_id]; + // Instruct PHY to look for neighbour cells on this frequency + phy->meas_start(o.earfcn); + for(std::map::iterator iter=o.cells.begin(); iter!=o.cells.end(); ++iter) { + // Instruct PHY to look for cells IDs on this frequency + phy->meas_start(o.earfcn, iter->second.pci); + } + } +} + + +uint8_t rrc::rrc_meas::value_to_range(quantity_t quant, float value) { + uint8_t range = 0; + switch(quant) { + case RSRP: + if (value < -140) { + range = 0; + } else if (-140 <= value && value < -44) { + range = 1 + (uint8_t) (value + 140); + } else { + range = 97; + } + break; + case RSRQ: + if (value < -19.5) { + range = 0; + } else if (-19.5 <= value && value < -3) { + range = 1 + (uint8_t) (2*(value + 19.5)); + } else { + range = 34; + } + break; + case BOTH: + printf("Error quantity both not supported in value_to_range\n"); + break; + } + return range; +} + +float rrc::rrc_meas::range_to_value(quantity_t quant, uint8_t range) { + float val = 0; + switch(quant) { + case RSRP: + val = -140+(float) range; + break; + case RSRQ: + val = -19.5+(float) range/2; + break; + case BOTH: + printf("Error quantity both not supported in range_to_value\n"); + break; + } + return val; +} + +const std::string rrc::rb_id_str[] = {"SRB0", "SRB1", "SRB2", + "DRB1", "DRB2", "DRB3", + "DRB4", "DRB5", "DRB6", + "DRB7", "DRB8"}; + +} // namespace srsue diff --git a/srsue/src/upper/usim.cc b/srsue/src/upper/usim.cc new file mode 100644 index 0000000..f134695 --- /dev/null +++ b/srsue/src/upper/usim.cc @@ -0,0 +1,533 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srsue/hdr/upper/usim.h" +#include "srslte/common/bcd_helpers.h" + +using namespace srslte; + +namespace srsue{ + +usim::usim() : initiated(false) +{} + +int usim::init(usim_args_t *args, srslte::log *usim_log_) +{ + usim_log = usim_log_; + imsi_str = args->imsi; + imei_str = args->imei; + + const char *imsi_c = args->imsi.c_str(); + const char *imei_c = args->imei.c_str(); + uint32_t i; + + if(32 == args->k.length()) { + str_to_hex(args->k, k); + } else { + usim_log->error("Invalid length for K: %zu should be %d\n", args->k.length(), 32); + usim_log->console("Invalid length for K: %zu should be %d\n", args->k.length(), 32); + } + + if(args->using_op) { + if(32 == args->op.length()) { + str_to_hex(args->op, op); + compute_opc(k,op,opc); + } else { + usim_log->error("Invalid length for OP: %zu should be %d\n", args->op.length(), 32); + usim_log->console("Invalid length for OP: %zu should be %d\n", args->op.length(), 32); + } + } + else { + if(32 == args->opc.length()) { + str_to_hex(args->opc, opc); + } else { + usim_log->error("Invalid length for OPc: %zu should be %d\n", args->opc.length(), 32); + usim_log->console("Invalid length for OPc: %zu should be %d\n", args->opc.length(), 32); + } + } + + if(15 == args->imsi.length()) { + imsi = 0; + for(i=0; i<15; i++) + { + imsi *= 10; + imsi += imsi_c[i] - '0'; + } + } else { + usim_log->error("Invalid length for ISMI: %zu should be %d\n", args->imsi.length(), 15); + usim_log->console("Invalid length for IMSI: %zu should be %d\n", args->imsi.length(), 15); + } + + if(15 == args->imei.length()) { + imei = 0; + for(i=0; i<15; i++) + { + imei *= 10; + imei += imei_c[i] - '0'; + } + } else { + usim_log->error("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + usim_log->console("Invalid length for IMEI: %zu should be %d\n", args->imei.length(), 15); + } + + auth_algo = auth_algo_milenage; + if("xor" == args->algo) { + auth_algo = auth_algo_xor; + } + initiated = true; + + return SRSLTE_SUCCESS; +} + +void usim::stop() +{} + +/******************************************************************************* + NAS interface +*******************************************************************************/ + +std::string usim::get_imsi_str() +{ + return imsi_str; +} +std::string usim::get_imei_str() +{ + return imei_str; +} + +bool usim::get_imsi_vec(uint8_t* imsi_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imsi_ || n < 15) { + usim_log->error("Invalid parameters to get_imsi_vec\n"); + return false; + } + + uint64_t temp = imsi; + for(int i=14;i>=0;i--) { + imsi_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool usim::get_imei_vec(uint8_t* imei_, uint32_t n) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + if(NULL == imei_ || n < 15) { + usim_log->error("Invalid parameters to get_imei_vec\n"); + return false; + } + + uint64 temp = imei; + for(int i=14;i>=0;i--) + { + imei_[i] = temp % 10; + temp /= 10; + } + return true; +} + +bool usim::get_home_plmn_id(LIBLTE_RRC_PLMN_IDENTITY_STRUCT *home_plmn_id) +{ + if (!initiated) { + fprintf(stderr, "USIM not initiated!\n"); + return false; + } + + int mcc_len = 3; + int mnc_len = 2; + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::ostringstream mcc_str, mnc_str; + + for (int i=0;imcc); + string_to_mnc(mnc_str.str(), &home_plmn_id->mnc); + + usim_log->info("Read Home PLMN Id=%s\n", + plmn_id_to_string(*home_plmn_id).c_str()); + + return true; +} + +auth_result_t usim::generate_authentication_response(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) +{ + if(auth_algo_xor == auth_algo) { + return gen_auth_res_xor(rand, autn_enb, mcc, mnc, res, res_len, k_asme); + } else { + return gen_auth_res_milenage(rand, autn_enb, mcc, mnc, res, res_len, k_asme); + } +} + +void usim::generate_nas_keys(uint8_t *k_asme, + uint8_t *k_nas_enc, + uint8_t *k_nas_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + // Generate K_nas_enc and K_nas_int + security_generate_k_nas( k_asme, + cipher_algo, + integ_algo, + k_nas_enc, + k_nas_int); + + +} + +/******************************************************************************* + RRC interface +*******************************************************************************/ + +void usim::generate_as_keys(uint8_t *k_asme, + uint32_t count_ul, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + + // Generate K_enb + security_generate_k_enb( k_asme, + count_ul, + k_enb); + + memcpy(this->k_asme, k_asme, 32); + + // Save initial k_enb + memcpy(k_enb_initial, k_enb, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); + + current_ncc = 0; +} + +void usim::generate_as_keys_ho(uint32_t pci, + uint32_t earfcn, + int ncc, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + uint8_t *enb_star_key = k_enb; + + if (ncc < 0) { + ncc = current_ncc; + } + + // Generate successive NH + while(current_ncc != (uint32_t) ncc) { + uint8_t *sync = NULL; + if (current_ncc) { + sync = nh; + } else { + sync = k_enb_initial; + } + // Generate NH + security_generate_nh(k_asme, + sync, + nh); + + current_ncc++; + if (current_ncc == 8) { + current_ncc = 0; + } + enb_star_key = nh; + } + + // Generate K_enb + security_generate_k_enb_star( enb_star_key, + pci, + earfcn, + k_enb_star); + + // K_enb becomes K_enb* + memcpy(k_enb, k_enb_star, 32); + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); +} + +/******************************************************************************* + Helpers +*******************************************************************************/ + +auth_result_t usim::gen_auth_res_milenage(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) +{ + auth_result_t result = AUTH_OK; + uint32_t i; + uint8_t sqn[6]; + + // Use RAND and K to compute RES, CK, IK and AK + security_milenage_f2345( k, + opc, + rand, + res, + ck, + ik, + ak); + + *res_len = 8; + + // Extract sqn from autn + for(i=0;i<6;i++) + { + sqn[i] = autn_enb[i] ^ ak[i]; + } + // Extract AMF from autn + for(int i=0;i<2;i++) + { + amf[i]=autn_enb[6+i]; + } + + // Generate MAC + security_milenage_f1( k, + opc, + rand, + sqn, + amf, + mac); + + // Construct AUTN + for(i=0; i<6; i++) + { + autn[i] = sqn[i] ^ ak[i]; + } + for(i=0; i<2; i++) + { + autn[6+i] = amf[i]; + } + for(i=0; i<8; i++) + { + autn[8+i] = mac[i]; + } + + // Compare AUTNs + for(i=0; i<16; i++) + { + if(autn[i] != autn_enb[i]) + { + result = AUTH_FAILED; + } + } + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + return result; +} + +// 3GPP TS 34.108 version 10.0.0 Section 8 +auth_result_t usim::gen_auth_res_xor(uint8_t *rand, + uint8_t *autn_enb, + uint16_t mcc, + uint16_t mnc, + uint8_t *res, + int *res_len, + uint8_t *k_asme) +{ + auth_result_t result = AUTH_OK; + uint32_t i; + uint8_t sqn[6]; + uint8_t xdout[16]; + uint8_t cdout[8]; + + // Use RAND and K to compute RES, CK, IK and AK + for(i=0; i<16; i++) { + xdout[i] = k[i]^rand[i]; + } + for(i=0; i<16; i++) { + res[i] = xdout[i]; + ck[i] = xdout[(i+1)%16]; + ik[i] = xdout[(i+2)%16]; + } + for(i=0; i<6; i++) { + ak[i] = xdout[i+3]; + } + + *res_len = 8; + + // Extract sqn from autn + for(i=0;i<6;i++) { + sqn[i] = autn_enb[i] ^ ak[i]; + } + // Extract AMF from autn + for(int i=0;i<2;i++){ + amf[i]=autn_enb[6+i]; + } + + // Generate cdout + for(i=0; i<6; i++) { + cdout[i] = sqn[i]; + } + for(i=0; i<2; i++) { + cdout[6+i] = amf[i]; + } + + // Generate MAC + for(i=0;i<8;i++) { + mac[i] = xdout[i] ^ cdout[i]; + } + + // Construct AUTN + for(i=0; i<6; i++) + { + autn[i] = sqn[i] ^ ak[i]; + } + for(i=0; i<2; i++) + { + autn[6+i] = amf[i]; + } + for(i=0; i<8; i++) + { + autn[8+i] = mac[i]; + } + + // Compare AUTNs + for(i=0; i<16; i++) + { + if(autn[i] != autn_enb[i]) + { + result = AUTH_FAILED; + } + } + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + return result; +} + +void usim::str_to_hex(std::string str, uint8_t *hex) +{ + uint32_t i; + const char *h_str = str.c_str(); + uint32_t len = str.length(); + + for(i=0; i= '0' && h_str[i*2+0] <= '9') + { + hex[i] = ( h_str[i*2+0] - '0') << 4; + }else if( h_str[i*2+0] >= 'A' && h_str[i*2+0] <= 'F'){ + hex[i] = (( h_str[i*2+0] - 'A') + 0xA) << 4; + }else{ + hex[i] = (( h_str[i*2+0] - 'a') + 0xA) << 4; + } + + if( h_str[i*2+1] >= '0' && h_str[i*2+1] <= '9') + { + hex[i] |= h_str[i*2+1] - '0'; + }else if( h_str[i*2+1] >= 'A' && h_str[i*2+1] <= 'F'){ + hex[i] |= ( h_str[i*2+1] - 'A') + 0xA; + }else{ + hex[i] |= ( h_str[i*2+1] - 'a') + 0xA; + } + } +} + +} // namespace srsue diff --git a/srsue/src/upper/usim_base.cc b/srsue/src/upper/usim_base.cc new file mode 100644 index 0000000..d6672cf --- /dev/null +++ b/srsue/src/upper/usim_base.cc @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#ifdef HAVE_PCSC +#include +#endif + +namespace srsue{ + +usim_base* usim_base::get_instance(usim_args_t *args, srslte::log *usim_log_) +{ + usim_base* instance = NULL; + if (args->mode == "soft") { + instance = new usim(); + } +#if HAVE_PCSC + else if (args->mode == "pcsc") { + instance = new pcsc_usim(); + } +#endif + else { + // default to soft USIM + instance = new usim(); + } + return(instance); +} + +usim_base::usim_base() { +} + +usim_base::~usim_base() { +} + +} // namespace srsue diff --git a/srsue/test/CMakeLists.txt b/srsue/test/CMakeLists.txt new file mode 100644 index 0000000..217c369 --- /dev/null +++ b/srsue/test/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2013-2017 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/. +# + +add_subdirectory(phy) +add_subdirectory(mac) +add_subdirectory(upper) + +add_executable(metrics_test metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc) +target_link_libraries(metrics_test srslte_phy srslte_common) +add_test(metrics_test metrics_test -o ${CMAKE_CURRENT_BINARY_DIR}/ue_metrics.csv) diff --git a/srsue/test/mac/CMakeLists.txt b/srsue/test/mac/CMakeLists.txt new file mode 100644 index 0000000..03407ce --- /dev/null +++ b/srsue/test/mac/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 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/. +# +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch") +add_executable(mac_test mac_test.cc) +target_link_libraries(mac_test srsue_mac srsue_phy srslte_common srslte_phy srslte_radio srslte_asn1 ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) + diff --git a/srsue/test/mac/mac_test.cc b/srsue/test/mac/mac_test.cc new file mode 100644 index 0000000..633bb3b --- /dev/null +++ b/srsue/test/mac/mac_test.cc @@ -0,0 +1,508 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include + +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/radio/radio_multi.h" +#include "srsue/hdr/phy/phy.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/log_filter.h" +#include "srsue/hdr/mac/mac.h" +#include "srslte/common/mac_pcap.h" + + + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + float rf_rx_freq; + float rf_tx_freq; + float rf_rx_gain; + float rf_tx_gain; + int verbose; + bool do_trace; + bool do_pcap; +}prog_args_t; + +void args_default(prog_args_t *args) { + args->rf_rx_freq = -1.0; + args->rf_tx_freq = -1.0; + args->rf_rx_gain = -1; // set to autogain + args->rf_tx_gain = -1; + args->verbose = 0; + args->do_trace = false; + args->do_pcap = false; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGtpv] -f rx_frequency (in Hz) -F tx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-G RF TX gain [Default same as RX gain (AGC)]\n"); + printf("\t-t Enable trace [Default disabled]\n"); + printf("\t-p Enable PCAP capture [Default disabled]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGftpFv")) != -1) { + switch (opt) { + case 'g': + args->rf_rx_gain = atof(argv[optind]); + break; + case 'G': + args->rf_tx_gain = atof(argv[optind]); + break; + case 'f': + args->rf_rx_freq = atof(argv[optind]); + break; + case 'F': + args->rf_tx_freq = atof(argv[optind]); + break; + case 't': + args->do_trace = true; + break; + case 'p': + args->do_pcap = true; + break; + case 'v': + args->verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_rx_freq < 0 || args->rf_tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +// Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message +uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x) { + return (period*10*(1+tti/(period*10))+x)%10240; // the 1 means next opportunity +} + +void setup_mac_phy_sib2(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2, srsue::mac *mac, srsue::phy *phy) { + + // Apply RACH configuration + srsue::mac_interface_rrc::mac_cfg_t mac_cfg; + mac->get_config(&mac_cfg); + memcpy(&mac_cfg.rach, &sib2->rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + mac->set_config(&mac_cfg); + + printf("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms, MaxTrials=%d\n", + liblte_rrc_number_of_ra_preambles_num[sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer], + liblte_rrc_preamble_trans_max_num[sib2->rr_config_common_sib.rach_cnfg.preamble_trans_max]); + + // Apply PHY RR Config Common + srsue::phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2->rr_config_common_sib.ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + if (sib2->rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + phy->configure_ul_params(); + + printf("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2->rr_config_common_sib.pusch_cnfg.n_sb); + + printf("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2->rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + printf("Set PRACH ConfigCommon: SeqIdx=%d, HS=%d, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2->rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag?1:0, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + printf("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%d\n", + sib2->rr_config_common_sib.srs_ul_cnfg.bw_cnfg, + sib2->rr_config_common_sib.srs_ul_cnfg.subfr_cnfg, + sib2->rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx); + +} + +void process_connsetup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *msg, srsue::mac *mac, srsue::phy *phy) { + + // FIXME: There's an error parsing the connectionSetup message. This value is hard-coded: + + if (msg->rr_cnfg.phy_cnfg_ded_present) { + phy->set_config_dedicated(&msg->rr_cnfg.phy_cnfg_ded); + } + printf("Set PHY configuration: SR-n_pucch=%d, SR-ConfigIndex=%d, SRS-ConfigIndex=%d, SRS-bw=%d, SRS-Nrcc=%d, SRS-hop=%d, SRS-Ncs=%d\n", + msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.sr_pucch_resource_idx, + msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.sr_cnfg_idx, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_cnfg_idx, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_bandwidth, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.freq_domain_pos, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.srs_hopping_bandwidth, + msg->rr_cnfg.phy_cnfg_ded.srs_ul_cnfg_ded.cyclic_shift); + + srsue::mac_interface_rrc::mac_cfg_t mac_set; + mac->get_config(&mac_set); + memcpy(&mac_set.main, &msg->rr_cnfg.mac_main_cnfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + // SR is a PHY config but is needed by SR procedure in 36.321 5.4.4 + memcpy(&mac_set.sr, &msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac->set_config(&mac_set); + + printf("Set MAC configuration: dsr-TransMAX: %d, harq-MaxReTX=%d, bsr-TimerReTX=%d, bsr-TimerPeriodic=%d\n", + liblte_rrc_dsr_trans_max_num[msg->rr_cnfg.phy_cnfg_ded.sched_request_cnfg.dsr_trans_max], + liblte_rrc_max_harq_tx_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.max_harq_tx], + liblte_rrc_retransmission_bsr_timer_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.retx_bsr_timer], + liblte_rrc_periodic_bsr_timer_num[msg->rr_cnfg.mac_main_cnfg.explicit_value.ulsch_cnfg.periodic_bsr_timer]); + + phy->configure_ul_params(); + + // Setup radio bearers + for (uint32_t i=0;irr_cnfg.srb_to_add_mod_list_size;i++) { + if (msg->rr_cnfg.srb_to_add_mod_list[i].lc_default_cnfg_present) { + printf("Setting up Default Configuration for SRB%d \n", msg->rr_cnfg.srb_to_add_mod_list[i].srb_id); + switch(msg->rr_cnfg.srb_to_add_mod_list[i].srb_id) { + case 1: + mac->setup_lcid(1, 0, 1, -1, -1); + break; + case 2: + mac->setup_lcid(2, 0, 3, -1, -1); + break; + } + } + } +// for (int i=0;irr_cnfg.drb_to_add_mod_list_size;i++) { +// printf("Setting up DRB%d\n", msg->rr_cnfg.drb_to_add_mod_list[i].drb_id); +// // todo +// } +} + + +// Hex bytes for the connection setup complete packet +// Got hex bytes from http://www.sharetechnote.com/html/RACH_LTE.html +uint8_t setupComplete_segm[2][41] ={ { + 0x88, 0x00, 0x00, 0x20, 0x21, 0x90, 0xa0, 0x12, 0x00, 0x00, 0x80, 0xf0, 0x5e, 0x3b, 0xf1, 0x04, + 0x64, 0x04, 0x1d, 0x20, 0x44, 0x2f, 0xd8, 0x4b, 0xd1, 0x02, 0x00, 0x00, 0x83, 0x03, 0x41, 0xb0, + 0xe5, 0x60, 0x13, 0x81, 0x83}, + + {0xb0, 0x01, 0x01, 0x01, 0x48, 0x4b, 0xd1, 0x00, 0x7d, 0x21, 0x70, 0x28, 0x01, 0x5c, 0x08, 0x80, + 0x00, 0xc4, 0x0f, 0x97, 0x80, 0xd0, 0x4c, 0x4b, 0xd1, 0x00, 0xc0, 0x58, 0x44, 0x0d, 0x5d, 0x62, + 0x99, 0x74, 0x04, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00} +}; +uint8_t setupComplete[80] = { + 0x88, 0x00, 0x00, 0x20, 0x21, 0x90, 0xa0, 0x12, 0x00, 0x00, 0x80, 0xf0, 0x5e, 0x3b, 0xf1, 0x04, + 0x64, 0x04, 0x1d, 0x20, 0x44, 0x2f, 0xd8, 0x4b, 0xd1, 0x02, 0x00, 0x00, 0x83, 0x03, 0x41, 0xb0, + 0xe5, 0x60, 0x13, 0x81, 0x83, 0x48, 0x4b, 0xd1, 0x00, 0x7d, 0x21, 0x70, 0x28, 0x01, 0x5c, 0x08, 0x80, + 0x00, 0xc4, 0x0f, 0x97, 0x80, 0xd0, 0x4c, 0x4b, 0xd1, 0x00, 0xc0, 0x58, 0x44, 0x0d, 0x5d, 0x62, + 0x99, 0x74, 0x04, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00}; + +uint32_t lengths[2] = {37, 41}; +uint8_t reply[2] = {0x00, 0x04}; + + +srslte::radio_multi radio; +srsue::phy phy; +srsue::mac mac; +srslte::mac_pcap mac_pcap; + +prog_args_t prog_args; + +void sig_int_handler(int signo) +{ + if (prog_args.do_trace) { + //radio.write_trace("radio"); + phy.write_trace("phy"); + } + if (prog_args.do_pcap) { + mac_pcap.close(); + } + mac.stop(); + exit(0); +} + +class rlctest : public srsue::rlc_interface_mac { +public: + bool mib_decoded; + bool sib1_decoded; + bool sib2_decoded; + bool connsetup_decoded; + int nsegm_dcch; + int send_ack; + uint8_t si_window_len, sib2_period; + + rlctest() { + mib_decoded = false; + sib1_decoded = false; + sib2_decoded = false; + connsetup_decoded = false; + nsegm_dcch = 0; + si_window_len = 0; + sib2_period = 0; + send_ack = 0; + } + uint32_t get_total_buffer_state(uint32_t lcid) { + return get_buffer_state(lcid); + } + uint32_t get_buffer_state(uint32_t lcid) { + if (lcid == 0) { + if (sib2_decoded && !connsetup_decoded) { + return 6; + } + } else if (lcid == 1) { + if (connsetup_decoded && nsegm_dcch < 2) { + return lengths[nsegm_dcch]; + } else if (send_ack == 1) { + return 2; + } + } + return 0; + } + + int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + if (lcid == 0) { + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + // Prepare ConnectionRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ; + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE; + ul_ccch_msg.msg.rrc_con_req.ue_id.random = 1000; + ul_ccch_msg.msg.rrc_con_req.cause = LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING; + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, &bit_msg); + + uint64_t uecri=0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = bit_msg.N_bits/8; + uint8_t *ptr = bit_msg.msg; + for (uint32_t i=0;i= 80) { + printf("Sending Connection Setup Complete length 80\n"); + memcpy(payload, setupComplete, 80); + return 80; + } else { + uint32_t r = 0; + if (nof_bytes >= lengths[nsegm_dcch]) { + printf("Sending Connection Setup Complete %d/2 length %d\n", nsegm_dcch, lengths[nsegm_dcch]); + memcpy(payload, setupComplete_segm[nsegm_dcch], lengths[nsegm_dcch]); + r = lengths[nsegm_dcch]; + nsegm_dcch++; + } else { + r = 0; + } + return r; + } + } else if (send_ack == 1) { + printf("Send RLC ACK\n"); + memcpy(payload, reply, 2*sizeof(uint8_t)); + send_ack = 2; + return 2; + } + } + return 0; + } + + void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) { + if (lcid == 0) { + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + printf("ConnSetup received %d bytes\n", nof_bytes); + srslte_vec_fprint_byte(stdout, payload, nof_bytes); + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_dl_ccch_msg(&bit_msg, &dl_ccch_msg); + printf("Response: %s\n", liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg.msg_type]); + switch (dl_ccch_msg.msg_type) { + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP: + // Process ConnectionSetup + process_connsetup(&dl_ccch_msg.msg.rrc_con_setup, &mac, &phy); + connsetup_decoded = true; + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ: + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST: + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ: + break; + } + } else if (lcid == 1) { + printf("Received on DCCH0 %d bytes\n", nof_bytes); + if (send_ack == 0) { + send_ack = 1; + } + } + } + + void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes) + { + LIBLTE_RRC_MIB_STRUCT mib; + srslte_vec_fprint_byte(stdout, payload, nof_bytes); + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_bcch_bch_msg(&bit_msg, &mib); + printf("MIB received %d bytes, BW=%s MHz\n", nof_bytes, liblte_rrc_dl_bandwidth_text[mib.dl_bw]); + mib_decoded = true; + } + + void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes) + { + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + srslte_bit_unpack_vector(payload, bit_msg.msg, nof_bytes*8); + bit_msg.N_bits = nof_bytes*8; + liblte_rrc_unpack_bcch_dlsch_msg(&bit_msg, &dlsch_msg); + if (dlsch_msg.N_sibs > 0) { + if (dlsch_msg.sibs[0].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 && !sib1_decoded) { + si_window_len = liblte_rrc_si_window_length_num[dlsch_msg.sibs[0].sib.sib1.si_window_length]; + sib2_period = liblte_rrc_si_periodicity_num[dlsch_msg.sibs[0].sib.sib1.sched_info[0].si_periodicity]; + printf("SIB1 received %d bytes, CellID=%d, si_window=%d, sib2_period=%d\n", + nof_bytes, dlsch_msg.sibs[0].sib.sib1.cell_id&0xfff, si_window_len, sib2_period); + sib1_decoded = true; + mac.clear_rntis(); + } else if (dlsch_msg.sibs[0].sib_type == LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2) { + + printf("SIB2 received %d bytes\n", nof_bytes); + setup_mac_phy_sib2(&dlsch_msg.sibs[0].sib.sib2, &mac, &phy); + sib2_decoded = true; + mac.clear_rntis(); + } + } + } + + void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes) {} + void write_pdu_mch(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) {} + +private: + LIBLTE_BIT_MSG_STRUCT bit_msg; + LIBLTE_BYTE_MSG_STRUCT byte_msg; +}; + + +int main(int argc, char *argv[]) +{ + srslte::log_filter mac_log("MAC"); + rlctest my_rlc; + parse_args(&prog_args, argc, argv); + + // Capture SIGINT to write traces + if (prog_args.do_trace) { + signal(SIGINT, sig_int_handler); + //radio.start_trace(); + phy.start_trace(); + } + + if (prog_args.do_pcap) { + if (!prog_args.do_trace) { + signal(SIGINT, sig_int_handler); + } + mac_pcap.open("/tmp/ue_mac.pcap"); + mac.start_pcap(&mac_pcap); + } + + // Init Radio and PHY + if (!radio.init()) { + exit(1); + } + + std::vector phy_log; + + srslte::log_filter *mylog = new srslte::log_filter("PHY"); + char tmp[16]; + sprintf(tmp, "PHY%d",0); + phy_log.push_back(mylog); + + switch (prog_args.verbose) { + case 1: + mac_log.set_level(srslte::LOG_LEVEL_INFO); + mylog->set_level(srslte::LOG_LEVEL_INFO); + break; + case 2: + mac_log.set_level(srslte::LOG_LEVEL_DEBUG); + mylog->set_level(srslte::LOG_LEVEL_DEBUG); + break; + } + + phy.init(&radio, &mac, NULL, phy_log); + if (prog_args.rf_rx_gain > 0 && prog_args.rf_tx_gain > 0) { + radio.set_rx_gain(prog_args.rf_rx_gain); + radio.set_tx_gain(prog_args.rf_tx_gain); + } else { + radio.start_agc(false); + radio.set_tx_rx_gain_offset(10); + phy.set_agc_enable(true); + } + // Init MAC + mac.init(&phy, &my_rlc, NULL, &mac_log); + + // Set RX freq + radio.set_rx_freq(prog_args.rf_rx_freq); + radio.set_tx_freq(prog_args.rf_tx_freq); + + + while(1) { + uint32_t tti; + if (my_rlc.mib_decoded && mac.get_current_tti()) { + if (!my_rlc.sib1_decoded) { + usleep(10000); + tti = mac.get_current_tti(); + mac.bcch_start_rx(sib_start_tti(tti, 2, 5), 1); + } else if (!my_rlc.sib2_decoded) { + usleep(10000); + tti = mac.get_current_tti(); + mac.bcch_start_rx(sib_start_tti(tti, my_rlc.sib2_period, 0), my_rlc.si_window_len); + } + } + usleep(50000); + } +} + + + diff --git a/srsue/test/metrics_test.cc b/srsue/test/metrics_test.cc new file mode 100644 index 0000000..4a23a55 --- /dev/null +++ b/srsue/test/metrics_test.cc @@ -0,0 +1,122 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include +#include +#include +#include "srsue/hdr/ue_metrics_interface.h" +#include "srslte/common/metrics_hub.h" +#include "srsue/hdr/metrics_stdout.h" +#include "srsue/hdr/metrics_csv.h" + +using namespace srsue; + +namespace srsue { + +char *csv_file_name = NULL; + +// fake classes +class ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t &m) + { + // fill dummy values + bzero(&m, sizeof(ue_metrics_t)); + m.rf.rf_o = 10; + m.phy.dl.rsrp = -10.0; + m.phy.dl.pathloss = 74; + return true; + } + + bool is_attached() + { + return (rand() % 2 == 0); + } +}; +} + +void usage(char *prog) { + printf("Usage: %s -o csv_output_file\n", prog); +} + +void parse_args(int argc, char **argv) { + int opt; + + while ((opt = getopt(argc, argv, "o")) != -1) { + switch(opt) { + case 'o': + csv_file_name = argv[optind]; + break; + default: + usage(argv[0]); + exit(-1); + } + } + if (!csv_file_name) { + usage(argv[0]); + exit(-1); + } +} + + +int main(int argc, char **argv) +{ + float period = 1.0; + ue_dummy ue; + + if (argc < 3) { + usage(argv[0]); + exit(-1); + } + + parse_args(argc,argv); + + // the default metrics type for stdout output + metrics_stdout metrics_screen; + metrics_screen.set_ue_handle(&ue); + + // the CSV file writer + metrics_csv metrics_file(csv_file_name); + metrics_file.set_ue_handle(&ue); + + // create metrics hub and register metrics for stdout + srslte::metrics_hub metricshub; + metricshub.init(&ue, period); + metricshub.add_listener(&metrics_screen); + metricshub.add_listener(&metrics_file); + + // enable printing + metrics_screen.toggle_print(true); + + std::cout << "Running for 2 seconds .." << std::endl; + usleep(2e6); + + metricshub.stop(); + return 0; +} diff --git a/srsue/test/phy/CMakeLists.txt b/srsue/test/phy/CMakeLists.txt new file mode 100644 index 0000000..a0570f0 --- /dev/null +++ b/srsue/test/phy/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2017 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/. +# + +#add_executable(ue_itf_test_sib1 ue_itf_test_sib1.cc) +#target_link_libraries(ue_itf_test_sib1 srsue_phy srslte_common srslte_phy srslte_radio ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) + +#add_executable(ue_itf_test_prach ue_itf_test_prach.cc) +#target_link_libraries(ue_itf_test_prach srsue_phy srslte_common srslte_phy srslte_radio ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) diff --git a/srsue/test/phy/ue_itf_test_prach.cc b/srsue/test/phy/ue_itf_test_prach.cc new file mode 100644 index 0000000..9ae63d0 --- /dev/null +++ b/srsue/test/phy/ue_itf_test_prach.cc @@ -0,0 +1,403 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/phy/utils/debug.h" +#include "srsue/hdr/phy/phy.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/log_filter.h" +#include "srslte/radio/radio_multi.h" + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +typedef struct { + float rf_rx_freq; + float rf_tx_freq; + float rf_rx_gain; + float rf_tx_gain; + bool continous; +}prog_args_t; + +prog_args_t prog_args; +uint32_t srsapps_verbose = 0; + +void args_default(prog_args_t *args) { + args->rf_rx_freq = -1.0; + args->rf_tx_freq = -1.0; + args->rf_rx_gain = -1; // set to autogain + args->rf_tx_gain = -1; + args->continous = false; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGcv] -f rx_frequency -F tx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-G RF TX gain [Default same as RX gain (AGC)]\n"); + printf("\t-c Run continuously [Default only once]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFcv")) != -1) { + switch (opt) { + case 'g': + args->rf_rx_gain = atof(argv[optind]); + break; + case 'G': + args->rf_tx_gain = atof(argv[optind]); + break; + case 'f': + args->rf_rx_freq = atof(argv[optind]); + break; + case 'F': + args->rf_tx_freq = atof(argv[optind]); + break; + case 'c': + args->continous = true; + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_rx_freq < 0 || args->rf_tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + + + +typedef enum{ + rar_header_type_bi = 0, + rar_header_type_rapid, + rar_header_type_n_items, +}rar_header_t; +static const char rar_header_text[rar_header_type_n_items][8] = {"BI", "RAPID"}; + +typedef struct { + rar_header_t hdr_type; + bool hopping_flag; + uint32_t tpc_command; + bool ul_delay; + bool csi_req; + uint16_t rba; + uint16_t timing_adv_cmd; + uint16_t temp_c_rnti; + uint8_t mcs; + uint8_t RAPID; + uint8_t BI; +}rar_msg_t; + + +int rar_unpack(uint8_t *buffer, rar_msg_t *msg) +{ + int ret = SRSLTE_ERROR; + uint8_t *ptr = buffer; + + if(buffer != NULL && + msg != NULL) + { + ptr++; + msg->hdr_type = (rar_header_t) *ptr++; + if(msg->hdr_type == rar_header_type_bi) { + ptr += 2; + msg->BI = srslte_bit_pack(&ptr, 4); + ret = SRSLTE_SUCCESS; + } else if (msg->hdr_type == rar_header_type_rapid) { + msg->RAPID = srslte_bit_pack(&ptr, 6); + ptr++; + + msg->timing_adv_cmd = srslte_bit_pack(&ptr, 11); + msg->hopping_flag = *ptr++; + msg->rba = srslte_bit_pack(&ptr, 10); + msg->mcs = srslte_bit_pack(&ptr, 4); + msg->tpc_command = srslte_bit_pack(&ptr, 3); + msg->ul_delay = *ptr++; + msg->csi_req = *ptr++; + msg->temp_c_rnti = srslte_bit_pack(&ptr, 16); + ret = SRSLTE_SUCCESS; + } + } + + return(ret); +} + + + +srsue::phy my_phy; +bool bch_decoded = false; + +uint8_t payload[SRSLTE_MAX_TB][10240]; +uint8_t payload_bits[SRSLTE_MAX_TB][10240]; +const uint8_t conn_request_msg[] = {0x20, 0x06, 0x1F, 0x5C, 0x2C, 0x04, 0xB2, 0xAC, 0xF6, 0x00, 0x00, 0x00}; + +enum mac_state {RA, RAR, CONNREQUEST, CONNSETUP} state = RA; + +uint32_t preamble_idx = 0; +rar_msg_t rar_msg; + +uint32_t nof_rtx_connsetup = 0; +uint32_t rv_value[4] = {0, 2, 3, 1}; + +void config_phy() { + srsue::phy_interface_rrc::phy_cfg_t config; + + config.common.prach_cnfg.prach_cnfg_info.prach_config_index = 0; + config.common.prach_cnfg.prach_cnfg_info.prach_freq_offset = 0; + config.common.prach_cnfg.prach_cnfg_info.high_speed_flag = false; + config.common.prach_cnfg.root_sequence_index = 0; + config.common.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config = 11; + + config.common.pusch_cnfg.ul_rs.group_hopping_enabled = false; + config.common.pusch_cnfg.ul_rs.sequence_hopping_enabled = false; + config.common.pusch_cnfg.n_sb = 2; + config.common.pusch_cnfg.ul_rs.cyclic_shift = 0; + config.common.pusch_cnfg.ul_rs.group_assignment_pusch = 0; + config.common.pusch_cnfg.pusch_hopping_offset = 0; + + config.common.pucch_cnfg.delta_pucch_shift = LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2; + config.common.pucch_cnfg.n_cs_an = 0; + config.common.pucch_cnfg.n1_pucch_an = 1; + + my_phy.configure_ul_params(); + my_phy.configure_prach_params(); +} + +srslte_softbuffer_rx_t softbuffers_rx[SRSLTE_MAX_TB]; +srslte_softbuffer_tx_t softbuffers_tx[SRSLTE_MAX_TB]; + +uint16_t temp_c_rnti; + + +class rrc_dummy : public srsue::rrc_interface_phy +{ +public: + void in_sync() {}; + void out_of_sync() {}; +}; + +/******** MAC Interface implementation */ +class testmac : public srsue::mac_interface_phy +{ +public: + + testmac() { + rar_rnti_set = false; + } + + bool rar_rnti_set; + + void pch_decoded_ok(uint32_t len) {} + + + void tti_clock(uint32_t tti) { + if (!rar_rnti_set) { + int prach_tti = my_phy.prach_tx_tti(); + if (prach_tti > 0) { + my_phy.pdcch_dl_search(SRSLTE_RNTI_RAR, 1+prach_tti%10, prach_tti+3, prach_tti+13); + rar_rnti_set = true; + } + } + } + + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action) { + printf("New grant UL\n"); + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb ++) { + memcpy(payload[tb], conn_request_msg, grant.n_bytes[tb]*sizeof(uint8_t)); + action->rv[tb] = rv_value[nof_rtx_connsetup%4]; + action->payload_ptr[tb] = payload[tb]; + if (action->rv[tb] == 0) { + srslte_softbuffer_tx_reset(&softbuffers_tx[tb]); + } + } + action->current_tx_nb = nof_rtx_connsetup; + action->softbuffers = softbuffers_tx; + action->rnti = temp_c_rnti; + action->expect_ack = (nof_rtx_connsetup < 5)?true:false; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + memcpy(&last_grant, &grant, sizeof(mac_grant_t)); + action->tx_enabled = true; + my_phy.pdcch_dl_search(SRSLTE_RNTI_USER, temp_c_rnti); + } + + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action) { + printf("New grant UL ACK\n"); + } + + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action) { + printf("harq recv hi=%d\n", ack?1:0); + if (!ack) { + nof_rtx_connsetup++; + action->current_tx_nb = nof_rtx_connsetup; + action->softbuffers = softbuffers_tx; + action->rnti = temp_c_rnti; + action->expect_ack = true; + memcpy(&action->phy_grant, &last_grant.phy_grant, sizeof(srslte_phy_grant_t)); + action->tx_enabled = true; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb ++) { + action->rv[tb] = rv_value[nof_rtx_connsetup%4]; + if (action->rv[tb] == 0) { + srslte_softbuffer_tx_reset(&softbuffers_tx[tb]); + } + printf("Retransmission %d (TB %d), rv=%d\n", nof_rtx_connsetup, tb, action->rv[tb]); + + } + } + } + + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action) { + action->decode_enabled[0] = true; + action->default_ack[0] = false; + if (grant.rnti == 2) { + action->generate_ack = false; + } else { + action->generate_ack = true; + } + action->rnti = grant.rnti; + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + memcpy(&last_grant, &grant, sizeof(mac_grant_t)); + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb ++) { + action->softbuffers[tb] = &softbuffers_rx[tb]; + action->rv[tb] = grant.rv[tb]; + action->payload_ptr[tb] = payload[tb]; + if (action->rv[tb] == 0) { + srslte_softbuffer_rx_reset(&softbuffers_rx[tb]); + } + } + } + + void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) { + if (ack) { + if (rnti_type == SRSLTE_RNTI_RAR) { + my_phy.pdcch_dl_search_reset(); + srslte_bit_unpack_vector(payload[tb_idx], payload_bits[tb_idx], last_grant.n_bytes[tb_idx]*8); + rar_unpack(payload_bits[tb_idx], &rar_msg); + if (rar_msg.RAPID == preamble_idx) { + + printf("Received RAR at TTI: %d\n", last_grant.tti); + my_phy.set_timeadv_rar(rar_msg.timing_adv_cmd); + + temp_c_rnti = rar_msg.temp_c_rnti; + + if (last_grant.n_bytes[0]*8 > 20 + SRSLTE_RAR_GRANT_LEN) { + uint8_t rar_grant[SRSLTE_RAR_GRANT_LEN]; + memcpy(rar_grant, &payload_bits[20], sizeof(uint8_t)*SRSLTE_RAR_GRANT_LEN); + my_phy.set_rar_grant(last_grant.tti, rar_grant); + } + } else { + printf("Received RAR RAPID=%d\n", rar_msg.RAPID); + } + } else { + printf("Received Connection Setup\n"); + my_phy.pdcch_dl_search_reset(); + } + } + } + + void bch_decoded_ok(uint8_t *payload, uint32_t len) { + printf("BCH decoded\n"); + bch_decoded = true; + srslte_cell_t cell; + my_phy.get_current_cell(&cell); + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + srslte_softbuffer_rx_init(&softbuffers_rx[tb], cell.nof_prb); + srslte_softbuffer_tx_init(&softbuffers_tx[tb], cell.nof_prb); + } + } + +private: + mac_grant_t last_grant; +}; + + +testmac my_mac; +srslte::radio_multi radio; +rrc_dummy rrc_dummy; + +int main(int argc, char *argv[]) +{ + srslte::log_filter log("PHY"); + + parse_args(&prog_args, argc, argv); + + // Init Radio and PHY + radio.init(); + my_phy.init(&radio, &my_mac, &rrc_dummy, &log); + if (prog_args.rf_rx_gain > 0 && prog_args.rf_tx_gain > 0) { + radio.set_rx_gain(prog_args.rf_rx_gain); + radio.set_tx_gain(prog_args.rf_tx_gain); + } else { + radio.start_agc(false); + radio.set_tx_rx_gain_offset(10); + my_phy.set_agc_enable(true); + } + + if (srsapps_verbose == 1) { + log.set_level(srslte::LOG_LEVEL_INFO); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log.set_level(srslte::LOG_LEVEL_DEBUG); + printf("Log level debug\n"); + } + + // Give it time to create thread + sleep(1); + + // Set RX freq + radio.set_rx_freq(prog_args.rf_rx_freq); + radio.set_tx_freq(prog_args.rf_tx_freq); + + // Instruct the PHY to configure PRACH parameters and sync to current cell + while(!my_phy.cell_is_camping()) { + usleep(20000); + } + + // Setup PHY parameters + config_phy(); + + /* Instruct PHY to send PRACH and prepare it for receiving RAR */ + my_phy.prach_send(preamble_idx); + + /* go to idle and process each tti */ + bool running = true; + while(running) { + sleep(1); + } + my_phy.stop(); + radio.stop_rx(); +} + + + diff --git a/srsue/test/phy/ue_itf_test_sib1.cc b/srsue/test/phy/ue_itf_test_sib1.cc new file mode 100644 index 0000000..bf1f9bb --- /dev/null +++ b/srsue/test/phy/ue_itf_test_sib1.cc @@ -0,0 +1,224 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 + +#include "srslte/phy/utils/debug.h" +#include "srsue/hdr/phy/phy.h" +#include "srslte/common/log_filter.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/radio/radio_multi.h" + + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +typedef struct { + float rf_freq; + float rf_gain; +}prog_args_t; + +uint32_t srsapps_verbose = 0; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->rf_freq = -1.0; + args->rf_gain = -1.0; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gv] -f rx_frequency (in Hz)\n", prog); + printf("\t-g RF RX gain [Default AGC]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gfv")) != -1) { + switch (opt) { + case 'g': + args->rf_gain = atof(argv[optind]); + break; + case 'f': + args->rf_freq = atof(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rf_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +srsue::phy my_phy; +bool bch_decoded = false; +uint32_t total_pkts=0; +uint32_t total_dci=0; +uint32_t total_oks=0; +uint8_t payload[SRSLTE_MAX_TB][1024]; +srslte_softbuffer_rx_t softbuffers[SRSLTE_MAX_TB]; + +class rrc_dummy : public srsue::rrc_interface_phy +{ +public: + void in_sync() {}; + void out_of_sync() {}; +}; + +/******** MAC Interface implementation */ +class testmac : public srsue::mac_interface_phy +{ +public: + void new_grant_ul(mac_grant_t grant, tb_action_ul_t *action) { + printf("New grant UL\n"); + } + void new_grant_ul_ack(mac_grant_t grant, bool ack, tb_action_ul_t *action) { + printf("New grant UL ACK\n"); + } + + void harq_recv(uint32_t tti, bool ack, tb_action_ul_t *action) { + printf("harq recv\n"); + } + + void new_grant_dl(mac_grant_t grant, tb_action_dl_t *action) { + total_dci++; + + + action->decode_enabled[0] = true; + action->default_ack[0] = false; + action->generate_ack = false; + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + action->payload_ptr[tb] = payload[tb]; + action->rv[tb] = ((uint32_t) ceilf((float) 3 * ((my_phy.tti_to_SFN(grant.tti) / 2) % 4) / 2)) % 4; + if (action->rv == 0) { + srslte_softbuffer_rx_reset(&softbuffers[tb]); + } + action->softbuffers[0] = &softbuffers[tb]; + } + memcpy(&action->phy_grant, &grant.phy_grant, sizeof(srslte_phy_grant_t)); + + action->rnti = grant.rnti; + } + + + + void tb_decoded(bool ack, uint32_t tb_idx, srslte_rnti_type_t rnti_type, uint32_t harq_pid) { + if (ack) { + total_oks++; + } + } + + void pch_decoded_ok(uint32_t len) {} + + void bch_decoded_ok(uint8_t *payload, uint32_t len) { + printf("BCH decoded\n"); + bch_decoded = true; + srslte_cell_t cell; + my_phy.get_current_cell(&cell); + for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) { + srslte_softbuffer_rx_init(&softbuffers[tb], cell.nof_prb); + } + } + void tti_clock(uint32_t tti) { + + } +}; + + +testmac my_mac; +srslte::radio_multi radio; +rrc_dummy rrc_dummy; + + + +int main(int argc, char *argv[]) +{ + srslte::log_filter log("PHY"); + + parse_args(&prog_args, argc, argv); + + // Init Radio and PHY + radio.init(); + my_phy.init(&radio, &my_mac, &rrc_dummy, &log); + if (prog_args.rf_gain > 0) { + radio.set_rx_gain(prog_args.rf_gain); + } else { + radio.start_agc(false); + my_phy.set_agc_enable(true); + } + + if (srsapps_verbose == 1) { + log.set_level(srslte::LOG_LEVEL_INFO); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log.set_level(srslte::LOG_LEVEL_DEBUG); + printf("Log level debug\n"); + } + + // Give it time to create thread + sleep(1); + + // Set RX freq and gain + radio.set_rx_freq(prog_args.rf_freq); + + bool running = true; + while(running) { + if (bch_decoded && my_phy.cell_is_camping()) { + uint32_t tti = my_phy.get_current_tti(); + + // SIB1 is scheduled in subframe #5 of even frames, try to decode next frame SIB1 + tti = (((tti/20)*20) + 25)%10240; + my_phy.pdcch_dl_search(SRSLTE_RNTI_SI, SRSLTE_SIRNTI, tti, tti+1); + + total_pkts++; + } + usleep(30000); + if (bch_decoded && my_phy.cell_is_camping() && total_pkts > 0) { + if (srslte_verbose == SRSLTE_VERBOSE_NONE && srsapps_verbose == 0) { + float gain = prog_args.rf_gain; + if (gain < 0) { + gain = radio.get_rx_gain(); + } + printf("PDCCH BLER %.1f \%% PDSCH BLER %.1f \%% (total pkts: %5u) Gain: %.1f dB\r", + 100-(float) 100*total_dci/total_pkts, + (float) 100*(1 - total_oks/total_pkts), + total_pkts, gain); + } + } + } + my_phy.stop(); + radio.stop_rx(); +} diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/test/upper/CMakeLists.txt new file mode 100644 index 0000000..a099ff4 --- /dev/null +++ b/srsue/test/upper/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# Copyright 2013-2017 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/. +# + +add_executable(usim_test usim_test.cc) +target_link_libraries(usim_test srsue_upper srslte_upper srslte_phy) +add_test(usim_test usim_test) + +if(HAVE_PCSC) + add_executable(pcsc_usim_test pcsc_usim_test.cc) + target_link_libraries(pcsc_usim_test srsue_upper srslte_upper srslte_phy) +endif(HAVE_PCSC) + +add_executable(rrc_reconfig_test rrc_reconfig_test.cc) +target_link_libraries(rrc_reconfig_test srsue_upper srslte_upper srslte_phy) +add_test(rrc_reconfig_test rrc_reconfig_test) + +add_executable(nas_test nas_test.cc) +target_link_libraries(nas_test srsue_upper srslte_upper srslte_phy) +add_test(nas_test nas_test) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "Added custom post-build command: ${BUILD_CMD}") + add_custom_command(TARGET ip_test POST_BUILD COMMAND ${BUILD_CMD}) +else(NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "No post-build command defined") +endif (NOT ${BUILD_CMD} STREQUAL "") + diff --git a/srsue/test/upper/ip_test.cc b/srsue/test/upper/ip_test.cc new file mode 100644 index 0000000..a4f9743 --- /dev/null +++ b/srsue/test/upper/ip_test.cc @@ -0,0 +1,646 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/phy/utils/debug.h" +#include "srsue/hdr/mac/mac.h" +#include "srsue/hdr/phy/phy.h" +#include "srslte/common/threads.h" +#include "srslte/common/common.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/upper/rlc.h" +#include "srsue/hdr/upper/rrc.h" +#include "srslte/radio/radio_multi.h" + +#define START_TUNTAP +#define USE_RADIO +#define PRINT_GW 0 + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +#define LCID 3 + +typedef struct { + float rx_freq; + float tx_freq; + float rx_gain; + float tx_gain; + int time_adv; + std::string ip_address; +}prog_args_t; + +uint32_t srsapps_verbose = 1; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->tx_freq = 2.505e9; + args->rx_freq = 2.625e9; + args->rx_gain = 50.0; + args->tx_gain = 70.0; + args->time_adv = -1; // calibrated for b210 + args->ip_address = "192.168.3.2"; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGIrfFtv]\n", prog); + printf("\t-f RX frequency [Default %.1f MHz]\n", args->rx_freq/1e6); + printf("\t-F TX frequency [Default %.1f MHz]\n", args->tx_freq/1e6); + printf("\t-g RX gain [Default %.1f]\n", args->rx_gain); + printf("\t-G TX gain [Default %.1f]\n", args->tx_gain); + printf("\t-I IP address [Default %s]\n", args->ip_address.c_str()); + printf("\t-t time advance (in samples) [Default %d]\n", args->time_adv); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFItv")) != -1) { + switch (opt) { + case 'g': + args->rx_gain = atof(argv[optind]); + break; + case 'G': + args->tx_gain = atof(argv[optind]); + break; + case 'f': + args->rx_freq = atof(argv[optind]); + break; + case 'F': + args->tx_freq = atof(argv[optind]); + break; + case 'I': + args->ip_address = argv[optind]; + break; + case 't': + args->time_adv = atoi(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rx_freq < 0 || args->tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +int setup_if_addr(char *ip_addr); + +// Define dummy RLC always transmitts +class tester : public srsue::pdcp_interface_rlc, + public srsue::rrc_interface_rlc, + public srsue::rrc_interface_phy, + public srsue::rrc_interface_mac, + public srsue::ue_interface, + public thread +{ +public: + + tester() { + state = srsue::RRC_STATE_SIB1_SEARCH; + read_enable = true; + } + + void init(srsue::phy *phy_, srsue::mac *mac_, srslte::rlc *rlc_, srslte::log *log_h_, std::string ip_address) { + log_h = log_h_; + rlc = rlc_; + mac = mac_; + phy = phy_; + +#ifdef START_TUNTAP + if (init_tuntap((char*) ip_address.c_str())) { + log_h->error("Initiating IP address\n"); + } +#endif + + pool = srslte::byte_buffer_pool::get_instance(); + + // Start reader thread + running=true; + start(); + + } + + + void sib_search() + { + bool searching = true; + uint32_t tti ; + uint32_t si_win_start, si_win_len; + uint16_t period; + uint32_t nof_sib1_trials = 0; + const int SIB1_SEARCH_TIMEOUT = 30; + + while(searching) + { + switch(state) + { + case srsue::RRC_STATE_SIB1_SEARCH: + // Instruct MAC to look for SIB1 + while(!phy->status_is_sync()){ + usleep(50000); + } + usleep(10000); + tti = mac->get_current_tti(); + si_win_start = sib_start_tti(tti, 2, 5); + mac->bcch_start_rx(si_win_start, 1); + log_h->info("Instructed MAC to search for SIB1, win_start=%d, win_len=%d\n", + si_win_start, 1); + nof_sib1_trials++; + if (nof_sib1_trials >= SIB1_SEARCH_TIMEOUT) { + log_h->info("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + log_h->console("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + phy->resync_sfn(); + nof_sib1_trials = 0; + } + break; + case srsue::RRC_STATE_SIB2_SEARCH: + // Instruct MAC to look for SIB2 + usleep(10000); + tti = mac->get_current_tti(); + period = liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]; + si_win_start = sib_start_tti(tti, period, 0); + si_win_len = liblte_rrc_si_window_length_num[sib1.si_window_length]; + + mac->bcch_start_rx(si_win_start, si_win_len); + log_h->info("Instructed MAC to search for SIB2, win_start=%d, win_len=%d\n", + si_win_start, si_win_len); + + break; + default: + searching = false; + break; + } + usleep(100000); + } + } + + bool is_sib_received() { + return state == srsue::RRC_STATE_WAIT_FOR_CON_SETUP; + } + + + void release_pucch_srs() {} + void ra_problem() {} + void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu) {} + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu) + { + log_h->info_hex(pdu->msg, pdu->N_bytes, "BCCH DLSCH message received."); + log_h->info("BCCH DLSCH message Stack latency: %ld us\n", pdu->get_latency_us()); + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + liblte_rrc_unpack_bcch_dlsch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dlsch_msg); + + if (dlsch_msg.N_sibs > 0) { + if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 == dlsch_msg.sibs[0].sib_type && srsue::RRC_STATE_SIB1_SEARCH == state) { + // Handle SIB1 + memcpy(&sib1, &dlsch_msg.sibs[0].sib.sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + log_h->info("SIB1 received, CellID=%d, si_window=%d, sib2_period=%d\n", + sib1.cell_id&0xfff, + liblte_rrc_si_window_length_num[sib1.si_window_length], + liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]); + std::stringstream ss; + for(uint32_t i=0;iconsole("SIB1 received, CellID=%d, %s\n", + sib1.cell_id&0xfff, + ss.str().c_str()); + + state = srsue::RRC_STATE_SIB2_SEARCH; + mac->bcch_stop_rx(); + //TODO: Use all SIB1 info + + } else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2 == dlsch_msg.sibs[0].sib_type && srsue::RRC_STATE_SIB2_SEARCH == state) { + // Handle SIB2 + memcpy(&sib2, &dlsch_msg.sibs[0].sib.sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + log_h->console("SIB2 received\n"); + log_h->info("SIB2 received\n"); + state = srsue::RRC_STATE_WAIT_FOR_CON_SETUP; + mac->bcch_stop_rx(); + apply_sib2_configs(); + + srslte::byte_buffer_t *sdu = pool_allocate; + assert(sdu); + + // Send Msg3 + sdu->N_bytes = 10; + for (uint32_t i=0;iN_bytes;i++) { + sdu->msg[i] = i+1; + } + uint64_t uecri = 0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = 6; + for (uint32_t i=0;imsg[i]; + } + log_h->info("Setting UE contention resolution ID: %d\n", uecri); + mac->set_contention_id(uecri); + + rlc->write_sdu(0, sdu); + + } + } + } + void write_pdu_pcch(srslte::byte_buffer_t *sdu) {} + void max_retx_attempted(){} + std::string get_rb_name(uint32_t lcid) { return std::string("rb"); } + void in_sync() {}; + void out_of_sync() {}; + + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu) + { + uint32_t n=0; + switch(lcid) { + case LCID: + n = write(tun_fd, sdu->msg, sdu->N_bytes); + if (n != sdu->N_bytes) { + log_h->error("TUN/TAP write failure n=%d, nof_bytes=%d\n", n, sdu->N_bytes); + return; + } + log_h->debug_hex(sdu->msg, sdu->N_bytes, + "Wrote %d bytes to TUN/TAP\n", + sdu->N_bytes); + pool->deallocate(sdu); + break; + case 0: + log_h->info("Received ConnectionSetupComplete\n"); + + // Setup a single UM bearer + LIBLTE_RRC_RLC_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS100; + cfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + rlc->add_bearer(LCID, &cfg); + + mac->setup_lcid(LCID, 0, 1, -1, 100000); + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + dedicated.pusch_cnfg_ded.beta_offset_ack_idx = 5; + dedicated.pusch_cnfg_ded.beta_offset_ri_idx = 12; + dedicated.pusch_cnfg_ded.beta_offset_cqi_idx = 15; + dedicated.pusch_cnfg_ded_present = true; + dedicated.sched_request_cnfg.dsr_trans_max = LIBLTE_RRC_DSR_TRANS_MAX_N4; + dedicated.sched_request_cnfg.sr_pucch_resource_idx = 0; + dedicated.sched_request_cnfg.sr_cnfg_idx = 35; + dedicated.sched_request_cnfg.setup_present = true; + dedicated.sched_request_cnfg_present = true; + phy->set_config_dedicated(&dedicated); + phy->configure_ul_params(); + + srsue::mac_interface_rrc::mac_cfg_t mac_cfg; + mac->get_config(&mac_cfg); + memcpy(&mac_cfg.sr, &dedicated.sched_request_cnfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac_cfg.main.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_SF40; + mac->set_config(&mac_cfg); + + break; + default: + log_h->error("Received message for lcid=%d\n", lcid); + break; + } + } + +private: + int tun_fd; + bool running; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srslte::rlc *rlc; + srsue::mac *mac; + srsue::phy *phy; + srslte::bit_buffer_t bit_buf; + srsue::rrc_state_t state; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + bool read_enable; + + + // Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message + uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x) { + return (period*10*(1+tti/(period*10))+x)%10240; // the 1 means next opportunity + } + + int init_tuntap(char *ip_address) { + read_enable = true; + tun_fd = setup_if_addr(ip_address); + if (tun_fd<0) { + fprintf(stderr, "Error setting up IP %s\n", ip_address); + return -1; + } + + printf("Created tun/tap interface at IP %s\n", ip_address); + return 0; + } + + void run_thread() { + struct iphdr *ip_pkt; + uint32_t idx = 0; + int32_t N_bytes; + srslte::byte_buffer_t *pdu = pool_allocate; + + log_h->info("TUN/TAP reader thread running\n"); + + while(running) { + N_bytes = read(tun_fd, &pdu->msg[idx], SRSLTE_MAX_BUFFER_SIZE_BYTES-SRSLTE_BUFFER_HEADER_OFFSET - idx); + if(N_bytes > 0 && read_enable) + { + pdu->N_bytes = idx + N_bytes; + ip_pkt = (struct iphdr*)pdu->msg; + + log_h->debug_hex(pdu->msg, pdu->N_bytes, + "Read %d bytes from TUN/TAP\n", + N_bytes); + + // Check if entire packet was received + if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) + { + log_h->info_hex(pdu->msg, pdu->N_bytes, "UL PDU"); + + // Send PDU directly to PDCP + pdu->set_timestamp(); + rlc->write_sdu(LCID, pdu); + + pdu = pool_allocate; + idx = 0; + } else{ + idx += N_bytes; + } + }else{ + log_h->error("Failed to read from TUN interface - gw receive thread exiting.\n"); + break; + } + } + } + + + void apply_sib2_configs() + { + + // Apply RACH timeAlginmentTimer configuration + srsue::mac_interface_rrc::mac_cfg_t cfg; + mac->get_config(&cfg); + cfg.main.time_alignment_timer = sib2.time_alignment_timer; + memcpy(&cfg.rach, &sib2.rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + cfg.prach_config_index = sib2.rr_config_common_sib.prach_cnfg.root_sequence_index; + mac->set_config(&cfg); + + log_h->info("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms\n", + liblte_rrc_number_of_ra_preambles_num[sib2.rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2.rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2.rr_config_common_sib.rach_cnfg.mac_con_res_timer]); + + // Apply PHY RR Config Common + srsue::phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2.rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2.rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2.rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2.rr_config_common_sib.ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2.rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + if (sib2.rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2.rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + + phy->configure_ul_params(); + + log_h->info("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2.rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2.rr_config_common_sib.pusch_cnfg.n_sb); + + log_h->info("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2.rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2.rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + log_h->info("Set PRACH ConfigCommon: SeqIdx=%d, HS=%d, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2.rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag?1:0, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + log_h->info("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%d\n", + sib2.rr_config_common_sib.srs_ul_cnfg.bw_cnfg, + sib2.rr_config_common_sib.srs_ul_cnfg.subfr_cnfg, + sib2.rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx); + + } +}; + + + +// Create classes +srslte::logger_file logger; +srslte::log_filter log_phy; +srslte::log_filter log_mac; +srslte::log_filter log_rlc; +srslte::log_filter log_tester; +srslte::mac_pcap mac_pcap; +srsue::phy my_phy; +srsue::mac my_mac; +srslte::rlc rlc; +srslte::radio_multi my_radio; + +// Local classes for testing +tester my_tester; + + +bool running = true; + +void sig_int_handler(int signo) +{ + running = false; +} + +int main(int argc, char *argv[]) +{ + + parse_args(&prog_args, argc, argv); + + // set to null to disable pcap + const char *pcap_filename = "/tmp/ip_test.pcap"; + + logger.init("/tmp/ip_test_ue.log"); + log_phy.init("PHY ", &logger, true); + log_mac.init("MAC ", &logger, true); + log_rlc.init("RLC ", &logger); + log_tester.init("TEST", &logger); + logger.log("\n\n"); + + if (srsapps_verbose == 1) { + log_phy.set_level(srslte::LOG_LEVEL_INFO); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(1000); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log_phy.set_level(srslte::LOG_LEVEL_DEBUG); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(100); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + printf("Log level debug\n"); + } + + // Init Radio and PHY +#ifdef USE_RADIO + my_radio.init(); +#else + my_radio.init(NULL, "dummy"); +#endif + + my_radio.set_tx_freq(prog_args.tx_freq); + my_radio.set_tx_gain(prog_args.tx_gain); + my_radio.set_rx_freq(prog_args.rx_freq); + my_radio.set_rx_gain(prog_args.rx_gain); + if (prog_args.time_adv >= 0) { + printf("Setting TA=%d samples\n",prog_args.time_adv); + my_radio.set_tx_adv(prog_args.time_adv); + } + + my_phy.init(&my_radio, &my_mac, &my_tester, &log_phy, NULL); + my_mac.init(&my_phy, &rlc, &my_tester, &log_mac); + rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac, 0 /* SRB0 */); + my_tester.init(&my_phy, &my_mac, &rlc, &log_tester, prog_args.ip_address); + + + if (pcap_filename) { + mac_pcap.open(pcap_filename); + my_mac.start_pcap(&mac_pcap); + signal(SIGINT, sig_int_handler); + } + + // Set MAC defaults + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT default_cfg; + bzero(&default_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + default_cfg.ulsch_cnfg.max_harq_tx = LIBLTE_RRC_MAX_HARQ_TX_N5; + default_cfg.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_INFINITY; + default_cfg.ulsch_cnfg.retx_bsr_timer = LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF2560; + default_cfg.ulsch_cnfg.tti_bundling = false; + default_cfg.drx_cnfg.setup_present = false; + default_cfg.phr_cnfg.setup_present = false; + default_cfg.time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + my_mac.set_config_main(&default_cfg); + + while(running) { + if (my_tester.is_sib_received()) { + printf("Main running\n"); + sleep(1); + } else { + my_tester.sib_search(); + } + } + + if (pcap_filename) { + mac_pcap.close(); + } + + my_phy.stop(); + my_mac.stop(); +} + + + + +/******************* This is copied from srsue gw **********************/ +int setup_if_addr(char *ip_addr) +{ + + char *dev = (char*) "tun_srsue"; + + // Construct the TUN device + int tun_fd = open("/dev/net/tun", O_RDWR); + if(0 > tun_fd) + { + perror("open"); + return(-1); + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); + if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) + { + perror("ioctl"); + return -1; + } + + // Bring up the interface + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) + { + perror("socket"); + return -1; + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) + { + perror("ioctl"); + return -1; + } + + // Setup the IP address + sock = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = inet_addr(ip_addr); + if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) + { + perror("ioctl"); + return -1; + } + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) + { + perror("ioctl"); + return -1; + } + + return(tun_fd); +} diff --git a/srsue/test/upper/nas_test.cc b/srsue/test/upper/nas_test.cc new file mode 100644 index 0000000..38a7f6c --- /dev/null +++ b/srsue/test/upper/nas_test.cc @@ -0,0 +1,321 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srsue/hdr/upper/usim_base.h" +#include "srsue/hdr/upper/usim.h" +#include "srsue/hdr/upper/nas.h" +#include "srslte/upper/rlc.h" +#include "srsue/hdr/upper/rrc.h" +#include "srsue/hdr/mac/mac.h" +#include "srslte/common/log_filter.h" +#include "srslte/upper/pdcp_entity.h" +#include "srslte/upper/pdcp.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/bcd_helpers.h" + + +using namespace srsue; + +#define LCID 1 + +uint8_t auth_request_pdu[] = { 0x07, 0x52, 0x01, 0x0c, 0x63, 0xa8, 0x54, 0x13, 0xe6, 0xa4, + 0xce, 0xd9, 0x86, 0xfb, 0xe5, 0xce, 0x9b, 0x62, 0x5e, 0x10, + 0x67, 0x57, 0xb3, 0xc2, 0xb9, 0x70, 0x90, 0x01, 0x0c, 0x72, + 0x8a, 0x67, 0x57, 0x92, 0x52, 0xb8 }; + +uint8_t sec_mode_command_pdu[] = { 0x37, 0x37, 0xc7, 0x67, 0xae, 0x00, 0x07, 0x5d, 0x02, 0x01, + 0x02, 0xe0, 0x60, 0xc1 }; + +uint8_t attach_accept_pdu[] = { 0x27, 0x0f, 0x4f, 0xb3, 0xef, 0x01, 0x07, 0x42, 0x01, 0x3e, + 0x06, 0x00, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x2a, 0x52, + 0x01, 0xc1, 0x01, 0x04, 0x1b, 0x07, 0x74, 0x65, 0x73, 0x74, + 0x31, 0x32, 0x33, 0x06, 0x6d, 0x6e, 0x63, 0x30, 0x30, 0x31, + 0x06, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x04, 0x67, 0x70, + 0x72, 0x73, 0x05, 0x01, 0xc0, 0xa8, 0x05, 0x02, 0x27, 0x01, + 0x80, 0x50, 0x0b, 0xf6, 0x00, 0xf1, 0x10, 0x80, 0x01, 0x01, + 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00 }; + +uint8_t esm_info_req_pdu[] = { 0x27, 0x1d, 0xbf, 0x7e, 0x05, 0x01, 0x02, 0x5a, 0xd9 }; + +uint16 mcc = 61441; +uint16 mnc = 65281; + +using namespace srslte; + +namespace srslte { + +// fake classes +class pdcp_dummy : public rrc_interface_pdcp +{ +public: + void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {} + void write_pdu_bcch_bch(byte_buffer_t *pdu) {} + void write_pdu_bcch_dlsch(byte_buffer_t *pdu) {} + void write_pdu_pcch(byte_buffer_t *pdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} + std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); } +}; + +class rrc_dummy : public rrc_interface_nas +{ +public: + rrc_dummy() : last_sdu_len(0) { + plmns.plmn_id.mcc = mcc; + plmns.plmn_id.mnc = mnc; + plmns.tac = 0xffff; + } + void write_sdu(uint32_t lcid, byte_buffer_t *sdu) + { + last_sdu_len = sdu->N_bytes; + //printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + //srslte_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + byte_buffer_pool::get_instance()->deallocate(sdu); + } + std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); } + uint32_t get_last_sdu_len() { return last_sdu_len; } + + int plmn_search(srsue::rrc_interface_nas::found_plmn_t* found) { + memcpy(found, &plmns, sizeof(found_plmn_t)); + return 1; + }; + void plmn_select(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) {}; + void set_ue_idenity(LIBLTE_RRC_S_TMSI_STRUCT s_tmsi) {} + bool connection_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_ENUM cause, srslte::byte_buffer_t *sdu) { + printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + last_sdu_len = sdu->N_bytes; + srslte_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + byte_buffer_pool::get_instance()->deallocate(sdu); + return true; + } + bool is_connected() {return false;} + + uint16_t get_mcc() { return mcc; } + uint16_t get_mnc() { return mnc; } + void enable_capabilities() {} + +private: + uint32_t last_sdu_len; + found_plmn_t plmns; +}; + +class gw_dummy : public gw_interface_nas, public gw_interface_pdcp +{ + error_t setup_if_addr(uint32_t ip_addr, char *err_str) { return ERROR_NONE; } + void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {} + void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} +}; + +} + +int security_command_test() +{ + int ret = SRSLTE_ERROR; + srslte::log_filter nas_log("NAS"); + srslte::log_filter rrc_log("RRC"); + srslte::log_filter mac_log("MAC"); + srslte::log_filter usim_log("USIM"); + + nas_log.set_level(srslte::LOG_LEVEL_DEBUG); + rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); + nas_log.set_hex_limit(100000); + rrc_log.set_hex_limit(100000); + + rrc_dummy rrc_dummy; + gw_dummy gw; + + usim_args_t args; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + + + // init USIM + srsue::usim usim; + bool net_valid; + uint8_t res[16]; + usim.init(&args, &usim_log); + + srsue::nas nas; + srslte_nas_config_t cfg; + nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); + + // push auth request PDU to NAS to generate security context + byte_buffer_t* tmp = byte_buffer_pool::get_instance()->allocate(); + memcpy(tmp->msg, auth_request_pdu, sizeof(auth_request_pdu)); + tmp->N_bytes = sizeof(auth_request_pdu); + nas.write_pdu(LCID, tmp); + + // TODO: add check for authentication response + + // reuse buffer for security mode command + memcpy(tmp->msg, sec_mode_command_pdu, sizeof(sec_mode_command_pdu)); + tmp->N_bytes = sizeof(sec_mode_command_pdu); + nas.write_pdu(LCID, tmp); + + // check length of generated NAS SDU + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSLTE_SUCCESS; + } + + byte_buffer_pool::get_instance()->cleanup(); + + return ret; +} + + +int mme_attach_request_test() +{ + int ret = SRSLTE_ERROR; + srslte::log_filter nas_log("NAS"); + srslte::log_filter rrc_log("RRC"); + srslte::log_filter mac_log("MAC"); + srslte::log_filter usim_log("USIM"); + + nas_log.set_level(srslte::LOG_LEVEL_DEBUG); + rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); + usim_log.set_level(srslte::LOG_LEVEL_DEBUG); + nas_log.set_hex_limit(100000); + rrc_log.set_hex_limit(100000); + usim_log.set_hex_limit(100000); + + rrc_dummy rrc_dummy; + gw_dummy gw; + srsue::usim usim; + usim_args_t args; + args.mode = "soft"; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + usim.init(&args, &usim_log); + + srslte_nas_config_t nas_cfg; + nas_cfg.apn = "test123"; + srsue::nas nas; + nas.init(&usim, &rrc_dummy, &gw, &nas_log, nas_cfg); + + nas.attach_request(); + + // this will time out in the first place + + // finally push attach accept + byte_buffer_t* tmp = byte_buffer_pool::get_instance()->allocate(); + memcpy(tmp->msg, attach_accept_pdu, sizeof(attach_accept_pdu)); + tmp->N_bytes = sizeof(attach_accept_pdu); + nas.write_pdu(LCID, tmp); + + // check length of generated NAS SDU (attach complete) + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSLTE_SUCCESS; + } + + byte_buffer_pool::get_instance()->cleanup(); + + return ret; +} + + + +int esm_info_request_test() +{ + int ret = SRSLTE_ERROR; + srslte::log_filter nas_log("NAS"); + srslte::log_filter rrc_log("RRC"); + srslte::log_filter mac_log("MAC"); + srslte::log_filter usim_log("USIM"); + + nas_log.set_level(srslte::LOG_LEVEL_DEBUG); + rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); + nas_log.set_hex_limit(100000); + rrc_log.set_hex_limit(100000); + + rrc_dummy rrc_dummy; + gw_dummy gw; + + usim_args_t args; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + + // init USIM + srsue::usim usim; + bool net_valid; + uint8_t res[16]; + usim.init(&args, &usim_log); + + srslte::byte_buffer_pool *pool; + pool = byte_buffer_pool::get_instance(); + + srsue::nas nas; + srslte_nas_config_t cfg; + cfg.apn = "srslte"; + cfg.user = "srsuser"; + cfg.pass = "srspass"; + nas.init(&usim, &rrc_dummy, &gw, &nas_log, cfg); + + // push ESM info request PDU to NAS to generate response + byte_buffer_t* tmp = pool->allocate(); + memcpy(tmp->msg, esm_info_req_pdu, sizeof(esm_info_req_pdu)); + tmp->N_bytes = sizeof(esm_info_req_pdu); + nas.write_pdu(LCID, tmp); + + // check length of generated NAS SDU + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSLTE_SUCCESS; + } + + pool->cleanup(); + + return ret; +} + + +int main(int argc, char **argv) +{ + if (security_command_test()) { + printf("Security command test failed.\n"); + return -1; + } + + if (mme_attach_request_test()) { + printf("Attach request test failed.\n"); + return -1; + } + + if (esm_info_request_test()) { + printf("ESM info request test failed.\n"); + return -1; + } + + return 0; +} diff --git a/srsue/test/upper/pcsc_usim_test.cc b/srsue/test/upper/pcsc_usim_test.cc new file mode 100644 index 0000000..b28b5e7 --- /dev/null +++ b/srsue/test/upper/pcsc_usim_test.cc @@ -0,0 +1,66 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srsue/hdr/upper/pcsc_usim.h" +#include "srslte/common/log_filter.h" +#include +#include + +using namespace srsue; +using namespace std; + + +uint8_t rand_enb[] = {0xbc, 0x4c, 0xb0, 0x27, 0xb3, 0x4b, 0x7f, 0x51, 0x21, 0x5e, 0x56, 0x5f, 0x67, 0x3f, 0xde, 0x4f}; +uint8_t autn_enb[] = {0x5a, 0x17, 0x77, 0x3c, 0x62, 0x57, 0x90, 0x01, 0xcf, 0x47, 0xf7, 0x6d, 0xb3, 0xa0, 0x19, 0x46}; + +int main(int argc, char **argv) +{ + srslte::log_filter usim_log("USIM"); + usim_log.set_level(srslte::LOG_LEVEL_DEBUG); + usim_log.set_hex_limit(100000); + uint8_t res[16]; + int res_len; + uint8_t k_asme[32]; + uint16 mcc = 0; + uint16 mnc = 0; + + usim_args_t args; + args.pin = "6129"; + args.imei = "353490069873319"; + + srsue::pcsc_usim usim; + if (usim.init(&args, &usim_log)) { + printf("Error initializing PC/SC USIM.\n"); + return SRSLTE_ERROR; + }; + + std::string imsi = usim.get_imsi_str(); + cout << "IMSI: " << imsi << endl; + auth_result_t result = usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme); + + return SRSLTE_SUCCESS; +} diff --git a/srsue/test/upper/rrc_reconfig_test.cc b/srsue/test/upper/rrc_reconfig_test.cc new file mode 100644 index 0000000..5280c33 --- /dev/null +++ b/srsue/test/upper/rrc_reconfig_test.cc @@ -0,0 +1,136 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include +#include "srslte/common/log_filter.h" +#include "srslte/asn1/liblte_rrc.h" +#include "srslte/asn1/liblte_mme.h" + +void nas_test() { + srslte::log_filter log1("NAS"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + uint32_t nas_message_len = 73; + uint8_t nas_message[128] = {0x27, 0x4f, 0xab, 0xef, 0x59, 0x01, 0x07, 0x42, + 0x01, 0x49, 0x06, 0x40, 0x00, 0xf1, 0x10, 0x31, + 0x32, 0x00, 0x22, 0x52, 0x01, 0xc1, 0x05, 0x07, + 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0b, 0x76, 0x7a, + 0x77, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x05, 0x01, 0x0e, 0x0e, 0x0e, 0x01, 0x5e, + 0x04, 0xfe, 0xfe, 0x81, 0x4e, 0x50, 0x0b, 0xf6, + 0x00, 0xf1, 0x10, 0x00, 0x02, 0x01, 0x01, 0x00, + 0x00, 0x62, 0x17, 0x2c, 0x59, 0x49, 0x64, 0x01, + 0x03}; + + uint8 pd; + uint8 msg_type; + LIBLTE_BYTE_MSG_STRUCT buf; + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept; + bzero(&attach_accept, sizeof(LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT)); + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req; + bzero(&act_def_eps_bearer_context_req, sizeof(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT)); + + bzero(&buf, sizeof(LIBLTE_BYTE_MSG_STRUCT)); + memcpy(buf.msg, nas_message, nas_message_len); + buf.N_bytes = nas_message_len; + liblte_mme_parse_msg_header(&buf, &pd, &msg_type); + switch(msg_type) + { + case LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT: + liblte_mme_unpack_attach_accept_msg(&buf, &attach_accept); + liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, &act_def_eps_bearer_context_req); + break; + case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT: + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST: + + break; + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT: + + break; + case LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST: + + break; + case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND: + + break; + case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT: + + break; + case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST: + + break; + case LIBLTE_MME_MSG_TYPE_EMM_INFORMATION: + + break; + default: + break; + } +} + +void basic_test() { + srslte::log_filter log1("RRC"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + + LIBLTE_BIT_MSG_STRUCT bit_buf; + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + + uint32_t rrc_message_len = 147; + uint8_t rrc_message[256] = {0x22, 0x16, 0x95, 0xa0, 0x18, 0x00, 0x05, 0xaa, + 0x50, 0x36, 0x00, 0x61, 0x08, 0x9c, 0xe3, 0x40, + 0xb0, 0x84, 0x4e, 0x71, 0xc0, 0x30, 0x84, 0x6e, + 0x71, 0xe0, 0x70, 0x84, 0x6e, 0x70, 0x6c, 0x63, + 0x1a, 0xc6, 0xb9, 0x8e, 0x7b, 0x1e, 0x84, 0xc0, + 0x01, 0x24, 0x9d, 0x3e, 0xaf, 0xbd, 0x64, 0x04, + 0x1d, 0x08, 0x05, 0x24, 0x19, 0x00, 0x03, 0xc4, + 0x40, 0xc4, 0xc8, 0x00, 0x89, 0x48, 0x07, 0x04, + 0x14, 0x1f, 0xff, 0xff, 0xff, 0xfc, 0x30, 0x2d, + 0xd9, 0xe9, 0xdd, 0xa5, 0xb9, 0xd1, 0x95, 0xc9, + 0xb9, 0x95, 0xd0, 0x14, 0x04, 0x38, 0x38, 0x38, + 0x05, 0x78, 0x13, 0xfb, 0xfa, 0x05, 0x39, 0x40, + 0x2f, 0xd8, 0x03, 0xc4, 0x40, 0x00, 0x08, 0x04, + 0x04, 0x00, 0x01, 0x88, 0x5c, 0xb1, 0x65, 0x25, + 0x90, 0x04, 0x0d, 0xa9, 0xc0, 0x2a, 0x9a, 0x01, + 0x99, 0x3b, 0x01, 0xf5, 0x12, 0xf0, 0x85, 0x0d, + 0x85, 0xef, 0xc0, 0x01, 0xf2, 0x20, 0x60, 0x18, + 0x07, 0x97, 0x09, 0x1f, 0xc3, 0x06, 0x00, 0x81, + 0x00, 0x00, 0x11}; + + srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8); + bit_buf.N_bits = rrc_message_len*8; + liblte_rrc_unpack_dl_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dl_dcch_msg); + + printf("done\n"); +} + + +int main(int argc, char **argv) { + basic_test(); + nas_test(); +} diff --git a/srsue/test/upper/usim_test.cc b/srsue/test/upper/usim_test.cc new file mode 100644 index 0000000..35688a2 --- /dev/null +++ b/srsue/test/upper/usim_test.cc @@ -0,0 +1,88 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE 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. + * + * srsUE 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 +#include "srsue/hdr/upper/usim.h" +#include "srslte/common/log_filter.h" +#include + +using namespace srsue; + +/* +Debug output generated from the OpenAirInterface HSS: + +Converted 02f839 to plmn 208.93 +Query: SELECT `key`,`sqn`,`rand`,`OPc` FROM `users` WHERE `users`.`imsi`='208930000000001' +Key: 8b.af.47.3f.2f.8f.d0.94.87.cc.cb.d7.09.7c.68.62. +Received SQN 00000000000000006999 converted to 6999 +SQN: 00.00.00.00.1b.57. +RAND: 7c.f6.e2.6b.20.0a.ca.27.a1.a0.91.40.f5.cf.9d.62. +OPc: 8e.27.b6.af.0e.69.2e.75.0f.32.66.7a.3b.14.60.5d. +Generated random +RijndaelKeySchedule: K 8BAF473F2F8FD09487CCCBD7097C6862 +MAC_A : 84.ba.37.b0.f6.73.4d.d1. +SQN : 00.00.00.00.1b.57. +RAND : 88.38.c3.55.c8.78.aa.57.21.49.fe.69.db.68.6b.5a. +RijndaelKeySchedule: K 8BAF473F2F8FD09487CCCBD7097C6862 +AK : d7.44.51.9b.3e.fd. +CK : 05.d3.53.3d.fe.7b.e7.2d.42.c7.bb.02.f2.8e.da.7f. +IK : 26.33.a2.0b.dc.a8.9d.78.58.ba.42.47.8b.e4.d2.4d. +XRES : e5.5d.88.27.91.8d.ac.c6. +AUTN : d7.44.51.9b.25.aa.80.00.84.ba.37.b0.f6.73.4d.d1. +0x05 0xd3 0x53 0x3d 0xfe 0x7b 0xe7 0x2d 0x42 0xc7 0xbb 0x02 0xf2 0x8e +0xda 0x7f 0x26 0x33 0xa2 0x0b 0xdc 0xa8 0x9d 0x78 0x58 0xba 0x42 0x47 +0x8b 0xe4 0xd2 0x4d 0x10 0x02 0xf8 0x39 0x00 0x03 0xd7 0x44 0x51 0x9b +0x25 0xaa 0x00 0x06 +KASME : a8.27.57.5e.ea.1a.10.17.3a.a1.bf.ce.4b.0c.21.85.e0.51.ef.bd.91.7f.fe.f5.1f.74.29.61.f9.03.7a.35. +*/ + +uint8_t rand_enb[] = {0x88, 0x38, 0xc3, 0x55, 0xc8, 0x78, 0xaa, 0x57, 0x21, 0x49, 0xfe, 0x69, 0xdb, 0x68, 0x6b, 0x5a}; +uint8_t autn_enb[] = {0xd7, 0x44, 0x51, 0x9b, 0x25, 0xaa, 0x80, 0x00, 0x84, 0xba, 0x37, 0xb0, 0xf6, 0x73, 0x4d, 0xd1}; + +uint16 mcc = 208; +uint16 mnc = 93; + +int main(int argc, char **argv) +{ + srslte::log_filter usim_log("USIM"); + bool net_valid; + uint8_t res[16]; + int res_len; + uint8_t k_asme[32]; + + usim_args_t args; + args.algo = "milenage"; + args.imei = "356092040793011"; + args.imsi = "208930000000001"; + args.k = "8BAF473F2F8FD09487CCCBD7097C6862"; + args.using_op = true; + args.op = "11111111111111111111111111111111"; + + srsue::usim usim; + usim.init(&args, &usim_log); + + assert(usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme) == AUTH_OK); +} diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example new file mode 100644 index 0000000..bee96c3 --- /dev/null +++ b/srsue/ue.conf.example @@ -0,0 +1,264 @@ +##################################################################### +# srsUE configuration file +##################################################################### +# RF configuration +# +# dl_earfcn: Downlink EARFCN code. +# freq_offset: Uplink and Downlink optional frequency offset (in Hz) +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn +# nof_rx_ant: Number of RX antennas (Default 1, supported 1 or 2) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +# continuous_tx: Transmit samples continuously to the radio or on bursts (auto/yes/no). +# Default is auto (yes for UHD, no for rest) +##################################################################### +[rf] +dl_earfcn = 3400 +freq_offset = 0 +tx_gain = 80 +#rx_gain = 40 + +#nof_rx_ant = 1 +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto +#continuous_tx = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/ue.pcap +nas_enable = false +nas_filename = /tmp/nas.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gw, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +phy_lib_level = none +all_hex_limit = 32 +filename = /tmp/ue.log +file_max_size = -1 + +##################################################################### +# USIM configuration +# +# mode: USIM mode (soft/pcsc) +# algo: Authentication algorithm (xor/milenage) +# op: 128-bit Operator Variant Algorithm Configuration Field (hex) +# k: 128-bit subscriber key (hex) +# imsi: 15 digit International Mobile Subscriber Identity +# imei: 15 digit International Mobile Station Equipment Identity +# pin: PIN in case real SIM card is used +# reader: Specify card reader by it's name as listed by 'pcsc_scan'. If empty, try all available readers. +##################################################################### +[usim] +mode = soft +algo = xor +opc = 63BFA50EE6523365FF14C1F45F88737D +k = 00112233445566778899aabbccddeeff +imsi = 001010123456789 +imei = 353490069873319 +#reader = +#pin = 1234 + +##################################################################### +# RRC configuration +# +# ue_category: Sets UE category (range 1-5). Default: 4 +# feature_group: Hex value of the featureGroupIndicators field in the +# UECapabilityInformation message. Default 0xe6041000 +##################################################################### +[rrc] +#ue_category = 4 +#feature_group = 0xe6041000 + +##################################################################### +# NAS configuration +# +# apn: Set Access Point Name (APN) +# user: Username for CHAP authentication +# pass: Password for CHAP authentication +# force_imsi_attach: Whether to always perform an IMSI +##################################################################### +[nas] +#apn = internetinternet +#user = srsuser +#pass = srspass +#force_imsi_attach = false + +[gui] +enable = false + +##################################################################### +# Expert configuration options +# +# ip_netmask: Netmask of the tun_srsue device. Default: 255.255.255.0 +# rssi_sensor_enabled: Enable or disable RF frontend RSSI sensor. Required for RSRP metrics but +# can cause UHD instability for long-duration testing. Default true. +# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings +# prach_gain: PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only., +# Default is to use tx_gain in [rf] section. +# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. +# cqi_fixed: Fixes the reported CQI to a constant value. Default disabled. +# snr_ema_coeff: Sets the SNR exponential moving average coefficient (Default 0.1) +# snr_estim_alg: Sets the noise estimation algorithm. (Default refs) +# Options: pss: use difference between received and known pss signal, +# refs: use difference between noise references and noiseless (after filtering) +# empty: use empty subcarriers in the boarder of pss/sss signal +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# attach_enable_64qam: Enables PUSCH 64QAM modulation before attachment (Necessary for old +# Amarisoft LTE 100 eNodeB, disabled by default) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any +# non-negative real number to indicate a regularized zf coefficient. +# Default is MMSE. +# time_correct_period: Period for sampling time offset correction. Default is 10 (ue_sync.c), +# good for long channels. For best performance at highest SNR reduce it to 1. +# sfo_correct_disable: Disables phase correction before channel estimation to compensate for +# sampling frequency offset. Default is enabled. +# sfo_ema: EMA coefficient to average sample offsets used to compute SFO +# sfo_correct_period: Period in ms to correct sample time to adjust for SFO +# sss_algorithm: Selects the SSS estimation algorithm. Can choose between +# {full, partial, diff}. +# estimator_fil_auto: The channel estimator smooths the channel estimate with an adaptative filter. +# estimator_fil_stddev: Sets the channel estimator smooth gaussian filter standard deviation. +# estimator_fil_order: Sets the channel estimator smooth gaussian filter order (even values perform better). +# The taps are [w, 1-2w, w] +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# +# average_subframe_enabled: Averages in the time domain the channel estimates within 1 subframe. +# Needs accurate CFO correction. +# +# sic_pss_enabled: Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. +# Must be disabled if cells have identical channel and timing, for instance if generated from +# the same source. +# +# metrics_csv_enable: Write UE metrics to CSV file. +# +# metrics_csv_filename: File path to use for CSV metrics. +# +# cfo_integer_enabled: Enables integer CFO estimation and correction. This needs improvement +# and may lead to incorrect synchronization. Use with caution. +# cfo_correct_tol_hz: Tolerance (in Hz) for digial CFO compensation. Lower tolerance means that +# a new table will be generated more often. +# +# cfo_is_doppler: Assume detected CFO is doppler and correct the UL in the same direction. If disabled, the CFO is assumed +# to be caused by the local oscillator and the UL correction is in the opposite direction. Default assumes oscillator. +# cfo_pss_ema: CFO Exponential Moving Average coefficient for PSS estimation during TRACK. +# cfo_ref_ema: CFO Exponential Moving Average coefficient for RS estimation after PSS acquisition +# cfo_ref_mask: Bitmask for subframes on which to run RS estimation (set to 0 to disable, default sf=[1, 5]) +# cfo_loop_bw: CFO feedback loop bandwidth for samples from PSS or RS +# cfo_loop_pss_tol: Tolerance (in Hz) of the PSS estimation method. Below this value, PSS estimation does not feeds back the loop +# and RS estimations are used instead (when available) +# cfo_loop_ref_min: Tolerance (in Hz) of the RS estimation method. Below this value, RS estimation does not feeds back the loop +# cfo_loop_pss_timeout: After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, +# RS adjustments are allowed. +# +# pdsch_csi_enabled: Stores the Channel State Information and uses it for weightening the softbits. It is only +# used in TM1. It is True by default. +# +##################################################################### +[expert] +#ip_netmask = 255.255.255.0 +#mbms_service = -1 +#rssi_sensor_enabled = false +#rx_gain_offset = 72 +#prach_gain = 30 +#cqi_max = 15 +#cqi_fixed = 10 +#snr_ema_coeff = 0.1 +#snr_estim_alg = refs +#pdsch_max_its = 4 +#attach_enable_64qam = false +#nof_phy_threads = 2 +#equalizer_mode = mmse +#time_correct_period = 5 +#sfo_correct_disable = false +#sfo_ema = 0.1 +#sfo_correct_period = 10 +#sss_algorithm = full +#estimator_fil_auto = false +#estimator_fil_stddev = 1.0 +#estimator_fil_order = 4 +#average_subframe_enabled = true +#sic_pss_enabled = true +#pregenerate_signals = false +#metrics_csv_enable = false +#metrics_csv_filename = /tmp/ue_metrics.csv +#pdsch_csi_enabled = true + +# CFO related values +#cfo_is_doppler = false +#cfo_integer_enabled = false +#cfo_correct_tol_hz = 1.0 +#cfo_pss_ema = 0.05 +#cfo_ref_mask = 1023 +#cfo_loop_bw_pss = 0.05 +#cfo_loop_bw_ref = 0.01 +#cfo_loop_pss_tol = 400 +#cfo_loop_ref_min = 0 +#cfo_loop_pss_conv = 20 + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97 -- cgit v1.2.3 From fcac7b6f578c9f9036dce30e195b29242f8c539d Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From 086f88f1d18aafafb2097f30dc6770c0f5f1359d Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 0782a343cdbdb480c1c2daed4d494f6c91d3143a Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From 051ad3f3048b987ea70e5b078722ee6a4a9f291c Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Thu, 4 Oct 2018 22:23:39 +0000 Subject: Disable architecture specific SIMD optimisations Gbp-Pq: Name 0004-Disable-architecture-specific-SIMD-optimisations.patch --- CMakeLists.txt | 56 +++++--------------------------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..d5c9892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,24 +245,14 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") - - find_package(SSE) - if (HAVE_AVX2) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") - else (HAVE_AVX2) - if(HAVE_AVX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") - elseif(HAVE_SSE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") - endif(HAVE_AVX) - endif (HAVE_AVX2) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -283,45 +272,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFORCE_STANDARD_RATE") endif (USE_LTE_RATES) - find_package(SSE) - if (HAVE_AVX2) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") - else (HAVE_AVX2) - if(HAVE_AVX) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") - elseif(HAVE_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") - endif(HAVE_AVX) - endif (HAVE_AVX2) - - if (HAVE_FMA) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -DLV_HAVE_FMA") - endif (HAVE_FMA) - - if (HAVE_AVX512) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") - endif(HAVE_AVX512) - - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - if(HAVE_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -funroll-loops") - endif(HAVE_SSE) - endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) - - if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) - message(FATAL_ERROR "no SIMD instructions found") - endif(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) + if(NOT WIN32) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) -- cgit v1.2.3 From ddbc65b105bd9a087b85b5a466f12a6fd09f9473 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From c5cc682a93bb79cd06a632166b3cee8d50e17acd Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From bd1e2e8ec755e335e2a7fc1a439c2b9808da09b2 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From 4c3e85a5f1c21cb67e3d1fc91838b15711b8bf3c Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Thu, 4 Oct 2018 22:23:39 +0000 Subject: Disable architecture specific SIMD optimisations Gbp-Pq: Name 0004-Disable-architecture-specific-SIMD-optimisations.patch --- CMakeLists.txt | 56 +++++--------------------------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..d5c9892 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,24 +245,14 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") - - find_package(SSE) - if (HAVE_AVX2) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") - else (HAVE_AVX2) - if(HAVE_AVX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") - elseif(HAVE_SSE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") - endif(HAVE_AVX) - endif (HAVE_AVX2) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -283,45 +272,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFORCE_STANDARD_RATE") endif (USE_LTE_RATES) - find_package(SSE) - if (HAVE_AVX2) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") - else (HAVE_AVX2) - if(HAVE_AVX) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") - elseif(HAVE_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") - endif(HAVE_AVX) - endif (HAVE_AVX2) - - if (HAVE_FMA) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfma -DLV_HAVE_FMA") - endif (HAVE_FMA) - - if (HAVE_AVX512) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx512f -mavx512cd -DLV_HAVE_AVX512") - endif(HAVE_AVX512) - - if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - if(HAVE_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -funroll-loops") - endif(HAVE_SSE) - endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) - - if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) - message(FATAL_ERROR "no SIMD instructions found") - endif(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) + if(NOT WIN32) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) -- cgit v1.2.3 From 7a2f2b64d56741982f9c2d405432f07be83fadc4 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From 7213113d94c66eb344df7e79fe6c3ac89d6f6efa Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 3f10da8cf3e176eb84f35540fbbd007ae02205c2 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From 4f5a5e0ca76d9814954910a71fc57e22c1bb2547 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 07:53:02 +0000 Subject: Patch for setting custom RPATH Gbp-Pq: Name 0005-Patch-for-setting-custom-RPATH.patch --- srsenb/src/CMakeLists.txt | 2 +- srsue/src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 0d2cc8d..8aa735c 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index f4eac8b..8057a77 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 34a271f2470ece138325ea672ec59cdc9925b746 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 15:20:22 +0000 Subject: Check whether code compiles instead of whether it runs Gbp-Pq: Name 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch --- cmake/modules/FindSSE.cmake | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 4c9673a..16b49e8 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -3,6 +3,7 @@ #endif() include(CheckCSourceRuns) +include(CheckCSourceCompiles) option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) option(ENABLE_AVX "Enable compile-time AVX support." ON) @@ -16,7 +17,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-msse4.1") - check_c_source_runs(" + check_c_source_compiles(" #include #include @@ -40,7 +41,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -74,7 +75,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx2") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -108,7 +109,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mfma") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -143,7 +144,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx512f") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { -- cgit v1.2.3 From 02cca3c4f8b4a855d231ffadd33853f95ceb11d9 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 16:21:37 +0100 Subject: No native in build Gbp-Pq: Name 0006-No-native-in-build.patch --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..e83977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") find_package(SSE) if (HAVE_AVX2) @@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") message(STATUS "have ARM") set(HAVE_NEON "True") else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") -- cgit v1.2.3 From c1fe0178aa58d35e337194faae6d0668351c0f84 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 17:00:28 +0100 Subject: Option for disabling NEON on arm Gbp-Pq: Name 0007-Option-for-disabling-NEON-on-arm.patch --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83977c..e5faa80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(HAVE_SSE) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + option(ENABLE_SSE "Enable NEON support for ARM" ON) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(ENABLE_NEON) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) -- cgit v1.2.3 From d8e5545ce872b56820706af74aad8b5fd9265b2b Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From fe2205ae3ac31734a13574be1e86e72951950fb3 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 02289ba0325aed0390d67d8f5bb253db4f96c689 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From bd9708fe1c61fe81fe2cecbd1069ef9fe87e73f3 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 07:53:02 +0000 Subject: Patch for setting custom RPATH Gbp-Pq: Name 0005-Patch-for-setting-custom-RPATH.patch --- srsenb/src/CMakeLists.txt | 2 +- srsue/src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 0d2cc8d..8aa735c 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index f4eac8b..8057a77 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 0a365101d0ae6fb027a6f4f0a0c3b8e960697d7e Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 15:20:22 +0000 Subject: Check whether code compiles instead of whether it runs Gbp-Pq: Name 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch --- cmake/modules/FindSSE.cmake | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 4c9673a..16b49e8 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -3,6 +3,7 @@ #endif() include(CheckCSourceRuns) +include(CheckCSourceCompiles) option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) option(ENABLE_AVX "Enable compile-time AVX support." ON) @@ -16,7 +17,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-msse4.1") - check_c_source_runs(" + check_c_source_compiles(" #include #include @@ -40,7 +41,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -74,7 +75,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx2") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -108,7 +109,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mfma") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -143,7 +144,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx512f") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { -- cgit v1.2.3 From a599f01bfe0051651210d2dc86738bf5a8db5d43 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 16:21:37 +0100 Subject: No native in build Gbp-Pq: Name 0006-No-native-in-build.patch --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..e83977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") find_package(SSE) if (HAVE_AVX2) @@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") message(STATUS "have ARM") set(HAVE_NEON "True") else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") -- cgit v1.2.3 From 970debc6dd591846bc9e254fe8078df2d68ec7b2 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 17:00:28 +0100 Subject: Option for disabling NEON on arm Gbp-Pq: Name 0007-Option-for-disabling-NEON-on-arm.patch --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83977c..e5faa80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(HAVE_SSE) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + option(ENABLE_SSE "Enable NEON support for ARM" ON) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(ENABLE_NEON) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) -- cgit v1.2.3 From 0252356d244a7d43b1eded6e7a049261a2742e09 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From e69fa24218c867097cc56cc878b933d9665abf7d Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 2ce0ca3b3ec5edd1f2166999f774264c6c605cff Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From 7b850776a7cb70617c5b5ee1676973166d984df5 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 07:53:02 +0000 Subject: Patch for setting custom RPATH Gbp-Pq: Name 0005-Patch-for-setting-custom-RPATH.patch --- srsenb/src/CMakeLists.txt | 2 +- srsue/src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 0d2cc8d..8aa735c 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index f4eac8b..8057a77 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 935a10e040e616d87c659a98fd61699b059a780c Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 15:20:22 +0000 Subject: Check whether code compiles instead of whether it runs Gbp-Pq: Name 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch --- cmake/modules/FindSSE.cmake | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 4c9673a..16b49e8 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -3,6 +3,7 @@ #endif() include(CheckCSourceRuns) +include(CheckCSourceCompiles) option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) option(ENABLE_AVX "Enable compile-time AVX support." ON) @@ -16,7 +17,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-msse4.1") - check_c_source_runs(" + check_c_source_compiles(" #include #include @@ -40,7 +41,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -74,7 +75,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx2") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -108,7 +109,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mfma") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -143,7 +144,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx512f") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { -- cgit v1.2.3 From b7c4310ff796ba098ebe4cc938b8c0fe8989cd2a Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 16:21:37 +0100 Subject: No native in build Gbp-Pq: Name 0006-No-native-in-build.patch --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..e83977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") find_package(SSE) if (HAVE_AVX2) @@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") message(STATUS "have ARM") set(HAVE_NEON "True") else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") -- cgit v1.2.3 From e8b5a79c4c7159fabc6104fcffdfa860d24439a6 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 17:00:28 +0100 Subject: Option for disabling NEON on arm Gbp-Pq: Name 0007-Option-for-disabling-NEON-on-arm.patch --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83977c..ddade72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(HAVE_SSE) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + option(ENABLE_NEON "Enable NEON support for ARM" ON) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(ENABLE_NEON) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) -- cgit v1.2.3 From 8110819003f7837f7a2ee2408458f4e580832b4f Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Wed, 28 Nov 2018 19:48:50 +0100 Subject: Import srslte_18.06.1-7.debian.tar.xz [dgit import tarball srslte 18.06.1-7 srslte_18.06.1-7.debian.tar.xz] --- changelog | 101 ++++++++ compat | 1 + control | 112 +++++++++ copyright | 118 +++++++++ default_config/drb.conf | 54 +++++ default_config/enb.conf | 180 ++++++++++++++ default_config/epc.conf | 77 ++++++ default_config/mbms.conf | 40 ++++ default_config/rr.conf | 50 ++++ default_config/sib.conf | 127 ++++++++++ default_config/ue.conf | 264 +++++++++++++++++++++ default_config/user_db.csv | 16 ++ gbp.conf | 2 + man/genmanpages.sh | 5 + man/srsenb.txt | 35 +++ man/srsepc.txt | 26 ++ man/srsue.txt | 32 +++ patches/0001-Install-dir-for-private-library.patch | 19 ++ patches/0002-Set-RPATH-to-usr-lib-srslte.patch | 51 ++++ patches/0003-Spelling-error-fixes.patch | 200 ++++++++++++++++ ...er-code-compiles-instead-of-whether-it-ru.patch | 65 +++++ patches/0005-Patch-for-setting-custom-RPATH.patch | 35 +++ patches/0006-No-native-in-build.patch | 47 ++++ .../0007-Option-for-disabling-NEON-on-arm.patch | 37 +++ patches/series | 7 + rules | 144 +++++++++++ script/srsenb | 33 +++ script/srsue | 33 +++ source/format | 1 + srsenb.install | 6 + srsenb.manpages | 1 + srsenb.service | 11 + srsepc.install | 6 + srsepc.manpages | 1 + srsepc.service | 11 + srslte-core.install | 2 + srslte-dev.install | 2 + srsue.install | 3 + srsue.manpages | 1 + srsue.service | 11 + tests/control | 2 + tests/srsenb-help | 7 + tests/srsepc-help | 7 + tests/srsue-help | 7 + upstream/metadata | 6 + watch | 4 + 46 files changed, 2000 insertions(+) create mode 100644 changelog create mode 100644 compat create mode 100644 control create mode 100644 copyright create mode 100644 default_config/drb.conf create mode 100644 default_config/enb.conf create mode 100644 default_config/epc.conf create mode 100644 default_config/mbms.conf create mode 100644 default_config/rr.conf create mode 100644 default_config/sib.conf create mode 100644 default_config/ue.conf create mode 100644 default_config/user_db.csv create mode 100644 gbp.conf create mode 100755 man/genmanpages.sh create mode 100644 man/srsenb.txt create mode 100644 man/srsepc.txt create mode 100644 man/srsue.txt create mode 100644 patches/0001-Install-dir-for-private-library.patch create mode 100644 patches/0002-Set-RPATH-to-usr-lib-srslte.patch create mode 100644 patches/0003-Spelling-error-fixes.patch create mode 100644 patches/0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch create mode 100644 patches/0005-Patch-for-setting-custom-RPATH.patch create mode 100644 patches/0006-No-native-in-build.patch create mode 100644 patches/0007-Option-for-disabling-NEON-on-arm.patch create mode 100644 patches/series create mode 100755 rules create mode 100755 script/srsenb create mode 100755 script/srsue create mode 100644 source/format create mode 100644 srsenb.install create mode 100644 srsenb.manpages create mode 100644 srsenb.service create mode 100644 srsepc.install create mode 100644 srsepc.manpages create mode 100644 srsepc.service create mode 100644 srslte-core.install create mode 100644 srslte-dev.install create mode 100644 srsue.install create mode 100644 srsue.manpages create mode 100644 srsue.service create mode 100644 tests/control create mode 100755 tests/srsenb-help create mode 100755 tests/srsepc-help create mode 100755 tests/srsue-help create mode 100644 upstream/metadata create mode 100644 watch diff --git a/changelog b/changelog new file mode 100644 index 0000000..311158d --- /dev/null +++ b/changelog @@ -0,0 +1,101 @@ +srslte (18.06.1-7) unstable; urgency=medium + + * Split srslte into multiple binary packages since there are several + daemons: + - srsenb + - srsue + - srsepc + - srslte-core + * srslte-dev depends on srslte-core:any + * srslte binary package changed to arch=all and binNMU-safe dependencies + added + * Move man pages to section 8. + * debian/rules: + - Change override_dh_install to override_dh_install-arch + - Separate build targets for -indep and -arch to make all-build faster + + -- Ruben Undheim Wed, 28 Nov 2018 19:48:50 +0100 + +srslte (18.06.1-6) unstable; urgency=medium + + * debian/control: + - srslte-dev set to "Multi-Arch: same" + - srslte set to "Multi-Arch: allowed" + - Dependency on srslte for srslte-dev changed to "srslte:any" + * debian/rules: + - Include FMA extension for AVX2 build + - Clean up properly in override_dh_auto_clean + - Tidy up commands a bit + - Disable build of Neon for ARM since it fails to build from source + - Workaround to get custom cmake invocations to pick up hardening flags + * d/patches/0007-Option-for-disabling-NEON-on-arm.patch: + - Fix typo in patch + * debian/script/srsenb: Start command with "exec" + * debian/script/srsue: Start command with "exec" + * debian/tests: + - Added autopkgtest for srslte checking that all the commands can run + + -- Ruben Undheim Sat, 24 Nov 2018 10:58:57 +0100 + +srslte (18.06.1-5) unstable; urgency=medium + + * debian/control: + - Added more build-dependencies + * debian/rules: + - Build separate executables for CPUs with AVX2 extensions, and CPUs + with SSE4.2 but no AVX extension. + - Print which version is chosen when starting. + - Add '-q' to grep invocation + + -- Ruben Undheim Fri, 23 Nov 2018 18:35:40 +0100 + +srslte (18.06.1-4) unstable; urgency=medium + + * debian/rules: + - Build one version with AVX SIMD support for Intel CPUs and one version + with NEON SIMD support for Arm CPUs. + * debian/script/srsenb and debian/script/srsue: + - Scripts which first check if avx2 or neon is available on the machine + before choosing which executable to start. + * Restructured patches a bit: + - 0004-Disable-architecture-specific-SIMD-optimisations.patch removed + - New patches: + - 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch + - 0005-Patch-for-setting-custom-RPATH.patch + - 0006-No-native-in-build.patch + - 0007-Option-for-disabling-NEON-on-arm.patch + + -- Ruben Undheim Fri, 23 Nov 2018 08:55:21 +0100 + +srslte (18.06.1-3) unstable; urgency=medium + + * Install systemd service files for the 3 services: + - srsenb.service + - srsue.service + - srsepc.service + * Install default configuration in /etc/srslte/: + - drb.conf + - enb.conf + - epc.conf + - mbms.conf + - rr.conf + - sib.conf + - ue.conf + - user_db.csv + * debian/rules: + - Do not enable or start services by default + + -- Ruben Undheim Sun, 18 Nov 2018 10:52:08 +0100 + +srslte (18.06.1-2) unstable; urgency=medium + + * d/patches/0004-Disable-architecture-specific-SIMD-optimisations.patch + - For fixing FTBFS on multiple architectures + + -- Ruben Undheim Fri, 05 Oct 2018 00:45:17 +0200 + +srslte (18.06.1-1) unstable; urgency=low + + * Initial release (Closes: #904221) + + -- Ruben Undheim Sun, 02 Sep 2018 22:56:55 +0200 diff --git a/compat b/compat new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/compat @@ -0,0 +1 @@ +11 diff --git a/control b/control new file mode 100644 index 0000000..6956939 --- /dev/null +++ b/control @@ -0,0 +1,112 @@ +Source: srslte +Section: net +Priority: optional +Maintainer: Debian Mobcom Maintainers +Uploaders: Ruben Undheim +Build-Depends: debhelper (>= 11), + cmake, + libfftw3-dev, + libmbedtls-dev, + libboost-program-options-dev, + libconfig++-dev, + libsctp-dev, + libuhd-dev, + uhd-host, + txt2man, + libpcsclite-dev, + libbladerf-dev, + libsoapysdr-dev +Standards-Version: 4.2.1 +Homepage: http://www.softwareradiosystems.com +Vcs-Git: https://salsa.debian.org/debian-mobcom-team/srslte.git +Vcs-Browser: https://salsa.debian.org/debian-mobcom-team/srslte + +Package: srslte-dev +Architecture: any +Multi-Arch: same +Section: libdevel +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srslte-core:any (= ${binary:Version}) +Description: Static libraries and headers for srslte + This software allows you to run a full end-to-end, open-source LTE system. + It contains a UE, eNB and EPC implementation. + . + This package contains the development files - static libraries and headers + +Package: srslte +Architecture: all +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srsenb (>= ${source:Version}), srsenb (<< ${source:Upstream-Version}.0~), + srsue (>= ${source:Version}), srsue (<< ${source:Upstream-Version}.0~), + srsepc (>= ${source:Version}), srsepc (<< ${source:Upstream-Version}.0~) +Suggests: nextepc +Description: LTE software suite for software defined radios (metapackage) + This software enables running a full end-to-end, open-source LTE system. It + contains a UE, eNB and EPC implementation. + . + For running a full network, also an implementation of the Evolved Packet Core + (EPC) is needed. + +Package: srsenb +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srslte-core (= ${binary:Version}) +Suggests: nextepc +Breaks: srslte (<< 18.06.1-7) +Replaces: srslte (<< 18.06.1-7) +Description: Evolved Node B in LTE network for Software Defined Radios + This software enables running a "base-station" for an LTE network. + . + For running a full network, also an implementation of the Evolved Packet Core + (EPC) is needed. (such as nextepc) + . + It contains the evolved Node B (eNB) component of the network. + +Package: srsue +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srslte-core (= ${binary:Version}) +Breaks: srslte (<< 18.06.1-7) +Replaces: srslte (<< 18.06.1-7) +Description: User Equipment implementation for LTE + This software enables using a software defined radio as the user equipment + connecting to an LTE network. + . + srsue provides a complete SDR LTE UE application featuring all layers from PHY + to IP. + . + For running an LTE network, 'srsenb' should be used instead. + +Package: srsepc +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + srslte-core (= ${binary:Version}) +Recommends: srsenb +Breaks: srslte (<< 18.06.1-7) +Replaces: srslte (<< 18.06.1-7) +Description: Light-weight LTE core network implementation + Together with 'srsenb', this software opens up the possibility to run an LTE + network using a software defined radio. + . + It is a lightweight alternative to tools such as nextepc. + +Package: srslte-core +Architecture: any +Multi-Arch: allowed +Depends: ${shlibs:Depends}, + ${misc:Depends} +Breaks: srslte (<< 18.06.1-7) +Replaces: srslte (<< 18.06.1-7) +Description: Common files for srsLTE + This software enables running a full end-to-end, open-source LTE system. It + contains a UE, eNB and EPC implementation. + . + For running a full network, also an implementation of the Evolved Packet Core + (EPC) is needed. + . + This package contains the common files for srsenb, srsue and srsepc. diff --git a/copyright b/copyright new file mode 100644 index 0000000..95b5e8f --- /dev/null +++ b/copyright @@ -0,0 +1,118 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: srsLTE +Upstream-Contact: srsLTE packaging team +License: AGPL-3+ +Copyright: 2013-2018, Software Radio Systems Limited. +Source: https://www.github.com/srsLTE + + +Files: * +Copyright: 2013-2018 Software Radio Systems Limited. + 2015 The srsUE Developers +License: AGPL-3+ + +Files: lib/src/phy/fec/viterbi37_port.c + lib/src/phy/fec/viterbi37_sse.c + lib/src/phy/fec/viterbi37_neon.c + lib/src/phy/fec/viterbi37_avx2_16bit.c + lib/src/phy/fec/viterbi37_avx2.c + lib/src/phy/fec/parity.* +Copyright: 2004,2009, Phil Karn, KA9Q +License: LGPL-2.1 + +Files: srsue/src/upper/pcsc_usim.cc + srsue/hdr/upper/pcsc_usim.h +Copyright: 2002-2014, Jouni Malinen +License: BSD-3-clause + +Files: cmake/modules/CheckCSourceRuns.cmake + cmake/modules/CheckFunctionExistsMath.cmake +Copyright: 2002-2011 Kitware, Inc. +License: BSD-3-clause +Comment: The header says "OSI approved BSD", but assuming + BSD-3-clause is meant since it is the most restrictive of the + "OSI approved BSD licenses" + +Files: lib/include/srslte/asn1/liblte_common.h + lib/include/srslte/asn1/liblte_rrc.h + lib/include/srslte/asn1/liblte_mme.h + lib/include/srslte/common/liblte_security.h + lib/src/asn1/liblte_common.cc + lib/src/asn1/liblte_mme.cc + lib/src/asn1/liblte_rrc.cc + lib/src/common/liblte_security.cc +Copyright: 2012-2015 Ben Wojtowicz + 2014 Andrew Murphy +License: AGPL-3+ + +Files: srsue/hdr/upper/pcsc_usim.h +Copyright: Jouni Malinen + 2013-2015 Software Radio Systems Limited +License: BSD-3-clause and AGPL-3+ +Comment: The class "scard" comes from the PC/SC smartcard implementation + WPA Supplicant and is written by Jouni Malinen and licensed with a BSD + license. + +Files: debian/* +Copyright: 2018 Ruben Undheim +License: AGPL-3+ + + +License: AGPL-3+ + This program 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. + . + This program 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. + . + On Debian systems, the complete text of the AGPL 3 can be found in + /usr/share/doc/srslte/LICENSE + + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +License: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + 3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/default_config/drb.conf b/default_config/drb.conf new file mode 100644 index 0000000..32b6e93 --- /dev/null +++ b/default_config/drb.conf @@ -0,0 +1,54 @@ + +// All times are in ms. Use -1 for infinity, where available + +qci_config = ( + +{ + qci=7; + pdcp_config = { + discard_timer = 100; + pdcp_sn_size = 12; + } + rlc_config = { + ul_um = { + sn_field_length = 10; + }; + dl_um = { + sn_field_length = 10; + t_reordering = 45; + }; + }; + logical_channel_config = { + priority = 13; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 2; + }; +}, +{ + qci=9; + pdcp_config = { + discard_timer = -1; + status_report_required = true; + } + rlc_config = { + ul_am = { + t_poll_retx = 120; + poll_pdu = 64; + poll_byte = 750; + max_retx_thresh = 16; + }; + dl_am = { + t_reordering = 50; + t_status_prohibit = 50; + }; + }; + logical_channel_config = { + priority = 11; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 3; + }; +} + +); diff --git a/default_config/enb.conf b/default_config/enb.conf new file mode 100644 index 0000000..293b0e6 --- /dev/null +++ b/default_config/enb.conf @@ -0,0 +1,180 @@ +##################################################################### +# srsENB configuration file +##################################################################### + +##################################################################### +# eNB configuration +# +# enb_id: 20-bit eNB identifier. +# cell_id: 8-bit cell identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# mme_addr: IP address of MME for S1 connnection +# gtp_bind_addr: Local IP address to bind for GTP connection +# s1c_bind_addr: Local IP address to bind for S1AP connection +# n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100) +# tm: Transmission mode 1-4 (TM1 default) +# nof_ports: Number of Tx ports (1 port default, set to 2 for TM2/3/4) +# +##################################################################### +[enb] +enb_id = 0x19B +cell_id = 0x01 +phy_cell_id = 1 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_addr = 127.0.1.100 +gtp_bind_addr = 127.0.1.1 +s1c_bind_addr = 127.0.1.1 +n_prb = 50 +#tm = 4 +#nof_ports = 2 + +##################################################################### +# eNB configuration files +# +# sib_config: SIB1, SIB2 and SIB3 configuration file +# note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 +# rr_config: Radio Resources configuration file +# drb_config: DRB configuration file +##################################################################### +[enb_files] +sib_config = /etc/srslte/sib.conf +rr_config = /etc/srslte/rr.conf +drb_config = /etc/srslte/drb.conf + +##################################################################### +# RF configuration +# +# dl_earfcn: EARFCN code for DL +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn (must be set if dl_freq is set) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +##################################################################### +[rf] +dl_earfcn = 3400 +tx_gain = 80 +rx_gain = 40 + +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/enb.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gtpu, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +all_hex_limit = 32 +filename = /tmp/enb.log +file_max_size = -1 + +[gui] +enable = false + +##################################################################### +# Scheduler configuration options +# +# pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) +# pdsch_max_mcs: Optional PDSCH MCS limit +# pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) +# pusch_max_mcs: Optional PUSCH MCS limit +# #nof_ctrl_symbols: Number of control symbols +# +##################################################################### +[scheduler] +#pdsch_mcs = -1 +#pdsch_max_mcs = -1 +#pusch_mcs = -1 +pusch_max_mcs = 16 +nof_ctrl_symbols = 3 + +##################################################################### +# Expert configuration options +# +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) +# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. +# a link failure is when SNR<0 and CRC=KO +# max_prach_offset_us: Maximum allowed RACH offset (in us) +# +##################################################################### +[expert] +#pdsch_max_its = 4 +#nof_phy_threads = 2 +#pregenerate_signals = false +#tx_amplitude = 0.6 +#link_failure_nof_err = 50 +#rrc_inactivity_timer = 60000 +#max_prach_offset_us = 30 +#enable_mbsfn = false + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97 diff --git a/default_config/epc.conf b/default_config/epc.conf new file mode 100644 index 0000000..17782ad --- /dev/null +++ b/default_config/epc.conf @@ -0,0 +1,77 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MME configuration +# +# mme_code: 8-bit MME code identifies the MME within a group. +# mme_group: 16-bit MME group identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# apn: Set Access Point Name (APN) +# mme_bind_addr: IP bind addr to listen for eNB S1-MME connnections +# dns_addr: DNS server address for the UEs +# +##################################################################### +[mme] +mme_code = 0x1a +mme_group = 0x0001 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_bind_addr = 127.0.1.100 +apn = srsapn +dns_addr = 8.8.8.8 + +##################################################################### +# HSS configuration +# +# algo: Authentication algorithm (xor/milenage) +# db_file: Location of .csv file that stores UEs information. +# +##################################################################### +[hss] +auth_algo = xor +db_file = /etc/srslte/user_db.csv + + +##################################################################### +# SP-GW configuration +# +# gtpu_bind_addr: GTP-U bind adress. +# +##################################################################### + +[spgw] +gtpu_bind_addr=127.0.1.100 +sgi_if_addr=172.16.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: s1ap, gtpc, spgw, hss, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +##################################################################### +[log] +all_level = debug +all_hex_limit = 32 +filename = /tmp/epc.log + +#s1ap_level = debug +#gtpc_level = debug +#spgw_level = debug +#hss_level = debug diff --git a/default_config/mbms.conf b/default_config/mbms.conf new file mode 100644 index 0000000..d923063 --- /dev/null +++ b/default_config/mbms.conf @@ -0,0 +1,40 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MBMS-GW configuration +# +# name: MBMS-GW name +# sgi_mb_if_addr: SGi-mb interface IP address +# m1u_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3) +# +##################################################################### +[mbms_gw] +name = srsmbmsgw01 +sgi_mb_if_addr = 172.16.0.254 +sgi_mb_if_mask = 255.255.255.255 +m1u_multi_addr = 239.255.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: mbms_gw, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +##################################################################### +[log] +all_level = debug +all_hex_limit = 32 +filename = /tmp/mbms.log diff --git a/default_config/rr.conf b/default_config/rr.conf new file mode 100644 index 0000000..e819cf4 --- /dev/null +++ b/default_config/rr.conf @@ -0,0 +1,50 @@ +mac_cnfg = +{ + phr_cnfg = + { + dl_pathloss_change = "3dB"; // Valid: 1, 3, 6 or INFINITY + periodic_phr_timer = 50; + prohibit_phr_timer = 0; + }; + ulsch_cnfg = + { + max_harq_tx = 4; + periodic_bsr_timer = 20; // in ms + retx_bsr_timer = 320; // in ms + }; + + time_alignment_timer = -1; // -1 is infinity +}; + +phy_cnfg = +{ + phich_cnfg = + { + duration = "Normal"; + resources = "1/6"; + }; + + pusch_cnfg_ded = + { + beta_offset_ack_idx = 6; + beta_offset_ri_idx = 6; + beta_offset_cqi_idx = 6; + }; + + // PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource. + sched_request_cnfg = + { + dsr_trans_max = 64; + period = 20; // in ms + subframe = [1]; // vector of subframe indices allowed for SR transmissions + nof_prb = 2; // number of PRBs on each extreme used for SR (total prb is twice this number) + }; + cqi_report_cnfg = + { + mode = "periodic"; + simultaneousAckCQI = true; + period = 40; // in ms + subframe = [0]; + nof_prb = 2; + }; +}; diff --git a/default_config/sib.conf b/default_config/sib.conf new file mode 100644 index 0000000..dded32c --- /dev/null +++ b/default_config/sib.conf @@ -0,0 +1,127 @@ +sib1 = +{ + intra_freq_reselection = "Allowed"; + q_rx_lev_min = -130; + //p_max = 3; + cell_barred = "Not Barred" + si_window_length = 20; + sched_info = + ( + { + si_periodicity = 16; + si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13). + // Leave empty or commented to just scheduler sib2 + } + ); + system_info_value_tag = 0; +}; + +sib2 = +{ + rr_config_common_sib = + { + rach_cnfg = + { + num_ra_preambles = 52; + preamble_init_rx_target_pwr = -104; + pwr_ramping_step = 6; // in dB + preamble_trans_max = 10; + ra_resp_win_size = 10; // in ms + mac_con_res_timer = 64; // in ms + max_harq_msg3_tx = 4; + }; + bcch_cnfg = + { + modification_period_coeff = 16; // in ms + }; + pcch_cnfg = + { + default_paging_cycle = 32; // in rf + nB = "1"; + }; + prach_cnfg = + { + root_sequence_index = 128; + prach_cnfg_info = + { + high_speed_flag = false; + prach_config_index = 3; + prach_freq_offset = 2; + zero_correlation_zone_config = 5; + }; + }; + pdsch_cnfg = + { + p_b = 0; + rs_power = 0; + }; + pusch_cnfg = + { + n_sb = 1; + hopping_mode = "inter-subframe"; + pusch_hopping_offset = 2; + enable_64_qam = false; // 64QAM PUSCH is not currently enabled + ul_rs = + { + cyclic_shift = 0; + group_assignment_pusch = 0; + group_hopping_enabled = false; + sequence_hopping_enabled = false; + }; + }; + pucch_cnfg = + { + delta_pucch_shift = 2; + n_rb_cqi = 2; + n_cs_an = 0; + n1_pucch_an = 12; + }; + ul_pwr_ctrl = + { + p0_nominal_pusch = -85; + alpha = 0.7; + p0_nominal_pucch = -107; + delta_flist_pucch = + { + format_1 = 0; + format_1b = 3; + format_2 = 1; + format_2a = 2; + format_2b = 2; + }; + delta_preamble_msg3 = 8; + }; + ul_cp_length = "Normal"; + }; + + ue_timers_and_constants = + { + t300 = 2000; // in ms + t301 = 100; // in ms + t310 = 1000; // in ms + n310 = 1; + t311 = 1000; // in ms + n311 = 1; + }; + + freqInfo = + { + ul_carrier_freq_present = true; + ul_bw_present = true; + additional_spectrum_emission = 1; + }; + + mbsfnSubframeConfigList = + { + radioframeAllocationPeriod = "1"; + subframeAllocationNumFrames = "1"; + radioframeAllocationOffset = 0; + subframeAllocation = 63; + + }; + + mbsfnSubframeConfigListLength = 0; + + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. +}; + diff --git a/default_config/ue.conf b/default_config/ue.conf new file mode 100644 index 0000000..bee96c3 --- /dev/null +++ b/default_config/ue.conf @@ -0,0 +1,264 @@ +##################################################################### +# srsUE configuration file +##################################################################### +# RF configuration +# +# dl_earfcn: Downlink EARFCN code. +# freq_offset: Uplink and Downlink optional frequency offset (in Hz) +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# dl_freq: Override DL frequency corresponding to dl_earfcn +# ul_freq: Override UL frequency corresponding to dl_earfcn +# nof_rx_ant: Number of RX antennas (Default 1, supported 1 or 2) +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. +# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" +# Default for bladeRF: "" +# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay +# from antenna to timestamp insertion. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +# continuous_tx: Transmit samples continuously to the radio or on bursts (auto/yes/no). +# Default is auto (yes for UHD, no for rest) +##################################################################### +[rf] +dl_earfcn = 3400 +freq_offset = 0 +tx_gain = 80 +#rx_gain = 40 + +#nof_rx_ant = 1 +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto +#continuous_tx = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/ue.pcap +nas_enable = false +nas_filename = /tmp/nas.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gw, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output. Can be set to stdout +# to print logs to standard output +# file_max_size: Maximum file size (in kilobytes). When passed, multiple files are created. +# If set to negative, a single log file will be created. +##################################################################### +[log] +all_level = warning +phy_lib_level = none +all_hex_limit = 32 +filename = /tmp/ue.log +file_max_size = -1 + +##################################################################### +# USIM configuration +# +# mode: USIM mode (soft/pcsc) +# algo: Authentication algorithm (xor/milenage) +# op: 128-bit Operator Variant Algorithm Configuration Field (hex) +# k: 128-bit subscriber key (hex) +# imsi: 15 digit International Mobile Subscriber Identity +# imei: 15 digit International Mobile Station Equipment Identity +# pin: PIN in case real SIM card is used +# reader: Specify card reader by it's name as listed by 'pcsc_scan'. If empty, try all available readers. +##################################################################### +[usim] +mode = soft +algo = xor +opc = 63BFA50EE6523365FF14C1F45F88737D +k = 00112233445566778899aabbccddeeff +imsi = 001010123456789 +imei = 353490069873319 +#reader = +#pin = 1234 + +##################################################################### +# RRC configuration +# +# ue_category: Sets UE category (range 1-5). Default: 4 +# feature_group: Hex value of the featureGroupIndicators field in the +# UECapabilityInformation message. Default 0xe6041000 +##################################################################### +[rrc] +#ue_category = 4 +#feature_group = 0xe6041000 + +##################################################################### +# NAS configuration +# +# apn: Set Access Point Name (APN) +# user: Username for CHAP authentication +# pass: Password for CHAP authentication +# force_imsi_attach: Whether to always perform an IMSI +##################################################################### +[nas] +#apn = internetinternet +#user = srsuser +#pass = srspass +#force_imsi_attach = false + +[gui] +enable = false + +##################################################################### +# Expert configuration options +# +# ip_netmask: Netmask of the tun_srsue device. Default: 255.255.255.0 +# rssi_sensor_enabled: Enable or disable RF frontend RSSI sensor. Required for RSRP metrics but +# can cause UHD instability for long-duration testing. Default true. +# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings +# prach_gain: PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only., +# Default is to use tx_gain in [rf] section. +# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. +# cqi_fixed: Fixes the reported CQI to a constant value. Default disabled. +# snr_ema_coeff: Sets the SNR exponential moving average coefficient (Default 0.1) +# snr_estim_alg: Sets the noise estimation algorithm. (Default refs) +# Options: pss: use difference between received and known pss signal, +# refs: use difference between noise references and noiseless (after filtering) +# empty: use empty subcarriers in the boarder of pss/sss signal +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# attach_enable_64qam: Enables PUSCH 64QAM modulation before attachment (Necessary for old +# Amarisoft LTE 100 eNodeB, disabled by default) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any +# non-negative real number to indicate a regularized zf coefficient. +# Default is MMSE. +# time_correct_period: Period for sampling time offset correction. Default is 10 (ue_sync.c), +# good for long channels. For best performance at highest SNR reduce it to 1. +# sfo_correct_disable: Disables phase correction before channel estimation to compensate for +# sampling frequency offset. Default is enabled. +# sfo_ema: EMA coefficient to average sample offsets used to compute SFO +# sfo_correct_period: Period in ms to correct sample time to adjust for SFO +# sss_algorithm: Selects the SSS estimation algorithm. Can choose between +# {full, partial, diff}. +# estimator_fil_auto: The channel estimator smooths the channel estimate with an adaptative filter. +# estimator_fil_stddev: Sets the channel estimator smooth gaussian filter standard deviation. +# estimator_fil_order: Sets the channel estimator smooth gaussian filter order (even values perform better). +# The taps are [w, 1-2w, w] +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# +# average_subframe_enabled: Averages in the time domain the channel estimates within 1 subframe. +# Needs accurate CFO correction. +# +# sic_pss_enabled: Applies Successive Interference Cancellation to PSS signals when searching for neighbour cells. +# Must be disabled if cells have identical channel and timing, for instance if generated from +# the same source. +# +# metrics_csv_enable: Write UE metrics to CSV file. +# +# metrics_csv_filename: File path to use for CSV metrics. +# +# cfo_integer_enabled: Enables integer CFO estimation and correction. This needs improvement +# and may lead to incorrect synchronization. Use with caution. +# cfo_correct_tol_hz: Tolerance (in Hz) for digial CFO compensation. Lower tolerance means that +# a new table will be generated more often. +# +# cfo_is_doppler: Assume detected CFO is doppler and correct the UL in the same direction. If disabled, the CFO is assumed +# to be caused by the local oscillator and the UL correction is in the opposite direction. Default assumes oscillator. +# cfo_pss_ema: CFO Exponential Moving Average coefficient for PSS estimation during TRACK. +# cfo_ref_ema: CFO Exponential Moving Average coefficient for RS estimation after PSS acquisition +# cfo_ref_mask: Bitmask for subframes on which to run RS estimation (set to 0 to disable, default sf=[1, 5]) +# cfo_loop_bw: CFO feedback loop bandwidth for samples from PSS or RS +# cfo_loop_pss_tol: Tolerance (in Hz) of the PSS estimation method. Below this value, PSS estimation does not feeds back the loop +# and RS estimations are used instead (when available) +# cfo_loop_ref_min: Tolerance (in Hz) of the RS estimation method. Below this value, RS estimation does not feeds back the loop +# cfo_loop_pss_timeout: After the PSS estimation is below cfo_loop_pss_tol for cfo_loop_pss_timeout times consecutively, +# RS adjustments are allowed. +# +# pdsch_csi_enabled: Stores the Channel State Information and uses it for weightening the softbits. It is only +# used in TM1. It is True by default. +# +##################################################################### +[expert] +#ip_netmask = 255.255.255.0 +#mbms_service = -1 +#rssi_sensor_enabled = false +#rx_gain_offset = 72 +#prach_gain = 30 +#cqi_max = 15 +#cqi_fixed = 10 +#snr_ema_coeff = 0.1 +#snr_estim_alg = refs +#pdsch_max_its = 4 +#attach_enable_64qam = false +#nof_phy_threads = 2 +#equalizer_mode = mmse +#time_correct_period = 5 +#sfo_correct_disable = false +#sfo_ema = 0.1 +#sfo_correct_period = 10 +#sss_algorithm = full +#estimator_fil_auto = false +#estimator_fil_stddev = 1.0 +#estimator_fil_order = 4 +#average_subframe_enabled = true +#sic_pss_enabled = true +#pregenerate_signals = false +#metrics_csv_enable = false +#metrics_csv_filename = /tmp/ue_metrics.csv +#pdsch_csi_enabled = true + +# CFO related values +#cfo_is_doppler = false +#cfo_integer_enabled = false +#cfo_correct_tol_hz = 1.0 +#cfo_pss_ema = 0.05 +#cfo_ref_mask = 1023 +#cfo_loop_bw_pss = 0.05 +#cfo_loop_bw_ref = 0.01 +#cfo_loop_pss_tol = 400 +#cfo_loop_ref_min = 0 +#cfo_loop_pss_conv = 20 + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97 diff --git a/default_config/user_db.csv b/default_config/user_db.csv new file mode 100644 index 0000000..54d61eb --- /dev/null +++ b/default_config/user_db.csv @@ -0,0 +1,16 @@ +# +# .csv to store UE's information in HSS +# Kept in the following format: "Name,IMSI,Key,OP_Type,OP,AMF,SQN,QCI" +# +# Name: Human readable name to help distinguish UE's. Ignored by the HSS +# IMSI: UE's IMSI value +# Key: UE's key, where other keys are derived from. Stored in hexadecimal +# OP_Type: Operator's code type, either OP or OPc +# OP/OPc: Operator Code/Cyphered Operator Code, stored in hexadecimal +# AMF: Authentication management field, stored in hexadecimal +# SQN: UE's Sequence number for freshness of the authentication +# QCI: QoS Class Identifier for the UE's default bearer. +# +# Note: Lines starting by '#' are ignored and will be overwritten +ue1,001010123456789,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,9001,000000001234,7 +ue2,001010123456780,00112233445566778899aabbccddeeff,opc,63bfa50ee6523365ff14c1f45f88737d,8000,000000001234,7 diff --git a/gbp.conf b/gbp.conf new file mode 100644 index 0000000..cec628c --- /dev/null +++ b/gbp.conf @@ -0,0 +1,2 @@ +[DEFAULT] +pristine-tar = True diff --git a/man/genmanpages.sh b/man/genmanpages.sh new file mode 100755 index 0000000..5b60de6 --- /dev/null +++ b/man/genmanpages.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +txt2man -d "${CHANGELOG_DATE}" -t SRSENB -s 8 srsenb.txt > srsenb.8 +txt2man -d "${CHANGELOG_DATE}" -t SRSEPC -s 8 srsepc.txt > srsepc.8 +txt2man -d "${CHANGELOG_DATE}" -t SRSUE -s 8 srsue.txt > srsue.8 diff --git a/man/srsenb.txt b/man/srsenb.txt new file mode 100644 index 0000000..e4fbc9b --- /dev/null +++ b/man/srsenb.txt @@ -0,0 +1,35 @@ +NAME + srsenb - Evolved Node B in LTE network for Software Defined Radios + +SYNOPSIS + srsenb [options] + +DESCRIPTION + + srsenb provides the Evolved Node B (eNodeB or eNB) part in the LTE network. + It is the hardware that is connected to the mobile phone network that + communicates directly wirelessly with mobile handsets (UEs), like a base + transceiver station (BTS) in GSM networks. + + Features: + + - Round Robin MAC scheduler with FAPI-like C++ API + - SR support + - Periodic and Aperiodic CQI feedback support + - Standard S1AP and GTP-U interfaces to the Core Network + - 150 Mbps DL in 20 MHz MIMO TM3/TM4 with commercial UEs + - 75 Mbps DL in SISO configuration with commercial UEs + - 50 Mbps UL in 20 MHz with commercial UEs + + An example configuration file is available in /usr/share/srslte/enb.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/man/srsepc.txt b/man/srsepc.txt new file mode 100644 index 0000000..9da3893 --- /dev/null +++ b/man/srsepc.txt @@ -0,0 +1,26 @@ +NAME + srsepc - light-weight LTE core network implementation with MME, HSS and S/P-GW + +SYNOPSIS + srsepc [options] + +DESCRIPTION + + srsepc provides a light-weight LTE core network implementation. It constist of a single binary, and provides the following features: + + - MME (Mobility Management Entity) with standard S1AP and GTP-U interface to eNB + - S/P-GW with standard SGi exposed as virtual network interface (TUN device) + - HSS (Home Subscriber Server) with configurable user database in CSV format + + An example configuration file is available in /usr/share/srslte/epc.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/man/srsue.txt b/man/srsue.txt new file mode 100644 index 0000000..46caa8f --- /dev/null +++ b/man/srsue.txt @@ -0,0 +1,32 @@ +NAME + srsue - User Equipment implementation for LTE + +SYNOPSIS + srsue [options] + +DESCRIPTION + + srsue provides a complete SDR LTE UE application featuring all layers from PHY to IP. + + Features: + + - Cell search and synchronization procedure for the UE + - Soft USIM supporting Milenage and XOR authentication + - Hard USIM support using PCSC framework + - Virtual network interface tun_srsue created upon network attach + - 150 Mbps DL in 20 MHz MIMO TM3/TM4 configuration in i7 Quad-Core CPU. + - 75 Mbps DL in 20 MHz SISO configuration in i7 Quad-Core CPU. + - 36 Mbps DL in 10 MHz SISO configuration in i5 Dual-Core CPU. + + An example configuration file is available in /usr/share/srslte/ue.conf.example + +OPTIONS + + -h/--help Produce help message + -v/--version Print version information and exit + + +AUTHOR + + This manual page was written by Ruben Undheim + for the Debian project (and may be used by others). diff --git a/patches/0001-Install-dir-for-private-library.patch b/patches/0001-Install-dir-for-private-library.patch new file mode 100644 index 0000000..303797a --- /dev/null +++ b/patches/0001-Install-dir-for-private-library.patch @@ -0,0 +1,19 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 13:12:07 +0000 +Subject: Install dir for private library + +--- + lib/src/phy/rf/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt +index 6cec5a6..d961106 100644 +--- a/lib/src/phy/rf/CMakeLists.txt ++++ b/lib/src/phy/rf/CMakeLists.txt +@@ -60,5 +60,5 @@ if(RF_FOUND) + endif (SOAPYSDR_FOUND) + + +- INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) ++ INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) + endif(RF_FOUND) diff --git a/patches/0002-Set-RPATH-to-usr-lib-srslte.patch b/patches/0002-Set-RPATH-to-usr-lib-srslte.patch new file mode 100644 index 0000000..d843c01 --- /dev/null +++ b/patches/0002-Set-RPATH-to-usr-lib-srslte.patch @@ -0,0 +1,51 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 13:25:31 +0000 +Subject: Set RPATH to /usr/lib/srslte + +--- + srsenb/src/CMakeLists.txt | 2 +- + srsepc/src/CMakeLists.txt | 4 ++-- + srsue/src/CMakeLists.txt | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt +index ef66c03..0d2cc8d 100644 +--- a/srsenb/src/CMakeLists.txt ++++ b/srsenb/src/CMakeLists.txt +@@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper + ${SCTP_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) +diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt +index 9fb56ce..f16c2bf 100644 +--- a/srsepc/src/CMakeLists.txt ++++ b/srsepc/src/CMakeLists.txt +@@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + if (RPATH) +- set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") +- set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") ++ set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) +diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt +index 0755a64..f4eac8b 100644 +--- a/srsue/src/CMakeLists.txt ++++ b/srsue/src/CMakeLists.txt +@@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac + ${Boost_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") ++ set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + endif (RPATH) + + install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) diff --git a/patches/0003-Spelling-error-fixes.patch b/patches/0003-Spelling-error-fixes.patch new file mode 100644 index 0000000..7c8019b --- /dev/null +++ b/patches/0003-Spelling-error-fixes.patch @@ -0,0 +1,200 @@ +From: Ruben Undheim +Date: Fri, 27 Jul 2018 18:16:22 +0000 +Subject: Spelling error fixes + +--- + srsenb/src/main.cc | 2 +- + srsenb/src/phy/txrx.cc | 2 +- + srsenb/src/upper/rrc.cc | 4 ++-- + srsenb/src/upper/s1ap.cc | 2 +- + srsepc/src/main.cc | 2 +- + srsepc/src/mbms-gw/mbms-gw.cc | 2 +- + srsepc/src/mme/mme_gtpc.cc | 2 +- + srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- + srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- + srsepc/src/mme/s1ap_nas_transport.cc | 2 +- + srsue/src/mac/proc_bsr.cc | 2 +- + srsue/src/main.cc | 2 +- + srsue/src/phy/phch_recv.cc | 2 +- + 13 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc +index 3622f8d..c0815ec 100644 +--- a/srsenb/src/main.cc ++++ b/srsenb/src/main.cc +@@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { + ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") +- ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") ++ ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") + ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") + ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") + ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") +diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc +index 7f6503b..d088550 100644 +--- a/srsenb/src/phy/txrx.cc ++++ b/srsenb/src/phy/txrx.cc +@@ -125,7 +125,7 @@ void txrx::run_thread() + srslte_timestamp_copy(&tx_time, &rx_time); + srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); + +- Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", ++ Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + tti, tx_mutex_cnt, + tx_time.full_secs, tx_time.frac_secs, + worker->get_id()); +diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc +index 2e481ce..1dc3359 100644 +--- a/srsenb/src/upper/rrc.cc ++++ b/srsenb/src/upper/rrc.cc +@@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) + if (users.count(rnti)) { + users[rnti].parse_ul_dcch(lcid, pdu); + } else { +- rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); ++ rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); + } + } + } +@@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) + rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); + } + } else { +- rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); ++ rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); + } + } + +diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc +index 1e2b259..1babe89 100644 +--- a/srsenb/src/upper/s1ap.cc ++++ b/srsenb/src/upper/s1ap.cc +@@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) + cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; + break; + default: +- cause += "unkown"; ++ cause += "unknown"; + break; + } + return cause; +diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc +index beed677..29f4659 100644 +--- a/srsepc/src/main.cc ++++ b/srsepc/src/main.cc +@@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { + ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") +- ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") ++ ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") + ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") + ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") + ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") +diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc +index b373df4..e87eed4 100644 +--- a/srsepc/src/mbms-gw/mbms-gw.cc ++++ b/srsepc/src/mbms-gw/mbms-gw.cc +@@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) + int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, + (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); + if(n<0){ +- m_mbms_gw_log->console("Error writting to M1-U socket.\n"); ++ m_mbms_gw_log->console("Error writing to M1-U socket.\n"); + } + else{ + m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); +diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc +index 6ef0e2c..94ab6f2 100644 +--- a/srsepc/src/mme/mme_gtpc.cc ++++ b/srsepc/src/mme/mme_gtpc.cc +@@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) + //Check UE Ipv4 address was allocated + if(cs_resp->paa_present != true) + { +- m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); ++ m_mme_gtpc_log->error("PDN Address Allocation not present\n"); + return; + } + if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) +diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +index b9d599d..97453d0 100644 +--- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc ++++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +@@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, + + struct in_addr addr; + addr.s_addr = htonl(sgw_s1u_ip); +- m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); ++ m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); + m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); +diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc +index 079eca1..dbd45cd 100644 +--- a/srsepc/src/mme/s1ap_mngmt_proc.cc ++++ b/srsepc/src/mme/s1ap_mngmt_proc.cc +@@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU + //Check matching PLMNs + if(enb_ctx.plmn!=m_s1ap->get_plmn()){ + +- m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); +- m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); ++ m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); ++ m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); + pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); + } + else{ +diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc +index 1bb871f..661c117 100644 +--- a/srsepc/src/mme/s1ap_nas_transport.cc ++++ b/srsepc/src/mme/s1ap_nas_transport.cc +@@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ + } + if(attach_req->additional_guti_present) + { +- m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); ++ m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); + } + if(attach_req->last_visited_registered_tai_present) + { +diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc +index bd49671..309c1f9 100644 +--- a/srsue/src/mac/proc_bsr.cc ++++ b/srsue/src/mac/proc_bsr.cc +@@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) + generate_bsr(bsr, 0); + bsr_sz = bsr->format==LONG_BSR?3:1; + if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { +- Debug("Grant is not enough to accomodate the BSR MAC CE\n"); ++ Debug("Grant is not enough to accommodate the BSR MAC CE\n"); + } else { + Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", + grant_size, total_data, bsr_sz); +diff --git a/srsue/src/main.cc b/srsue/src/main.cc +index f71f9ac..ea5b528 100644 +--- a/srsue/src/main.cc ++++ b/srsue/src/main.cc +@@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { + ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") + ("usim.k", bpo::value(&args->usim.k), "USIM K") + ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") +- ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") ++ ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") + + /* Expert section */ + ("expert.ip_netmask", +diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc +index 9f9ef94..e16ccfd 100644 +--- a/srsue/src/phy/phch_recv.cc ++++ b/srsue/src/phy/phch_recv.cc +@@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples + } + if (receiving == true) { + if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { +- Warning("Error writting to ringbuffer\n"); ++ Warning("Error writing to ringbuffer\n"); + receiving = false; + } else { + receive_cnt++; diff --git a/patches/0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch b/patches/0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch new file mode 100644 index 0000000..1933c0e --- /dev/null +++ b/patches/0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch @@ -0,0 +1,65 @@ +From: Ruben Undheim +Date: Fri, 23 Nov 2018 15:20:22 +0000 +Subject: Check whether code compiles instead of whether it runs + +--- + cmake/modules/FindSSE.cmake | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake +index 4c9673a..16b49e8 100644 +--- a/cmake/modules/FindSSE.cmake ++++ b/cmake/modules/FindSSE.cmake +@@ -3,6 +3,7 @@ + #endif() + + include(CheckCSourceRuns) ++include(CheckCSourceCompiles) + + option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) + option(ENABLE_AVX "Enable compile-time AVX support." ON) +@@ -16,7 +17,7 @@ if (ENABLE_SSE) + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-msse4.1") +- check_c_source_runs(" ++ check_c_source_compiles(" + #include + #include + +@@ -40,7 +41,7 @@ if (ENABLE_SSE) + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx") +- check_c_source_runs(" ++ check_c_source_compiles(" + #include + int main() + { +@@ -74,7 +75,7 @@ if (ENABLE_SSE) + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx2") +- check_c_source_runs(" ++ check_c_source_compiles(" + #include + int main() + { +@@ -108,7 +109,7 @@ if (ENABLE_SSE) + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mfma") +- check_c_source_runs(" ++ check_c_source_compiles(" + #include + int main() + { +@@ -143,7 +144,7 @@ if (ENABLE_SSE) + # + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) + set(CMAKE_REQUIRED_FLAGS "-mavx512f") +- check_c_source_runs(" ++ check_c_source_compiles(" + #include + int main() + { diff --git a/patches/0005-Patch-for-setting-custom-RPATH.patch b/patches/0005-Patch-for-setting-custom-RPATH.patch new file mode 100644 index 0000000..0869220 --- /dev/null +++ b/patches/0005-Patch-for-setting-custom-RPATH.patch @@ -0,0 +1,35 @@ +From: Ruben Undheim +Date: Fri, 23 Nov 2018 07:53:02 +0000 +Subject: Patch for setting custom RPATH + +--- + srsenb/src/CMakeLists.txt | 2 +- + srsue/src/CMakeLists.txt | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt +index 0d2cc8d..8aa735c 100644 +--- a/srsenb/src/CMakeLists.txt ++++ b/srsenb/src/CMakeLists.txt +@@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper + ${SCTP_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") ++ set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") + endif (RPATH) + + install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) +diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt +index f4eac8b..8057a77 100644 +--- a/srsue/src/CMakeLists.txt ++++ b/srsue/src/CMakeLists.txt +@@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac + ${Boost_LIBRARIES}) + + if (RPATH) +- set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") ++ set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") + endif (RPATH) + + install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) diff --git a/patches/0006-No-native-in-build.patch b/patches/0006-No-native-in-build.patch new file mode 100644 index 0000000..43deb8b --- /dev/null +++ b/patches/0006-No-native-in-build.patch @@ -0,0 +1,47 @@ +From: Ruben Undheim +Date: Fri, 23 Nov 2018 16:21:37 +0100 +Subject: No native in build + +--- + CMakeLists.txt | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index fad9252..e83977c 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) + + option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) + +-set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") + + + ######################################################################## +@@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) + endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") ++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + + find_package(SSE) + if (HAVE_AVX2) +@@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) + + if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + + if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") +@@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + + + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") diff --git a/patches/0007-Option-for-disabling-NEON-on-arm.patch b/patches/0007-Option-for-disabling-NEON-on-arm.patch new file mode 100644 index 0000000..05fa412 --- /dev/null +++ b/patches/0007-Option-for-disabling-NEON-on-arm.patch @@ -0,0 +1,37 @@ +From: Ruben Undheim +Date: Fri, 23 Nov 2018 17:00:28 +0100 +Subject: Option for disabling NEON on arm + +--- + CMakeLists.txt | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e83977c..e5faa80 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + endif(HAVE_SSE) + endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + ++ option(ENABLE_NEON "Enable NEON support for ARM" ON) + +- if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") +- message(STATUS "have ARM") +- set(HAVE_NEON "True") +- else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +- set(HAVE_NEON "False") +- endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") ++ if(ENABLE_NEON) ++ if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") ++ message(STATUS "have ARM") ++ set(HAVE_NEON "True") ++ else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") ++ set(HAVE_NEON "False") ++ endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") ++ endif() + set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) + + if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) diff --git a/patches/series b/patches/series new file mode 100644 index 0000000..49dac6f --- /dev/null +++ b/patches/series @@ -0,0 +1,7 @@ +0001-Install-dir-for-private-library.patch +0002-Set-RPATH-to-usr-lib-srslte.patch +0003-Spelling-error-fixes.patch +0005-Patch-for-setting-custom-RPATH.patch +0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch +0006-No-native-in-build.patch +0007-Option-for-disabling-NEON-on-arm.patch diff --git a/rules b/rules new file mode 100755 index 0000000..e839ecf --- /dev/null +++ b/rules @@ -0,0 +1,144 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE=1 + +include /usr/share/dpkg/pkg-info.mk + + +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +# To get cmake to pick up hardening flags when invoked directly: +CFLAGS+=$(CPPFLAGS) +CXXFLAGS+=$(CPPFLAGS) +# + + +CHANGELOG_DATE ?= $(shell LC_ALL=C date -u -d @$(SOURCE_DATE_EPOCH) +"%d %B %Y") + +multiarch = $(shell dpkg-architecture -qDEB_BUILD_MULTIARCH) +arch = $(shell dpkg-architecture -qDEB_BUILD_ARCH) + +%: + dh $@ + +override_dh_auto_configure-indep: + +override_dh_auto_configure-arch: + dh_auto_configure -a -- -DRPATH=1 -DCUSTOM_RPATH=/usr/lib/srslte -DENABLE_SSE=0 -DENABLE_AVX=0 -DENABLE_AVX2=0 -DENABLE_FMA=0 -DENABLE_AVX512=0 -DENABLE_NEON=0 -DDISABLE_SIMD=1 + if [ "${arch}" = "amd64" ] || \ + [ "${arch}" = "i386" ] ; then \ + mkdir -p obj_avx && cd obj_avx && cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=None -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_RUNSTATEDIR=/run "-GUnix Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_LIBDIR=lib/$(multiarch) -DRPATH=1 -DCUSTOM_RPATH=/usr/lib/srslte/avx -DENABLE_AVX512=0 -DENABLE_FMA=0 -DENABLE_AVX2=0 -DDISABLE_SIMD=1 .. ; cd .. ; \ + mkdir -p obj_avx2 && cd obj_avx2 && cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=None -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_RUNSTATEDIR=/run "-GUnix Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_LIBDIR=lib/$(multiarch) -DRPATH=1 -DCUSTOM_RPATH=/usr/lib/srslte/avx2 -DENABLE_AVX512=0 -DDISABLE_SIMD=1 .. ; cd .. ; \ + mkdir -p obj_sse4 && cd obj_sse4 && cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=None -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_RUNSTATEDIR=/run "-GUnix Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_LIBDIR=lib/$(multiarch) -DRPATH=1 -DCUSTOM_RPATH=/usr/lib/srslte/sse4 -DENABLE_AVX512=0 -DENABLE_FMA=0 -DENABLE_AVX2=0 -DENABLE_AVX=0 -DDISABLE_SIMD=1 .. ; cd .. ; \ + fi + # Build of NEON for ARM has been disabled since it FTBFS with the current version: + #if [ "${arch}" = "armel" ] || \ + # [ "${arch}" = "armhf" ] ; then \ + # mkdir -p obj_neon && cd obj_neon && cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=None -DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_EXPORT_NO_PACKAGE_REGISTRY=ON -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_RUNSTATEDIR=/run "-GUnix Makefiles" -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_LIBDIR=lib/$(multiarch) -DRPATH=1 -DCUSTOM_RPATH=/usr/lib/srslte/neon -DENABLE_AVX512=0 -DENABLE_FMA=0 -DENABLE_AVX2=0 -DDISABLE_SIMD=1 .. ; \ + #fi + + +override_dh_installman-indep: + +override_dh_installman-arch: + cd debian/man ; CHANGELOG_DATE="$(CHANGELOG_DATE)" ./genmanpages.sh + dh_installman -a + +override_dh_auto_clean: + dh_auto_clean + $(RM) debian/man/*.1 + $(RM) -r obj_avx + $(RM) -r obj_avx2 + $(RM) -r obj_sse4 + $(RM) -r obj_neon + $(RM) -r debian/tmp_avx + $(RM) -r debian/tmp_avx2 + $(RM) -r debian/tmp_sse4 + $(RM) -r debian/tmp_neon + + +override_dh_auto_build-indep: + +override_dh_auto_build-arch: + dh_auto_build -a + if [ "${arch}" = "amd64" ] || \ + [ "${arch}" = "i386" ] ; then \ + cd obj_avx && make ; cd .. ; \ + cd obj_avx2 && make ; cd .. ; \ + cd obj_sse4 && make ; cd .. ; \ + fi + #if [ "${arch}" = "armel" ] || \ + # [ "${arch}" = "armhf" ] ; then \ + # cd obj_neon && make ; \ + #fi + + +override_dh_auto_install-indep: + +override_dh_auto_install-arch: + dh_auto_install -a + if [ "${arch}" = "amd64" ] || \ + [ "${arch}" = "i386" ] ; then \ + cd obj_avx && mkdir -p ../debian/tmp_avx && make DESTDIR=../debian/tmp_avx install ; cd .. ; \ + cd obj_avx2 && mkdir -p ../debian/tmp_avx2 && make DESTDIR=../debian/tmp_avx2 install ; cd .. ; \ + cd obj_sse4 && mkdir -p ../debian/tmp_sse4 && make DESTDIR=../debian/tmp_sse4 install ; cd .. ; \ + fi + #if [ "${arch}" = "armel" ] || \ + # [ "${arch}" = "armhf" ] ; then \ + # cd obj_neon && mkdir -p ../debian/tmp_neon && make DESTDIR=../debian/tmp_neon install ; \ + #fi + +override_dh_install-arch: + dh_install -a + if [ "${arch}" = "amd64" ] || \ + [ "${arch}" = "i386" ] ; then \ + mkdir -p debian/srslte-core/usr/lib/srslte/avx ; \ + mkdir -p debian/srslte-core/usr/lib/srslte/avx2 ; \ + mkdir -p debian/srslte-core/usr/lib/srslte/sse4 ; \ + mkdir -p debian/srsenb/usr/lib/srslte/bin ; \ + mkdir -p debian/srsue/usr/lib/srslte/bin ; \ + cp debian/tmp_avx/usr/lib/srslte/libsrslte_rf.so debian/srslte-core/usr/lib/srslte/avx/ ; \ + cp debian/tmp_avx/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-avx ; \ + cp debian/tmp_avx/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-avx ; \ + cp debian/tmp_avx2/usr/lib/srslte/libsrslte_rf.so debian/srslte-core/usr/lib/srslte/avx2/ ; \ + cp debian/tmp_avx2/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-avx2 ; \ + cp debian/tmp_avx2/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-avx2 ; \ + cp debian/tmp_sse4/usr/lib/srslte/libsrslte_rf.so debian/srslte-core/usr/lib/srslte/sse4/ ; \ + cp debian/tmp_sse4/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-sse4 ; \ + cp debian/tmp_sse4/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-sse4 ; \ + mv debian/srsue/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-generic ; \ + mv debian/srsenb/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-generic ; \ + cp debian/script/srsenb debian/srsenb/usr/bin/srsenb ; \ + cp debian/script/srsue debian/srsue/usr/bin/srsue ; \ + fi + #if [ "${arch}" = "armel" ] || \ + # [ "${arch}" = "armhf" ] ; then \ + # mkdir -p debian/srslte-core/usr/lib/srslte/neon ; \ + # mkdir -p debian/srslte-core/usr/lib/srslte/bin ; \ + # cp debian/tmp_avx/usr/lib/srslte/libsrslte_rf.so debian/srslte-core/usr/lib/srslte/neon/ ; \ + # cp debian/tmp_avx/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-neon ; \ + # cp debian/tmp_avx/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-neon ; \ + # mv debian/srslte/usr/bin/srsue debian/srsue/usr/lib/srslte/bin/srsue-generic ; \ + # mv debian/srslte/usr/bin/srsenb debian/srsenb/usr/lib/srslte/bin/srsenb-generic ; \ + # cp debian/script/srsenb debian/srsenb/usr/bin/srsenb ; \ + # cp debian/script/srsue debian/srsue/usr/bin/srsue ; \ + #fi + + +override_dh_auto_test-indep: + +# Tests pass when building without SIMD extensions, but not with: +# - AVX extensions (without AVX2) +# - SSE4.2 extensions (without AVX2 and AVX) +override_dh_auto_test-arch: + dh_auto_test -a + #if [ "${arch}" = "amd64" ] || \ + # [ "${arch}" = "i386" ] ; then \ + # cd obj_avx && make test ; cd .. ; \ + # cd obj_avx2 && make test ; cd .. ; \ + # cd obj_sse4 && make test ; cd .. ; \ + #fi + +override_dh_installsystemd: + dh_installsystemd --no-enable --no-start diff --git a/script/srsenb b/script/srsenb new file mode 100755 index 0000000..b3ee475 --- /dev/null +++ b/script/srsenb @@ -0,0 +1,33 @@ +#!/bin/bash + +if [ -f /proc/cpuinfo ] ; then + grep -q " avx2 " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with AVX2 extension" + exec /usr/lib/srslte/bin/srsenb-avx2 $* + else + grep -q " avx " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with AVX extension" + exec /usr/lib/srslte/bin/srsenb-avx $* + else + grep -q " sse4_2 " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with SSE4.2 extension" + exec /usr/lib/srslte/bin/srsenb-sse4 $* + else + grep -q " neon " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with NEON extension" + exec /usr/lib/srslte/bin/srsenb-neon $* + else + echo "Starting without SIMD extensions" + exec /usr/lib/srslte/bin/srsenb-generic $* + fi + fi + fi + fi +else + echo "Starting without SIMD extensions" + exec /usr/lib/srslte/bin/srsenb-generic $* +fi diff --git a/script/srsue b/script/srsue new file mode 100755 index 0000000..7da6b65 --- /dev/null +++ b/script/srsue @@ -0,0 +1,33 @@ +#!/bin/bash + +if [ -f /proc/cpuinfo ] ; then + grep -q " avx2 " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with AVX2 extension" + exec /usr/lib/srslte/bin/srsue-avx2 $* + else + grep -q " avx " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with AVX extension" + exec /usr/lib/srslte/bin/srsue-avx $* + else + grep -q " sse4_2 " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with SSE4.2 extension" + exec /usr/lib/srslte/bin/srsue-sse4 $* + else + grep -q " neon " /proc/cpuinfo + if [ "$?" == "0" ] ; then + echo "Starting with NEON extension" + exec /usr/lib/srslte/bin/srsue-neon $* + else + echo "Starting without SIMD extensions" + exec /usr/lib/srslte/bin/srsue-generic $* + fi + fi + fi + fi +else + echo "Starting without SIMD extensions" + exec /usr/lib/srslte/bin/srsue-generic $* +fi diff --git a/source/format b/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/srsenb.install b/srsenb.install new file mode 100644 index 0000000..4f1b4ff --- /dev/null +++ b/srsenb.install @@ -0,0 +1,6 @@ +usr/bin/srsenb +debian/srsenb.service lib/systemd/system/ +debian/default_config/enb.conf etc/srslte/ +debian/default_config/sib.conf etc/srslte/ +debian/default_config/rr.conf etc/srslte/ +debian/default_config/drb.conf etc/srslte/ diff --git a/srsenb.manpages b/srsenb.manpages new file mode 100644 index 0000000..a130466 --- /dev/null +++ b/srsenb.manpages @@ -0,0 +1 @@ +debian/man/srsenb.8 diff --git a/srsenb.service b/srsenb.service new file mode 100644 index 0000000..9e58b0c --- /dev/null +++ b/srsenb.service @@ -0,0 +1,11 @@ +[Unit] +Description=Software Radio System ENB implementation + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/srsenb /etc/srslte/enb.conf +RestartSec=2 + +[Install] +WantedBy=multi-user.target diff --git a/srsepc.install b/srsepc.install new file mode 100644 index 0000000..30bf5af --- /dev/null +++ b/srsepc.install @@ -0,0 +1,6 @@ +usr/bin/srsepc +debian/srsepc.service lib/systemd/system/ +debian/default_config/epc.conf etc/srslte/ +debian/default_config/user_db.csv etc/srslte/ +debian/default_config/mbms.conf etc/srslte/ + diff --git a/srsepc.manpages b/srsepc.manpages new file mode 100644 index 0000000..bf7cc6b --- /dev/null +++ b/srsepc.manpages @@ -0,0 +1 @@ +debian/man/srsepc.8 diff --git a/srsepc.service b/srsepc.service new file mode 100644 index 0000000..8ac8b96 --- /dev/null +++ b/srsepc.service @@ -0,0 +1,11 @@ +[Unit] +Description=Software Radio System basic EPC implementation + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/srsepc /etc/srslte/epc.conf +RestartSec=2 + +[Install] +WantedBy=multi-user.target diff --git a/srslte-core.install b/srslte-core.install new file mode 100644 index 0000000..db948ca --- /dev/null +++ b/srslte-core.install @@ -0,0 +1,2 @@ +usr/lib/*/libsrslte_rf.so +usr/share/srslte diff --git a/srslte-dev.install b/srslte-dev.install new file mode 100644 index 0000000..d6ba448 --- /dev/null +++ b/srslte-dev.install @@ -0,0 +1,2 @@ +usr/lib/*/*.a +usr/include/srslte diff --git a/srsue.install b/srsue.install new file mode 100644 index 0000000..0e54978 --- /dev/null +++ b/srsue.install @@ -0,0 +1,3 @@ +usr/bin/srsue +debian/srsue.service lib/systemd/system/ +debian/default_config/ue.conf etc/srslte/ diff --git a/srsue.manpages b/srsue.manpages new file mode 100644 index 0000000..1732496 --- /dev/null +++ b/srsue.manpages @@ -0,0 +1 @@ +debian/man/srsue.8 diff --git a/srsue.service b/srsue.service new file mode 100644 index 0000000..d4109a3 --- /dev/null +++ b/srsue.service @@ -0,0 +1,11 @@ +[Unit] +Description=Software Radio Systems UE implementation + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/srsue /etc/srslte/ue.conf +RestartSec=2 + +[Install] +WantedBy=multi-user.target diff --git a/tests/control b/tests/control new file mode 100644 index 0000000..6134ab8 --- /dev/null +++ b/tests/control @@ -0,0 +1,2 @@ +Tests: srsenb-help, srsue-help, srsepc-help +Depends: srslte diff --git a/tests/srsenb-help b/tests/srsenb-help new file mode 100755 index 0000000..4a5b5f9 --- /dev/null +++ b/tests/srsenb-help @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +srsenb -h 2> /dev/null + +echo "done" diff --git a/tests/srsepc-help b/tests/srsepc-help new file mode 100755 index 0000000..0c99175 --- /dev/null +++ b/tests/srsepc-help @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +srsepc -h 2> /dev/null + +echo "done" diff --git a/tests/srsue-help b/tests/srsue-help new file mode 100755 index 0000000..09caf90 --- /dev/null +++ b/tests/srsue-help @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +srsue -h 2> /dev/null + +echo "done" diff --git a/upstream/metadata b/upstream/metadata new file mode 100644 index 0000000..d606377 --- /dev/null +++ b/upstream/metadata @@ -0,0 +1,6 @@ +--- +Bug-Database: https://github.com/srsLTE/srsLTE/issues +Bug-Submit: https://github.com/srsLTE/srsLTE/issues/new +Name: srsLTE +Repository: https://github.com/srsLTE/srsLTE.git +Repository-Browse: https://github.com/srsLTE/srsLTE diff --git a/watch b/watch new file mode 100644 index 0000000..af69794 --- /dev/null +++ b/watch @@ -0,0 +1,4 @@ +version=3 +opts=dversionmangle=s/\./_/g,\ +oversionmangle=s/_/\./g \ +https://github.com/srsLTE/srsLTE/releases /srsLTE/srsLTE/archive/release_(\d\S+)\.tar\.(?:bz2|gz|xz) -- cgit v1.2.3 From 01dd90f2d4c4dd8195e734ddbfdb1e655566f00a Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From f4e68530ccd408796a8aed3ad7f3d6d75d8209ad Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 68716cb94a5855f7570f6134698a7fee4827ef3b Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From 7609c507e1127b906622ebd8a112915481fcdb6e Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 07:53:02 +0000 Subject: Patch for setting custom RPATH Gbp-Pq: Name 0005-Patch-for-setting-custom-RPATH.patch --- srsenb/src/CMakeLists.txt | 2 +- srsue/src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 0d2cc8d..8aa735c 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index f4eac8b..8057a77 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 007493bfe3c1451261e84f164c3c0962c0364eea Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 15:20:22 +0000 Subject: Check whether code compiles instead of whether it runs Gbp-Pq: Name 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch --- cmake/modules/FindSSE.cmake | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 4c9673a..16b49e8 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -3,6 +3,7 @@ #endif() include(CheckCSourceRuns) +include(CheckCSourceCompiles) option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) option(ENABLE_AVX "Enable compile-time AVX support." ON) @@ -16,7 +17,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-msse4.1") - check_c_source_runs(" + check_c_source_compiles(" #include #include @@ -40,7 +41,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -74,7 +75,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx2") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -108,7 +109,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mfma") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -143,7 +144,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx512f") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { -- cgit v1.2.3 From 5b5dcf1c3acd7779f2623be61bc10eed3a4e1c76 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 16:21:37 +0100 Subject: No native in build Gbp-Pq: Name 0006-No-native-in-build.patch --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..e83977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") find_package(SSE) if (HAVE_AVX2) @@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") message(STATUS "have ARM") set(HAVE_NEON "True") else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") -- cgit v1.2.3 From 93cb5f6fce4dba21d9e7ad5c6fa128a88fa9ce69 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 17:00:28 +0100 Subject: Option for disabling NEON on arm Gbp-Pq: Name 0007-Option-for-disabling-NEON-on-arm.patch --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83977c..ddade72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(HAVE_SSE) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + option(ENABLE_NEON "Enable NEON support for ARM" ON) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(ENABLE_NEON) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) -- cgit v1.2.3 From 55258c080b95d53e01c8bc9c62b9fb3be974ab3f Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:12:07 +0000 Subject: Install dir for private library Gbp-Pq: Name 0001-Install-dir-for-private-library.patch --- lib/src/phy/rf/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index 6cec5a6..d961106 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -60,5 +60,5 @@ if(RF_FOUND) endif (SOAPYSDR_FOUND) - INSTALL(TARGETS srslte_rf DESTINATION ${LIBRARY_DIR}) + INSTALL(TARGETS srslte_rf DESTINATION lib/srslte) endif(RF_FOUND) -- cgit v1.2.3 From 110455812dcfb3943e4b665c98938d7087471538 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 13:25:31 +0000 Subject: Set RPATH to /usr/lib/srslte Gbp-Pq: Name 0002-Set-RPATH-to-usr-lib-srslte.patch --- srsenb/src/CMakeLists.txt | 2 +- srsepc/src/CMakeLists.txt | 4 ++-- srsue/src/CMakeLists.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index ef66c03..0d2cc8d 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 9fb56ce..f16c2bf 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -56,8 +56,8 @@ target_link_libraries(srsmbms srsepc_mbms_gw ${LIBCONFIGPP_LIBRARIES} ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") - set_target_properties(srsmbms PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsepc PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsmbms PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 0755a64..f4eac8b 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 799d6409e903e6b9eceac1a4486ac2fbda1f2d8d Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 27 Jul 2018 18:16:22 +0000 Subject: Spelling error fixes Gbp-Pq: Name 0003-Spelling-error-fixes.patch --- srsenb/src/main.cc | 2 +- srsenb/src/phy/txrx.cc | 2 +- srsenb/src/upper/rrc.cc | 4 ++-- srsenb/src/upper/s1ap.cc | 2 +- srsepc/src/main.cc | 2 +- srsepc/src/mbms-gw/mbms-gw.cc | 2 +- srsepc/src/mme/mme_gtpc.cc | 2 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 2 +- srsepc/src/mme/s1ap_mngmt_proc.cc | 4 ++-- srsepc/src/mme/s1ap_nas_transport.cc | 2 +- srsue/src/mac/proc_bsr.cc | 2 +- srsue/src/main.cc | 2 +- srsue/src/phy/phch_recv.cc | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 3622f8d..c0815ec 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -75,7 +75,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) { ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.s1c_bind_addr", bpo::value(&args->enb.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index 7f6503b..d088550 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -125,7 +125,7 @@ void txrx::run_thread() srslte_timestamp_copy(&tx_time, &rx_time); srslte_timestamp_add(&tx_time, 0, HARQ_DELAY_MS*1e-3); - Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", + Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d\n", tti, tx_mutex_cnt, tx_time.full_secs, tx_time.frac_secs, worker->get_id()); diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc index 2e481ce..1dc3359 100644 --- a/srsenb/src/upper/rrc.cc +++ b/srsenb/src/upper/rrc.cc @@ -550,7 +550,7 @@ void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) if (users.count(rnti)) { users[rnti].parse_ul_dcch(lcid, pdu); } else { - rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + rrc_log->error("Processing %s: Unknown rnti=0x%x\n", rb_id_text[lcid], rnti); } } } @@ -574,7 +574,7 @@ void rrc::process_rl_failure(uint16_t rnti) rrc_log->info("%d Radio-Link failure detected rnti=0x%x\n", n_rfl, rnti); } } else { - rrc_log->error("Radio-Link failure detected for uknown rnti=0x%x\n", rnti); + rrc_log->error("Radio-Link failure detected for unknown rnti=0x%x\n", rnti); } } diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc index 1e2b259..1babe89 100644 --- a/srsenb/src/upper/s1ap.cc +++ b/srsenb/src/upper/s1ap.cc @@ -1033,7 +1033,7 @@ std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; break; default: - cause += "unkown"; + cause += "unknown"; break; } return cause; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index beed677..29f4659 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -106,7 +106,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) { ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") - ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connection") ("mme.dns_addr", bpo::value(&dns_addr)->default_value("8.8.8.8"),"IP address of the DNS server for the UEs") ("mme.apn", bpo::value(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index b373df4..e87eed4 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -316,7 +316,7 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg) int n = sendto(m_m1u, msg->msg, msg->N_bytes, 0, (sockaddr *) &m_m1u_multi_addr, sizeof(struct sockaddr)); if(n<0){ - m_mbms_gw_log->console("Error writting to M1-U socket.\n"); + m_mbms_gw_log->console("Error writing to M1-U socket.\n"); } else{ m_mbms_gw_log->debug("Sent %d Bytes\n", msg->N_bytes); diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 6ef0e2c..94ab6f2 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -212,7 +212,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) //Check UE Ipv4 address was allocated if(cs_resp->paa_present != true) { - m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); + m_mme_gtpc_log->error("PDN Address Allocation not present\n"); return; } if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b9d599d..97453d0 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -191,7 +191,7 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, struct in_addr addr; addr.s_addr = htonl(sgw_s1u_ip); - m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Sent Initial Context Setup Request. E-RAB id %d \n",erab_ctx_req->e_RAB_ID.E_RAB_ID); m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", sgw_s1u_teid,inet_ntoa(addr)); m_s1ap_log->console("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %d\n",in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID, in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID); m_s1ap_log->console("Initial Context Setup Request -- E-RAB id %d\n",erab_ctx_req->e_RAB_ID.E_RAB_ID); diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 079eca1..dbd45cd 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -94,8 +94,8 @@ s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRU //Check matching PLMNs if(enb_ctx.plmn!=m_s1ap->get_plmn()){ - m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); - m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->console("Sending S1 Setup Failure - Unknown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unknown PLMN\n"); pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); } else{ diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 1bb871f..661c117 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -2083,7 +2083,7 @@ s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQ } if(attach_req->additional_guti_present) { - m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + m_s1ap_log->warning("NAS attach request: Additional GUTI present, but not handled.\n"); } if(attach_req->last_visited_registered_tai_present) { diff --git a/srsue/src/mac/proc_bsr.cc b/srsue/src/mac/proc_bsr.cc index bd49671..309c1f9 100644 --- a/srsue/src/mac/proc_bsr.cc +++ b/srsue/src/mac/proc_bsr.cc @@ -308,7 +308,7 @@ bool bsr_proc::need_to_send_bsr_on_ul_grant(uint32_t grant_size, bsr_t *bsr) generate_bsr(bsr, 0); bsr_sz = bsr->format==LONG_BSR?3:1; if (total_data <= (int)grant_size && total_data + 1 + bsr_sz > grant_size) { - Debug("Grant is not enough to accomodate the BSR MAC CE\n"); + Debug("Grant is not enough to accommodate the BSR MAC CE\n"); } else { Debug("BSR: Including Regular BSR: grant_size=%d, total_data=%d, bsr_sz=%d\n", grant_size, total_data, bsr_sz); diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f71f9ac..ea5b528 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -137,7 +137,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) { ("usim.imei", bpo::value(&args->usim.imei), "USIM IMEI") ("usim.k", bpo::value(&args->usim.k), "USIM K") ("usim.pin", bpo::value(&args->usim.pin), "PIN in case real SIM card is used") - ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specifiy PCSC reader. Default: Try all available readers.") + ("usim.reader", bpo::value(&args->usim.reader)->default_value(""), "Force specify PCSC reader. Default: Try all available readers.") /* Expert section */ ("expert.ip_netmask", diff --git a/srsue/src/phy/phch_recv.cc b/srsue/src/phy/phch_recv.cc index 9f9ef94..e16ccfd 100644 --- a/srsue/src/phy/phch_recv.cc +++ b/srsue/src/phy/phch_recv.cc @@ -1616,7 +1616,7 @@ void phch_recv::intra_measure::write(uint32_t tti, cf_t *data, uint32_t nsamples } if (receiving == true) { if (srslte_ringbuffer_write(&ring_buffer, data, nsamples*sizeof(cf_t)) < (int) (nsamples*sizeof(cf_t))) { - Warning("Error writting to ringbuffer\n"); + Warning("Error writing to ringbuffer\n"); receiving = false; } else { receive_cnt++; -- cgit v1.2.3 From e5719f1141ecf4d64b49543b7f83e15edad72066 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 07:53:02 +0000 Subject: Patch for setting custom RPATH Gbp-Pq: Name 0005-Patch-for-setting-custom-RPATH.patch --- srsenb/src/CMakeLists.txt | 2 +- srsue/src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 0d2cc8d..8aa735c 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(srsenb srsenb_upper ${SCTP_LIBRARIES}) if (RPATH) - set_target_properties(srsenb PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsenb PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index f4eac8b..8057a77 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -43,7 +43,7 @@ target_link_libraries(srsue srsue_mac ${Boost_LIBRARIES}) if (RPATH) - set_target_properties(srsue PROPERTIES INSTALL_RPATH "/usr/lib/srslte") + set_target_properties(srsue PROPERTIES INSTALL_RPATH "${CUSTOM_RPATH}") endif (RPATH) install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) -- cgit v1.2.3 From 8ea78c9d3b7885aa5cdaec790783c84f68bcfbc5 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 15:20:22 +0000 Subject: Check whether code compiles instead of whether it runs Gbp-Pq: Name 0005-Check-whether-code-compiles-instead-of-whether-it-ru.patch --- cmake/modules/FindSSE.cmake | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 4c9673a..16b49e8 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -3,6 +3,7 @@ #endif() include(CheckCSourceRuns) +include(CheckCSourceCompiles) option(ENABLE_SSE "Enable compile-time SSE4.1 support." ON) option(ENABLE_AVX "Enable compile-time AVX support." ON) @@ -16,7 +17,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-msse4.1") - check_c_source_runs(" + check_c_source_compiles(" #include #include @@ -40,7 +41,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -74,7 +75,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx2") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -108,7 +109,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mfma") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { @@ -143,7 +144,7 @@ if (ENABLE_SSE) # if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG ) set(CMAKE_REQUIRED_FLAGS "-mavx512f") - check_c_source_runs(" + check_c_source_compiles(" #include int main() { -- cgit v1.2.3 From 4770c9ca98db7106daf67ce9f93c55a0a21669c9 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 16:21:37 +0100 Subject: No native in build Gbp-Pq: Name 0006-No-native-in-build.patch --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad9252..e83977c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ option(ENABLE_ASAN "Enable gcc address sanitizer" OFF) option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) -set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") ######################################################################## @@ -246,7 +245,7 @@ macro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable -Wformat -Wmissing-field-initializers -Wtype-limits -std=c++03") find_package(SSE) if (HAVE_AVX2) @@ -263,7 +262,7 @@ endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clan ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR_INCOMPATIBLE) if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Winline -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -D_GNU_SOURCE") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -311,7 +310,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=native -DIS_ARM -DHAVE_NEON") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") message(STATUS "have ARM") set(HAVE_NEON "True") else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") -- cgit v1.2.3 From 39dbd5db3c1491b2fe82cabaf688685ac0b9fa38 Mon Sep 17 00:00:00 2001 From: Ruben Undheim Date: Fri, 23 Nov 2018 17:00:28 +0100 Subject: Option for disabling NEON on arm Gbp-Pq: Name 0007-Option-for-disabling-NEON-on-arm.patch --- CMakeLists.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e83977c..ddade72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,14 +308,17 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif(HAVE_SSE) endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + option(ENABLE_NEON "Enable NEON support for ARM" ON) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") - message(STATUS "have ARM") - set(HAVE_NEON "True") - else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - set(HAVE_NEON "False") - endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(ENABLE_NEON) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -DIS_ARM -DHAVE_NEON") + message(STATUS "have ARM") + set(HAVE_NEON "True") + else(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + set(HAVE_NEON "False") + endif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS}) if(NOT HAVE_SSE AND NOT HAVE_NEON AND NOT DISABLE_SIMD) -- cgit v1.2.3