<p>Harald Welte <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/10460">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;">trxcon/scheduler: add TCH/H channel support<br><br>Change-Id: Ibb2a0850692c5ff86b13b820af10b12085589e67<br>---<br>M src/host/trxcon/sched_lchan_desc.c<br>M src/host/trxcon/sched_lchan_tchh.c<br>M src/host/trxcon/sched_trx.c<br>M src/host/trxcon/sched_trx.h<br>4 files changed, 331 insertions(+), 7 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/sched_lchan_desc.c</span><br><span>index 37d1273..4cac439 100644</span><br><span>--- a/src/host/trxcon/sched_lchan_desc.c</span><br><span>+++ b/src/host/trxcon/sched_lchan_desc.c</span><br><span>@@ -27,10 +27,7 @@</span><br><span> </span><br><span> /* TODO: implement */</span><br><span> #define tx_pdtch_fn   NULL</span><br><span style="color: hsl(0, 100%, 40%);">-#define tx_tchh_fn  NULL</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> #define rx_pdtch_fn     NULL</span><br><span style="color: hsl(0, 100%, 40%);">-#define rx_tchh_fn  NULL</span><br><span> </span><br><span> /* Forward declaration of handlers */</span><br><span> int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span>@@ -54,6 +51,14 @@</span><br><span> int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span>         struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span style="color: hsl(120, 100%, 40%);">+    struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,</span><br><span style="color: hsl(120, 100%, 40%);">+      sbit_t *bits, int8_t rssi, int16_t toa256);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span style="color: hsl(120, 100%, 40%);">+    struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {</span><br><span>       {</span><br><span>            TRXC_IDLE,              "IDLE",</span><br><span>diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c</span><br><span>index 316f995..7fb2809 100644</span><br><span>--- a/src/host/trxcon/sched_lchan_tchh.c</span><br><span>+++ b/src/host/trxcon/sched_lchan_tchh.c</span><br><span>@@ -3,6 +3,7 @@</span><br><span>  * TDMA scheduler: handlers for DL / UL bursts on logical channels</span><br><span>  *</span><br><span>  * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com></span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2018 by Harald Welte <laforge@gnumonks.org></span><br><span>  *</span><br><span>  * All Rights Reserved</span><br><span>  *</span><br><span>@@ -23,11 +24,27 @@</span><br><span>  */</span><br><span> </span><br><span> #include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span> #include <stdint.h></span><br><span> #include <stdbool.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/bits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/protocol/gsm_04_08.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/gsm/gsm_utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/coding/gsm0503_coding.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/codec/codec.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "l1ctl_proto.h"</span><br><span> #include "scheduler.h"</span><br><span> #include "sched_trx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "logging.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "trx_if.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "trxcon.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "l1ctl.h"</span><br><span> </span><br><span> static const uint8_t tch_h0_traffic_block_map[3][4] = {</span><br><span>         /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */</span><br><span>@@ -181,3 +198,305 @@</span><br><span>        /* Couldn't calculate the first fn, return the last */</span><br><span>   return last_fn;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span style="color: hsl(120, 100%, 40%);">+   struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,</span><br><span style="color: hsl(120, 100%, 40%);">+      sbit_t *bits, int8_t rssi, int16_t toa256)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct trx_lchan_desc *lchan_desc;</span><br><span style="color: hsl(120, 100%, 40%);">+      int n_errors = -1, n_bits_total, rc;</span><br><span style="color: hsl(120, 100%, 40%);">+  sbit_t *buffer, *offset;</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t l2[128], *mask;</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t l2_len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Set up pointers */</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan_desc = &trx_lchan_desc[lchan->type];</span><br><span style="color: hsl(120, 100%, 40%);">+     mask = &lchan->rx_burst_mask;</span><br><span style="color: hsl(120, 100%, 40%);">+  buffer = lchan->rx_bursts;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n",</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_desc->name, fn, ts->index, bid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (*mask == 0x00) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Align to the first burst */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (bid > 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%);">+           /* Align reception of the first FACCH/H frame */</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->tch_mode == GSM48_CMODE_SIGN) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!sched_tchh_facch_start(lchan->type, fn, 0))</span><br><span style="color: hsl(120, 100%, 40%);">+                           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else { /* or TCH/H traffic frame */</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!sched_tchh_traffic_start(lchan->type, fn, 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%);">+   /* Update mask */</span><br><span style="color: hsl(120, 100%, 40%);">+     *mask |= (1 << bid);</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%);">+    * FIXME: properly update measurements</span><br><span style="color: hsl(120, 100%, 40%);">+         *</span><br><span style="color: hsl(120, 100%, 40%);">+     * Since TCH/H channel is using block-diagonal interleaving,</span><br><span style="color: hsl(120, 100%, 40%);">+   * a single burst may carry 57 bits of one encoded frame,</span><br><span style="color: hsl(120, 100%, 40%);">+      * and 57 bits of another. This should be taken into account.</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan->meas.rssi_sum += rssi;</span><br><span style="color: hsl(120, 100%, 40%);">+      lchan->meas.toa256_sum += toa256;</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan->meas.rssi_num++;</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan->meas.toa256_num++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Copy burst to the end of buffer of 6 bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+     offset = buffer + bid * 116 + 464;</span><br><span style="color: hsl(120, 100%, 40%);">+    memcpy(offset, bits + 3, 58);</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(offset + 58, bits + 87, 58);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Wait until the second burst */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (bid != 1)</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%);">+   /* Wait for complete set of bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (lchan->tch_mode == GSM48_CMODE_SIGN) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* FACCH/H is interleaved over 6 bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+            if ((*mask & 0x3f) != 0x3f)</span><br><span style="color: hsl(120, 100%, 40%);">+                       goto bfi_shift;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Traffic is interleaved over 4 bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+            if ((*mask & 0x0f) != 0x0f)</span><br><span style="color: hsl(120, 100%, 40%);">+                       goto bfi_shift;</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%);">+   /* Skip decoding attempt in case of FACCH/H */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lchan->dl_ongoing_facch) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan->dl_ongoing_facch = false;</span><br><span style="color: hsl(120, 100%, 40%);">+           goto bfi_shift; /* 2/2 BFI */</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%);">+   switch (lchan->tch_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case GSM48_CMODE_SIGN:</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM48_CMODE_SPEECH_V1: /* HR */</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = gsm0503_tch_hr_decode(l2, buffer,</span><br><span style="color: hsl(120, 100%, 40%);">+                        !sched_tchh_facch_end(lchan->type, fn, 0),</span><br><span style="color: hsl(120, 100%, 40%);">+                 &n_errors, &n_bits_total);</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM48_CMODE_SPEECH_AMR: /* AMR */</span><br><span style="color: hsl(120, 100%, 40%);">+                /**</span><br><span style="color: hsl(120, 100%, 40%);">+            * TODO: AMR requires a dedicated loop,</span><br><span style="color: hsl(120, 100%, 40%);">+                * which will be implemented later...</span><br><span style="color: hsl(120, 100%, 40%);">+          */</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return -ENOTSUP;</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);</span><br><span style="color: hsl(120, 100%, 40%);">+              return -EINVAL;</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%);">+   /* Shift buffer by 4 bursts for interleaving */</span><br><span style="color: hsl(120, 100%, 40%);">+       memcpy(buffer, buffer + 232, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+    memcpy(buffer + 232, buffer + 464, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Shift burst mask */</span><br><span style="color: hsl(120, 100%, 40%);">+        *mask = *mask << 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check decoding result */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (rc < 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at "</span><br><span style="color: hsl(120, 100%, 40%);">+                 "fn=%u on %s (rc=%d)\n", fn, lchan_desc->name, rc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Send BFI */</span><br><span style="color: hsl(120, 100%, 40%);">+                goto bfi;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (rc == GSM_MACBLOCK_LEN) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Skip decoding of the next 2 stolen bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan->dl_ongoing_facch = true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Calculate TDMA frame number of the first burst */</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,</span><br><span style="color: hsl(120, 100%, 40%);">+                  fn, true); /* FACCH/H */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            /* FACCH/H received, forward to the higher layers */</span><br><span style="color: hsl(120, 100%, 40%);">+          sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,</span><br><span style="color: hsl(120, 100%, 40%);">+                       n_errors, false, false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            /* 1/2 BFI */</span><br><span style="color: hsl(120, 100%, 40%);">+         goto bfi;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* A good TCH frame received */</span><br><span style="color: hsl(120, 100%, 40%);">+               l2_len = 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%);">+   /* Calculate TDMA frame number of the first burst */</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,</span><br><span style="color: hsl(120, 100%, 40%);">+          fn, false); /* TCH/H */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Send a traffic frame to the higher layers */</span><br><span style="color: hsl(120, 100%, 40%);">+       return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,</span><br><span style="color: hsl(120, 100%, 40%);">+          n_errors, false, true);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bfi_shift:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Shift buffer */</span><br><span style="color: hsl(120, 100%, 40%);">+    memcpy(buffer, buffer + 232, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+    memcpy(buffer + 232, buffer + 464, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Shift burst mask */</span><br><span style="color: hsl(120, 100%, 40%);">+        *mask = *mask << 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bfi:</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Didn't try to decode */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (n_errors < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+          n_errors = 116 * 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Calculate TDMA frame number of the first burst */</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,</span><br><span style="color: hsl(120, 100%, 40%);">+          fn, false); /* TCH/H */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* BFI is not applicable in signalling mode */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lchan->tch_mode == GSM48_CMODE_SIGN)</span><br><span style="color: hsl(120, 100%, 40%);">+           return sched_send_dt_ind(trx, ts, lchan, NULL, 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                     n_errors, true, false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Bad frame indication */</span><br><span style="color: hsl(120, 100%, 40%);">+    l2_len = sched_bad_frame_ind(l2, lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Send a BFI frame to the higher layers */</span><br><span style="color: hsl(120, 100%, 40%);">+   return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,</span><br><span style="color: hsl(120, 100%, 40%);">+          n_errors, true, true);</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 tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,</span><br><span style="color: hsl(120, 100%, 40%);">+      struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct trx_lchan_desc *lchan_desc;</span><br><span style="color: hsl(120, 100%, 40%);">+      ubit_t burst[GSM_BURST_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+  ubit_t *buffer, *offset;</span><br><span style="color: hsl(120, 100%, 40%);">+      const uint8_t *tsc;</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *mask;</span><br><span style="color: hsl(120, 100%, 40%);">+        size_t l2_len;</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%);">+     /* Set up pointers */</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan_desc = &trx_lchan_desc[lchan->type];</span><br><span style="color: hsl(120, 100%, 40%);">+     mask = &lchan->tx_burst_mask;</span><br><span style="color: hsl(120, 100%, 40%);">+  buffer = lchan->tx_bursts;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (bid > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Align to the first burst */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (*mask == 0x00)</span><br><span style="color: hsl(120, 100%, 40%);">+                    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             goto send_burst;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (*mask == 0x00) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Align transmission of the first FACCH/H frame */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (lchan->tch_mode == GSM48_CMODE_SIGN)</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (!sched_tchh_facch_start(lchan->type, fn, 1))</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%);">+   /* Shift buffer by 2 bursts back for interleaving */</span><br><span style="color: hsl(120, 100%, 40%);">+  memcpy(buffer, buffer + 232, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Also shift TX burst mask */</span><br><span style="color: hsl(120, 100%, 40%);">+        *mask = *mask << 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If FACCH/H blocks are still pending */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->ul_facch_blocks > 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+               memcpy(buffer + 232, buffer + 464, 232);</span><br><span style="color: hsl(120, 100%, 40%);">+              goto send_burst;</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 the current TCH mode */</span><br><span style="color: hsl(120, 100%, 40%);">+      switch (lchan->tch_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case GSM48_CMODE_SIGN:</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM48_CMODE_SPEECH_V1: /* HR */</span><br><span style="color: hsl(120, 100%, 40%);">+          l2_len = GSM_HR_BYTES + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM48_CMODE_SPEECH_AMR: /* AMR */</span><br><span style="color: hsl(120, 100%, 40%);">+                /**</span><br><span style="color: hsl(120, 100%, 40%);">+            * TODO: AMR requires a dedicated loop,</span><br><span style="color: hsl(120, 100%, 40%);">+                * which will be implemented later...</span><br><span style="color: hsl(120, 100%, 40%);">+          */</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, "</span><br><span style="color: hsl(120, 100%, 40%);">+                     "dropping frame...\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Forget this primitive */</span><br><span style="color: hsl(120, 100%, 40%);">+           sched_prim_drop(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ENOTSUP;</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, "</span><br><span style="color: hsl(120, 100%, 40%);">+                    "dropping frame...\n", lchan->tch_mode);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Forget this primitive */</span><br><span style="color: hsl(120, 100%, 40%);">+           sched_prim_drop(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EINVAL;</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%);">+   /* Determine payload length */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (PRIM_IS_FACCH(lchan->prim)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          l2_len = GSM_MACBLOCK_LEN; /* FACCH */</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (lchan->prim->payload_len != l2_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu "</span><br><span style="color: hsl(120, 100%, 40%);">+                     "(expected %zu for TCH or %u for FACCH), so dropping...\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          /* Forget this primitive */</span><br><span style="color: hsl(120, 100%, 40%);">+           sched_prim_drop(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EINVAL;</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 the payload */</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rc) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Forget this primitive */</span><br><span style="color: hsl(120, 100%, 40%);">+           sched_prim_drop(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+               return -EINVAL;</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 FACCH/H frame occupies 6 bursts */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (PRIM_IS_FACCH(lchan->prim))</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan->ul_facch_blocks = 6;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+send_burst:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Determine which burst should be sent */</span><br><span style="color: hsl(120, 100%, 40%);">+    offset = buffer + bid * 116;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Update mask */</span><br><span style="color: hsl(120, 100%, 40%);">+     *mask |= (1 << bid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Choose proper TSC */</span><br><span style="color: hsl(120, 100%, 40%);">+       tsc = sched_nb_training_bits[trx->tsc];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Compose a new burst */</span><br><span style="color: hsl(120, 100%, 40%);">+     memset(burst, 0, 3); /* TB */</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(burst + 3, offset, 58); /* Payload 1/2 */</span><br><span style="color: hsl(120, 100%, 40%);">+      memcpy(burst + 61, tsc, 26); /* TSC */</span><br><span style="color: hsl(120, 100%, 40%);">+        memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */</span><br><span style="color: hsl(120, 100%, 40%);">+        memset(burst + 145, 0, 3); /* TB */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_desc->name, fn, ts->index, bid);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Forward burst to transceiver */</span><br><span style="color: hsl(120, 100%, 40%);">+    sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* In case of a FACCH/H frame, one block less */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (lchan->ul_facch_blocks)</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan->ul_facch_blocks--;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if ((*mask & 0x0f) == 0x0f) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /**</span><br><span style="color: hsl(120, 100%, 40%);">+            * If no more FACCH/H blocks pending,</span><br><span style="color: hsl(120, 100%, 40%);">+          * confirm data / traffic sending</span><br><span style="color: hsl(120, 100%, 40%);">+              */</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!lchan->ul_facch_blocks)</span><br><span style="color: hsl(120, 100%, 40%);">+                       sched_send_dt_conf(trx, ts, lchan, fn,</span><br><span style="color: hsl(120, 100%, 40%);">+                                PRIM_IS_TCH(lchan->prim));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Forget processed primitive */</span><br><span style="color: hsl(120, 100%, 40%);">+              sched_prim_drop(lchan);</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/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c</span><br><span>index e20da39..023764d 100644</span><br><span>--- a/src/host/trxcon/sched_trx.c</span><br><span>+++ b/src/host/trxcon/sched_trx.c</span><br><span>@@ -463,7 +463,7 @@</span><br><span>  /* TCH specific variables */</span><br><span>         if (CHAN_IS_TCH(lchan->type)) {</span><br><span>           lchan->dl_ongoing_facch = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-         lchan->ul_ongoing_facch = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan->ul_facch_blocks = 0;</span><br><span> </span><br><span>           lchan->tch_mode = GSM48_CMODE_SIGN;</span><br><span> </span><br><span>diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h</span><br><span>index 2c8b4d8..08e2489 100644</span><br><span>--- a/src/host/trxcon/sched_trx.h</span><br><span>+++ b/src/host/trxcon/sched_trx.h</span><br><span>@@ -171,9 +171,9 @@</span><br><span>    uint8_t tch_mode;</span><br><span> </span><br><span>        /*! \brief FACCH/H on downlink */</span><br><span style="color: hsl(0, 100%, 40%);">-       uint8_t dl_ongoing_facch;</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! \brief FACCH/H on uplink */</span><br><span style="color: hsl(0, 100%, 40%);">- uint8_t ul_ongoing_facch;</span><br><span style="color: hsl(120, 100%, 40%);">+     bool dl_ongoing_facch;</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! \brief pending FACCH/H blocks on Uplink */</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t ul_facch_blocks;</span><br><span> </span><br><span>         struct {</span><br><span>             /*! \brief Number of RSSI values */</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/10460">change 10460</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/10460"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmocom-bb </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ibb2a0850692c5ff86b13b820af10b12085589e67 </div>
<div style="display:none"> Gerrit-Change-Number: 10460 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Vadim Yanitskiy <axilirator@gmail.com> </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: Vadim Yanitskiy <axilirator@gmail.com> </div>