<p>pespin has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-pcu/+/22385">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">WIP: Introduce NACC support<br><br>TODO: timeout timers to abort NACC?<br><br>Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca<br>---<br>M configure.ac<br>M src/Makefile.am<br>M src/bts.cpp<br>M src/bts.h<br>M src/encoding.cpp<br>M src/encoding.h<br>M src/gprs_bssgp_rim.c<br>M src/gprs_debug.cpp<br>M src/gprs_debug.h<br>M src/gprs_ms.c<br>M src/gprs_ms.h<br>M src/gprs_pcu.c<br>M src/gprs_pcu.h<br>M src/gprs_rlcmac_sched.cpp<br>A src/nacc_fsm.c<br>A src/nacc_fsm.h<br>A src/neigh_cache.c<br>A src/neigh_cache.h<br>M src/pcu_vty.c<br>M src/pdch.cpp<br>M src/pdch.h<br>M src/tbf.cpp<br>M src/tbf.h<br>M tests/Makefile.am<br>24 files changed, 1,347 insertions(+), 5 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-pcu refs/changes/85/22385/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure.ac b/configure.ac</span><br><span>index 2e99a15..11de328 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -84,6 +84,7 @@</span><br><span> dnl checks for libraries</span><br><span> PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.4.0)</span><br><span> PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.4.0)</span><br><span style="color: hsl(120, 100%, 40%);">+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.4.0)</span><br><span> PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.4.0)</span><br><span> PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 1.4.0)</span><br><span> </span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index f85a456..eefbea1 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -19,7 +19,7 @@</span><br><span> #</span><br><span> </span><br><span> AUTOMAKE_OPTIONS = subdir-objects</span><br><span style="color: hsl(0, 100%, 40%);">-AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS)</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS)</span><br><span> </span><br><span> if ENABLE_SYSMODSP</span><br><span> AM_CPPFLAGS += -DENABLE_DIRECT_PHY</span><br><span>@@ -55,6 +55,8 @@</span><br><span>        pcu_vty.c \</span><br><span>  pcu_vty_functions.cpp \</span><br><span>      mslot_class.c \</span><br><span style="color: hsl(120, 100%, 40%);">+       nacc_fsm.c \</span><br><span style="color: hsl(120, 100%, 40%);">+  neigh_cache.c \</span><br><span>      tbf.cpp \</span><br><span>    tbf_ul.cpp \</span><br><span>         tbf_dl.cpp \</span><br><span>@@ -91,6 +93,8 @@</span><br><span>     pcu_vty.h \</span><br><span>  pcu_vty_functions.h \</span><br><span>        mslot_class.h \</span><br><span style="color: hsl(120, 100%, 40%);">+       nacc_fsm.h \</span><br><span style="color: hsl(120, 100%, 40%);">+  neigh_cache.h \</span><br><span>      tbf.h \</span><br><span>      tbf_ul.h \</span><br><span>   tbf_dl.h \</span><br><span>@@ -143,6 +147,7 @@</span><br><span>     libgprs.la \</span><br><span>         $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOCORE_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(COMMON_LA)</span><br><span> endif</span><br><span>@@ -191,6 +196,7 @@</span><br><span>  libgprs.la \</span><br><span>         $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOCORE_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(COMMON_LA)</span><br><span> </span><br><span>diff --git a/src/bts.cpp b/src/bts.cpp</span><br><span>index b5bb3a2..fb7de64 100644</span><br><span>--- a/src/bts.cpp</span><br><span>+++ b/src/bts.cpp</span><br><span>@@ -140,6 +140,9 @@</span><br><span>      { "pkt:ul_assignment",                "Packet UL Assignment "},</span><br><span>  { "pkt:access_reject",          "Packet Access Reject "},</span><br><span>        { "pkt:dl_assignment",                "Packet DL Assignment "},</span><br><span style="color: hsl(120, 100%, 40%);">+   { "pkt:cell_chg_notification",        "Packet Cell Change Notification"},</span><br><span style="color: hsl(120, 100%, 40%);">+ { "pkt:cell_chg_continue",    "Packet Cell Change Continue"},</span><br><span style="color: hsl(120, 100%, 40%);">+     { "pkt:neigh_cell_data",      "Packet Neighbour Cell Data"},</span><br><span>     { "ul:control",                       "UL control Block     "},</span><br><span>  { "ul:assignment_poll_timeout",       "UL Assign Timeout    "},</span><br><span>  { "ul:assignment_failed",     "UL Assign Failed     "},</span><br><span>@@ -1284,3 +1287,8 @@</span><br><span> {</span><br><span>     return bts_ms_store(bts)->get_ms(0, 0, imsi);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return bts_ms_store(bts)->ms_list();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/bts.h b/src/bts.h</span><br><span>index 7f437e3..ea15caf 100644</span><br><span>--- a/src/bts.h</span><br><span>+++ b/src/bts.h</span><br><span>@@ -2,6 +2,7 @@</span><br><span>  *</span><br><span>  * Copyright (C) 2012 Ivan Klyuchnikov</span><br><span>  * Copyright (C) 2013 by Holger Hans Peter Freyther</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de</span><br><span>  *</span><br><span>  * This program is free software; you can redistribute it and/or</span><br><span>  * modify it under the terms of the GNU General Public License</span><br><span>@@ -125,6 +126,9 @@</span><br><span>       CTR_PKT_UL_ASSIGNMENT,</span><br><span>       CTR_PKT_ACCESS_REJ,</span><br><span>  CTR_PKT_DL_ASSIGNMENT,</span><br><span style="color: hsl(120, 100%, 40%);">+        CTR_PKT_CELL_CHG_NOTIFICATION,</span><br><span style="color: hsl(120, 100%, 40%);">+        CTR_PKT_CELL_CHG_CONTINUE,</span><br><span style="color: hsl(120, 100%, 40%);">+    CTR_PKT_NEIGH_CELL_DATA,</span><br><span>     CTR_RLC_RECV_CONTROL,</span><br><span>        CTR_PUA_POLL_TIMEDOUT,</span><br><span>       CTR_PUA_POLL_FAILED,</span><br><span>@@ -337,6 +341,7 @@</span><br><span> void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);</span><br><span> void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);</span><br><span> bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);</span><br><span style="color: hsl(120, 100%, 40%);">+const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts);</span><br><span> #ifdef __cplusplus</span><br><span> }</span><br><span> #endif</span><br><span>diff --git a/src/encoding.cpp b/src/encoding.cpp</span><br><span>index f605ca2..24e1d68 100644</span><br><span>--- a/src/encoding.cpp</span><br><span>+++ b/src/encoding.cpp</span><br><span>@@ -1731,3 +1731,56 @@</span><br><span>         bitvec_write_field(dest, &wp, 5, 8);  //  WAIT_INDICATION value</span><br><span>  bitvec_write_field(dest, &wp, 0, 1);  //  WAIT_INDICATION size in seconds</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,</span><br><span style="color: hsl(120, 100%, 40%);">+          bool tfi_is_dl, uint8_t tfi, uint8_t container_id,</span><br><span style="color: hsl(120, 100%, 40%);">+            uint8_t container_idx, PNCDContainer_t *container)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       block->PAYLOAD_TYPE = 0x1;  // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header</span><br><span style="color: hsl(120, 100%, 40%);">+       block->RRBP         = 0;  // 0: N+13</span><br><span style="color: hsl(120, 100%, 40%);">+       block->SP           = 0; // RRBP field is valid</span><br><span style="color: hsl(120, 100%, 40%);">+    block->USF          = 0x0;  // Uplink state flag</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ block->u.Packet_Neighbour_Cell_Data.MESSAGE_TYPE = MT_PACKET_NEIGHBOUR_CELL_DATA;</span><br><span style="color: hsl(120, 100%, 40%);">+  block->u.Packet_Neighbour_Cell_Data.PAGE_MODE    = 0x0;  // Normal Paging</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        block->u.Packet_Neighbour_Cell_Data.Global_TFI.UnionType    = tfi_is_dl; // 0=UPLINK TFI, 1=DL TFI</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tfi_is_dl) {</span><br><span style="color: hsl(120, 100%, 40%);">+              block->u.Packet_Neighbour_Cell_Data.Global_TFI.u.DOWNLINK_TFI = tfi;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              block->u.Packet_Neighbour_Cell_Data.Global_TFI.u.UPLINK_TFI = tfi;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     block->u.Packet_Neighbour_Cell_Data.CONTAINER_ID = container_id;</span><br><span style="color: hsl(120, 100%, 40%);">+   block->u.Packet_Neighbour_Cell_Data.spare = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     block->u.Packet_Neighbour_Cell_Data.CONTAINER_INDEX = container_idx;</span><br><span style="color: hsl(120, 100%, 40%);">+       block->u.Packet_Neighbour_Cell_Data.Container = *container;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void write_packet_cell_change_continue(RlcMacDownlink_t *block,</span><br><span style="color: hsl(120, 100%, 40%);">+          bool tfi_is_dl, uint8_t tfi, bool exist_id,</span><br><span style="color: hsl(120, 100%, 40%);">+           uint16_t arfcn, uint8_t bsic, uint8_t container_id)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      block->PAYLOAD_TYPE = 0x1;  // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header</span><br><span style="color: hsl(120, 100%, 40%);">+       block->RRBP         = 0;  // 0: N+13</span><br><span style="color: hsl(120, 100%, 40%);">+       block->SP           = 0; // RRBP field is valid</span><br><span style="color: hsl(120, 100%, 40%);">+    block->USF          = 0x0;  // Uplink state flag</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ block->u.Packet_Cell_Change_Continue.MESSAGE_TYPE = MT_PACKET_CELL_CHANGE_CONTINUE;</span><br><span style="color: hsl(120, 100%, 40%);">+        block->u.Packet_Cell_Change_Continue.PAGE_MODE    = 0x0;  // Normal Paging</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       block->u.Packet_Cell_Change_Continue.Global_TFI.UnionType    = tfi_is_dl; // 0=UPLINK TFI, 1=DL TFI</span><br><span style="color: hsl(120, 100%, 40%);">+        if (tfi_is_dl) {</span><br><span style="color: hsl(120, 100%, 40%);">+              block->u.Packet_Cell_Change_Continue.Global_TFI.u.DOWNLINK_TFI = tfi;</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              block->u.Packet_Cell_Change_Continue.Global_TFI.u.UPLINK_TFI = tfi;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   block->u.Packet_Cell_Change_Continue.Exist_ID = exist_id;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (exist_id) {</span><br><span style="color: hsl(120, 100%, 40%);">+               block->u.Packet_Cell_Change_Continue.ARFCN = arfcn;</span><br><span style="color: hsl(120, 100%, 40%);">+                block->u.Packet_Cell_Change_Continue.BSIC = bsic;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     block->u.Packet_Cell_Change_Continue.CONTAINER_ID = container_id;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/encoding.h b/src/encoding.h</span><br><span>index da63a61..4ebfa35 100644</span><br><span>--- a/src/encoding.h</span><br><span>+++ b/src/encoding.h</span><br><span>@@ -22,17 +22,22 @@</span><br><span> </span><br><span> #include <stdint.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef __cplusplus</span><br><span> extern "C" {</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> #include <osmocom/gsm/l1sap.h></span><br><span> #include "coding_scheme.h"</span><br><span> #include "gsm_rlcmac.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef __cplusplus</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> struct gprs_rlcmac_tbf;</span><br><span> struct bitvec;</span><br><span> struct gprs_llc;</span><br><span> struct gprs_rlc_data_block_info;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef __cplusplus</span><br><span> /**</span><br><span>  * I help with encoding data into CSN1 messages.</span><br><span>  * TODO: Nobody can remember a function signature like this. One should</span><br><span>@@ -108,3 +113,21 @@</span><br><span>            const struct gprs_rlc_data_block_info *rdbi,</span><br><span>                 int *offset, int *num_chunks, uint8_t *data_block);</span><br><span> };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* ifdef __cplusplus */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef __cplusplus</span><br><span style="color: hsl(120, 100%, 40%);">+extern "C" {</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,</span><br><span style="color: hsl(120, 100%, 40%);">+           bool tfi_is_dl, uint8_t tfi, uint8_t container_id,</span><br><span style="color: hsl(120, 100%, 40%);">+            uint8_t container_idx, PNCDContainer_t *container);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void write_packet_cell_change_continue(RlcMacDownlink_t *block,</span><br><span style="color: hsl(120, 100%, 40%);">+                bool tfi_is_dl, uint8_t tfi, bool exist_id,</span><br><span style="color: hsl(120, 100%, 40%);">+           uint16_t arfcn, uint8_t bsic, uint8_t container_id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef __cplusplus</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>diff --git a/src/gprs_bssgp_rim.c b/src/gprs_bssgp_rim.c</span><br><span>index cf43a8f..3c273ec 100644</span><br><span>--- a/src/gprs_bssgp_rim.c</span><br><span>+++ b/src/gprs_bssgp_rim.c</span><br><span>@@ -26,6 +26,9 @@</span><br><span> </span><br><span> #include "gprs_debug.h"</span><br><span> #include "gprs_pcu.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "bts.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "gprs_ms.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "nacc_fsm.h"</span><br><span> </span><br><span> #define LOGPRIM(nsei, level, fmt, args...) \</span><br><span>  LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args)</span><br><span>@@ -97,6 +100,51 @@</span><br><span>   return false;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_ran_info_response(struct osmo_bssgp_prim *bp, struct gprs_rlcmac_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct msgb *msg = bp->oph.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct bssgp_ran_inf_rim_cont *ran_info = &pdu->decoded.rim_cont;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bssgp_ran_inf_app_cont_nacc *nacc;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct si_cache_value val;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct si_cache_entry *entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+       int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ran_info->app_err) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DRIM, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                     "RAN-INFO answered with an app error! cause=0x%x\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                ran_info->u.app_err_cont_nacc.nacc_cause);</span><br><span style="color: hsl(120, 100%, 40%);">+            return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   nacc = &ran_info->u.app_cont_nacc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGP(DRIM, LOGL_INFO, "Received RAN-INFO type=%sBCCH num_si=%d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+       nacc->type_psi ? "B" : "", nacc->num_si);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    val.type_psi = nacc->type_psi;</span><br><span style="color: hsl(120, 100%, 40%);">+     val.si_len = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       for (i = 0; i < nacc->num_si; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+            size_t len = val.type_psi ? BSSGP_RIM_PSI_LEN : BSSGP_RIM_SI_LEN;</span><br><span style="color: hsl(120, 100%, 40%);">+             memcpy(&val.si_buf[val.si_len], nacc->si[i], len);</span><br><span style="color: hsl(120, 100%, 40%);">+             val.si_len += len;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each(tmp, bts_ms_list(bts)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!ms->nacc)</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (ms->nacc->fi->state != NACC_ST_WAIT_REQUEST_SI)</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (memcmp(&nacc->reprt_cell, &ms->nacc->cgi_ps, sizeof(nacc->reprt_cell)) != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_SI_INFO_RECEIVED, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int handle_rim(struct osmo_bssgp_prim *bp)</span><br><span> {</span><br><span>    struct msgb *msg = bp->oph.msg;</span><br><span>@@ -151,8 +199,7 @@</span><br><span>             LOGPRIM(nsei, LOGL_NOTICE, "Responding to RAN INFORMATION REQUEST not yet implemented!\n");</span><br><span>                break;</span><br><span>       case BSSGP_IE_RI_RIM_CONTAINER:</span><br><span style="color: hsl(0, 100%, 40%);">-         LOGPRIM(nsei, LOGL_NOTICE, "Responding to RAN INFORMATION not yet implemented!\n");</span><br><span style="color: hsl(0, 100%, 40%);">-           break;</span><br><span style="color: hsl(120, 100%, 40%);">+                return handle_ran_info_response(bp, bts);</span><br><span>    case BSSGP_IE_RI_APP_ERROR_RIM_CONT:</span><br><span>         case BSSGP_IE_RI_ACK_RIM_CONTAINER:</span><br><span>  case BSSGP_IE_RI_ERROR_RIM_COINTAINER:</span><br><span>diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp</span><br><span>index 669ea27..0cbc488 100644</span><br><span>--- a/src/gprs_debug.cpp</span><br><span>+++ b/src/gprs_debug.cpp</span><br><span>@@ -119,6 +119,13 @@</span><br><span>           .loglevel = LOGL_NOTICE,</span><br><span>             .enabled = 1,</span><br><span>        },</span><br><span style="color: hsl(120, 100%, 40%);">+    [DNACC] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "DNACC",</span><br><span style="color: hsl(120, 100%, 40%);">+            .color = "\033[1;37m",</span><br><span style="color: hsl(120, 100%, 40%);">+              .description = "Network Assisted Cell Change (NACC)",</span><br><span style="color: hsl(120, 100%, 40%);">+               .loglevel = LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+              .enabled = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span>   [DRIM] = {</span><br><span>           .name = "DRIM",</span><br><span>            .color = "\033[1;38m",</span><br><span>diff --git a/src/gprs_debug.h b/src/gprs_debug.h</span><br><span>index 8df405a..cebeabc 100644</span><br><span>--- a/src/gprs_debug.h</span><br><span>+++ b/src/gprs_debug.h</span><br><span>@@ -46,6 +46,7 @@</span><br><span>    DTBFUL,</span><br><span>      DNS,</span><br><span>         DPCU,</span><br><span style="color: hsl(120, 100%, 40%);">+ DNACC,</span><br><span>       DRIM,</span><br><span>        aDebug_LastEntry</span><br><span> };</span><br><span>diff --git a/src/gprs_ms.c b/src/gprs_ms.c</span><br><span>index ea497a3..5d7afa1 100644</span><br><span>--- a/src/gprs_ms.c</span><br><span>+++ b/src/gprs_ms.c</span><br><span>@@ -26,6 +26,7 @@</span><br><span> #include "gprs_debug.h"</span><br><span> #include "gprs_codel.h"</span><br><span> #include "pcu_utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "nacc_fsm.h"</span><br><span> </span><br><span> #include <time.h></span><br><span> </span><br><span>@@ -902,3 +903,36 @@</span><br><span> </span><br><span>     return NULL;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!ms->nacc)</span><br><span style="color: hsl(120, 100%, 40%);">+             ms->nacc = nacc_fsm_alloc(ms);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ms->nacc)</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CELL_CHG_NOTIFICATION, notif);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool ms_nacc_rts(const struct GprsMs *ms)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ms->nacc)</span><br><span style="color: hsl(120, 100%, 40%);">+             return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||</span><br><span style="color: hsl(120, 100%, 40%);">+          ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)</span><br><span style="color: hsl(120, 100%, 40%);">+             return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct nacc_ev_create_rlcmac_msg_ctx data_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      data_ctx.tbf = tbf;</span><br><span style="color: hsl(120, 100%, 40%);">+   data_ctx.msg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (rc != 0 || !data_ctx.msg)</span><br><span style="color: hsl(120, 100%, 40%);">+         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  return data_ctx.msg;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/gprs_ms.h b/src/gprs_ms.h</span><br><span>index 12809f1..6587231 100644</span><br><span>--- a/src/gprs_ms.h</span><br><span>+++ b/src/gprs_ms.h</span><br><span>@@ -40,6 +40,7 @@</span><br><span> #include <osmocom/gsm/gsm48.h></span><br><span> </span><br><span> #include "coding_scheme.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include <gsm_rlcmac.h></span><br><span> </span><br><span> #include <stdint.h></span><br><span> #include <stddef.h></span><br><span>@@ -100,6 +101,7 @@</span><br><span>  enum mcs_kind mode;</span><br><span> </span><br><span>      struct rate_ctr_group *ctrs;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct nacc_fsm_ctx *nacc;</span><br><span> };</span><br><span> </span><br><span> struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);</span><br><span>@@ -140,6 +142,10 @@</span><br><span> </span><br><span> void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif);</span><br><span style="color: hsl(120, 100%, 40%);">+bool ms_nacc_rts(const struct GprsMs *ms);</span><br><span style="color: hsl(120, 100%, 40%);">+struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static inline bool ms_is_idle(const struct GprsMs *ms)</span><br><span> {</span><br><span>   return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);</span><br><span>diff --git a/src/gprs_pcu.c b/src/gprs_pcu.c</span><br><span>index 2f18dfd..9679914 100644</span><br><span>--- a/src/gprs_pcu.c</span><br><span>+++ b/src/gprs_pcu.c</span><br><span>@@ -22,6 +22,7 @@</span><br><span> </span><br><span> #include <osmocom/core/utils.h></span><br><span> #include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/ctrl/ports.h></span><br><span> </span><br><span> #include "gprs_pcu.h"</span><br><span> #include "bts.h"</span><br><span>@@ -40,12 +41,20 @@</span><br><span>    { .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ neigh_cache_free(pcu->neigh_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+        si_cache_free(pcu->si_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct gprs_pcu *gprs_pcu_alloc(void *ctx)</span><br><span> {</span><br><span>    struct gprs_pcu *pcu;</span><br><span> </span><br><span>    pcu = (struct gprs_pcu *)talloc_zero(ctx, struct gprs_pcu);</span><br><span>  OSMO_ASSERT(pcu);</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_set_destructor(pcu, gprs_pcu_talloc_destructor);</span><br><span> </span><br><span>  pcu->vty.fc_interval = 1;</span><br><span>         pcu->vty.max_cs_ul = MAX_GPRS_CS;</span><br><span>@@ -97,12 +106,17 @@</span><br><span>  pcu->vty.ws_pdch = 0;</span><br><span>     pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;</span><br><span>         pcu->vty.llc_idle_ack_csec = 10;</span><br><span style="color: hsl(120, 100%, 40%);">+   pcu->vty.neigh_ctrl_addr = talloc_strdup(pcu, "127.0.0.1");</span><br><span style="color: hsl(120, 100%, 40%);">+      pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;</span><br><span> </span><br><span>  pcu->T_defs = T_defs_pcu;</span><br><span>         osmo_tdefs_reset(pcu->T_defs);</span><br><span> </span><br><span>        INIT_LLIST_HEAD(&pcu->bts_list);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   pcu->neigh_cache = neigh_cache_alloc(pcu);</span><br><span style="color: hsl(120, 100%, 40%);">+ pcu->si_cache = si_cache_alloc(pcu);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    return pcu;</span><br><span> }</span><br><span> </span><br><span>diff --git a/src/gprs_pcu.h b/src/gprs_pcu.h</span><br><span>index a1cd1ed..8e18f89 100644</span><br><span>--- a/src/gprs_pcu.h</span><br><span>+++ b/src/gprs_pcu.h</span><br><span>@@ -29,6 +29,8 @@</span><br><span> #include "gprs_bssgp_pcu.h"</span><br><span> #include "coding_scheme.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "neigh_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define LLC_CODEL_DISABLE 0</span><br><span> #define LLC_CODEL_USE_DEFAULT (-1)</span><br><span> </span><br><span>@@ -102,6 +104,9 @@</span><br><span>              uint32_t llc_discard_csec;</span><br><span>           uint32_t llc_idle_ack_csec;</span><br><span>          uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Remote BSS resolution sevice (CTRL iface) */</span><br><span style="color: hsl(120, 100%, 40%);">+               char *neigh_ctrl_addr;</span><br><span style="color: hsl(120, 100%, 40%);">+                uint16_t neigh_ctrl_port;</span><br><span>    } vty;</span><br><span> </span><br><span>   struct gsmtap_inst *gsmtap;</span><br><span>@@ -116,6 +121,9 @@</span><br><span>    struct gprs_bssgp_pcu bssgp;</span><br><span> </span><br><span>     struct osmo_tdef *T_defs; /* timers controlled by PCU */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    struct neigh_cache *neigh_cache; /* ARFC+BSIC -> CGI PS cache */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct si_cache *si_cache; /* ARFC+BSIC -> CGI PS cache */</span><br><span> };</span><br><span> </span><br><span> </span><br><span>diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp</span><br><span>index 5640158..66e9c6b 100644</span><br><span>--- a/src/gprs_rlcmac_sched.cpp</span><br><span>+++ b/src/gprs_rlcmac_sched.cpp</span><br><span>@@ -38,6 +38,7 @@</span><br><span>     struct gprs_rlcmac_tbf *poll;</span><br><span>        struct gprs_rlcmac_tbf *ul_ass;</span><br><span>      struct gprs_rlcmac_tbf *dl_ass;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_rlcmac_tbf *nacc;</span><br><span>        struct gprs_rlcmac_ul_tbf *ul_ack;</span><br><span> };</span><br><span> </span><br><span>@@ -71,6 +72,9 @@</span><br><span>             if (ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)</span><br><span>              || ul_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))</span><br><span>                  tbf_cand->ul_ass = ul_tbf;</span><br><span style="color: hsl(120, 100%, 40%);">+         /* NACC ready to send */</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ms_nacc_rts(ul_tbf->ms()))</span><br><span style="color: hsl(120, 100%, 40%);">+                     tbf_cand->nacc = ul_tbf;</span><br><span> /* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all</span><br><span> states? */</span><br><span>   }</span><br><span>@@ -88,6 +92,9 @@</span><br><span>                if (dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS)</span><br><span>              || dl_tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ))</span><br><span>                  tbf_cand->ul_ass = dl_tbf;</span><br><span style="color: hsl(120, 100%, 40%);">+         /* NACC ready to send */</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ms_nacc_rts(dl_tbf->ms()))</span><br><span style="color: hsl(120, 100%, 40%);">+                     tbf_cand->nacc = dl_tbf;</span><br><span>  }</span><br><span> </span><br><span>        return poll_fn;</span><br><span>@@ -166,7 +173,8 @@</span><br><span>        struct gprs_rlcmac_tbf *tbf = NULL;</span><br><span>  struct gprs_rlcmac_tbf *next_list[] = { tbfs->ul_ass,</span><br><span>                                             tbfs->dl_ass,</span><br><span style="color: hsl(0, 100%, 40%);">-                                                tbfs->ul_ack };</span><br><span style="color: hsl(120, 100%, 40%);">+                                            tbfs->ul_ack,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              tbfs->nacc };</span><br><span> </span><br><span>         /* Send Packet Application Information first (ETWS primary notifications) */</span><br><span>         msg = sched_app_info(tbfs->dl_ass);</span><br><span>@@ -194,6 +202,9 @@</span><br><span>                                 msg = tbfs->dl_ass->create_dl_ass(fn, ts);</span><br><span>                     else if (tbf == tbfs->ul_ack)</span><br><span>                             msg = tbfs->ul_ack->create_ul_ack(fn, ts);</span><br><span style="color: hsl(120, 100%, 40%);">+                      else if (tbf == tbfs->nacc) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+                   }</span><br><span>                    /* else: if tbf/ms is pending to send tx_neigbhourData or tx_CellchangeContinue, send it */</span><br><span> </span><br><span>                      if (!msg) {</span><br><span>diff --git a/src/nacc_fsm.c b/src/nacc_fsm.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1a60792</span><br><span>--- /dev/null</span><br><span>+++ b/src/nacc_fsm.c</span><br><span>@@ -0,0 +1,633 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* nacc_fsm.c</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Pau Espin Pedrol <pespin@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ * modify it under the terms of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * as published by the Free Software Foundation; either version 2</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program; if not, write to the Free Software</span><br><span style="color: hsl(120, 100%, 40%);">+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <unistd.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/rate_ctr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/ctrl/control_cmd.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/ctrl/control_if.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm48.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/gprs_bssgp.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gprs/gprs_bssgp_rim.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <nacc_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <gprs_rlcmac.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <gprs_debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <gprs_ms.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <encoding.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <bts.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <neigh_cache.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define X(s) (1 << (s))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define nacc_fsm_state_chg(fi, NEXT_STATE) \</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct value_string nacc_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ { NACC_EV_CELL_CHG_NOTIFICATION, "CELL_CHG_NOTIFICATION" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { NACC_EV_RX_RESOLVE_RAC_CI, "RX_RESOLVE_RAC_CI" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { NACC_EV_SI_INFO_RECEIVED, "SI_INFO_RECEIVED" },</span><br><span style="color: hsl(120, 100%, 40%);">+   { NACC_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },</span><br><span style="color: hsl(120, 100%, 40%);">+ { 0, NULL }</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TS 44 060 11.2.9e Packet Neighbour Cell Data */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_packet_neighbour_cell_data(struct nacc_fsm_ctx *ctx, struct gprs_rlcmac_tbf *tbf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       RlcMacDownlink_t *mac_control_block;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct GprsMs *ms = tbf_ms(tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(tbf_is_tfi_assigned(tbf));</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t tfi = tbf_tfi(tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t container_id = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     PNCDContainer_t container;</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t max_len, len_to_write;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *cont_buf;</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t si_type = ctx->si_info.type_psi ? 0x01 : 0x0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    memset(&container, 0, sizeof(container));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ctx->container_idx == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             container.UnionType = 1; /* with ID */</span><br><span style="color: hsl(120, 100%, 40%);">+                container.u.PNCD_Container_With_ID.ARFCN = ctx->neigh_key.tgt_arfcn;</span><br><span style="color: hsl(120, 100%, 40%);">+               container.u.PNCD_Container_With_ID.BSIC = ctx->neigh_key.tgt_bsic;</span><br><span style="color: hsl(120, 100%, 40%);">+         cont_buf = &container.u.PNCD_Container_With_ID.CONTAINER[0];</span><br><span style="color: hsl(120, 100%, 40%);">+              max_len = sizeof(container.u.PNCD_Container_With_ID.CONTAINER) - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              container.UnionType = 0; /* without ID */</span><br><span style="color: hsl(120, 100%, 40%);">+             cont_buf = &container.u.PNCD_Container_Without_ID.CONTAINER[0];</span><br><span style="color: hsl(120, 100%, 40%);">+           max_len = sizeof(container.u.PNCD_Container_Without_ID.CONTAINER) - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   len_to_write = ctx->si_info.si_len - ctx->si_info_bytes_sent;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len_to_write == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* We sent all info on last message filing it exactly, we now send a zeroed one to finish */</span><br><span style="color: hsl(120, 100%, 40%);">+          ctx->all_si_info_sent = true;</span><br><span style="color: hsl(120, 100%, 40%);">+              *cont_buf = (si_type << 5) | 0x00;</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (len_to_write >= max_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* We fill the rlcmac block, we'll need more messages */</span><br><span style="color: hsl(120, 100%, 40%);">+          *cont_buf = (si_type << 5) |  0x1F;</span><br><span style="color: hsl(120, 100%, 40%);">+             memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], max_len);</span><br><span style="color: hsl(120, 100%, 40%);">+               ctx->si_info_bytes_sent += max_len;</span><br><span style="color: hsl(120, 100%, 40%);">+        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Last block, we don't fill it exactly */</span><br><span style="color: hsl(120, 100%, 40%);">+                ctx->all_si_info_sent = true;</span><br><span style="color: hsl(120, 100%, 40%);">+              *cont_buf = (si_type << 5) | (len_to_write & 0x1F);</span><br><span style="color: hsl(120, 100%, 40%);">+         memcpy(cont_buf + 1, &ctx->si_info.si_buf[ctx->si_info_bytes_sent], len_to_write);</span><br><span style="color: hsl(120, 100%, 40%);">+          ctx->si_info_bytes_sent += len_to_write;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg = msgb_alloc(GSM_MACBLOCK_LEN, "neighbour_cell_data");</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!msg)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Initialize a bit vector that uses allocated msgb as the data buffer. */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bitvec bv = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .data = msgb_put(msg, GSM_MACBLOCK_LEN),</span><br><span style="color: hsl(120, 100%, 40%);">+              .data_len = GSM_MACBLOCK_LEN,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    bitvec_unhex(&bv, DUMMY_VEC);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  write_packet_neighbour_cell_data(mac_control_block,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    tfi_is_dl, tfi, container_id,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         ctx->container_idx, &container);</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Neighbour Cell Data +++++++++++++++++++++++++\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Neighbour Cell Data failed (%d)\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+         goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Neighbour Cell Data -------------------------\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_NEIGH_CELL_DATA]);</span><br><span style="color: hsl(120, 100%, 40%);">+    talloc_free(mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ctx->container_idx++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+free_ret:</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+       msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct msgb *create_packet_cell_chg_continue(struct nacc_fsm_ctx *ctx, struct gprs_rlcmac_tbf *tbf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       RlcMacDownlink_t *mac_control_block;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct GprsMs *ms = tbf_ms(tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_cell_chg_continue");</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!msg)</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Initialize a bit vector that uses allocated msgb as the data buffer. */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bitvec bv = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .data = msgb_put(msg, GSM_MACBLOCK_LEN),</span><br><span style="color: hsl(120, 100%, 40%);">+              .data_len = GSM_MACBLOCK_LEN,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+    bitvec_unhex(&bv, DUMMY_VEC);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->ms, RlcMacDownlink_t);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(tbf_is_tfi_assigned(tbf));</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t tfi = tbf_tfi(tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t container_id = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     write_packet_cell_change_continue(mac_control_block, tfi_is_dl, tfi, true,</span><br><span style="color: hsl(120, 100%, 40%);">+                    ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic, container_id);</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Cell Change Continue +++++++++++++++++++++++++\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DTBF, LOGL_ERROR, "Encoding of Packet Cell Change Continue failed (%d)\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+                goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Cell Change Continue -------------------------\n");</span><br><span style="color: hsl(120, 100%, 40%);">+  rate_ctr_inc(&bts_rate_counters(ms->bts)->ctr[CTR_PKT_CELL_CHG_CONTINUE]);</span><br><span style="color: hsl(120, 100%, 40%);">+  talloc_free(mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+       return msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+free_ret:</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(mac_control_block);</span><br><span style="color: hsl(120, 100%, 40%);">+       msgb_free(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int fill_rim_ran_info_req(struct nacc_fsm_ctx *ctx, struct bssgp_ran_information_pdu *pdu)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gprs_rlcmac_bts *bts = ctx->ms->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *pdu = (struct bssgp_ran_information_pdu){</span><br><span style="color: hsl(120, 100%, 40%);">+            .routing_info_dest = {</span><br><span style="color: hsl(120, 100%, 40%);">+                        .discr = BSSGP_RIM_ROUTING_INFO_GERAN,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .geran = {</span><br><span style="color: hsl(120, 100%, 40%);">+                            .raid = {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     .mcc = ctx->cgi_ps.rai.lac.plmn.mcc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       .mnc = ctx->cgi_ps.rai.lac.plmn.mnc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       .mnc_3_digits = ctx->cgi_ps.rai.lac.plmn.mnc_3_digits,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     .lac = ctx->cgi_ps.rai.lac.lac,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    .rac = ctx->cgi_ps.rai.rac,</span><br><span style="color: hsl(120, 100%, 40%);">+                                },</span><br><span style="color: hsl(120, 100%, 40%);">+                            .cid = ctx->cgi_ps.cell_identity,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .routing_info_src = {</span><br><span style="color: hsl(120, 100%, 40%);">+                 .discr = BSSGP_RIM_ROUTING_INFO_GERAN,</span><br><span style="color: hsl(120, 100%, 40%);">+                        .geran = {</span><br><span style="color: hsl(120, 100%, 40%);">+                            .raid = { /* TODO: fill properly */</span><br><span style="color: hsl(120, 100%, 40%);">+                                   .mcc = bts->cgi_ps.rai.lac.plmn.mcc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       .mnc = bts->cgi_ps.rai.lac.plmn.mnc,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       .mnc_3_digits = bts->cgi_ps.rai.lac.plmn.mnc_3_digits,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     .lac = bts->cgi_ps.rai.lac.lac,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    .rac = bts->cgi_ps.rai.rac,</span><br><span style="color: hsl(120, 100%, 40%);">+                                },</span><br><span style="color: hsl(120, 100%, 40%);">+                            .cid = bts->cgi_ps.cell_identity,</span><br><span style="color: hsl(120, 100%, 40%);">+                  },</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+            .rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER,</span><br><span style="color: hsl(120, 100%, 40%);">+                .decoded_present = true,</span><br><span style="color: hsl(120, 100%, 40%);">+              .decoded = {</span><br><span style="color: hsl(120, 100%, 40%);">+                  .req_rim_cont = {</span><br><span style="color: hsl(120, 100%, 40%);">+                             .app_id = BSSGP_RAN_INF_APP_ID_NACC,</span><br><span style="color: hsl(120, 100%, 40%);">+                          .seq_num = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+                         .pdu_ind = {</span><br><span style="color: hsl(120, 100%, 40%);">+                                  .ack_requested = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   .pdu_type_ext = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+                            },</span><br><span style="color: hsl(120, 100%, 40%);">+                            .prot_ver = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+                                .son_trans_app_id = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+                             .son_trans_app_id_len = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                            .u = {</span><br><span style="color: hsl(120, 100%, 40%);">+                                        .app_cont_nacc = {</span><br><span style="color: hsl(120, 100%, 40%);">+                                            .reprt_cell = ctx->cgi_ps,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 },</span><br><span style="color: hsl(120, 100%, 40%);">+                            },</span><br><span style="color: hsl(120, 100%, 40%);">+                    },</span><br><span style="color: hsl(120, 100%, 40%);">+            },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+// FSM states //</span><br><span style="color: hsl(120, 100%, 40%);">+////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_rlcmac_bts *bts = ctx->ms->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+     Packet_Cell_Change_Notification_t *notif;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NACC_EV_CELL_CHG_NOTIFICATION:</span><br><span style="color: hsl(120, 100%, 40%);">+           notif = (Packet_Cell_Change_Notification_t *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+            switch (notif->Target_Cell.UnionType) {</span><br><span style="color: hsl(120, 100%, 40%);">+            case 0: /* GSM */</span><br><span style="color: hsl(120, 100%, 40%);">+                     ctx->neigh_key.local_lac = bts->cgi_ps.rai.lac.lac;</span><br><span style="color: hsl(120, 100%, 40%);">+                     ctx->neigh_key.local_ci = bts->cgi_ps.cell_identity;</span><br><span style="color: hsl(120, 100%, 40%);">+                    ctx->neigh_key.tgt_arfcn = notif->Target_Cell.u.Target_Cell_GSM_Notif.ARFCN;</span><br><span style="color: hsl(120, 100%, 40%);">+                    ctx->neigh_key.tgt_bsic = notif->Target_Cell.u.Target_Cell_GSM_Notif.BSIC;</span><br><span style="color: hsl(120, 100%, 40%);">+                      nacc_fsm_state_chg(fi, NACC_ST_WAIT_RESOLVE_RAC_CI);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOGPFSML(fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                            notif->Target_Cell.UnionType);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_wait_resolve_rac_ci_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_rlcmac_bts *bts = ctx->ms->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_pcu *pcu = bts->pcu;</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct osmo_cell_global_id_ps *cgi_ps;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct ctrl_cmd *cmd;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* First try to find the value in the cache */</span><br><span style="color: hsl(120, 100%, 40%);">+        cgi_ps = neigh_cache_lookup_value(pcu->neigh_cache, &ctx->neigh_key);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cgi_ps) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ctx->cgi_ps = *cgi_ps;</span><br><span style="color: hsl(120, 100%, 40%);">+             nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* CGI-PS not in cache, resolve it using BSC Neighbor Resolution CTRL interface */</span><br><span style="color: hsl(120, 100%, 40%);">+    cmd = ctrl_cmd_create(ctx, CTRL_TYPE_GET);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGPFSML(fi, LOGL_ERROR, "CTRL msg creation failed\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   cmd->id = talloc_asprintf(cmd, "1");</span><br><span style="color: hsl(120, 100%, 40%);">+     cmd->variable = talloc_asprintf(cmd, "neighbor_resolve_cgi_ps_from_lac_ci.%d.%d.%d.%d",</span><br><span style="color: hsl(120, 100%, 40%);">+                                  ctx->neigh_key.local_lac, ctx->neigh_key.local_ci,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = ctrl_cmd_send(&ctx->neigh_ctrl_conn->write_queue, cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGPFSML(fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_wait_resolve_rac_ci(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NACC_EV_CELL_CHG_NOTIFICATION:</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case NACC_EV_RX_RESOLVE_RAC_CI:</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Assumption: ctx->cgi_ps has been filled by caller of the event */</span><br><span style="color: hsl(120, 100%, 40%);">+               nacc_fsm_state_chg(fi, NACC_ST_WAIT_REQUEST_SI);</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* At this point, we expect correct tgt cell info to be already in ctx->cgi_ps */</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_wait_request_si_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gprs_rlcmac_bts *bts = ctx->ms->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_pcu *pcu = bts->pcu;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct bssgp_ran_information_pdu pdu;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct si_cache_value *si;</span><br><span style="color: hsl(120, 100%, 40%);">+      int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* First check if we have SI info for the target cell in cache */</span><br><span style="color: hsl(120, 100%, 40%);">+     si = si_cache_lookup_value(pcu->si_cache, &ctx->cgi_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (si) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Copy info since cache can be deleted at any point */</span><br><span style="color: hsl(120, 100%, 40%);">+               memcpy(&ctx->si_info, si, sizeof(ctx->si_info));</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Tell the PCU scheduler we are ready to go, from here one we</span><br><span style="color: hsl(120, 100%, 40%);">+                 * are polled/driven by the scheduler */</span><br><span style="color: hsl(120, 100%, 40%);">+              nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* SI info not in cache, resolve it using RIM procedure against SGSN */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (fill_rim_ran_info_req(ctx, &pdu) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = bssgp_tx_rim(&pdu, gprs_ns2_nse_nsei(ctx->ms->bts->nse));</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGPFSML(fi, LOGL_ERROR, "Failed transmitting RIM PDU: %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_wait_request_si(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct si_cache_entry *entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NACC_EV_SI_INFO_RECEIVED:</span><br><span style="color: hsl(120, 100%, 40%);">+                entry = (struct si_cache_entry *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Copy info since cache can be deleted at any point */</span><br><span style="color: hsl(120, 100%, 40%);">+               memcpy(&ctx->si_info, &entry->value, sizeof(ctx->si_info));</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Tell the PCU scheduler we are ready to go, from here one we</span><br><span style="color: hsl(120, 100%, 40%);">+                 * are polled/driven by the scheduler */</span><br><span style="color: hsl(120, 100%, 40%);">+              nacc_fsm_state_chg(fi, NACC_ST_TX_NEIGHBOUR_DATA);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_tx_neighbour_data_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ /* At this point, we already received all required RIM messages or we</span><br><span style="color: hsl(120, 100%, 40%);">+    have them cached. We now wait for scheduler to ask us to construct</span><br><span style="color: hsl(120, 100%, 40%);">+    RLCMAC DL CTRL messages to move FSM states forward */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_tx_neighbour_data(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NACC_EV_CREATE_RLCMAC_MSG:</span><br><span style="color: hsl(120, 100%, 40%);">+               data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+              data_ctx->msg = create_packet_neighbour_cell_data(ctx, data_ctx->tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!data_ctx->msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (ctx->all_si_info_sent) /* DONE */</span><br><span style="color: hsl(120, 100%, 40%);">+                      nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_cell_cgh_continue_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   /* At this point, we already sent all Pkt Cell Neighbour Change rlcmac</span><br><span style="color: hsl(120, 100%, 40%);">+           blocks, and we only need to wait to be scheduled again to send PKT</span><br><span style="color: hsl(120, 100%, 40%);">+    CELL CHANGE NOTIFICATION */</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_cell_cgh_continue(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct nacc_ev_create_rlcmac_msg_ctx *data_ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+      case NACC_EV_CREATE_RLCMAC_MSG:</span><br><span style="color: hsl(120, 100%, 40%);">+               data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+              data_ctx->msg = create_packet_cell_chg_continue(ctx, data_ctx->tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+            /* TODO: logic if no more Neighbour cell data messages need to be send, then: */</span><br><span style="color: hsl(120, 100%, 40%);">+              nacc_fsm_state_chg(fi, NACC_ST_DONE);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(0);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void st_done_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void nacc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* after cleanup() finishes, FSM termination calls osmo_fsm_inst_free,</span><br><span style="color: hsl(120, 100%, 40%);">+           so we need to avoid double-freeing it during ctx talloc free</span><br><span style="color: hsl(120, 100%, 40%);">+          destructor */</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_reparent(ctx, ctx->ms, ctx->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->fi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* remove references from owning MS and free entire ctx */</span><br><span style="color: hsl(120, 100%, 40%);">+    ctx->ms->nacc = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm_state nacc_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_INITIAL] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .in_event_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_EV_CELL_CHG_NOTIFICATION),</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                     X(NACC_ST_WAIT_RESOLVE_RAC_CI) |</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_ST_TX_NEIGHBOUR_DATA),</span><br><span style="color: hsl(120, 100%, 40%);">+         .name = "INITIAL",</span><br><span style="color: hsl(120, 100%, 40%);">+          //.onenter = st_initial_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+             .action = st_initial,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_WAIT_RESOLVE_RAC_CI] = {</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_EV_RX_RESOLVE_RAC_CI),</span><br><span style="color: hsl(120, 100%, 40%);">+         .out_state_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                     X(NACC_ST_WAIT_REQUEST_SI) |</span><br><span style="color: hsl(120, 100%, 40%);">+                  X(NACC_ST_TX_CELL_CHG_CONTINUE),</span><br><span style="color: hsl(120, 100%, 40%);">+              .name = "WAIT_RESOLVE_RAC_CI",</span><br><span style="color: hsl(120, 100%, 40%);">+              .onenter = st_wait_resolve_rac_ci_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+           .action = st_wait_resolve_rac_ci,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_WAIT_REQUEST_SI] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .in_event_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_EV_CELL_CHG_NOTIFICATION) |</span><br><span style="color: hsl(120, 100%, 40%);">+                    X(NACC_EV_SI_INFO_RECEIVED),</span><br><span style="color: hsl(120, 100%, 40%);">+          .out_state_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                     X(NACC_ST_TX_NEIGHBOUR_DATA),</span><br><span style="color: hsl(120, 100%, 40%);">+         .name = "WAIT_REQUEST_SI",</span><br><span style="color: hsl(120, 100%, 40%);">+          .onenter = st_wait_request_si_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+               .action = st_wait_request_si,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_TX_NEIGHBOUR_DATA] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .in_event_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_EV_CELL_CHG_NOTIFICATION) |</span><br><span style="color: hsl(120, 100%, 40%);">+                    X(NACC_EV_SI_INFO_RECEIVED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                 X(NACC_EV_CREATE_RLCMAC_MSG),</span><br><span style="color: hsl(120, 100%, 40%);">+         .out_state_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                     X(NACC_ST_TX_CELL_CHG_CONTINUE),</span><br><span style="color: hsl(120, 100%, 40%);">+              .name = "TX_NEIGHBOUR_DATA",</span><br><span style="color: hsl(120, 100%, 40%);">+                .onenter = st_tx_neighbour_data_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+             .action = st_tx_neighbour_data,</span><br><span style="color: hsl(120, 100%, 40%);">+       },</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_TX_CELL_CHG_CONTINUE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .in_event_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                      X(NACC_EV_CELL_CHG_NOTIFICATION) |</span><br><span style="color: hsl(120, 100%, 40%);">+                    X(NACC_EV_SI_INFO_RECEIVED) |</span><br><span style="color: hsl(120, 100%, 40%);">+                 X(NACC_EV_CREATE_RLCMAC_MSG),</span><br><span style="color: hsl(120, 100%, 40%);">+         .out_state_mask =</span><br><span style="color: hsl(120, 100%, 40%);">+                     X(NACC_ST_DONE),</span><br><span style="color: hsl(120, 100%, 40%);">+              .name = "TX_CELL_CHG_CONTINUE",</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = st_cell_cgh_continue_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+             .action = st_cell_cgh_continue,</span><br><span style="color: hsl(120, 100%, 40%);">+       },</span><br><span style="color: hsl(120, 100%, 40%);">+    [NACC_ST_DONE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .in_event_mask = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+           .out_state_mask = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+          .name = "DONE",</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = st_done_on_enter,</span><br><span style="color: hsl(120, 100%, 40%);">+  },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm nacc_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "NACC",</span><br><span style="color: hsl(120, 100%, 40%);">+     .states = nacc_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_states = ARRAY_SIZE(nacc_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+    .cleanup = nacc_fsm_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+  .log_subsys = DNACC,</span><br><span style="color: hsl(120, 100%, 40%);">+  .event_names = nacc_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static __attribute__((constructor)) void nacc_fsm_init(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(osmo_fsm_register(&nacc_fsm) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void nacc_fsm_ctrl_reply_cb(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+       char *tmp = NULL, *tok, *saveptr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGPFSML(ctx->fi, LOGL_NOTICE, "Received CTRL message: type=%d %s: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 cmd->type, cmd->variable, osmo_escape_str(cmd->reply, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cmd->type != CTRL_TYPE_GET_REPLY || !cmd->reply) {</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* TODO: Potentially validate cmd->variable contains same params as we</span><br><span style="color: hsl(120, 100%, 40%);">+        sent, and that cmd->id matches the original set. We may want to keep</span><br><span style="color: hsl(120, 100%, 40%);">+       the original cmd around by setting cmd->defer=1 when sending it. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   tmp = talloc_strdup(cmd, cmd->reply);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!tmp)</span><br><span style="color: hsl(120, 100%, 40%);">+             goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!(tok = strtok_r(tmp, "-", &saveptr)))</span><br><span style="color: hsl(120, 100%, 40%);">+              goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx->cgi_ps.rai.lac.plmn.mcc = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!(tok = strtok_r(NULL, "-", &saveptr)))</span><br><span style="color: hsl(120, 100%, 40%);">+             goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx->cgi_ps.rai.lac.plmn.mnc = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!(tok = strtok_r(NULL, "-", &saveptr)))</span><br><span style="color: hsl(120, 100%, 40%);">+             goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx->cgi_ps.rai.lac.lac = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!(tok = strtok_r(NULL, "-", &saveptr)))</span><br><span style="color: hsl(120, 100%, 40%);">+             goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx->cgi_ps.rai.rac = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(tok = strtok_r(NULL, "\0", &saveptr)))</span><br><span style="color: hsl(120, 100%, 40%);">+            goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx->cgi_ps.cell_identity = atoi(tok);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   // TODO: cache the cgi_ps so we can avoid requesting again same resolution</span><br><span style="color: hsl(120, 100%, 40%);">+    neigh_cache_add(ctx->ms->bts->pcu->neigh_cache, &ctx->neigh_key, &ctx->cgi_ps);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_fsm_inst_dispatch(ctx->fi, NACC_EV_RX_RESOLVE_RAC_CI, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+free_ret:</span><br><span style="color: hsl(120, 100%, 40%);">+  talloc_free(tmp);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_term(ctx->fi, OSMO_FSM_TERM_ERROR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    return;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int nacc_fsm_ctx_talloc_destructor(struct nacc_fsm_ctx *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (ctx->fi) {</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_free(ctx->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+               ctx->fi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ctx->neigh_ctrl_conn) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ctx->neigh_ctrl_conn->write_queue.bfd.fd != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   osmo_wqueue_clear(&ctx->neigh_ctrl_conn->write_queue);</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fd_unregister(&ctx->neigh_ctrl_conn->write_queue.bfd);</span><br><span style="color: hsl(120, 100%, 40%);">+                 close(ctx->neigh_ctrl_conn->write_queue.bfd.fd);</span><br><span style="color: hsl(120, 100%, 40%);">+                        ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_rlcmac_bts *bts = ms->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gprs_pcu *pcu = bts->pcu;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct nacc_fsm_ctx *ctx = talloc_zero(ms, struct nacc_fsm_ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+      char buf[64];</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     talloc_set_destructor(ctx, nacc_fsm_ctx_talloc_destructor);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ctx->ms = ms;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    snprintf(buf, sizeof(buf), "TLLI-0x%08x", ms_tlli(ms));</span><br><span style="color: hsl(120, 100%, 40%);">+     ctx->fi = osmo_fsm_inst_alloc(&nacc_fsm, ctx, ctx, LOGL_INFO, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!ctx->fi)</span><br><span style="color: hsl(120, 100%, 40%);">+              goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      ctx->neigh_ctrl = ctrl_handle_alloc(ctx, ctx, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       ctx->neigh_ctrl->reply_cb = nacc_fsm_ctrl_reply_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+     ctx->neigh_ctrl_conn = osmo_ctrl_conn_alloc(ctx, ctx->neigh_ctrl);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!ctx->neigh_ctrl_conn)</span><br><span style="color: hsl(120, 100%, 40%);">+         goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = osmo_sock_init2_ofd(&ctx->neigh_ctrl_conn->write_queue.bfd,</span><br><span style="color: hsl(120, 100%, 40%);">+                             AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,</span><br><span style="color: hsl(120, 100%, 40%);">+                          NULL, 0, pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port,</span><br><span style="color: hsl(120, 100%, 40%);">+                            OSMO_SOCK_F_CONNECT);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DNACC, LOGL_ERROR, "Can't connect to CTRL @ %s:%u\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                   pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port);</span><br><span style="color: hsl(120, 100%, 40%);">+               goto free_ret;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_add(&ctx->neigh_ctrl_conn->list_entry, &ctx->neigh_ctrl->ccon_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return ctx;</span><br><span style="color: hsl(120, 100%, 40%);">+free_ret:</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/nacc_fsm.h b/src/nacc_fsm.h</span><br><span>new file mode 100644</span><br><span>index 0000000..d660454</span><br><span>--- /dev/null</span><br><span>+++ b/src/nacc_fsm.h</span><br><span>@@ -0,0 +1,65 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* nacc_fsm.h</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Pau Espin Pedrol <pespin@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ * modify it under the terms of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * as published by the Free Software Foundation; either version 2</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program; if not, write to the Free Software</span><br><span style="color: hsl(120, 100%, 40%);">+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <neigh_cache.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct GprsMs;</span><br><span style="color: hsl(120, 100%, 40%);">+struct gprs_rlcmac_tbf;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum nacc_fsm_event {</span><br><span style="color: hsl(120, 100%, 40%);">+  NACC_EV_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */</span><br><span style="color: hsl(120, 100%, 40%);">+ NACC_EV_RX_RESOLVE_RAC_CI,</span><br><span style="color: hsl(120, 100%, 40%);">+    NACC_EV_SI_INFO_RECEIVED, /* data: struct si_cache_entry* */</span><br><span style="color: hsl(120, 100%, 40%);">+  NACC_EV_CREATE_RLCMAC_MSG, /* data: struct nacc_ev_create_rlcmac_msg_ctx* */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum nacc_fsm_states {</span><br><span style="color: hsl(120, 100%, 40%);">+    NACC_ST_INITIAL,</span><br><span style="color: hsl(120, 100%, 40%);">+      NACC_ST_WAIT_RESOLVE_RAC_CI,</span><br><span style="color: hsl(120, 100%, 40%);">+  NACC_ST_WAIT_REQUEST_SI,</span><br><span style="color: hsl(120, 100%, 40%);">+      NACC_ST_TX_NEIGHBOUR_DATA,</span><br><span style="color: hsl(120, 100%, 40%);">+    NACC_ST_TX_CELL_CHG_CONTINUE,</span><br><span style="color: hsl(120, 100%, 40%);">+ NACC_ST_DONE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct nacc_fsm_ctx {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct GprsMs* ms; /* back pointer */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ctrl_handle *neigh_ctrl;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ctrl_connection *neigh_ctrl_conn;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct neigh_cache_entry_key neigh_key; /* target cell info from MS */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_cell_global_id_ps cgi_ps; /* target cell info resolved from req_{arfcn+bsic} */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct si_cache_value si_info; /* SI info resolved from SGSN, to be sent to MS */</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t si_info_bytes_sent; /* How many bytes out of si_info->si_len were already sent to MS */</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t container_idx; /* Next container_idx to assign when sending Packet Neighbor Data message */</span><br><span style="color: hsl(120, 100%, 40%);">+    bool all_si_info_sent; /* Did we already encode all the SI info? */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* passed as data in NACC_EV_CREATE_RLCMAC_MSG */</span><br><span style="color: hsl(120, 100%, 40%);">+struct nacc_ev_create_rlcmac_msg_ctx {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct msgb *msg; /* to be filled by FSM during event processing */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms);</span><br><span>diff --git a/src/neigh_cache.c b/src/neigh_cache.c</span><br><span>new file mode 100644</span><br><span>index 0000000..cf77dc8</span><br><span>--- /dev/null</span><br><span>+++ b/src/neigh_cache.c</span><br><span>@@ -0,0 +1,254 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* si_cache.c</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Pau Espin Pedrol <pespin@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ * modify it under the terms of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * as published by the Free Software Foundation; either version 2</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program; if not, write to the Free Software</span><br><span style="color: hsl(120, 100%, 40%);">+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <neigh_cache.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define KEEP_TIME_DEFAULT_SEC 5</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*TODO: add a timer to the_pcu T_defs, pass value to struct neigh_cache instead of KEEP_TIME_DEFAULT_SEC */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void neigh_cache_schedule_cleanup(struct neigh_cache *cache);</span><br><span style="color: hsl(120, 100%, 40%);">+static void neigh_cache_cleanup_cb(void *data) {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct timespec now, threshold;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct neigh_cache *cache = (struct neigh_cache *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct neigh_cache_entry *it, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Instead of adding keep_time_intval to each, substract it from now once */</span><br><span style="color: hsl(120, 100%, 40%);">+  timespecsub(&now, &cache->keep_time_intval, &threshold);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(it, tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (timespeccmp(&threshold, &it->update_ts, >))</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(it);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   neigh_cache_schedule_cleanup(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void neigh_cache_schedule_cleanup(struct neigh_cache *cache) {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct neigh_cache_entry *it;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timespec now, threshold, result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* First item is the one with oldest update_ts */</span><br><span style="color: hsl(120, 100%, 40%);">+     it = llist_first_entry_or_null(&cache->list, struct neigh_cache_entry, list);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!it)</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (timespeccmp(&now, &threshold, >=)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Too late, let's flush now synchonously */</span><br><span style="color: hsl(120, 100%, 40%);">+              neigh_cache_cleanup_cb(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              timespecsub(&threshold, &now, &result);</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache *neigh_cache_alloc(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct neigh_cache *cache = talloc_zero(ctx, struct neigh_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+     INIT_LLIST_HEAD(&cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_setup(&cache->cleanup_timer, neigh_cache_cleanup_cb, cache);</span><br><span style="color: hsl(120, 100%, 40%);">+        cache->keep_time_intval = (struct timespec){ .tv_sec = KEEP_TIME_DEFAULT_SEC, .tv_nsec = 0};</span><br><span style="color: hsl(120, 100%, 40%);">+       return cache;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        const struct neigh_cache_entry_key *key,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct osmo_cell_global_id_ps *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct neigh_cache_entry *it;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* First check if it already exists. If so, simply update timer+value */</span><br><span style="color: hsl(120, 100%, 40%);">+      it = neigh_cache_lookup_entry(cache, key);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!it) {</span><br><span style="color: hsl(120, 100%, 40%);">+            it = talloc_zero(cache, struct neigh_cache_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* remove item, we'll add it to the end to have them sorted by last update */</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   it->value = *value;</span><br><span style="color: hsl(120, 100%, 40%);">+        memcpy(&it->value, value, sizeof(it->value));</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_add_tail(&it->list, &cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+        neigh_cache_schedule_cleanup(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+  return it;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   const struct neigh_cache_entry_key *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct neigh_cache_entry *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry(tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (memcmp(&tmp->key, key, sizeof(*key)) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                 return tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                         const struct neigh_cache_entry_key *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (it)</span><br><span style="color: hsl(120, 100%, 40%);">+               return &it->value;</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void neigh_cache_free(struct neigh_cache *cache)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct neigh_cache_entry *it, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!cache)</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(it, tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(it);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_del(&cache->cleanup_timer);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+////////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+// SI CACHE</span><br><span style="color: hsl(120, 100%, 40%);">+///////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*TODO: add a timer to the_pcu T_defs, pass value to struct neigh_cache instead of KEEP_TIME_DEFAULT_SEC */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void si_cache_schedule_cleanup(struct si_cache *cache);</span><br><span style="color: hsl(120, 100%, 40%);">+static void si_cache_cleanup_cb(void *data) {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timespec now, threshold;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct si_cache *cache = (struct si_cache *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct si_cache_entry *it, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Instead of adding keep_time_intval to each, substract it from now once */</span><br><span style="color: hsl(120, 100%, 40%);">+  timespecsub(&now, &cache->keep_time_intval, &threshold);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(it, tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (timespeccmp(&threshold, &it->update_ts, >))</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(it);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   si_cache_schedule_cleanup(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void si_cache_schedule_cleanup(struct si_cache *cache) {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct si_cache_entry *it;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timespec now, threshold, result;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* First item is the one with oldest update_ts */</span><br><span style="color: hsl(120, 100%, 40%);">+     it = llist_first_entry_or_null(&cache->list, struct si_cache_entry, list);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!it)</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_clock_gettime(CLOCK_MONOTONIC, &now);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      timespecadd(&it->update_ts, &cache->keep_time_intval, &threshold);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (timespeccmp(&now, &threshold, >=)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Too late, let's flush now synchonously */</span><br><span style="color: hsl(120, 100%, 40%);">+              si_cache_cleanup_cb(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              timespecsub(&threshold, &now, &result);</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache *si_cache_alloc(void *ctx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct si_cache *cache = talloc_zero(ctx, struct si_cache);</span><br><span style="color: hsl(120, 100%, 40%);">+   INIT_LLIST_HEAD(&cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_setup(&cache->cleanup_timer, si_cache_cleanup_cb, cache);</span><br><span style="color: hsl(120, 100%, 40%);">+   cache->keep_time_intval = (struct timespec){ .tv_sec = KEEP_TIME_DEFAULT_SEC, .tv_nsec = 0};</span><br><span style="color: hsl(120, 100%, 40%);">+       return cache;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_entry *si_cache_add(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const struct osmo_cell_global_id_ps *key,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const struct si_cache_value *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct si_cache_entry *it;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* First check if it already exists. If so, simply update timer+value */</span><br><span style="color: hsl(120, 100%, 40%);">+      it = si_cache_lookup_entry(cache, key);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!it) {</span><br><span style="color: hsl(120, 100%, 40%);">+            it = talloc_zero(cache, struct si_cache_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* remove item, we'll add it to the end to have them sorted by last update */</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   it->value = *value;</span><br><span style="color: hsl(120, 100%, 40%);">+        memcpy(&it->value, value, sizeof(it->value));</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(osmo_clock_gettime(CLOCK_MONOTONIC, &it->update_ts) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_add_tail(&it->list, &cache->list);</span><br><span style="color: hsl(120, 100%, 40%);">+        si_cache_schedule_cleanup(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+     return it;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct osmo_cell_global_id_ps *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct si_cache_entry *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (memcmp(&tmp->key, key, sizeof(*key)) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                 return tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            const struct osmo_cell_global_id_ps *key)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct si_cache_entry *it = si_cache_lookup_entry(cache, key);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (it)</span><br><span style="color: hsl(120, 100%, 40%);">+               return &it->value;</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void si_cache_free(struct si_cache *cache)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct si_cache_entry *it, *tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!cache)</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry_safe(it, tmp, &cache->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_del(&it->list);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(it);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_timer_del(&cache->cleanup_timer);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(cache);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/neigh_cache.h b/src/neigh_cache.h</span><br><span>new file mode 100644</span><br><span>index 0000000..68f5a5d</span><br><span>--- /dev/null</span><br><span>+++ b/src/neigh_cache.h</span><br><span>@@ -0,0 +1,99 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* neigh_cache.h</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Pau Espin Pedrol <pespin@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or</span><br><span style="color: hsl(120, 100%, 40%);">+ * modify it under the terms of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * as published by the Free Software Foundation; either version 2</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program; if not, write to the Free Software</span><br><span style="color: hsl(120, 100%, 40%);">+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm23003.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+////////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+// NEIGH CACHE</span><br><span style="color: hsl(120, 100%, 40%);">+///////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* ARFC+BSIC -> CGI PS cache */</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct llist_head list; /* list of neigh_cache_entry items */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timespec keep_time_intval;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry_key {</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t local_lac;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint16_t local_ci;</span><br><span style="color: hsl(120, 100%, 40%);">+    uint16_t tgt_arfcn;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t tgt_bsic;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+   struct llist_head list; /* to be included in neigh_cache->list */</span><br><span style="color: hsl(120, 100%, 40%);">+  //struct neigh_cache *cache; /* backpointer */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct timespec update_ts;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct neigh_cache_entry_key key;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cell_global_id_ps value;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache *neigh_cache_alloc(void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     const struct neigh_cache_entry_key *key,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct osmo_cell_global_id_ps *value);</span><br><span style="color: hsl(120, 100%, 40%);">+struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            const struct neigh_cache_entry_key *key);</span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                              const struct neigh_cache_entry_key *key);</span><br><span style="color: hsl(120, 100%, 40%);">+void neigh_cache_free(struct neigh_cache *cache);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+////////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+// SI CACHE</span><br><span style="color: hsl(120, 100%, 40%);">+///////////////////</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* CGI-PS-> SI cache */</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head list; /* list of si_cache_entry items */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_timer_list cleanup_timer; /* Timer removing too-old entries */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct timespec keep_time_intval;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_value {</span><br><span style="color: hsl(120, 100%, 40%);">+      char si_buf[512];</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t si_len;</span><br><span style="color: hsl(120, 100%, 40%);">+        bool type_psi;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head list; /* to be included in si_cache->list */</span><br><span style="color: hsl(120, 100%, 40%);">+     //struct si_cache *cache; /* backpointer */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct timespec update_ts;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_cell_global_id_ps key;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct si_cache_value value;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache *si_cache_alloc(void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_entry *si_cache_add(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                              const struct osmo_cell_global_id_ps *key,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const struct si_cache_value *value);</span><br><span style="color: hsl(120, 100%, 40%);">+struct si_cache_entry *si_cache_lookup_entry(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                             const struct osmo_cell_global_id_ps *key);</span><br><span style="color: hsl(120, 100%, 40%);">+const struct si_cache_value *si_cache_lookup_value(struct si_cache *cache,</span><br><span style="color: hsl(120, 100%, 40%);">+                                              const struct osmo_cell_global_id_ps *key);</span><br><span style="color: hsl(120, 100%, 40%);">+void si_cache_free(struct si_cache *cache);</span><br><span>diff --git a/src/pcu_vty.c b/src/pcu_vty.c</span><br><span>index 288f241..b7021c8 100644</span><br><span>--- a/src/pcu_vty.c</span><br><span>+++ b/src/pcu_vty.c</span><br><span>@@ -14,6 +14,7 @@</span><br><span> #include <osmocom/vty/misc.h></span><br><span> #include <osmocom/core/linuxlist.h></span><br><span> #include <osmocom/core/rate_ctr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/ctrl/ports.h></span><br><span> #include <osmocom/pcu/pcuif_proto.h></span><br><span> #include <osmocom/gprs/gprs_ns2.h></span><br><span> #include "pcu_vty.h"</span><br><span>@@ -1018,6 +1019,22 @@</span><br><span>  return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_neighbor_resolution, cfg_neighbor_resolution_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+       "neighbor resolution " VTY_IPV46_CMD " [<0-65535>]",</span><br><span style="color: hsl(120, 100%, 40%);">+       "Manage local and remote-BSS neighbor cells\n"</span><br><span style="color: hsl(120, 100%, 40%);">+       "Connect to Neighbor Resolution Service (CTRL interface) to given ip and port\n"</span><br><span style="color: hsl(120, 100%, 40%);">+       "IPv4 address to connect to\n" "IPv6 address to connect to\n"</span><br><span style="color: hsl(120, 100%, 40%);">+       "Port to connect to (default 4248)\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_talloc_replace_string(the_pcu, &the_pcu->vty.neigh_ctrl_addr, argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (argc > 1)</span><br><span style="color: hsl(120, 100%, 40%);">+              the_pcu->vty.neigh_ctrl_port = atoi(argv[1]);</span><br><span style="color: hsl(120, 100%, 40%);">+      else</span><br><span style="color: hsl(120, 100%, 40%);">+          the_pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;</span><br><span style="color: hsl(120, 100%, 40%);">+   return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN(show_bts_timer, show_bts_timer_cmd,</span><br><span>       "show bts-timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,</span><br><span>       SHOW_STR "Show BTS controlled timers\n"</span><br><span>@@ -1220,6 +1237,7 @@</span><br><span>        install_element(PCU_NODE, &cfg_pcu_no_gsmtap_categ_cmd);</span><br><span>         install_element(PCU_NODE, &cfg_pcu_sock_cmd);</span><br><span>    install_element(PCU_NODE, &cfg_pcu_gb_dialect_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_element(PCU_NODE, &cfg_neighbor_resolution_cmd);</span><br><span>         install_element(PCU_NODE, &cfg_pcu_timer_cmd);</span><br><span> </span><br><span>       install_element_ve(&show_bts_stats_cmd);</span><br><span>diff --git a/src/pdch.cpp b/src/pdch.cpp</span><br><span>index 5a329f3..2028ba2 100644</span><br><span>--- a/src/pdch.cpp</span><br><span>+++ b/src/pdch.cpp</span><br><span>@@ -685,6 +685,33 @@</span><br><span>     gprs_rlcmac_meas_rep(ms, report);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void gprs_rlcmac_pdch::rcv_cell_change_notification(Packet_Cell_Change_Notification_t *notif,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 uint32_t fn, struct pcu_l1_meas *meas)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ GprsMs *ms;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ bts_do_rate_ctr_inc(bts(), CTR_PKT_CELL_CHG_NOTIFICATION);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (notif->Global_TFI.UnionType == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+            struct gprs_rlcmac_ul_tbf *ul_tbf = ul_tbf_by_tfi(notif->Global_TFI.u.UPLINK_TFI);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!ul_tbf) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGP(DRLCMAC, LOGL_NOTICE, "UL TBF TFI=0x%2x not found\n", notif->Global_TFI.u.UPLINK_TFI);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             ms = ul_tbf->ms();</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (notif->Global_TFI.UnionType == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+             struct gprs_rlcmac_dl_tbf *dl_tbf = dl_tbf_by_tfi(notif->Global_TFI.u.DOWNLINK_TFI);</span><br><span style="color: hsl(120, 100%, 40%);">+               if (!dl_tbf) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGP(DRLCMAC, LOGL_NOTICE, "DL TBF TFI=0x%2x not found\n", notif->Global_TFI.u.DOWNLINK_TFI);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             ms = dl_tbf->ms();</span><br><span style="color: hsl(120, 100%, 40%);">+ } else { OSMO_ASSERT(0); }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ms_update_l1_meas(ms, meas);</span><br><span style="color: hsl(120, 100%, 40%);">+  ms_nacc_start(ms, notif);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Received Uplink RLC control block. */</span><br><span> int gprs_rlcmac_pdch::rcv_control_block(const uint8_t *data, uint8_t data_len,</span><br><span>                                         uint32_t fn, struct pcu_l1_meas *meas, enum CodingScheme cs)</span><br><span>@@ -734,6 +761,9 @@</span><br><span>   case MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK:</span><br><span>           /* ignoring it. change the SI to not force sending these? */</span><br><span>                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case MT_PACKET_CELL_CHANGE_NOTIFICATION:</span><br><span style="color: hsl(120, 100%, 40%);">+              rcv_cell_change_notification(&ul_control_block->u.Packet_Cell_Change_Notification, fn, meas);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span>       default:</span><br><span>             bts_do_rate_ctr_inc(bts(), CTR_DECODE_ERRORS);</span><br><span>               LOGP(DRLCMAC, LOGL_NOTICE,</span><br><span>diff --git a/src/pdch.h b/src/pdch.h</span><br><span>index 8871986..d596531 100644</span><br><span>--- a/src/pdch.h</span><br><span>+++ b/src/pdch.h</span><br><span>@@ -139,6 +139,7 @@</span><br><span>        void rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *, uint32_t fn, struct pcu_l1_meas *meas);</span><br><span>     void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn, struct pcu_l1_meas *meas);</span><br><span>      void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);</span><br><span style="color: hsl(120, 100%, 40%);">+     void rcv_cell_change_notification(Packet_Cell_Change_Notification_t *, uint32_t fn, struct pcu_l1_meas *meas);</span><br><span>       gprs_rlcmac_tbf *tbf_from_list_by_tfi(</span><br><span>               LListHead<gprs_rlcmac_tbf> *tbf_list, uint8_t tfi,</span><br><span>             enum gprs_rlcmac_tbf_direction dir);</span><br><span>diff --git a/src/tbf.cpp b/src/tbf.cpp</span><br><span>index 37af21f..5b2fe3d 100644</span><br><span>--- a/src/tbf.cpp</span><br><span>+++ b/src/tbf.cpp</span><br><span>@@ -1197,3 +1197,8 @@</span><br><span> {</span><br><span>   return tbf->is_tfi_assigned();</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+uint8_t tbf_tfi(const struct gprs_rlcmac_tbf *tbf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return tbf->tfi();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/tbf.h b/src/tbf.h</span><br><span>index d616076..815d254 100644</span><br><span>--- a/src/tbf.h</span><br><span>+++ b/src/tbf.h</span><br><span>@@ -204,6 +204,7 @@</span><br><span> uint8_t tbf_dl_slots(const struct gprs_rlcmac_tbf *tbf);</span><br><span> uint8_t tbf_ul_slots(const struct gprs_rlcmac_tbf *tbf);</span><br><span> bool tbf_is_tfi_assigned(const struct gprs_rlcmac_tbf *tbf);</span><br><span style="color: hsl(120, 100%, 40%);">+uint8_t tbf_tfi(const struct gprs_rlcmac_tbf *tbf);</span><br><span> int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);</span><br><span> #ifdef __cplusplus</span><br><span> }</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index c599636..a7771b9 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -1,4 +1,4 @@</span><br><span style="color: hsl(0, 100%, 40%);">-AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/ -I$(top_srcdir)/include/</span><br><span style="color: hsl(120, 100%, 40%);">+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/ -I$(top_srcdir)/include/</span><br><span> AM_LDFLAGS = -lrt -no-install</span><br><span> </span><br><span> check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest alloc/MslotTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest codel/codel_test edge/EdgeTest bitcomp/BitcompTest fn/FnTest app_info/AppInfoTest</span><br><span>@@ -15,6 +15,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -23,6 +24,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -31,6 +33,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> tbf_TbfTest_LDFLAGS = -Wl,--wrap=pcu_sock_send</span><br><span>@@ -38,6 +41,7 @@</span><br><span> bitcomp_BitcompTest_SOURCES = bitcomp/BitcompTest.cpp ../src/egprs_rlc_compression.cpp</span><br><span> bitcomp_BitcompTest_LDADD = \</span><br><span>        $(top_builddir)/src/libgprs.la \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -46,6 +50,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -56,6 +61,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -64,6 +70,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -72,6 +79,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -83,6 +91,7 @@</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -97,6 +106,7 @@</span><br><span> codel_codel_test_SOURCES = codel/codel_test.c</span><br><span> codel_codel_test_LDADD = \</span><br><span>         $(top_builddir)/src/libgprs.la \</span><br><span style="color: hsl(120, 100%, 40%);">+      $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -105,6 +115,7 @@</span><br><span>       $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span>@@ -113,6 +124,7 @@</span><br><span>       $(top_builddir)/src/libgprs.la \</span><br><span>     $(LIBOSMOGB_LIBS) \</span><br><span>  $(LIBOSMOGSM_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+  $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOCORE_LIBS) \</span><br><span>        $(COMMON_LA)</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-pcu/+/22385">change 22385</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-pcu/+/22385"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-pcu </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca </div>
<div style="display:none"> Gerrit-Change-Number: 22385 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>