<p>Pau Espin Pedrol <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/11040">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  Harald Welte: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">osmotrx: Introduce code architecture chapter<br><br>Change-Id: I21084e6315d79a1adcb305e12343da218837dc31<br>---<br>A OsmoTRX/chapters/code-architecture.adoc<br>M OsmoTRX/osmotrx-usermanual.adoc<br>M common/chapters/trx_if.adoc<br>3 files changed, 145 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/OsmoTRX/chapters/code-architecture.adoc b/OsmoTRX/chapters/code-architecture.adoc</span><br><span>new file mode 100644</span><br><span>index 0000000..18d0e3a</span><br><span>--- /dev/null</span><br><span>+++ b/OsmoTRX/chapters/code-architecture.adoc</span><br><span>@@ -0,0 +1,141 @@</span><br><span style="color: hsl(120, 100%, 40%);">+[[code_architecture]]</span><br><span style="color: hsl(120, 100%, 40%);">+== Code Architecture</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[[fig-code-architecture-general]]</span><br><span style="color: hsl(120, 100%, 40%);">+.General overview of main OsmoTRX components</span><br><span style="color: hsl(120, 100%, 40%);">+[graphviz]</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+digraph hierarchy {</span><br><span style="color: hsl(120, 100%, 40%);">+node[shape=record,style=filled,fillcolor=gray95]</span><br><span style="color: hsl(120, 100%, 40%);">+edge[dir=back, arrowtail=empty]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"]</span><br><span style="color: hsl(120, 100%, 40%);">+3[label = "{RadioInterface|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+4[label = "{RadioInterfaceResamp|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+5[label = "{RadioInterfaceMulti|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+6[label = "{RadioDevice|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+7[label = "{UHDDevice|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+8[label = "{LMSDevice|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+9[label = "{USRPDevice|...}"]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+2->3[arrowtail=odiamond]</span><br><span style="color: hsl(120, 100%, 40%);">+3->4[constraint=false]</span><br><span style="color: hsl(120, 100%, 40%);">+3->5[constraint=false]</span><br><span style="color: hsl(120, 100%, 40%);">+3->6[arrowtail=odiamond]</span><br><span style="color: hsl(120, 100%, 40%);">+6->7</span><br><span style="color: hsl(120, 100%, 40%);">+6->8</span><br><span style="color: hsl(120, 100%, 40%);">+6->9</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%);">+[[fig-code-architecture-threads]]</span><br><span style="color: hsl(120, 100%, 40%);">+.Example of thread architecture with OsmoTRX configured to use 2 logical RF channels (Trx=Transceiver, RI=RadioIface)</span><br><span style="color: hsl(120, 100%, 40%);">+[graphviz]</span><br><span style="color: hsl(120, 100%, 40%);">+----</span><br><span style="color: hsl(120, 100%, 40%);">+digraph hierarchy {</span><br><span style="color: hsl(120, 100%, 40%);">+node[shape=record,style=filled,fillcolor=gray95]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+trans [label="Transceiver"];</span><br><span style="color: hsl(120, 100%, 40%);">+radioiface [label="RadioInterface"];</span><br><span style="color: hsl(120, 100%, 40%);">+radiodev [label="RadioDevice"];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+trans:nw->trans:ne [label="Trx.ControlServiceLoop_0"];</span><br><span style="color: hsl(120, 100%, 40%);">+trans:nw->trans:ne [label="Trx.ControlServiceLoop_1"];</span><br><span style="color: hsl(120, 100%, 40%);">+trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_0"];</span><br><span style="color: hsl(120, 100%, 40%);">+trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_1"];</span><br><span style="color: hsl(120, 100%, 40%);">+radioiface:e->trans:e [label="Trx.RxServiceLoop_0"];</span><br><span style="color: hsl(120, 100%, 40%);">+radioiface:e->trans:e [label="Trx.RxServiceLoop_1"];</span><br><span style="color: hsl(120, 100%, 40%);">+radioiface->radiodev[label="RI.AlignRadioServiceLoop"];</span><br><span style="color: hsl(120, 100%, 40%);">+radioiface:sw->radiodev:nw [label="Trx.TxLowerLoop"];</span><br><span style="color: hsl(120, 100%, 40%);">+radiodev:ne->radioiface:se [label="Trx.RxLowerLoop"];</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%);">+[[code_component_transceiver]]</span><br><span style="color: hsl(120, 100%, 40%);">+=== Transceiver</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The Transceiver is the main component managing the other components running in</span><br><span style="color: hsl(120, 100%, 40%);">+the OsmoTRX process. There's a unique instance per process.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This class is quite complex from code point of view, as it starts lots of</span><br><span style="color: hsl(120, 100%, 40%);">+different threads and hence the interaction with this class from the outside is</span><br><span style="color: hsl(120, 100%, 40%);">+quite limited. Only interaction possible is to:</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+* `Transceiver()`: Create an instance through its constructor, at this time most</span><br><span style="color: hsl(120, 100%, 40%);">+  configuration is handed to it.</span><br><span style="color: hsl(120, 100%, 40%);">+* `init()`: Start running all the threads.</span><br><span style="color: hsl(120, 100%, 40%);">+* `receiveFIFO()`: Attach a `radioInterface` channel FIFO in order to use it.</span><br><span style="color: hsl(120, 100%, 40%);">+* `setSignalHandler()`: Used to set up a callback to receive certain events</span><br><span style="color: hsl(120, 100%, 40%);">+  asynchronously from the Transceiver. No assumptions can be made about from</span><br><span style="color: hsl(120, 100%, 40%);">+  which thread is the callback being called, which means multi-thread locking</span><br><span style="color: hsl(120, 100%, 40%);">+  precautions may be required in certain cases, similar to usual signal handler</span><br><span style="color: hsl(120, 100%, 40%);">+  processing. One important event received through this path is for instance</span><br><span style="color: hsl(120, 100%, 40%);">+  when the Transceiver detected a fatal error which requires it to stop. Since</span><br><span style="color: hsl(120, 100%, 40%);">+  it cannot stop itself (see destructor below), stopping procedure must be</span><br><span style="color: hsl(120, 100%, 40%);">+  delegated to the user who created the instance.</span><br><span style="color: hsl(120, 100%, 40%);">+* `~Transceiver()`: The destructor, which stops all running threads created at</span><br><span style="color: hsl(120, 100%, 40%);">+  `init()` time. Destroying the object is the only way to stop the `Transceiver`</span><br><span style="color: hsl(120, 100%, 40%);">+  completely, and must be called from a thread not managed by the</span><br><span style="color: hsl(120, 100%, 40%);">+  `Transceiver`, otherwise it will deadlock. Usually it is stopped from the main</span><br><span style="color: hsl(120, 100%, 40%);">+  thread, the one that called the constructor during startup.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+During `init()` time, `Transceiver` will create a noticeable amount of threads,</span><br><span style="color: hsl(120, 100%, 40%);">+which may vary depending on the amount of RF channels requested.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Static amount of Threads (1 per `Transceiver` instance):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+* `RxLowerLoop`: This thread is responsible for reading bursts from the</span><br><span style="color: hsl(120, 100%, 40%);">+  `RadioInterface`, storing them into its FIFO and sending Clock Indications</span><br><span style="color: hsl(120, 100%, 40%);">+  (<<trx_if_clock_ind>>) to _osmo-bts_trx_.</span><br><span style="color: hsl(120, 100%, 40%);">+* `TxLowerLoop`: Manages pushing bursts from buffers in the FIFO into the</span><br><span style="color: hsl(120, 100%, 40%);">+  `RadioInterface` at expected correct time based on the Transceiver clock.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Dynamic amount of Threads (1 per RF logical channel on the `Transceiver` instance):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+* `ControlServiceLoop`: Handles commands from the Per-ARFCN Control Interface</span><br><span style="color: hsl(120, 100%, 40%);">+  socket (<<trx_if_control>>). Each thread is responsible for managing one</span><br><span style="color: hsl(120, 100%, 40%);">+  socket related to one ARFCN or which is the same, to one RF logical channel.</span><br><span style="color: hsl(120, 100%, 40%);">+  These are the only threads expected to use the private `start()` and `stop()`</span><br><span style="color: hsl(120, 100%, 40%);">+  methods of the `Transceiver()` class, since those methods don't stop any of</span><br><span style="color: hsl(120, 100%, 40%);">+  the `ControlServiceLoop` threads as they must keep running to handle new</span><br><span style="color: hsl(120, 100%, 40%);">+  commands (for instance, to re-start processing samples with the _POWERON_</span><br><span style="color: hsl(120, 100%, 40%);">+  command).</span><br><span style="color: hsl(120, 100%, 40%);">+* `RxServiceLoop`: Each thread of this type pulls bursts from the</span><br><span style="color: hsl(120, 100%, 40%);">+  `RadioInterface` FIFO for one specific logical RF channel and handles it</span><br><span style="color: hsl(120, 100%, 40%);">+  according to the slot and burst correlation type, finally sending proper data</span><br><span style="color: hsl(120, 100%, 40%);">+  over the TRX Manager UDP socket (<<trx_if>>).</span><br><span style="color: hsl(120, 100%, 40%);">+* `TxPriorityQueueServiceLoop`: Blocks reading from one ARFCN specific TRX</span><br><span style="color: hsl(120, 100%, 40%);">+  Manager UDP socket (<<trx_if>>), and fills the `RadioInterface` with it</span><br><span style="color: hsl(120, 100%, 40%);">+  setting clock related information.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[[code_component_radioiface]]</span><br><span style="color: hsl(120, 100%, 40%);">+=== RadioInterface</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The `RadioInterface` sits between the `Transceiver` and the `RadioDevice`,  and</span><br><span style="color: hsl(120, 100%, 40%);">+provides extra features to the pipe like channelizers, resamplers, Tx/Rx</span><br><span style="color: hsl(120, 100%, 40%);">+synchronization on some devices, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+If the `RadioDevice` it drives requires it (only _USRP1_ so far), the</span><br><span style="color: hsl(120, 100%, 40%);">+`RadioIntercace` will start and manage a thread internally called</span><br><span style="color: hsl(120, 100%, 40%);">+`AlignRadioServiceLoop` which will align current RX and TX timestamps.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Different features are offered through different `RadioInterface` subclasses</span><br><span style="color: hsl(120, 100%, 40%);">+which are selected based on configuration and device detected at runtime. Using</span><br><span style="color: hsl(120, 100%, 40%);">+these features may impact on the amount of CPU required to run the entire pipe.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== RadioInterfaceResamp</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This subclass of `RadioInterface` is automatically selected when some known</span><br><span style="color: hsl(120, 100%, 40%);">+specific UHD are to be used, since they require resampling to work properly.</span><br><span style="color: hsl(120, 100%, 40%);">+Some of this devices are for instance Ettus B100, USRP2 and X3XX models.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+==== RadioInterfaceMulti</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+This subclass of `RadioInterface` is used when <<multiarfcn_mode>> is requested.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+[[code_component_radiodev]]</span><br><span style="color: hsl(120, 100%, 40%);">+=== RadioDevice</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The `RadioDevice` class is responsible for driving the actual Hardware device.</span><br><span style="color: hsl(120, 100%, 40%);">+It is actually only an interface, and it is implemented in each backend which in</span><br><span style="color: hsl(120, 100%, 40%);">+turn becomes a specific OsmoTRX binary, see <<trx_backends>>.</span><br><span>diff --git a/OsmoTRX/osmotrx-usermanual.adoc b/OsmoTRX/osmotrx-usermanual.adoc</span><br><span>index a713e4a..14f5514 100644</span><br><span>--- a/OsmoTRX/osmotrx-usermanual.adoc</span><br><span>+++ b/OsmoTRX/osmotrx-usermanual.adoc</span><br><span>@@ -29,6 +29,8 @@</span><br><span> </span><br><span> include::chapters/trx-backends.adoc[]</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+include::chapters/code-architecture.adoc[]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> include::../common/chapters/trx_if.adoc[]</span><br><span> </span><br><span> include::../common/chapters/port_numbers.adoc[]</span><br><span>diff --git a/common/chapters/trx_if.adoc b/common/chapters/trx_if.adoc</span><br><span>index 4fb8f9b..b684b7b 100644</span><br><span>--- a/common/chapters/trx_if.adoc</span><br><span>+++ b/common/chapters/trx_if.adoc</span><br><span>@@ -11,6 +11,7 @@</span><br><span> every socket is at P+100. For any given build, the number of ARFCN interfaces</span><br><span> can be fixed.</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+[[trx_if_clock_ind]]</span><br><span> === Indications on the Master Clock Interface</span><br><span> </span><br><span> The master clock interface is output only (from the radio).</span><br><span>@@ -24,6 +25,7 @@</span><br><span> IND CLOCK <totalFrames></span><br><span> ----</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+[[trx_if_control]]</span><br><span> === Commands on the Per-ARFCN Control Interface</span><br><span> </span><br><span> The per-ARFCN control interface uses a command-reponse protocol. Commands are</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/11040">change 11040</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/11040"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-manuals </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I21084e6315d79a1adcb305e12343da218837dc31 </div>
<div style="display:none"> Gerrit-Change-Number: 11040 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>