<p>laforge <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/15373">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Cell Broadcast: CBSP and CBCH scheduling support<br><br>This adds code to handle CBSP (Cell Broadcast Service Protocol)<br>from the CBC (Cell Broadcast Centre), as well as BSC-internal data<br>structures for scheduling the various SMSCB on the CBCH of each BTS.<br><br>There are currently one known shortcoming in the code: We don't yet<br>verify if keepalives are received within repetition period.<br><br>Change-Id: Ia0a0de862a104d0f447a5d6e56c7c83981b825c7<br>---<br>A doc/manuals/chapters/smscb.adoc<br>M doc/manuals/osmobsc-usermanual.adoc<br>M doc/manuals/vty/bsc_vty_reference.xml<br>M include/osmocom/bsc/Makefile.am<br>M include/osmocom/bsc/bsc_msc_data.h<br>M include/osmocom/bsc/debug.h<br>M include/osmocom/bsc/gsm_data.h<br>A include/osmocom/bsc/smscb.h<br>M include/osmocom/bsc/vty.h<br>M src/osmo-bsc/Makefile.am<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bsc_init.c<br>M src/osmo-bsc/bsc_vty.c<br>A src/osmo-bsc/cbch_scheduler.c<br>A src/osmo-bsc/cbsp_link.c<br>M src/osmo-bsc/gsm_data.c<br>M src/osmo-bsc/osmo_bsc_main.c<br>A src/osmo-bsc/smscb.c<br>M tests/handover/Makefile.am<br>19 files changed, 1,898 insertions(+), 7 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/manuals/chapters/smscb.adoc b/doc/manuals/chapters/smscb.adoc</span><br><span>new file mode 100644</span><br><span>index 0000000..f7469a3</span><br><span>--- /dev/null</span><br><span>+++ b/doc/manuals/chapters/smscb.adoc</span><br><span>@@ -0,0 +1,82 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[[smscb]]</span><br><span style="color: hsl(120, 100%, 40%);">+== SMSCB (Cell Broadcast)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC supports SMS Cell Broadcast (SMSCB) services (CBS).  This</span><br><span style="color: hsl(120, 100%, 40%);">+includes the CBSP protocol to interact with a CBC (Cell Broadcast</span><br><span style="color: hsl(120, 100%, 40%);">+Centre) such as OsmoCBC, as well as the scheduling of SMSCB messages on</span><br><span style="color: hsl(120, 100%, 40%);">+both the BASIC and EXTENDED CBCH and transmission of related RSL</span><br><span style="color: hsl(120, 100%, 40%);">+messages to the attached BTS.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+More high-level information can be found at</span><br><span style="color: hsl(120, 100%, 40%);">+https://en.wikipedia.org/wiki/Cell_Broadcast and the related</span><br><span style="color: hsl(120, 100%, 40%);">+specification is <<3gpp-ts-23-041>>.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+In order to use SMSCB with OsmoBSC, you will need to</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+* Configure the CBSP server and/or client</span><br><span style="color: hsl(120, 100%, 40%);">+* Use a channel combination including a CBCH on the BTSs</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+=== Enabling a CBCH channel combination</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+On the Um interface, SMSCB are transmitted via the CBCH (Cell Broadcast</span><br><span style="color: hsl(120, 100%, 40%);">+Channel).  The CBCH is a separate downlink-only logical channel which</span><br><span style="color: hsl(120, 100%, 40%);">+must be activated on any of the BTSs requiring CBSP support.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The channel combination is configured in the `timeslot` node of each TRX.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The two `phys_chan_config` supporting CBCH are `CCCH+SDCCH4+CBCH` and</span><br><span style="color: hsl(120, 100%, 40%);">+`SDCCH/8+CBCH`.  Please note that the CBCH steals one of the SDCCH, so</span><br><span style="color: hsl(120, 100%, 40%);">+a SDCCH/4 will only have three remaining SDCCH, and a SDCCH/8 will</span><br><span style="color: hsl(120, 100%, 40%);">+have only seven remaining SDCCH.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+=== Configuring the CBSP connection</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+CBSP is the protocol between BSC and CBC.  It operates over TCP.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+According to 3GPP TS 48.049, a BSC typically operates as a TCP server,</span><br><span style="color: hsl(120, 100%, 40%);">+and the CBC connects as TCP client.  This would require the CBC to have</span><br><span style="color: hsl(120, 100%, 40%);">+out-of-band knowledge of all the BSCs in the network (and their IP</span><br><span style="color: hsl(120, 100%, 40%);">+addresses).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+In order to comply with the specifications, OsmoBSC supports this mode</span><br><span style="color: hsl(120, 100%, 40%);">+of operation as CBSP TCP server.  However, to make network operation and</span><br><span style="color: hsl(120, 100%, 40%);">+configuration more simple, it also can operate in TCP client mode,</span><br><span style="color: hsl(120, 100%, 40%);">+connecting to the CBC.  This way the all the BSCs need to know is the CBC IP</span><br><span style="color: hsl(120, 100%, 40%);">+address, but not vice-versa.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The BSC can operate both CBSP TCP server and CBSP TCP client mode in</span><br><span style="color: hsl(120, 100%, 40%);">+parallel.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The CBC related configuration of OsmoBSC can be found in the `cbc` configuration</span><br><span style="color: hsl(120, 100%, 40%);">+node of the VTY interface.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+.Example: Configure CBSP TCP client to connect to CBC at 1.2.3.4:48049</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC> enable</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config)# cbc</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# remote-ip 1.2.3.4</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# remote-port 48049</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# end</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%);">+.Example: Disable CBSP TCP client</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC> enable</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config)# cbc</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# no remote-ip</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# end</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%);">+.Example: Configure CBSP TCP server to listen for CBC at 127.0.0.2:9999</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC> enable</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC# configure terminal</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config)# cbc</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# listen-ip 127.0.0.2</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# listen-port 9999</span><br><span style="color: hsl(120, 100%, 40%);">+OsmoBSC(config-cbc)# end</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%);">+For more details on the available configuration commands, please check the OsmoBSC VTY Reference.</span><br><span>diff --git a/doc/manuals/osmobsc-usermanual.adoc b/doc/manuals/osmobsc-usermanual.adoc</span><br><span>index 766a11f..26f49da 100644</span><br><span>--- a/doc/manuals/osmobsc-usermanual.adoc</span><br><span>+++ b/doc/manuals/osmobsc-usermanual.adoc</span><br><span>@@ -24,6 +24,8 @@</span><br><span> </span><br><span> include::{srcdir}/chapters/handover.adoc[]</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+include::{srcdir}/chapters/smscb.adoc[]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> include::./common/chapters/counters-overview.adoc[]</span><br><span> </span><br><span> include::{srcdir}/chapters/counters.adoc[]</span><br><span>@@ -34,8 +36,6 @@</span><br><span> </span><br><span> include::{srcdir}/chapters/control.adoc[]</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-include::./common/chapters/cell-broadcast.adoc[]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> include::{srcdir}/chapters/osmux_bsc.adoc[]</span><br><span> </span><br><span> include::./common/chapters/port_numbers.adoc[]</span><br><span>diff --git a/doc/manuals/vty/bsc_vty_reference.xml b/doc/manuals/vty/bsc_vty_reference.xml</span><br><span>index 85c0cb7..6a3e4fb 100644</span><br><span>--- a/doc/manuals/vty/bsc_vty_reference.xml</span><br><span>+++ b/doc/manuals/vty/bsc_vty_reference.xml</span><br><span>@@ -320,7 +320,7 @@</span><br><span>         <param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(0, 100%, 40%);">-    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|cbs|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span>       <params></span><br><span>         <param name='logging' doc='Configure logging' /></span><br><span>         <param name='level' doc='Set the log level for a specified category' /></span><br><span>@@ -343,6 +343,7 @@</span><br><span>         <param name='chan' doc='lchan FSM' /></span><br><span>         <param name='ts' doc='timeslot FSM' /></span><br><span>         <param name='as' doc='assignment FSM' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='cbs' doc='Cell Broadcast System' /></span><br><span>         <param name='lglobal' doc='Library-internal global log family' /></span><br><span>         <param name='llapd' doc='LAPD in libosmogsm' /></span><br><span>         <param name='linp' doc='A-bis Intput Subsystem' /></span><br><span>@@ -514,6 +515,22 @@</span><br><span>         <param name='all' doc='Display a list of all FSM instances of all finite state machine' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='show cbc'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='show' doc='Show running system information' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='cbc' doc='Display state of CBC / CBSP' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='show bts &lt;0-255&gt; smscb [(basic|extended)]'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='show' doc='Show running system information' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='bts' doc='Display information about a BTS' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='&lt;0-255&gt;' doc='BTS number' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='smscb' doc='SMS Cell Broadcast State' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='[basic]' doc='Show only information related to CBCH BASIC' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='[extended]' doc='Show only information related to CBCH EXTENDED' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span>     <command id='show statistics'></span><br><span>       <params></span><br><span>         <param name='show' doc='Show running system information' /></span><br><span>@@ -971,7 +988,7 @@</span><br><span>         <param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(0, 100%, 40%);">-    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|cbs|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span>       <params></span><br><span>         <param name='logging' doc='Configure logging' /></span><br><span>         <param name='level' doc='Set the log level for a specified category' /></span><br><span>@@ -994,6 +1011,7 @@</span><br><span>         <param name='chan' doc='lchan FSM' /></span><br><span>         <param name='ts' doc='timeslot FSM' /></span><br><span>         <param name='as' doc='assignment FSM' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='cbs' doc='Cell Broadcast System' /></span><br><span>         <param name='lglobal' doc='Library-internal global log family' /></span><br><span>         <param name='llapd' doc='LAPD in libosmogsm' /></span><br><span>         <param name='linp' doc='A-bis Intput Subsystem' /></span><br><span>@@ -1356,6 +1374,16 @@</span><br><span>         <param name='all' doc='Display a list of all FSM instances of all finite state machine' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='show bts &lt;0-255&gt; smscb [(basic|extended)]'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='show' doc='Show running system information' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='bts' doc='Display information about a BTS' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='&lt;0-255&gt;' doc='BTS number' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='smscb' doc='SMS Cell Broadcast State' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='[basic]' doc='Show only information related to CBCH BASIC' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='[extended]' doc='Show only information related to CBCH EXTENDED' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span>     <command id='show statistics'></span><br><span>       <params></span><br><span>         <param name='show' doc='Show running system information' /></span><br><span>@@ -1735,6 +1763,11 @@</span><br><span>         <param name='e1_input' doc='Configure E1/T1/J1 TDM input' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='cbc'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='cbc' doc='Configure CBSP Link to Cell Broadcast Centre' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span>     <command id='msc [&lt;0-1000&gt;]'></span><br><span>       <params></span><br><span>         <param name='msc' doc='Configure MSC details' /></span><br><span>@@ -1833,7 +1866,7 @@</span><br><span>         <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' /></span><br><span>       </params></span><br><span>     </command></span><br><span style="color: hsl(0, 100%, 40%);">-    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='logging level (rll|mm|rr|rsl|nm|pag|meas|msc|ho|hodec|ref|nat|ctrl|filter|pcu|lcls|chan|ts|as|cbs|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'></span><br><span>       <params></span><br><span>         <param name='logging' doc='Configure logging' /></span><br><span>         <param name='level' doc='Set the log level for a specified category' /></span><br><span>@@ -1856,6 +1889,7 @@</span><br><span>         <param name='chan' doc='lchan FSM' /></span><br><span>         <param name='ts' doc='timeslot FSM' /></span><br><span>         <param name='as' doc='assignment FSM' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='cbs' doc='Cell Broadcast System' /></span><br><span>         <param name='lglobal' doc='Library-internal global log family' /></span><br><span>         <param name='llapd' doc='LAPD in libosmogsm' /></span><br><span>         <param name='linp' doc='A-bis Intput Subsystem' /></span><br><span>@@ -4930,4 +4964,43 @@</span><br><span>       </params></span><br><span>     </command></span><br><span>   </node></span><br><span style="color: hsl(120, 100%, 40%);">+  <node id='config-cbc'></span><br><span style="color: hsl(120, 100%, 40%);">+    <name>config-cbc</name></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='remote-ip A.B.C.D'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='remote-ip' doc='IP Address of the Cell Broadcast Centre' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='A.B.C.D' doc='IP Address of the Cell Broadcast Centre' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='no remote-ip'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='no' doc='Negate a command or set its defaults' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='remote-ip' doc='Remove IP address of CBC; disables outbound CBSP connections' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='remote-port &lt;1-65535&gt;'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='remote-port' doc='TCP Port number of the Cell Broadcast Centre (Default: 48049)' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='&lt;1-65535&gt;' doc='TCP Port number of the Cell Broadcast Centre (Default: 48049)' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='listen-port &lt;1-65535&gt;'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='listen-port' doc='Local TCP port at which BSC listens for incoming CBSP connections from CBC' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='&lt;1-65535&gt;' doc='Local TCP port at which BSC listens for incoming CBSP connections from CBC' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='no listen-port'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='no' doc='Negate a command or set its defaults' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='listen-port' doc='Remove CBSP Listen Port; disables inbound CBSP connections' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+    <command id='listen-ip A.B.C.D'></span><br><span style="color: hsl(120, 100%, 40%);">+      <params></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='listen-ip' doc='Local IP Address where BSC listens for incoming CBC connections (Default: 0.0.0.0)' /></span><br><span style="color: hsl(120, 100%, 40%);">+        <param name='A.B.C.D' doc='Local IP Address where BSC listens for incoming CBC connections' /></span><br><span style="color: hsl(120, 100%, 40%);">+      </params></span><br><span style="color: hsl(120, 100%, 40%);">+    </command></span><br><span style="color: hsl(120, 100%, 40%);">+  </node></span><br><span> </vtydoc></span><br><span>diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am</span><br><span>index f44e7fc..396604e 100644</span><br><span>--- a/include/osmocom/bsc/Makefile.am</span><br><span>+++ b/include/osmocom/bsc/Makefile.am</span><br><span>@@ -55,4 +55,5 @@</span><br><span>  gsm_08_08.h \</span><br><span>        penalty_timers.h \</span><br><span>   osmo_bsc_lcls.h \</span><br><span style="color: hsl(120, 100%, 40%);">+     smscb.h \</span><br><span>    $(NULL)</span><br><span>diff --git a/include/osmocom/bsc/bsc_msc_data.h b/include/osmocom/bsc/bsc_msc_data.h</span><br><span>index 5612483..b9df4ba 100644</span><br><span>--- a/include/osmocom/bsc/bsc_msc_data.h</span><br><span>+++ b/include/osmocom/bsc/bsc_msc_data.h</span><br><span>@@ -150,6 +150,7 @@</span><br><span> /*</span><br><span>  * Per BSC data.</span><br><span>  */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bsc_cbc_link;</span><br><span> struct osmo_bsc_data {</span><br><span>  struct gsm_network *network;</span><br><span> </span><br><span>@@ -167,6 +168,8 @@</span><br><span>       char *ussd_no_msc_txt;</span><br><span> </span><br><span>   char *acc_lst_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bsc_cbc_link *cbc;</span><br><span> };</span><br><span> </span><br><span> </span><br><span>diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h</span><br><span>index 3260121..adc6abb 100644</span><br><span>--- a/include/osmocom/bsc/debug.h</span><br><span>+++ b/include/osmocom/bsc/debug.h</span><br><span>@@ -27,6 +27,7 @@</span><br><span>         DCHAN,</span><br><span>       DTS,</span><br><span>         DAS,</span><br><span style="color: hsl(120, 100%, 40%);">+  DCBS,</span><br><span>        Debug_LastEntry,</span><br><span> };</span><br><span> </span><br><span>diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h</span><br><span>index d82d1ba..8dfbc64 100644</span><br><span>--- a/include/osmocom/bsc/gsm_data.h</span><br><span>+++ b/include/osmocom/bsc/gsm_data.h</span><br><span>@@ -14,6 +14,7 @@</span><br><span> #include <osmocom/core/stat_item.h></span><br><span> #include <osmocom/gsm/bts_features.h></span><br><span> #include <osmocom/gsm/protocol/gsm_08_08.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_48_049.h></span><br><span> #include <osmocom/gsm/gsm0808.h></span><br><span> #include <osmocom/gsm/gsm48.h></span><br><span> #include <osmocom/core/fsm.h></span><br><span>@@ -957,6 +958,53 @@</span><br><span>     struct gsm_bts *bts;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* A single Page of a SMSCB message */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_page {</span><br><span style="color: hsl(120, 100%, 40%);">+       /* SMSCB message we're part of */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bts_smscb_message *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Page Number within message (1 to 15) */</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t nr;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* number of valid blocks in data (up to 4) */</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t num_blocks;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* up to four blocks of 22 bytes each */</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t data[88];</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%);">+/* A SMSCB message (received from CBSP) */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_message {</span><br><span style="color: hsl(120, 100%, 40%);">+       /* entry in bts_smscb_chan_state.messages */</span><br><span style="color: hsl(120, 100%, 40%);">+  struct llist_head list;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* input data from CBSP (CBC) side */</span><br><span style="color: hsl(120, 100%, 40%);">+         uint16_t msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+              uint16_t serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+           enum cbsp_category category;</span><br><span style="color: hsl(120, 100%, 40%);">+          uint16_t rep_period;</span><br><span style="color: hsl(120, 100%, 40%);">+          uint16_t num_bcast_req;</span><br><span style="color: hsl(120, 100%, 40%);">+               uint8_t dcs;</span><br><span style="color: hsl(120, 100%, 40%);">+  } input;</span><br><span style="color: hsl(120, 100%, 40%);">+      /* how often have all pages of this message been broadcast? */</span><br><span style="color: hsl(120, 100%, 40%);">+        uint32_t bcast_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* actual page data of this message */</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t num_pages; /* up to 15 */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bts_smscb_page page[15];</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%);">+/* per-channel (basic/extended) CBCH state for a single BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_chan_state {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* back-pointer to BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_bts *bts;</span><br><span style="color: hsl(120, 100%, 40%);">+  /* list of bts_smscb_message */</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head messages;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* scheduling array; pointer of SMSCB pages */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct bts_smscb_page **sched_arr;</span><br><span style="color: hsl(120, 100%, 40%);">+    size_t sched_arr_size;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* index of the next to be transmitted page into the scheduler array */</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t next_idx;</span><br><span style="color: hsl(120, 100%, 40%);">+      /* number of messages we have to pause due to overflow */</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t overflow;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* One BTS */</span><br><span> struct gsm_bts {</span><br><span>         /* list header in net->bts_list */</span><br><span>@@ -1213,6 +1261,11 @@</span><br><span>       struct load_counter chan_load_samples[7];</span><br><span>    int chan_load_samples_idx;</span><br><span>   uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* cell broadcast system */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_timer_list cbch_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bts_smscb_chan_state cbch_basic;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bts_smscb_chan_state cbch_extended;</span><br><span> };</span><br><span> </span><br><span> /* One rejected BTS */</span><br><span>diff --git a/include/osmocom/bsc/smscb.h b/include/osmocom/bsc/smscb.h</span><br><span>new file mode 100644</span><br><span>index 0000000..22a258d</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/bsc/smscb.h</span><br><span>@@ -0,0 +1,59 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/netif/stream.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/cbsp.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct bsc_cbc_link;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* smscb.c */</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_smscb_del(struct bts_smscb_message *smscb, struct bts_smscb_chan_state *cstate,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const char *reason);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *bts_smscb_msg2str(const struct bts_smscb_message *smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_chan_state *bts_get_smscb_chan(struct gsm_bts *bts, bool extended);</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_rx_decoded(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec);</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_tx_restart(struct bsc_cbc_link *cbc, bool is_emerg);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *bts_smscb_chan_state_name(const struct bts_smscb_chan_state *cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int bts_smscb_chan_load_percent(const struct bts_smscb_chan_state *cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+unsigned int bts_smscb_chan_page_count(const struct bts_smscb_chan_state *cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+void smscb_vty_init(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* cbch_scheduler.c */</span><br><span style="color: hsl(120, 100%, 40%);">+int bts_smscb_gen_sched_arr(struct bts_smscb_chan_state *cstate, struct bts_smscb_page ***arr_out);</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_page *bts_smscb_pull_page(struct bts_smscb_chan_state *cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_smscb_page_done(struct bts_smscb_chan_state *cstate, struct bts_smscb_page *page);</span><br><span style="color: hsl(120, 100%, 40%);">+int bts_smscb_rx_cbch_load_ind(struct gsm_bts *bts, bool cbch_extended, bool is_overflow,</span><br><span style="color: hsl(120, 100%, 40%);">+                           uint8_t slot_count);</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_cbch_timer_schedule(struct gsm_bts *bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* cbsp_link.c */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bsc_cbc_link {</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_network *net;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* hostname/IP of CBC */</span><br><span style="color: hsl(120, 100%, 40%);">+              char *cbc_hostname;</span><br><span style="color: hsl(120, 100%, 40%);">+           /* TCP port (Default: 48049) of CBC */</span><br><span style="color: hsl(120, 100%, 40%);">+                int cbc_port;</span><br><span style="color: hsl(120, 100%, 40%);">+         /* local listening port (0 for disabling local server) */</span><br><span style="color: hsl(120, 100%, 40%);">+             int listen_port;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* local listening hostname/IP */</span><br><span style="color: hsl(120, 100%, 40%);">+             char *listen_hostname;</span><br><span style="color: hsl(120, 100%, 40%);">+        } config;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* for handling inbound TCP connections */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct osmo_stream_srv *srv;</span><br><span style="color: hsl(120, 100%, 40%);">+          struct osmo_stream_srv_link *link;</span><br><span style="color: hsl(120, 100%, 40%);">+            char *sock_name;</span><br><span style="color: hsl(120, 100%, 40%);">+              struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     } server;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* for handling outbound TCP connections */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct osmo_stream_cli *cli;</span><br><span style="color: hsl(120, 100%, 40%);">+          char *sock_name;</span><br><span style="color: hsl(120, 100%, 40%);">+              struct msgb *msg;</span><br><span style="color: hsl(120, 100%, 40%);">+     } client;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+void cbc_vty_init(void);</span><br><span style="color: hsl(120, 100%, 40%);">+int bsc_cbc_link_restart(void);</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_tx_decoded(struct bsc_cbc_link *cbc, struct osmo_cbsp_decoded *decoded);</span><br><span>diff --git a/include/osmocom/bsc/vty.h b/include/osmocom/bsc/vty.h</span><br><span>index 7e3c505..10ce16b 100644</span><br><span>--- a/include/osmocom/bsc/vty.h</span><br><span>+++ b/include/osmocom/bsc/vty.h</span><br><span>@@ -24,6 +24,7 @@</span><br><span>     OM2K_NODE,</span><br><span>   OM2K_CON_GROUP_NODE,</span><br><span>         BSC_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+     CBC_NODE,</span><br><span> };</span><br><span> </span><br><span> struct log_info;</span><br><span>diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am</span><br><span>index d50515b..51d8875 100644</span><br><span>--- a/src/osmo-bsc/Makefile.am</span><br><span>+++ b/src/osmo-bsc/Makefile.am</span><br><span>@@ -88,6 +88,9 @@</span><br><span>     rest_octets.c \</span><br><span>      system_information.c \</span><br><span>       timeslot_fsm.c \</span><br><span style="color: hsl(120, 100%, 40%);">+      smscb.c \</span><br><span style="color: hsl(120, 100%, 40%);">+     cbch_scheduler.c \</span><br><span style="color: hsl(120, 100%, 40%);">+    cbsp_link.c \</span><br><span>        $(NULL)</span><br><span> </span><br><span> osmo_bsc_LDADD = \</span><br><span>@@ -96,6 +99,7 @@</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(LIBOSMOVTY_LIBS) \</span><br><span>         $(LIBOSMOCTRL_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMONETIF_LIBS) \</span><br><span>       $(COVERAGE_LDFLAGS) \</span><br><span>        $(LIBOSMOABIS_LIBS) \</span><br><span>        $(LIBOSMOSIGTRAN_LIBS) \</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index 0b68d7c..06d19a5 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -1,7 +1,7 @@</span><br><span> /* GSM Radio Signalling Link messages on the A-bis interface</span><br><span>  * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+/* (C) 2008-2019 by Harald Welte <laforge@gnumonks.org></span><br><span>  * (C) 2012 by Holger Hans Peter Freyther</span><br><span>  *</span><br><span>  * All Rights Reserved</span><br><span>@@ -52,6 +52,7 @@</span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span> #include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span> #include <osmocom/bsc/handover_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span> </span><br><span> #define RSL_ALLOC_SIZE          1024</span><br><span> #define RSL_ALLOC_HEADROOM      128</span><br><span>@@ -1489,6 +1490,36 @@</span><br><span>         return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* 8.5.9 current load on the CBCH (Cell Broadcast) */</span><br><span style="color: hsl(120, 100%, 40%);">+static int rsl_rx_cbch_load(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct e1inp_sign_link *sign_link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_bts *bts = sign_link->trx->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+      bool cbch_extended = false;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool is_overflow = false;</span><br><span style="color: hsl(120, 100%, 40%);">+     int8_t load_info;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct tlv_parsed tp;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t slot_count;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg) - sizeof(*rslh));</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!TLVP_PRESENT(&tp, RSL_IE_CBCH_LOAD_INFO)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_BTS(bts, DRSL, LOGL_ERROR, "CBCH LOAD IND without mandatory CBCH Load Info IE\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* 9.4.43 */</span><br><span style="color: hsl(120, 100%, 40%);">+  load_info = *TLVP_VAL(&tp, RSL_IE_CBCH_LOAD_INFO);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (load_info & 0x80)</span><br><span style="color: hsl(120, 100%, 40%);">+             is_overflow = true;</span><br><span style="color: hsl(120, 100%, 40%);">+   slot_count = load_info & 0x0F;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (TLVP_PRES_LEN(&tp, RSL_IE_SMSCB_CHAN_INDICATOR, 1) &&</span><br><span style="color: hsl(120, 100%, 40%);">+     (*TLVP_VAL(&tp, RSL_IE_SMSCB_CHAN_INDICATOR) & 0x0F) == 0x01)</span><br><span style="color: hsl(120, 100%, 40%);">+             cbch_extended = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return bts_smscb_rx_cbch_load_ind(bts, cbch_extended, is_overflow, slot_count);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Ericsson specific: Immediate Assign Sent */</span><br><span> static int rsl_rx_ericsson_imm_assign_sent(struct msgb *msg)</span><br><span> {</span><br><span>@@ -1537,7 +1568,7 @@</span><br><span>          break;</span><br><span>       case RSL_MT_CBCH_LOAD_IND:</span><br><span>           /* current load on the CBCH */</span><br><span style="color: hsl(0, 100%, 40%);">-          /* FIXME: handle this. Ignore for now */</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = rsl_rx_cbch_load(msg);</span><br><span>          break;</span><br><span>       case RSL_MT_ERICSSON_IMM_ASS_SENT:</span><br><span>           rc = rsl_rx_ericsson_imm_assign_sent(msg);</span><br><span>diff --git a/src/osmo-bsc/bsc_init.c b/src/osmo-bsc/bsc_init.c</span><br><span>index 7d29d4f..18776f3 100644</span><br><span>--- a/src/osmo-bsc/bsc_init.c</span><br><span>+++ b/src/osmo-bsc/bsc_init.c</span><br><span>@@ -37,6 +37,9 @@</span><br><span> #include <osmocom/bsc/gsm_04_08_rr.h></span><br><span> #include <osmocom/bsc/neighbor_ident.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_48_049.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include <time.h></span><br><span> #include <limits.h></span><br><span> #include <stdbool.h></span><br><span>@@ -244,6 +247,11 @@</span><br><span>                 talloc_free(net);</span><br><span>            return NULL;</span><br><span>         }</span><br><span style="color: hsl(120, 100%, 40%);">+     net->bsc_data->cbc = talloc_zero(net->bsc_data, struct bsc_cbc_link);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!net->bsc_data->cbc) {</span><br><span style="color: hsl(120, 100%, 40%);">+              talloc_free(net);</span><br><span style="color: hsl(120, 100%, 40%);">+             return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span> </span><br><span>        /* Init back pointer */</span><br><span>      net->bsc_data->auto_off_timeout = -1;</span><br><span>@@ -272,6 +280,13 @@</span><br><span>   osmo_timer_setup(&net->t3122_chan_load_timer, update_t3122_chan_load_timer, net);</span><br><span>     osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      net->bsc_data->cbc->net = net;</span><br><span style="color: hsl(120, 100%, 40%);">+       /* no cbc_hostname: client not started by default */</span><br><span style="color: hsl(120, 100%, 40%);">+  net->bsc_data->cbc->config.cbc_port = CBSP_TCP_PORT;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* listen_port == -1: server not started by default */</span><br><span style="color: hsl(120, 100%, 40%);">+        net->bsc_data->cbc->config.listen_port = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ net->bsc_data->cbc->config.listen_hostname = talloc_strdup(net->bsc_data->cbc, "127.0.0.1");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      return net;</span><br><span> }</span><br><span> </span><br><span>diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c</span><br><span>index 6de2d4b..06c06de 100644</span><br><span>--- a/src/osmo-bsc/bsc_vty.c</span><br><span>+++ b/src/osmo-bsc/bsc_vty.c</span><br><span>@@ -70,6 +70,7 @@</span><br><span> #include <osmocom/bsc/timeslot_fsm.h></span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span> #include <osmocom/bsc/lchan_select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span> #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h></span><br><span> </span><br><span> #include <inttypes.h></span><br><span>@@ -318,6 +319,14 @@</span><br><span>           vty_out(vty, " (%d)", count);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void bts_dump_vty_cbch(struct vty *vty, const struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        vty_out(vty, "  CBCH %s: %u messages, %u pages, %lu-entry sched_arr, %u%% load%s",</span><br><span style="color: hsl(120, 100%, 40%);">+          bts_smscb_chan_state_name(cstate), llist_count(&cstate->messages),</span><br><span style="color: hsl(120, 100%, 40%);">+             bts_smscb_chan_page_count(cstate), cstate->sched_arr_size,</span><br><span style="color: hsl(120, 100%, 40%);">+         bts_smscb_chan_load_percent(cstate), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void bts_dump_vty_features(struct vty *vty, struct gsm_bts *bts)</span><br><span> {</span><br><span>      unsigned int i;</span><br><span>@@ -504,6 +513,9 @@</span><br><span>        vty_out(vty, "  Current Channel Load:%s", VTY_NEWLINE);</span><br><span>    dump_pchan_load_vty(vty, "    ", &pl);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+      bts_dump_vty_cbch(vty, &bts->cbch_basic);</span><br><span style="color: hsl(120, 100%, 40%);">+      bts_dump_vty_cbch(vty, &bts->cbch_extended);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        vty_out(vty, "  Channel Requests        : %"PRIu64" total, %"PRIu64" no channel%s",</span><br><span>            bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL].current,</span><br><span>               bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL].current,</span><br><span>@@ -5427,6 +5439,8 @@</span><br><span>  osmo_fsm_vty_add_cmds();</span><br><span> </span><br><span>         ho_vty_init();</span><br><span style="color: hsl(120, 100%, 40%);">+        cbc_vty_init();</span><br><span style="color: hsl(120, 100%, 40%);">+       smscb_vty_init();</span><br><span> </span><br><span>        bsc_vty_init_extra();</span><br><span> </span><br><span>diff --git a/src/osmo-bsc/cbch_scheduler.c b/src/osmo-bsc/cbch_scheduler.c</span><br><span>new file mode 100644</span><br><span>index 0000000..ef93b7a</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/cbch_scheduler.c</span><br><span>@@ -0,0 +1,287 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* CBCH (Cell Broadcast Channel) Scheduler for OsmoBSC */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</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 modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (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 Affero 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 Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</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%);">+#include <osmocom/core/stats.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/abis_rsl.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* add all pages of given SMSCB so they appear as soon as possible *after* (included) base_idx. */</span><br><span style="color: hsl(120, 100%, 40%);">+static int bts_smscb_sched_add_after(struct bts_smscb_page **sched_arr, int sched_arr_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  int base_idx, struct bts_smscb_message *smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int arr_idx = base_idx;</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%);">+      OSMO_ASSERT(smscb->num_pages <= ARRAY_SIZE(smscb->page));</span><br><span style="color: hsl(120, 100%, 40%);">+    for (i = 0; i < smscb->num_pages; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                while (sched_arr[arr_idx]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  arr_idx++;</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (arr_idx >= sched_arr_size)</span><br><span style="color: hsl(120, 100%, 40%);">+                             return -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             sched_arr[arr_idx] = &smscb->page[i];</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return arr_idx;</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%);">+/* add all pages of given smscb so they appear *before* (included) last_idx. */</span><br><span style="color: hsl(120, 100%, 40%);">+static int bts_smscb_sched_add_before(struct bts_smscb_page **sched_arr, int sched_arr_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                                 int last_idx, struct bts_smscb_message *smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int arr_idx = last_idx;</span><br><span style="color: hsl(120, 100%, 40%);">+       int last_used_idx = 0;</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%);">+      OSMO_ASSERT(smscb->num_pages <= ARRAY_SIZE(smscb->page));</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(smscb->num_pages >= 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = smscb->num_pages - 1; i >= 0; i--) {</span><br><span style="color: hsl(120, 100%, 40%);">+           while (sched_arr[arr_idx]) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  arr_idx--;</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (arr_idx < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                           return -ENOSPC;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             sched_arr[arr_idx] = &smscb->page[i];</span><br><span style="color: hsl(120, 100%, 40%);">+          if (i == smscb->num_pages)</span><br><span style="color: hsl(120, 100%, 40%);">+                 last_used_idx = i;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     return last_used_idx;</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%);">+/* obtain the least frequently scheduled SMSCB for given SMSCB channel */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct bts_smscb_message *</span><br><span style="color: hsl(120, 100%, 40%);">+bts_smscb_chan_get_least_frequent_smscb(struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (llist_empty(&cstate->messages))</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  /* messages are expected to be ordered with increasing period, so we're</span><br><span style="color: hsl(120, 100%, 40%);">+    * able to return the last message in the list */</span><br><span style="color: hsl(120, 100%, 40%);">+     return llist_entry(cstate->messages.prev, struct bts_smscb_message, 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%);">+/*! Generate per-BTS SMSCB scheduling array</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cstate BTS CBCH channel state</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[out] arr_out return argument for allocated + generated scheduling array</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return size of returned scheduling array arr_out in number of entries; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int bts_smscb_gen_sched_arr(struct bts_smscb_chan_state *cstate, struct bts_smscb_page ***arr_out)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bts_smscb_message *smscb, *least_freq;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bts_smscb_page **arr;</span><br><span style="color: hsl(120, 100%, 40%);">+  int arr_size;</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%);">+     /* start with one instance of the least frequent message at position 0, as we</span><br><span style="color: hsl(120, 100%, 40%);">+  * need to transmit it exactly once during the duration of the scheduling array */</span><br><span style="color: hsl(120, 100%, 40%);">+    least_freq = bts_smscb_chan_get_least_frequent_smscb(cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!least_freq) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_BTS(cstate->bts, DCBS, LOGL_DEBUG, "No SMSCB; cannot create schedule array\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              *arr_out = NULL;</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%);">+     arr_size = least_freq->input.rep_period;</span><br><span style="color: hsl(120, 100%, 40%);">+   arr = talloc_zero_array(cstate->bts, struct bts_smscb_page *, arr_size);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(arr);</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = bts_smscb_sched_add_after(arr, arr_size, 0, least_freq);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_BTS(cstate->bts, DCBS, LOGL_ERROR, "Unable to schedule first instance of "</span><br><span style="color: hsl(120, 100%, 40%);">+                   "very first SMSCB %s ?!?\n", bts_smscb_msg2str(least_freq));</span><br><span style="color: hsl(120, 100%, 40%);">+                talloc_free(arr);</span><br><span style="color: hsl(120, 100%, 40%);">+             return rc;</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%);">+   /* continue filling with repetitions of the more frequent messages, starting from</span><br><span style="color: hsl(120, 100%, 40%);">+      * the most frequent message to the least frequent one, repeating them as needed</span><br><span style="color: hsl(120, 100%, 40%);">+       * throughout the duration of the array */</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_for_each_entry(smscb, &cstate->messages, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+         int last_page;</span><br><span style="color: hsl(120, 100%, 40%);">+                if (smscb == least_freq)</span><br><span style="color: hsl(120, 100%, 40%);">+                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             /* messages are expected to be ordered with increasing period, so we're</span><br><span style="color: hsl(120, 100%, 40%);">+            * starting with the most frequent / shortest period first */</span><br><span style="color: hsl(120, 100%, 40%);">+         rc = bts_smscb_sched_add_after(arr, arr_size, 0, smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_BTS(cstate->bts, DCBS, LOGL_ERROR, "Unable to schedule first instance of "</span><br><span style="color: hsl(120, 100%, 40%);">+                           "SMSCB %s\n", bts_smscb_msg2str(smscb));</span><br><span style="color: hsl(120, 100%, 40%);">+                    talloc_free(arr);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             last_page = rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             while (last_page < cstate->sched_arr_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* store further instances in a way that the last block of the N+1th instance</span><br><span style="color: hsl(120, 100%, 40%);">+                  * happens no later than "interval" after the last block of the Nth instance */</span><br><span style="color: hsl(120, 100%, 40%);">+                     rc = bts_smscb_sched_add_before(arr, arr_size,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                        last_page + smscb->input.rep_period, smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              LOG_BTS(cstate->bts, DCBS, LOGL_ERROR, "Unable to schedule further "</span><br><span style="color: hsl(120, 100%, 40%);">+                                     "SMSCB %s\n", bts_smscb_msg2str(smscb));</span><br><span style="color: hsl(120, 100%, 40%);">+                            talloc_free(arr);</span><br><span style="color: hsl(120, 100%, 40%);">+                             return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     last_page = rc;</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%);">+     *arr_out = arr;</span><br><span style="color: hsl(120, 100%, 40%);">+       return arr_size;</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%);">+/*! Pull the next to-be-transmitted SMSCB page out of the scheduler for the given channel */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_page *bts_smscb_pull_page(struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bts_smscb_page *page;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* if there are no messages to schedule, there is no array */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!cstate->sched_arr)</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%);">+        /* obtain the page from the scheduler array */</span><br><span style="color: hsl(120, 100%, 40%);">+        page = cstate->sched_arr[cstate->next_idx];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* increment the index for the next call to this function */</span><br><span style="color: hsl(120, 100%, 40%);">+  cstate->next_idx = (cstate->next_idx + 1) % cstate->sched_arr_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* the array can have gaps in between where there is nothing scheduled */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!page)</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%);">+        return page;</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%);">+/*! To be called after bts_smscb_pull_page() in order to update transmission count and</span><br><span style="color: hsl(120, 100%, 40%);">+ *  check if SMSCB is complete.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cstate BTS CBC channel state</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] page SMSCB Page which had been returned by bts_smscb_pull_page() and which</span><br><span style="color: hsl(120, 100%, 40%);">+ *                      is no longer needed now */</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_smscb_page_done(struct bts_smscb_chan_state *cstate, struct bts_smscb_page *page)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bts_smscb_message *smscb = page->msg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If this is the last page of a SMSCB, increment the SMSCB number-of-xmit counter */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (page->nr == smscb->num_pages) {</span><br><span style="color: hsl(120, 100%, 40%);">+             smscb->bcast_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Check if the SMSCB transmission duration is now over */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (smscb->bcast_count >= smscb->input.num_bcast_req)</span><br><span style="color: hsl(120, 100%, 40%);">+                        bts_smscb_del(smscb, cstate, "COMPLETE");</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%);">+ * BTS / RSL side</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 bts_cbch_send_one(struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bts_smscb_page *page;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm_bts *bts = cstate->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct rsl_ie_cb_cmd_type cb_cmd;</span><br><span style="color: hsl(120, 100%, 40%);">+     bool is_extended = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (cstate == &bts->cbch_extended)</span><br><span style="color: hsl(120, 100%, 40%);">+             is_extended = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cstate->overflow) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_BTS(bts, DCBS, LOGL_DEBUG, "Skipping SMSCB due to overflow (%u)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     cstate->overflow);</span><br><span style="color: hsl(120, 100%, 40%);">+         cstate->overflow--;</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%);">+   page = bts_smscb_pull_page(cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!page) {</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_BTS(bts, DCBS, LOGL_DEBUG, "Skipping SMSCB: No page available\n");</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%);">+   cb_cmd.spare = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     cb_cmd.def_bcast = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL;</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (page->num_blocks) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case 1:</span><br><span style="color: hsl(120, 100%, 40%);">+               cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 2:</span><br><span style="color: hsl(120, 100%, 40%);">+               cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 3:</span><br><span style="color: hsl(120, 100%, 40%);">+               cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3;</span><br><span style="color: hsl(120, 100%, 40%);">+           break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case 4:</span><br><span style="color: hsl(120, 100%, 40%);">+               cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4;</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_panic("SMSCB Page must have 1..4 blocks, not %d\n", page->num_blocks);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, is_extended,</span><br><span style="color: hsl(120, 100%, 40%);">+                       page->data, sizeof(page->data));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   bts_smscb_page_done(cstate, page);</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 bts_cbch_timer(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gsm_bts *bts = (struct gsm_bts *)data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       bts_cbch_send_one(&bts->cbch_basic);</span><br><span style="color: hsl(120, 100%, 40%);">+   bts_cbch_send_one(&bts->cbch_extended);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      bts_cbch_timer_schedule(bts);</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%);">+/* There is one SMSCB message (page) per eight 51-multiframes, i.e. 1.882 seconds */</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_cbch_timer_schedule(struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_timer_setup(&bts->cbch_timer, &bts_cbch_timer, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_timer_schedule(&bts->cbch_timer, 1, 882920);</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%);">+/*! Receive a (decoded) incoming CBCH LOAD IND from given bts. See TS 48.058 8.5.9</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] bts The BTS for which the load indication was received</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cbch_extended Is this report for extended (true) or basic CBCH</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] is_overflow Is this report and overflow (true) or underflow report</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] slot_count amount of SMSCB messages needed / delay needed */</span><br><span style="color: hsl(120, 100%, 40%);">+int bts_smscb_rx_cbch_load_ind(struct gsm_bts *bts, bool cbch_extended, bool is_overflow,</span><br><span style="color: hsl(120, 100%, 40%);">+                           uint8_t slot_count)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bts_smscb_chan_state *cstate = bts_get_smscb_chan(bts, cbch_extended);</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 (!gsm_bts_get_cbch(bts))</span><br><span style="color: hsl(120, 100%, 40%);">+           return -ENODEV;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (is_overflow) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* halt/delay transmission of further CBCH messages */</span><br><span style="color: hsl(120, 100%, 40%);">+                cstate->overflow = slot_count;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              for (i = 0; i < slot_count; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+                   bts_cbch_send_one(cstate);</span><br><span style="color: hsl(120, 100%, 40%);">+            /* re-schedule the timer to count from now on */</span><br><span style="color: hsl(120, 100%, 40%);">+              bts_cbch_timer_schedule(bts);</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>diff --git a/src/osmo-bsc/cbsp_link.c b/src/osmo-bsc/cbsp_link.c</span><br><span>new file mode 100644</span><br><span>index 0000000..8840afa</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/cbsp_link.c</span><br><span>@@ -0,0 +1,417 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* CBSP (Cell Broadcast Service Protocol) Handling for OsmoBSC */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</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 modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (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 Affero 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 Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</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%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/vty.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bsc_msc_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/cbsp.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* if a CBC IP/port has been configured, we continuously try to re-establish the TCP</span><br><span style="color: hsl(120, 100%, 40%);">+ * connection (as a client) to the CBC.  If none has been configured, and we have a listen</span><br><span style="color: hsl(120, 100%, 40%);">+ * TCP port, we expect the CBC to connect to us.  If neither of the two is configured,</span><br><span style="color: hsl(120, 100%, 40%);">+ * CBSP is effectively disabled */</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%);">+ * CBSP Server (inbound TCP connection from CBC)</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 cbsp_srv_closed_cb(struct osmo_stream_srv *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct bsc_cbc_link *cbc = osmo_stream_srv_get_data(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+    //struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DCBS, LOGL_NOTICE, "CBSP Server lost connection from %s\n", cbc->server.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(cbc->server.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        cbc->server.sock_name = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      cbc->server.srv = NULL;</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%);">+static int cbsp_srv_cb(struct osmo_stream_srv *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bsc_cbc_link *cbc = osmo_stream_srv_get_data(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_cbsp_decoded *decoded;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* READ */</span><br><span style="color: hsl(120, 100%, 40%);">+    rc = osmo_cbsp_recv_buffered(cbc, ofd->fd, &msg, &cbc->server.msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rc == -EAGAIN || rc == -EINTR) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* more data needs to be read */</span><br><span style="color: hsl(120, 100%, 40%);">+                      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (rc == -EPIPE || rc == -ECONNRESET) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       /* lost connection */</span><br><span style="color: hsl(120, 100%, 40%);">+         } else if (rc == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* connection closed */</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_stream_srv_destroy(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+                cbc->server.srv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+            return -EBADF;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     decoded = osmo_cbsp_decode(conn, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (decoded) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DCBS, LOGL_DEBUG, "Received CBSP %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        get_value_string(cbsp_msg_type_names, decoded->msg_type));</span><br><span style="color: hsl(120, 100%, 40%);">+         cbsp_rx_decoded(cbc, decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+                talloc_free(decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DCBS, LOGL_ERROR, "Unable to decode CBSP %s: '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  msgb_hexdump(msg), osmo_cbsp_errstr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     msgb_free(msg);</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%);">+static int cbsp_srv_link_accept_cb(struct osmo_stream_srv_link *link, int fd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bsc_cbc_link *cbc = osmo_stream_srv_link_get_data(link);</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_stream_srv *srv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        LOGP(DCBS, LOGL_INFO, "CBSP Server received inbound connection from CBC: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_sock_get_name2(fd));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (cbc->server.srv) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DCBS, LOGL_NOTICE, "CBSP Server refusing further connection (%s) "</span><br><span style="color: hsl(120, 100%, 40%);">+                  "while we already have another connection (%s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  osmo_sock_get_name2(fd), cbc->server.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+              return -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%);">+   srv = osmo_stream_srv_create(cbc, link, fd, cbsp_srv_cb, cbsp_srv_closed_cb, cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!srv) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DCBS, LOGL_ERROR, "Unable to create stream server for %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                   osmo_sock_get_name2(fd));</span><br><span style="color: hsl(120, 100%, 40%);">+             return -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%);">+   cbc->server.srv = srv;</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->server.sock_name)</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_free(cbc->server.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        cbc->server.sock_name = osmo_sock_get_name(cbc, fd);</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGP(DCBS, LOGL_NOTICE, "CBSP Server link established from CBC %s\n", cbc->server.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+    /* TODO: introduce ourselves to the peer using some osmcoom extensions */</span><br><span style="color: hsl(120, 100%, 40%);">+     cbsp_tx_restart(cbc, false);</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%);">+ * CBSP Client (outbound TCP connection to CBC)</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 cbsp_client_connect_cb(struct osmo_stream_cli *cli)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bsc_cbc_link *cbc = osmo_stream_cli_get_data(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cbc->client.sock_name)</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_free(cbc->client.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        cbc->client.sock_name = osmo_sock_get_name(cbc, ofd->fd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     LOGP(DCBS, LOGL_NOTICE, "CBSP Client connected to CBC: %s\n", cbc->client.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* TODO: introduce ourselves to the peer using some osmcoom extensions */</span><br><span style="color: hsl(120, 100%, 40%);">+     cbsp_tx_restart(cbc, false);</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%);">+static int cbsp_client_disconnect_cb(struct osmo_stream_cli *cli)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct bsc_cbc_link *cbc = osmo_stream_cli_get_data(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGP(DCBS, LOGL_NOTICE, "CBSP Client lost connection to %s\n", cbc->client.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(cbc->client.sock_name);</span><br><span style="color: hsl(120, 100%, 40%);">+        cbc->client.sock_name = NULL;</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%);">+static int cbsp_client_read_cb(struct osmo_stream_cli *cli)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct bsc_cbc_link *cbc = osmo_stream_cli_get_data(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fd *ofd = osmo_stream_cli_get_ofd(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_cbsp_decoded *decoded;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct msgb *msg = NULL;</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%);">+     /* READ */</span><br><span style="color: hsl(120, 100%, 40%);">+    rc = osmo_cbsp_recv_buffered(cbc, ofd->fd, &msg, &cbc->client.msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rc == -EAGAIN || rc == -EINTR) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* more data needs to be read */</span><br><span style="color: hsl(120, 100%, 40%);">+                      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (rc == -EPIPE || rc == -ECONNRESET) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       /* lost connection */</span><br><span style="color: hsl(120, 100%, 40%);">+         } else if (rc == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* connection closed */</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_stream_cli_reconnect(cli);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EBADF;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     decoded = osmo_cbsp_decode(cli, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (decoded) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DCBS, LOGL_DEBUG, "Received CBSP %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        get_value_string(cbsp_msg_type_names, decoded->msg_type));</span><br><span style="color: hsl(120, 100%, 40%);">+         cbsp_rx_decoded(cbc, decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+                talloc_free(decoded);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DCBS, LOGL_ERROR, "Unable to decode CBSP %s: '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  msgb_hexdump(msg), osmo_cbsp_errstr);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     msgb_free(msg);</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%);">+int bsc_cbc_link_restart(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bsc_cbc_link *cbc = bsc_gsmnet->bsc_data->cbc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* shut down client, if no longer configured */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cbc->client.cli && !cbc->config.cbc_hostname) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP client\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_stream_cli_close(cbc->client.cli);</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_stream_cli_destroy(cbc->client.cli);</span><br><span style="color: hsl(120, 100%, 40%);">+          cbc->client.cli = 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%);">+   /* shut down server, if no longer configured */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cbc->config.listen_port == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               if (cbc->server.srv || cbc->server.link)</span><br><span style="color: hsl(120, 100%, 40%);">+                        LOGP(DCBS, LOGL_NOTICE, "Stopping CBSP server\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          if (cbc->server.srv) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_stream_srv_destroy(cbc->server.srv);</span><br><span style="color: hsl(120, 100%, 40%);">+                  cbc->server.srv = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (cbc->server.link) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    osmo_stream_srv_link_close(cbc->server.link);</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_stream_srv_link_destroy(cbc->server.link);</span><br><span style="color: hsl(120, 100%, 40%);">+                    cbc->server.link = 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* start client, if configured */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->config.cbc_hostname) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Client (to CBC at %s:%u)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 cbc->config.cbc_hostname, cbc->config.cbc_port);</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!cbc->client.cli) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    cbc->client.cli = osmo_stream_cli_create(cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_stream_cli_set_data(cbc->client.cli, cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+                    osmo_stream_cli_set_connect_cb(cbc->client.cli, cbsp_client_connect_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+                   osmo_stream_cli_set_disconnect_cb(cbc->client.cli, cbsp_client_disconnect_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_stream_cli_set_read_cb(cbc->client.cli, cbsp_client_read_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             /* CBC side */</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_stream_cli_set_addr(cbc->client.cli, cbc->config.cbc_hostname);</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_stream_cli_set_port(cbc->client.cli, cbc->config.cbc_port);</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Close/Reconnect? */</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_stream_cli_open(cbc->client.cli);</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%);">+   /* start server, if configured */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->config.listen_port != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGP(DCBS, LOGL_NOTICE, "Starting CBSP Server (bound to %s:%u)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  cbc->config.listen_hostname, cbc->config.listen_port);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!cbc->server.srv) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    cbc->server.link = osmo_stream_srv_link_create(cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_stream_srv_link_set_data(cbc->server.link, cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_stream_srv_link_set_accept_cb(cbc->server.link, cbsp_srv_link_accept_cb);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_stream_srv_link_set_addr(cbc->server.link, cbc->config.listen_hostname);</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_stream_srv_link_set_port(cbc->server.link, cbc->config.listen_port);</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%);">+/*! Encode + Transmit a 'decoded' CBSP message over given CBC link</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cbc Data structure representing the BSCs link to the CBC</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cbsp Decoded CBSP message to be transmitted. Ownership is transferred.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return 0 on success, negative otherwise */</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_tx_decoded(struct bsc_cbc_link *cbc, struct osmo_cbsp_decoded *cbsp)</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msg = osmo_cbsp_encode(cbc, cbsp);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DCBS, LOGL_ERROR, "Unable to encode CBSP Message Type %s: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       get_value_string(cbsp_msg_type_names, cbsp->msg_type), osmo_cbsp_errstr);</span><br><span style="color: hsl(120, 100%, 40%);">+          talloc_free(cbsp);</span><br><span style="color: hsl(120, 100%, 40%);">+            return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->client.cli)</span><br><span style="color: hsl(120, 100%, 40%);">+               osmo_stream_cli_send(cbc->client.cli, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+        else if (cbc->server.srv)</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_stream_srv_send(cbc->server.srv, msg);</span><br><span style="color: hsl(120, 100%, 40%);">+        else {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DCBS, LOGL_ERROR, "Discarding CBSP Message, link is down: %s\n", msgb_hexdump(msg));</span><br><span style="color: hsl(120, 100%, 40%);">+           msgb_free(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%);">+   talloc_free(cbsp);</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%);">+static struct bsc_cbc_link *vty_cbc_data(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return bsc_gsmnet->bsc_data->cbc;</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%);">+ * VTY Interface (Configuration + Introspection)</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%);">+DEFUN(cfg_cbc, cfg_cbc_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+        "cbc", "Configure CBSP Link to Cell Broadcast Centre\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       vty->node = CBC_NODE;</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%);">+DEFUN(cfg_cbc_remote_ip, cfg_cbc_remote_ip_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "remote-ip A.B.C.D",</span><br><span style="color: hsl(120, 100%, 40%);">+        "IP Address of the Cell Broadcast Centre\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "IP Address of the Cell Broadcast Centre\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_talloc_replace_string(cbc, &cbc->config.cbc_hostname, argv[0]);</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%);">+DEFUN(cfg_cbc_no_remote_ip, cfg_cbc_no_remote_ip_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "no remote-ip",</span><br><span style="color: hsl(120, 100%, 40%);">+     NO_STR "Remove IP address of CBC; disables outbound CBSP connections\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(cbc->config.cbc_hostname);</span><br><span style="color: hsl(120, 100%, 40%);">+     cbc->config.cbc_hostname = NULL;</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%);">+DEFUN(cfg_cbc_remote_port, cfg_cbc_remote_port_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "remote-port <1-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+      "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+   "TCP Port number of the Cell Broadcast Centre (Default: 48049)\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ cbc->config.cbc_port = atoi(argv[0]);</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%);">+DEFUN(cfg_cbc_listen_port, cfg_cbc_listen_port_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "listen-port <1-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+      "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      "Local TCP port at which BSC listens for incoming CBSP connections from CBC\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ cbc->config.listen_port = atoi(argv[0]);</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%);">+DEFUN(cfg_cbc_no_listen_port, cfg_cbc_no_listen_port_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "no listen-port",</span><br><span style="color: hsl(120, 100%, 40%);">+   NO_STR "Remove CBSP Listen Port; disables inbound CBSP connections\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ cbc->config.listen_port = -1;</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%);">+DEFUN(cfg_cbc_listen_ip, cfg_cbc_listen_ip_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+     "listen-ip A.B.C.D",</span><br><span style="color: hsl(120, 100%, 40%);">+        "Local IP Address where BSC listens for incoming CBC connections (Default: 0.0.0.0)\n"</span><br><span style="color: hsl(120, 100%, 40%);">+      "Local IP Address where BSC listens for incoming CBC connections\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_talloc_replace_string(cbc, &cbc->config.listen_hostname, argv[0]);</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%);">+static struct cmd_node cbc_node = {</span><br><span style="color: hsl(120, 100%, 40%);">+ CBC_NODE,</span><br><span style="color: hsl(120, 100%, 40%);">+     "%s(config-cbc)# ",</span><br><span style="color: hsl(120, 100%, 40%);">+ 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%);">+static int config_write_cbc(struct vty *vty)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       vty_out(vty, "cbc%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (cbc->config.cbc_hostname) {</span><br><span style="color: hsl(120, 100%, 40%);">+            vty_out(vty, " remote-ip %s%s", cbc->config.cbc_hostname, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+          vty_out(vty, " remote-port %u%s", cbc->config.cbc_port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, " no remote-ip%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->config.listen_port >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, " listen-port %u%s", cbc->config.listen_port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+         vty_out(vty, " listen-ip %s%s", cbc->config.listen_hostname, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, " no listen-port%s", VTY_NEWLINE);</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%);">+DEFUN(show_cbc, show_cbc_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "show cbc",</span><br><span style="color: hsl(120, 100%, 40%);">+ SHOW_STR "Display state of CBC / CBSP\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct bsc_cbc_link *cbc = vty_cbc_data(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!cbc->config.cbc_hostname)</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "CBSP Client Config: Disabled%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+        else {</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, "CBSP Client Config: CBC IP=%s, CBC Port=%u%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                        cbc->config.cbc_hostname, cbc->config.cbc_port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "CBSP Client Connection: %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                        cbc->client.sock_name ? cbc->client.sock_name : "Disconnected", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cbc->config.listen_port < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, "CBSP Server Config: Disabled%s\n", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+      else {</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, "CBSP Server Config: Listen IP=%s, Port=%u%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       cbc->config.listen_hostname, cbc->config.listen_port, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+             vty_out(vty, "CBSP Server Connection: %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                        cbc->server.sock_name ? cbc->server.sock_name : "Disconnected", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</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%);">+void cbc_vty_init(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  install_element(VIEW_NODE, &show_cbc_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element(CONFIG_NODE, &cfg_cbc_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+       install_node(&cbc_node, config_write_cbc);</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element(CBC_NODE, &cfg_cbc_remote_ip_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element(CBC_NODE, &cfg_cbc_no_remote_ip_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+     install_element(CBC_NODE, &cfg_cbc_remote_port_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+      install_element(CBC_NODE, &cfg_cbc_listen_port_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+      install_element(CBC_NODE, &cfg_cbc_no_listen_port_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+   install_element(CBC_NODE, &cfg_cbc_listen_ip_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c</span><br><span>index ea33817..c2cfacf 100644</span><br><span>--- a/src/osmo-bsc/gsm_data.c</span><br><span>+++ b/src/osmo-bsc/gsm_data.c</span><br><span>@@ -767,6 +767,12 @@</span><br><span>    .initial_mcs = 6,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     cstate->bts = bts;</span><br><span style="color: hsl(120, 100%, 40%);">+ INIT_LLIST_HEAD(&cstate->messages);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Initialize those parts that don't require osmo-bsc specific dependencies.</span><br><span>  * This part is shared among the thin programs in osmo-bsc/src/utils/.</span><br><span>  * osmo-bsc requires further initialization that pulls in more dependencies (see</span><br><span>@@ -945,6 +951,9 @@</span><br><span>      }</span><br><span>    bts->mr_half.num_modes = 3;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    bts_init_cbch_state(&bts->cbch_basic, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    bts_init_cbch_state(&bts->cbch_extended, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      return bts;</span><br><span> }</span><br><span> </span><br><span>diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c</span><br><span>index aba8c40..dacd61a 100644</span><br><span>--- a/src/osmo-bsc/osmo_bsc_main.c</span><br><span>+++ b/src/osmo-bsc/osmo_bsc_main.c</span><br><span>@@ -37,6 +37,7 @@</span><br><span> #include <osmocom/bsc/bsc_subscriber.h></span><br><span> #include <osmocom/bsc/assignment_fsm.h></span><br><span> #include <osmocom/bsc/handover_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span> </span><br><span> #include <osmocom/ctrl/control_cmd.h></span><br><span> #include <osmocom/ctrl/control_if.h></span><br><span>@@ -310,6 +311,10 @@</span><br><span>                 OSMO_ASSERT(ts->fi);</span><br><span>              osmo_fsm_inst_dispatch(ts->fi, TS_EV_RSL_READY, NULL);</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Start CBCH transmit timer if CBCH is present */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (trx->nr == 0 && gsm_bts_get_cbch(trx->bts))</span><br><span style="color: hsl(120, 100%, 40%);">+         bts_cbch_timer_schedule(trx->bts);</span><br><span> }</span><br><span> </span><br><span> static void all_ts_dispatch_event(struct gsm_bts_trx *trx, uint32_t event)</span><br><span>@@ -379,6 +384,8 @@</span><br><span>                   rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL]);</span><br><span>                   acc_ramp_abort(&trx->bts->acc_ramp);</span><br><span>                       all_ts_dispatch_event(trx, TS_EV_RSL_DOWN);</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (trx->nr == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                          osmo_timer_del(&trx->bts->cbch_timer);</span><br><span>             }</span><br><span> </span><br><span>                gsm_bts_mo_reset(trx->bts);</span><br><span>@@ -764,6 +771,11 @@</span><br><span>                .description = "Local Call, Local Switch",</span><br><span>                 .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span>       },</span><br><span style="color: hsl(120, 100%, 40%);">+    [DCBS] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "DCBS",</span><br><span style="color: hsl(120, 100%, 40%);">+             .description = "Cell Broadcast System",</span><br><span style="color: hsl(120, 100%, 40%);">+             .enabled = 1, .loglevel = LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span> </span><br><span> };</span><br><span> </span><br><span>@@ -912,6 +924,7 @@</span><br><span> </span><br><span>    handover_decision_1_init();</span><br><span>  hodec2_init(bsc_gsmnet);</span><br><span style="color: hsl(120, 100%, 40%);">+      bsc_cbc_link_restart();</span><br><span> </span><br><span>  signal(SIGINT, &signal_handler);</span><br><span>         signal(SIGTERM, &signal_handler);</span><br><span>diff --git a/src/osmo-bsc/smscb.c b/src/osmo-bsc/smscb.c</span><br><span>new file mode 100644</span><br><span>index 0000000..6b96086</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/smscb.c</span><br><span>@@ -0,0 +1,823 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* SMSCB (SMS Cell Broadcast) Handling for OsmoBSC */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by Harald Welte <laforge@gnumonks.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</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 modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (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 Affero 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 Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</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%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/cbsp.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_23_041.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_48_049.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/netif/stream.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/debug.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/smscb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/vty.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%);">+ * Helper Functions</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%);">+/* replace the old head of an entire list with a new head; effectively moves the entire</span><br><span style="color: hsl(120, 100%, 40%);">+ * list from old to new head */</span><br><span style="color: hsl(120, 100%, 40%);">+static void llist_replace_head(struct llist_head *new, struct llist_head *old)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (llist_empty(old))</span><br><span style="color: hsl(120, 100%, 40%);">+         INIT_LLIST_HEAD(new);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+          __llist_add(new, old->prev, old->next);</span><br><span style="color: hsl(120, 100%, 40%);">+ INIT_LLIST_HEAD(old);</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%);">+/*! Obtain SMSCB Channel State for given BTS (basic or extended CBCH) */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_chan_state *bts_get_smscb_chan(struct gsm_bts *bts, bool extended)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct bts_smscb_chan_state *chan_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (extended)</span><br><span style="color: hsl(120, 100%, 40%);">+         chan_state = &bts->cbch_extended;</span><br><span style="color: hsl(120, 100%, 40%);">+      else</span><br><span style="color: hsl(120, 100%, 40%);">+          chan_state = &bts->cbch_basic;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return chan_state;</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%);">+/* do an ordered list insertion. we keep the list with increasing period, i.e. the most</span><br><span style="color: hsl(120, 100%, 40%);">+ * frequent message first */</span><br><span style="color: hsl(120, 100%, 40%);">+static void __bts_smscb_add(struct bts_smscb_chan_state *cstate, struct bts_smscb_message *new)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct bts_smscb_message *tmp, *tmp2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (llist_empty(&cstate->messages)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_add(&new->list, &cstate->messages);</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%);">+   llist_for_each_entry_safe(tmp, tmp2, &cstate->messages, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                if (tmp->input.rep_period > new->input.rep_period) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* we found the first message with longer period than the new message,</span><br><span style="color: hsl(120, 100%, 40%);">+                         * we must insert ourselves before that one */</span><br><span style="color: hsl(120, 100%, 40%);">+                        __llist_add(&new->list, tmp->list.prev, &tmp->list);</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%);">+/* stringify a SMSCB for logging */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *bts_smscb_msg2str(const struct bts_smscb_message *smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  static char buf[128];</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(buf, sizeof(buf), "MsgId=0x%04x/SerialNr=0x%04x/Pages=%u/Period=%u/NumBcastReq=%u",</span><br><span style="color: hsl(120, 100%, 40%);">+                 smscb->input.msg_id, smscb->input.serial_nr, smscb->num_pages,</span><br><span style="color: hsl(120, 100%, 40%);">+               smscb->input.rep_period, smscb->input.num_bcast_req);</span><br><span style="color: hsl(120, 100%, 40%);">+  return buf;</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 char *bts_smscb_chan_state_name(const struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cstate == &cstate->bts->cbch_basic)</span><br><span style="color: hsl(120, 100%, 40%);">+             return "BASIC";</span><br><span style="color: hsl(120, 100%, 40%);">+     else if (cstate == &cstate->bts->cbch_extended)</span><br><span style="color: hsl(120, 100%, 40%);">+             return "EXTENDED";</span><br><span style="color: hsl(120, 100%, 40%);">+  else</span><br><span style="color: hsl(120, 100%, 40%);">+          return "UNKNOWN";</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%);">+unsigned int bts_smscb_chan_load_percent(const struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned int sched_arr_used = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (cstate->sched_arr_size == 0)</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%);">+   /* count the number of used slots */</span><br><span style="color: hsl(120, 100%, 40%);">+  for (i = 0; i < cstate->sched_arr_size; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (cstate->sched_arr[i])</span><br><span style="color: hsl(120, 100%, 40%);">+                  sched_arr_used++;</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%);">+   OSMO_ASSERT(sched_arr_used <= UINT_MAX/100);</span><br><span style="color: hsl(120, 100%, 40%);">+       return (sched_arr_used * 100) / cstate->sched_arr_size;</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%);">+unsigned int bts_smscb_chan_page_count(const struct bts_smscb_chan_state *cstate)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct bts_smscb_message *smscb;</span><br><span style="color: hsl(120, 100%, 40%);">+      unsigned int page_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry(smscb, &cstate->messages, list)</span><br><span style="color: hsl(120, 100%, 40%);">+           page_count += smscb->num_pages;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return page_count;</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%);">+/*! Obtain the Cell Global Identifier (CGI) of given BTS; returned in static buffer. */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_cell_global_id *bts_get_cgi(struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    static struct osmo_cell_global_id cgi;</span><br><span style="color: hsl(120, 100%, 40%);">+        cgi.lai.plmn = bts->network->plmn;</span><br><span style="color: hsl(120, 100%, 40%);">+      cgi.lai.lac = bts->location_area_code;</span><br><span style="color: hsl(120, 100%, 40%);">+     cgi.cell_identity = bts->cell_identity;</span><br><span style="color: hsl(120, 100%, 40%);">+    return &cgi;</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%);">+/* represents the various lists that the BSC can create as part of a response */</span><br><span style="color: hsl(120, 100%, 40%);">+struct response_state {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_cbsp_cell_list success;     /* osmo_cbsp_cell_ent */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct llist_head fail;                 /* osmo_cbsp_fail_ent */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_cbsp_num_compl_list num_completed;  /* osmo_cbsp_num_compl_ent */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_cbsp_loading_list loading;  /* osmo_cbsp_loading_ent */</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%);">+/*! per-BTS callback function used by cbsp_per_bts().</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] bts BTS currently being processed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] dec decoded CBSP message currently being processed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param r_state response state accumulating cell lists (success/failure/...)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param priv opaque private data provided by caller of cbsp_per_bts()</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 on success; negative TS 48.049 cause value on error */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef int bts_cb_fn(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,</span><br><span style="color: hsl(120, 100%, 40%);">+                    struct response_state *r_state, void *priv);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* append a success for given cell to response state */</span><br><span style="color: hsl(120, 100%, 40%);">+static void append_success(struct response_state *r_state, struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cbsp_cell_ent *cent = talloc_zero(r_state, struct osmo_cbsp_cell_ent);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_cell_global_id *cgi = bts_get_cgi(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_BTS(bts, DCBS, LOGL_INFO, "Success\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(cent);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  cent->cell_id.global = *cgi;</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_add_tail(&cent->list, &r_state->success.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%);">+/* append a failure for given cell to response state */</span><br><span style="color: hsl(120, 100%, 40%);">+static void append_fail(struct response_state *r_state, struct gsm_bts *bts, uint8_t cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_cbsp_fail_ent *fent = talloc_zero(r_state, struct osmo_cbsp_fail_ent);</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_cell_global_id *cgi = bts_get_cgi(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_BTS(bts, DCBS, LOGL_NOTICE, "Failure Cause 0x%02x\n", cause);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(fent);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  fent->id_discr = CELL_IDENT_WHOLE_GLOBAL;</span><br><span style="color: hsl(120, 100%, 40%);">+  fent->cell_id.global = *cgi;</span><br><span style="color: hsl(120, 100%, 40%);">+       fent->cause = cause;</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_add_tail(&fent->list, &r_state->fail);</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%);">+/* append a 'number of broadcasts completed' for given cell to response state */</span><br><span style="color: hsl(120, 100%, 40%);">+static void append_bcast_compl(struct response_state *r_state, struct gsm_bts *bts,</span><br><span style="color: hsl(120, 100%, 40%);">+                                struct bts_smscb_message *smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_cbsp_num_compl_ent *cent = talloc_zero(r_state, struct osmo_cbsp_num_compl_ent);</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_cell_global_id *cgi = bts_get_cgi(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOG_BTS(bts, DCBS, LOGL_DEBUG, "Number of Broadcasts Completed: %u\n", smscb->bcast_count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(cent);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  r_state->num_completed.id_discr = CELL_IDENT_WHOLE_GLOBAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ cent->cell_id.global = *cgi;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (smscb->bcast_count > INT16_MAX) {</span><br><span style="color: hsl(120, 100%, 40%);">+           cent->num_compl = INT16_MAX;</span><br><span style="color: hsl(120, 100%, 40%);">+               cent->num_bcast_info = 0x01; /* Overflow */</span><br><span style="color: hsl(120, 100%, 40%);">+        } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              cent->num_compl = smscb->bcast_count;</span><br><span style="color: hsl(120, 100%, 40%);">+           cent->num_bcast_info = 0x00;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_add_tail(&cent->list, &r_state->num_completed.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%);">+/*! Iterate over all BTSs, find matching ones, execute command on BTS, add result</span><br><span style="color: hsl(120, 100%, 40%);">+ *  to succeeded/failed lists.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] net GSM network in which we operate</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] caller-allocated Response state structure collecting results</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cell_list Decoded CBSP cell list describing BTSs to operate on</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cb_fn Call-back function to call for each matching BTS</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] priv Opqaue private data; passed to cb_fn</span><br><span style="color: hsl(120, 100%, 40%);">+ *  */</span><br><span style="color: hsl(120, 100%, 40%);">+static int cbsp_per_bts(struct gsm_network *net, struct response_state *r_state,</span><br><span style="color: hsl(120, 100%, 40%);">+                   const struct osmo_cbsp_cell_list *cell_list,</span><br><span style="color: hsl(120, 100%, 40%);">+                  bts_cb_fn *cb_fn, const struct osmo_cbsp_decoded *dec, void *priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_cbsp_cell_ent *ent;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_bts *bts;</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t bts_status[net->num_bts];</span><br><span style="color: hsl(120, 100%, 40%);">+  int rc, ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    memset(bts_status, 0, sizeof(bts_status));</span><br><span style="color: hsl(120, 100%, 40%);">+    INIT_LLIST_HEAD(&r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+       INIT_LLIST_HEAD(&r_state->fail);</span><br><span style="color: hsl(120, 100%, 40%);">+       INIT_LLIST_HEAD(&r_state->num_completed.list);</span><br><span style="color: hsl(120, 100%, 40%);">+ INIT_LLIST_HEAD(&r_state->loading.list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* special case as cell_list->list is empty in this case */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (cell_list->id_discr == CELL_IDENT_BSS) {</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_for_each_entry(bts, &net->bts_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      bts_status[bts->nr] = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* call function on this BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+                       rc = cb_fn(bts, dec, r_state, priv);</span><br><span style="color: hsl(120, 100%, 40%);">+                  if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              append_fail(r_state, bts, -rc);</span><br><span style="color: hsl(120, 100%, 40%);">+                               ret = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else</span><br><span style="color: hsl(120, 100%, 40%);">+                                append_success(r_state, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* normal case: iterate over cell list */</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_for_each_entry(ent, &cell_list->list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    bool found_at_least_one = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* find all matching BTSs for this entry */</span><br><span style="color: hsl(120, 100%, 40%);">+                   llist_for_each_entry(bts, &net->bts_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              struct gsm0808_cell_id cell_id = {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    .id_discr = cell_list->id_discr,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   .id = ent->cell_id</span><br><span style="color: hsl(120, 100%, 40%);">+                         };</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (!gsm_bts_matches_cell_id(bts, &cell_id))</span><br><span style="color: hsl(120, 100%, 40%);">+                                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                             found_at_least_one = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                            /* skip any BTSs which we've already processed */</span><br><span style="color: hsl(120, 100%, 40%);">+                         if (bts_status[bts->nr])</span><br><span style="color: hsl(120, 100%, 40%);">+                                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+                             bts_status[bts->nr] = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                           /* call function on this BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+                               rc = cb_fn(bts, dec, r_state, priv);</span><br><span style="color: hsl(120, 100%, 40%);">+                          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      append_fail(r_state, bts, -rc);</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ret = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                             } else</span><br><span style="color: hsl(120, 100%, 40%);">+                                        append_success(r_state, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+                 }</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!found_at_least_one) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            struct osmo_cbsp_fail_ent *fent;</span><br><span style="color: hsl(120, 100%, 40%);">+                              LOGP(DCBS, LOGL_NOTICE, "CBSP: Couldn't find a single matching BTS\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                         fent = talloc_zero(r_state, struct osmo_cbsp_fail_ent);</span><br><span style="color: hsl(120, 100%, 40%);">+                               OSMO_ASSERT(fent);</span><br><span style="color: hsl(120, 100%, 40%);">+                            fent->id_discr = cell_list->id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+                           fent->cell_id = ent->cell_id;</span><br><span style="color: hsl(120, 100%, 40%);">+                           llist_add_tail(&fent->list, &r_state->fail);</span><br><span style="color: hsl(120, 100%, 40%);">+                            ret = -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 ret;</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%);">+/*! Find an existing SMSCB message within given BTS.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] chan_state BTS CBCH channel state</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] msg_id Message Id of to-be-found message</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] serial_nr Serial Number of to-be-found message</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns SMSCB message if found; NULL otherwise */</span><br><span style="color: hsl(120, 100%, 40%);">+struct bts_smscb_message *bts_find_smscb(struct bts_smscb_chan_state *chan_state,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    uint16_t msg_id, uint16_t serial_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bts_smscb_message *smscb;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    llist_for_each_entry(smscb, &chan_state->messages, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (smscb->input.msg_id == msg_id && smscb->input.serial_nr == serial_nr)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return smscb;</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%);">+/*! create a new SMSCB message for specified BTS; don't link it yet.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] bts BTS for which the SMSCB is to be allocated</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] wrepl CBSP write-replace message</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns callee-allocated SMSCB message filled with data from wrepl */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct bts_smscb_message *bts_smscb_msg_from_wrepl(struct gsm_bts *bts,</span><br><span style="color: hsl(120, 100%, 40%);">+                                               const struct osmo_cbsp_write_replace *wrepl)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bts_smscb_message *smscb = talloc_zero(bts, struct bts_smscb_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_cbsp_content *cont;</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 (!smscb)</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%);">+        OSMO_ASSERT(wrepl->is_cbs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* initialize all pages inside the message */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ARRAY_SIZE(smscb->page); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+         struct bts_smscb_page *page = &smscb->page[i];</span><br><span style="color: hsl(120, 100%, 40%);">+         page->nr = i+1; /* page numbers are 1-based */</span><br><span style="color: hsl(120, 100%, 40%);">+             page->msg = smscb;</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%);">+   /* initialize "header" part */</span><br><span style="color: hsl(120, 100%, 40%);">+      smscb->input.msg_id = wrepl->msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+    smscb->input.serial_nr = wrepl->new_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+  smscb->input.category = wrepl->u.cbs.category;</span><br><span style="color: hsl(120, 100%, 40%);">+  smscb->input.rep_period = wrepl->u.cbs.rep_period;</span><br><span style="color: hsl(120, 100%, 40%);">+      smscb->input.num_bcast_req = wrepl->u.cbs.num_bcast_req;</span><br><span style="color: hsl(120, 100%, 40%);">+        smscb->input.dcs = wrepl->u.cbs.dcs;</span><br><span style="color: hsl(120, 100%, 40%);">+    smscb->num_pages = llist_count(&wrepl->u.cbs.msg_content);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (smscb->num_pages > ARRAY_SIZE(smscb->page)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_BTS(bts, DCBS, LOGL_ERROR, "SMSCB with too many pages (%u > %lu)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 smscb->num_pages, ARRAY_SIZE(smscb->page));</span><br><span style="color: hsl(120, 100%, 40%);">+             talloc_free(smscb);</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%);">+   i = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_for_each_entry(cont, &wrepl->u.cbs.msg_content, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+          struct gsm23041_msg_param_gsm *msg_param;</span><br><span style="color: hsl(120, 100%, 40%);">+             struct bts_smscb_page *page;</span><br><span style="color: hsl(120, 100%, 40%);">+          size_t bytes_used;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          /* we have just ensured a few lines above that this cannot overflow */</span><br><span style="color: hsl(120, 100%, 40%);">+                page = &smscb->page[i++];</span><br><span style="color: hsl(120, 100%, 40%);">+              msg_param = (struct gsm23041_msg_param_gsm *) &page->data[0];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                /* build 6 byte header according to TS 23.041 9.4.1.2 */</span><br><span style="color: hsl(120, 100%, 40%);">+              osmo_store16be(wrepl->new_serial_nr, &msg_param->serial_nr);</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_store16be(wrepl->msg_id, &msg_param->message_id);</span><br><span style="color: hsl(120, 100%, 40%);">+              msg_param->dcs = wrepl->u.cbs.dcs;</span><br><span style="color: hsl(120, 100%, 40%);">+              msg_param->page_param.num_pages = smscb->num_pages;</span><br><span style="color: hsl(120, 100%, 40%);">+             msg_param->page_param.page_nr = page->nr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             OSMO_ASSERT(cont->user_len <= ARRAY_SIZE(cont->data));</span><br><span style="color: hsl(120, 100%, 40%);">+               OSMO_ASSERT(cont->user_len <= ARRAY_SIZE(page->data) - sizeof(*msg_param));</span><br><span style="color: hsl(120, 100%, 40%);">+          memcpy(&msg_param->content, cont->data, cont->user_len);</span><br><span style="color: hsl(120, 100%, 40%);">+         bytes_used = sizeof(*msg_param) + cont->user_len;</span><br><span style="color: hsl(120, 100%, 40%);">+          /* compute number of valid blocks in page */</span><br><span style="color: hsl(120, 100%, 40%);">+          page->num_blocks = bytes_used / 22;</span><br><span style="color: hsl(120, 100%, 40%);">+                if (bytes_used % 22)</span><br><span style="color: hsl(120, 100%, 40%);">+                  page->num_blocks += 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%);">+   return smscb;</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%);">+/*! remove a SMSCB message */</span><br><span style="color: hsl(120, 100%, 40%);">+void bts_smscb_del(struct bts_smscb_message *smscb, struct bts_smscb_chan_state *cstate,</span><br><span style="color: hsl(120, 100%, 40%);">+              const char *reason)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bts_smscb_page **arr;</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%);">+     LOG_BTS(cstate->bts, DCBS, LOGL_INFO, "%s Deleting %s (Reason: %s)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+           bts_smscb_chan_state_name(cstate), bts_smscb_msg2str(smscb), reason);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_del(&smscb->list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* we must recompute the scheduler array here, as the old one will have pointers</span><br><span style="color: hsl(120, 100%, 40%);">+       * to the pages of the just-to-be-deleted message */</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = bts_smscb_gen_sched_arr(cstate, &arr);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_BTS(cstate->bts, DCBS, LOGL_ERROR, "Cannot generate new CBCH scheduler array after "</span><br><span style="color: hsl(120, 100%, 40%);">+                 "removing message %s. WTF?\n", bts_smscb_msg2str(smscb));</span><br><span style="color: hsl(120, 100%, 40%);">+           /* we cannot free the message now, to ensure the page pointers in the old</span><br><span style="color: hsl(120, 100%, 40%);">+              * array are still valid. let's re-add it to keep things sane */</span><br><span style="color: hsl(120, 100%, 40%);">+          __bts_smscb_add(cstate, smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* success */</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_free(smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* replace array with new one */</span><br><span style="color: hsl(120, 100%, 40%);">+              talloc_free(cstate->sched_arr);</span><br><span style="color: hsl(120, 100%, 40%);">+            cstate->sched_arr = arr;</span><br><span style="color: hsl(120, 100%, 40%);">+           cstate->sched_arr_size = rc;</span><br><span style="color: hsl(120, 100%, 40%);">+               cstate->next_idx = 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%);">+/*********************************************************************************</span><br><span style="color: hsl(120, 100%, 40%);">+ * Transmit of CBSP to CBC</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%);">+/* transmit a CBSP RESTART message stating all message data was lost for entire BSS */</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_tx_restart(struct bsc_cbc_link *cbc, bool is_emerg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_RESTART);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (is_emerg)</span><br><span style="color: hsl(120, 100%, 40%);">+         cbsp->u.restart.bcast_msg_type = 0x01;</span><br><span style="color: hsl(120, 100%, 40%);">+     cbsp->u.restart.recovery_ind = 0x01; /* message data lost */</span><br><span style="color: hsl(120, 100%, 40%);">+       cbsp->u.restart.cell_list.id_discr = CELL_IDENT_BSS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     return cbsp_tx_decoded(cbc, cbsp);</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%);">+/* transmit a CBSP KEEPALIVE COMPLETE to the CBC */</span><br><span style="color: hsl(120, 100%, 40%);">+static int tx_cbsp_keepalive_compl(struct bsc_cbc_link *cbc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_cbsp_decoded *cbsp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_KEEP_ALIVE_COMPL);</span><br><span style="color: hsl(120, 100%, 40%);">+    return cbsp_tx_decoded(cbc, cbsp);</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%);">+ * Per-BTS Processing of CBSP from CBC, called via cbsp_per_bts()</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%);">+/*! Try to execute a write-replace operation; roll-back if it fails.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] chan_state BTS CBCH channel state</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] extended_cbch Basic (false) or Extended (true) CBCH</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] new_msg New SMSCB message which should be added</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] exclude_msg Existing SMSCB message that shall be replaced (if possible). Can be NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \return 0 on success; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+static int bts_try_write_replace(struct bts_smscb_chan_state *chan_state,</span><br><span style="color: hsl(120, 100%, 40%);">+                              struct bts_smscb_message *new_msg,</span><br><span style="color: hsl(120, 100%, 40%);">+                            struct bts_smscb_message *exclude_msg,</span><br><span style="color: hsl(120, 100%, 40%);">+                                struct response_state *r_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct bts_smscb_page **arr;</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%);">+     if (exclude_msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* temporarily remove from list of SMSCB */</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_del(&exclude_msg->list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     /* temporarily add new_msg to list of SMSCB */</span><br><span style="color: hsl(120, 100%, 40%);">+        __bts_smscb_add(chan_state, new_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* attempt to create scheduling array */</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = bts_smscb_gen_sched_arr(chan_state, &arr);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* it didn't work out; we couldn't schedule it */</span><br><span style="color: hsl(120, 100%, 40%);">+             /* remove the new message again */</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_del(&new_msg->list);</span><br><span style="color: hsl(120, 100%, 40%);">+             /* up to the caller to free() it */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (exclude_msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* re-add the temporarily removed message */</span><br><span style="color: hsl(120, 100%, 40%);">+                  __bts_smscb_add(chan_state, new_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             return -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%);">+   /* success! */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (exclude_msg) {</span><br><span style="color: hsl(120, 100%, 40%);">+            LOG_BTS(chan_state->bts, DCBS, LOGL_INFO, "%s Replaced MsgId=0x%04x/Serial=0x%04x, "</span><br><span style="color: hsl(120, 100%, 40%);">+                     "pages(%u -> %u), period(%u -> %u), num_bcast(%u -> %u)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        bts_smscb_chan_state_name(chan_state),</span><br><span style="color: hsl(120, 100%, 40%);">+                        new_msg->input.msg_id, new_msg->input.serial_nr,</span><br><span style="color: hsl(120, 100%, 40%);">+                        exclude_msg->num_pages, new_msg->num_pages,</span><br><span style="color: hsl(120, 100%, 40%);">+                     exclude_msg->input.rep_period, new_msg->input.rep_period,</span><br><span style="color: hsl(120, 100%, 40%);">+                       exclude_msg->input.num_bcast_req, new_msg->input.num_bcast_req);</span><br><span style="color: hsl(120, 100%, 40%);">+                append_bcast_compl(r_state, chan_state->bts, exclude_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+         talloc_free(exclude_msg);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else</span><br><span style="color: hsl(120, 100%, 40%);">+                LOG_BTS(chan_state->bts, DCBS, LOGL_INFO, "%s Added %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       bts_smscb_chan_state_name(chan_state), bts_smscb_msg2str(new_msg));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* replace array with new one */</span><br><span style="color: hsl(120, 100%, 40%);">+      talloc_free(chan_state->sched_arr);</span><br><span style="color: hsl(120, 100%, 40%);">+        chan_state->sched_arr = arr;</span><br><span style="color: hsl(120, 100%, 40%);">+       chan_state->sched_arr_size = rc;</span><br><span style="color: hsl(120, 100%, 40%);">+   chan_state->next_idx = 0;</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%);">+static int bts_rx_write_replace(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,</span><br><span style="color: hsl(120, 100%, 40%);">+                           struct response_state *r_state, void *priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_cbsp_write_replace *wrepl = &dec->u.write_replace;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool extended_cbch = wrepl->u.cbs.channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bts_smscb_chan_state *chan_state = bts_get_smscb_chan(bts, extended_cbch);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct bts_smscb_message *smscb;</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%);">+     if (!wrepl->is_cbs) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_BTS(bts, DCBS, LOGL_ERROR, "(Primary) Emergency Message not supported\n");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -CBSP_CAUSE_CB_NOT_SUPPORTED;</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%);">+   /* check if cell has a CBCH at all */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!gsm_bts_get_cbch(bts))</span><br><span style="color: hsl(120, 100%, 40%);">+           return -CBSP_CAUSE_CB_NOT_SUPPORTED;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* check for duplicate */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (bts_find_smscb(chan_state, wrepl->msg_id, wrepl->new_serial_nr))</span><br><span style="color: hsl(120, 100%, 40%);">+            return -CBSP_CAUSE_MSG_REF_ALREADY_USED;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!wrepl->old_serial_nr) { /* new message */</span><br><span style="color: hsl(120, 100%, 40%);">+             /* create new message */</span><br><span style="color: hsl(120, 100%, 40%);">+              smscb = bts_smscb_msg_from_wrepl(bts, wrepl);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -CBSP_CAUSE_BSC_MEMORY_EXCEEDED;</span><br><span style="color: hsl(120, 100%, 40%);">+               /* check if scheduling permits this additional message */</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = bts_try_write_replace(chan_state, smscb, NULL, r_state);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      talloc_free(smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -CBSP_CAUSE_BSC_CAPACITY_EXCEEDED;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else { /* modify / replace existing message */</span><br><span style="color: hsl(120, 100%, 40%);">+              struct bts_smscb_message *smscb_old;</span><br><span style="color: hsl(120, 100%, 40%);">+          /* find existing message */</span><br><span style="color: hsl(120, 100%, 40%);">+           smscb_old = bts_find_smscb(chan_state, wrepl->msg_id, *wrepl->old_serial_nr);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!smscb_old)</span><br><span style="color: hsl(120, 100%, 40%);">+                       return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+            /* create new message */</span><br><span style="color: hsl(120, 100%, 40%);">+              smscb = bts_smscb_msg_from_wrepl(bts, wrepl);</span><br><span style="color: hsl(120, 100%, 40%);">+         if (!smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -CBSP_CAUSE_BSC_MEMORY_EXCEEDED;</span><br><span style="color: hsl(120, 100%, 40%);">+               /* check if scheduling permits this modified message */</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = bts_try_write_replace(chan_state, smscb, smscb_old, r_state);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      talloc_free(smscb);</span><br><span style="color: hsl(120, 100%, 40%);">+                   return -CBSP_CAUSE_BSC_CAPACITY_EXCEEDED;</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%);">+static int bts_rx_kill(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,</span><br><span style="color: hsl(120, 100%, 40%);">+                      struct response_state *r_state, void *priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_cbsp_kill *kill = &dec->u.kill;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bts_smscb_chan_state *chan_state;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bts_smscb_message *smscb;</span><br><span style="color: hsl(120, 100%, 40%);">+      bool extended = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (kill->channel_ind && *kill->channel_ind == 0x01)</span><br><span style="color: hsl(120, 100%, 40%);">+            extended = true;</span><br><span style="color: hsl(120, 100%, 40%);">+      chan_state = bts_get_smscb_chan(bts, extended);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Find message by msg_id + old_serial_nr */</span><br><span style="color: hsl(120, 100%, 40%);">+  smscb = bts_find_smscb(chan_state, kill->msg_id, kill->old_serial_nr);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!smscb)</span><br><span style="color: hsl(120, 100%, 40%);">+           return -CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Remove it */</span><br><span style="color: hsl(120, 100%, 40%);">+       bts_smscb_del(smscb, chan_state, "KILL");</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%);">+static int bts_rx_reset(struct gsm_bts *bts, const struct osmo_cbsp_decoded *dec,</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct response_state *r_state, void *priv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct bts_smscb_chan_state *chan_state;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct bts_smscb_message *smscb, *smscb2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* remove all SMSCB from CBCH BASIC this BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+       chan_state = bts_get_smscb_chan(bts, false);</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry_safe(smscb, smscb2, &chan_state->messages, list)</span><br><span style="color: hsl(120, 100%, 40%);">+          bts_smscb_del(smscb, chan_state, "RESET");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* remove all SMSCB from CBCH EXTENDED this BTS */</span><br><span style="color: hsl(120, 100%, 40%);">+    chan_state = bts_get_smscb_chan(bts, true);</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry_safe(smscb, smscb2, &chan_state->messages, list)</span><br><span style="color: hsl(120, 100%, 40%);">+          bts_smscb_del(smscb, chan_state, "RESET");</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%);">+ * Receive of CBSP from CBC</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 cbsp_rx_write_replace(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct osmo_cbsp_write_replace *wrepl = &dec->u.write_replace;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_network *net = cbc->net;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct response_state *r_state = talloc_zero(cbc, struct response_state);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cbsp_decoded *resp;</span><br><span style="color: hsl(120, 100%, 40%);">+       enum cbsp_channel_ind channel_ind;</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%);">+     LOGP(DCBS, LOGL_INFO, "CBSP Rx WRITE_REPLACE (%s)\n", wrepl->is_cbs ? "CBS" : "EMERGENCY");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = cbsp_per_bts(net, r_state, &dec->u.write_replace.cell_list,</span><br><span style="color: hsl(120, 100%, 40%);">+                         bts_rx_write_replace, dec, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   /* generate response */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_WRITE_REPLACE_FAIL);</span><br><span style="color: hsl(120, 100%, 40%);">+            struct osmo_cbsp_write_replace_failure *fail = &resp->u.write_replace_fail;</span><br><span style="color: hsl(120, 100%, 40%);">+            fail->msg_id = wrepl->msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+           fail->new_serial_nr = wrepl->new_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+             fail->old_serial_nr = wrepl->old_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_replace_head(&fail->fail_list, &r_state->fail);</span><br><span style="color: hsl(120, 100%, 40%);">+           fail->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_replace_head(&fail->cell_list.list, &r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (wrepl->is_cbs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       channel_ind = wrepl->u.cbs.channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+                    fail->channel_ind = &channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (wrepl->old_serial_nr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        fail->num_compl_list.id_discr = r_state->num_completed.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+                        llist_replace_head(&fail->num_compl_list.list, &r_state->num_completed.list);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_WRITE_REPLACE_COMPL);</span><br><span style="color: hsl(120, 100%, 40%);">+           struct osmo_cbsp_write_replace_complete *compl = &resp->u.write_replace_compl;</span><br><span style="color: hsl(120, 100%, 40%);">+         compl->msg_id = wrepl->msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+          compl->new_serial_nr = wrepl->new_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+            compl->old_serial_nr = wrepl->old_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+            compl->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_replace_head(&compl->cell_list.list, &r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (wrepl->is_cbs) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       channel_ind = wrepl->u.cbs.channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+                    compl->channel_ind = &channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (wrepl->old_serial_nr) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        compl->num_compl_list.id_discr = r_state->num_completed.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+                       llist_replace_head(&compl->num_compl_list.list, &r_state->num_completed.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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   cbsp_tx_decoded(cbc, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(r_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</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 cbsp_rx_keep_alive(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      LOGP(DCBS, LOGL_DEBUG, "CBSP Rx KEEP_ALIVE\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* FIXME: repetition period */</span><br><span style="color: hsl(120, 100%, 40%);">+        return tx_cbsp_keepalive_compl(cbc);</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 cbsp_rx_kill(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  const struct osmo_cbsp_kill *kill = &dec->u.kill;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_network *net = cbc->net;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct response_state *r_state = talloc_zero(cbc, struct response_state);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cbsp_decoded *resp;</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%);">+     LOGP(DCBS, LOGL_DEBUG, "CBSP Rx KILL\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = cbsp_per_bts(net, r_state, &dec->u.kill.cell_list, bts_rx_kill, dec, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_KILL_FAIL);</span><br><span style="color: hsl(120, 100%, 40%);">+             struct osmo_cbsp_kill_failure *fail = &resp->u.kill_fail;</span><br><span style="color: hsl(120, 100%, 40%);">+              fail->msg_id = kill->msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+            fail->old_serial_nr = kill->old_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+              fail->channel_ind = kill->channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_replace_head(&fail->fail_list, &r_state->fail);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         fail->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_replace_head(&fail->cell_list.list, &r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            fail->num_compl_list.id_discr = r_state->num_completed.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+                llist_replace_head(&fail->num_compl_list.list, &r_state->num_completed.list);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_KILL_COMPL);</span><br><span style="color: hsl(120, 100%, 40%);">+            struct osmo_cbsp_kill_complete *compl = &resp->u.kill_compl;</span><br><span style="color: hsl(120, 100%, 40%);">+           compl->msg_id = kill->msg_id;</span><br><span style="color: hsl(120, 100%, 40%);">+           compl->old_serial_nr = kill->old_serial_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+             compl->channel_ind = kill->channel_ind;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               compl->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_replace_head(&compl->cell_list.list, &r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           compl->num_compl_list.id_discr = r_state->num_completed.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+               llist_replace_head(&compl->num_compl_list.list, &r_state->num_completed.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%);">+   cbsp_tx_decoded(cbc, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(r_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</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 cbsp_rx_reset(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_network *net = cbc->net;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct response_state *r_state = talloc_zero(cbc, struct response_state);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_cbsp_decoded *resp;</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%);">+     LOGP(DCBS, LOGL_DEBUG, "CBSP Rx RESET\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = cbsp_per_bts(net, r_state, &dec->u.reset.cell_list, bts_rx_reset, dec, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_RESET_FAIL);</span><br><span style="color: hsl(120, 100%, 40%);">+            struct osmo_cbsp_reset_failure *fail = &resp->u.reset_fail;</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_replace_head(&fail->fail_list, &r_state->fail);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         fail->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+           llist_replace_head(&fail->cell_list.list, &r_state->success.list);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              resp = osmo_cbsp_decoded_alloc(cbc, CBSP_MSGT_RESET_COMPL);</span><br><span style="color: hsl(120, 100%, 40%);">+           struct osmo_cbsp_reset_complete *compl = &resp->u.reset_compl;</span><br><span style="color: hsl(120, 100%, 40%);">+         if (dec->u.reset.cell_list.id_discr == CELL_IDENT_BSS) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* replace the list of individual cell identities with CELL_IDENT_BSS */</span><br><span style="color: hsl(120, 100%, 40%);">+                      compl->cell_list.id_discr = CELL_IDENT_BSS;</span><br><span style="color: hsl(120, 100%, 40%);">+                        /* no need to free success_list entries, hierarchical talloc works */</span><br><span style="color: hsl(120, 100%, 40%);">+         } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      compl->cell_list.id_discr = r_state->success.id_discr;</span><br><span style="color: hsl(120, 100%, 40%);">+                  llist_replace_head(&compl->cell_list.list, &r_state->success.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%);">+     cbsp_tx_decoded(cbc, resp);</span><br><span style="color: hsl(120, 100%, 40%);">+   talloc_free(r_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</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%);">+/*! process an incoming, already decoded CBSP message from the CBC.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] cbc link to the CBC</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] dec decoded CBSP message structure. Ownership not transferred.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns 0 on success; negative on error. */</span><br><span style="color: hsl(120, 100%, 40%);">+int cbsp_rx_decoded(struct bsc_cbc_link *cbc, const struct osmo_cbsp_decoded *dec)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int rc = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (dec->msg_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+   case CBSP_MSGT_WRITE_REPLACE:   /* create or modify message */</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = cbsp_rx_write_replace(cbc, dec);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case CBSP_MSGT_KEEP_ALIVE:      /* solicit an acknowledgement */</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = cbsp_rx_keep_alive(cbc, dec);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case CBSP_MSGT_KILL:            /* remove message */</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = cbsp_rx_kill(cbc, dec);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case CBSP_MSGT_RESET:           /* stop broadcasting of all messages */</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = cbsp_rx_reset(cbc, dec);</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case CBSP_MSGT_LOAD_QUERY:</span><br><span style="color: hsl(120, 100%, 40%);">+    case CBSP_MSGT_MSG_STATUS_QUERY:</span><br><span style="color: hsl(120, 100%, 40%);">+      case CBSP_MSGT_SET_DRX:</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGP(DCBS, LOGL_ERROR, "Received Unimplemented CBSP Message Type %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                       get_value_string(cbsp_msg_type_names, dec->msg_type));</span><br><span style="color: hsl(120, 100%, 40%);">+             /* we should implement those eventually */</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%);">+              LOGP(DCBS, LOGL_ERROR, "Received Unknown/Unexpected CBSP Message Type %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                  get_value_string(cbsp_msg_type_names, dec->msg_type));</span><br><span style="color: hsl(120, 100%, 40%);">+             break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     return rc;</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%);">+ * VTY Interface (Introspection)</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 vty_dump_smscb_chan_state(struct vty *vty, const struct bts_smscb_chan_state *cs)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct bts_smscb_message *sm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%s CBCH:%s", cs == &cs->bts->cbch_basic ? "BASIC" : "EXTENDED", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, " MsgId | SerNo | Pg |      Category | Perd | #Tx  | #Req | DCS%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+      vty_out(vty, "-------|-------|----|---------------|------|------|------|----%s", VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_for_each_entry(sm, &cs->messages, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                vty_out(vty, "  %04x |  %04x | %2u | %13s | %4u | %4u | %4u | %02x%s",</span><br><span style="color: hsl(120, 100%, 40%);">+                      sm->input.msg_id, sm->input.serial_nr, sm->num_pages,</span><br><span style="color: hsl(120, 100%, 40%);">+                        get_value_string(cbsp_category_names, sm->input.category),</span><br><span style="color: hsl(120, 100%, 40%);">+                 sm->input.rep_period, sm->bcast_count, sm->input.num_bcast_req,</span><br><span style="color: hsl(120, 100%, 40%);">+                      sm->input.dcs, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     vty_out(vty, "%s", VTY_NEWLINE);</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%);">+DEFUN(bts_show_cbs, bts_show_cbs_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+        "show bts <0-255> smscb [(basic|extended)]",</span><br><span style="color: hsl(120, 100%, 40%);">+  SHOW_STR "Display information about a BTS\n" "BTS number\n"</span><br><span style="color: hsl(120, 100%, 40%);">+       "SMS Cell Broadcast State\n"</span><br><span style="color: hsl(120, 100%, 40%);">+        "Show only information related to CBCH BASIC\n"</span><br><span style="color: hsl(120, 100%, 40%);">+     "Show only information related to CBCH EXTENDED\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_network *net = gsmnet_from_vty(vty);</span><br><span style="color: hsl(120, 100%, 40%);">+       int bts_nr = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_bts *bts;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (bts_nr >= net->num_bts) {</span><br><span style="color: hsl(120, 100%, 40%);">+           vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+         return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+     bts = gsm_bts_num(net, bts_nr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (argc < 2 || !strcmp(argv[1], "basic"))</span><br><span style="color: hsl(120, 100%, 40%);">+               vty_dump_smscb_chan_state(vty, &bts->cbch_basic);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (argc < 2 || !strcmp(argv[1], "extended"))</span><br><span style="color: hsl(120, 100%, 40%);">+            vty_dump_smscb_chan_state(vty, &bts->cbch_extended);</span><br><span style="color: hsl(120, 100%, 40%);">+</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%);">+void smscb_vty_init(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        install_element_ve(&bts_show_cbs_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am</span><br><span>index 84c341e..8bd0012 100644</span><br><span>--- a/tests/handover/Makefile.am</span><br><span>+++ b/tests/handover/Makefile.am</span><br><span>@@ -11,6 +11,7 @@</span><br><span>       $(LIBOSMOCTRL_CFLAGS) \</span><br><span>      $(LIBOSMOVTY_CFLAGS) \</span><br><span>       $(LIBOSMOABIS_CFLAGS) \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(LIBOSMONETIF_CFLAGS) \</span><br><span>     $(LIBOSMOSIGTRAN_CFLAGS) \</span><br><span>   $(LIBOSMOMGCPCLIENT_CFLAGS) \</span><br><span>        $(NULL)</span><br><span>@@ -91,11 +92,15 @@</span><br><span>        $(top_builddir)/src/osmo-bsc/rest_octets.o \</span><br><span>         $(top_builddir)/src/osmo-bsc/system_information.o \</span><br><span>  $(top_builddir)/src/osmo-bsc/timeslot_fsm.o \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(top_builddir)/src/osmo-bsc/smscb.o \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(top_builddir)/src/osmo-bsc/cbch_scheduler.o \</span><br><span style="color: hsl(120, 100%, 40%);">+       $(top_builddir)/src/osmo-bsc/cbsp_link.o \</span><br><span>   $(LIBOSMOCORE_LIBS) \</span><br><span>        $(LIBOSMOGSM_LIBS) \</span><br><span>         $(LIBOSMOCTRL_LIBS) \</span><br><span>        $(LIBOSMOVTY_LIBS) \</span><br><span>         $(LIBOSMOABIS_LIBS) \</span><br><span style="color: hsl(120, 100%, 40%);">+ $(LIBOSMONETIF_LIBS) \</span><br><span>       $(LIBOSMOSIGTRAN_LIBS) \</span><br><span>     $(LIBOSMOMGCPCLIENT_LIBS) \</span><br><span>  $(NULL)</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bsc/+/15373">change 15373</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-bsc/+/15373"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ia0a0de862a104d0f447a5d6e56c7c83981b825c7 </div>
<div style="display:none"> Gerrit-Change-Number: 15373 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-CC: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>