This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
fixeria gerrit-no-reply at lists.osmocom.orgfixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/18820 )
Change subject: osmo-bts-trx: move logical channel handlers to separate files
......................................................................
osmo-bts-trx: move logical channel handlers to separate files
It's easier to maintain the logical channel handlers in separate
files, rather than in a huge one (scheduler_trx.c, ~2k lines).
Change-Id: Ie5663fd90596b4800a4546675a323250bbb24c80
---
M src/osmo-bts-trx/Makefile.am
A src/osmo-bts-trx/sched_lchan_fcch_sch.c
A src/osmo-bts-trx/sched_lchan_pdtch.c
A src/osmo-bts-trx/sched_lchan_rach.c
A src/osmo-bts-trx/sched_lchan_tchf.c
A src/osmo-bts-trx/sched_lchan_tchh.c
A src/osmo-bts-trx/sched_lchan_xcch.c
A src/osmo-bts-trx/sched_utils.h
M src/osmo-bts-trx/scheduler_trx.c
9 files changed, 1,816 insertions(+), 1,562 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/20/18820/1
diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am
index b2d9b19..ae69000 100644
--- a/src/osmo-bts-trx/Makefile.am
+++ b/src/osmo-bts-trx/Makefile.am
@@ -28,6 +28,7 @@
$(NULL)
noinst_HEADERS = \
+ sched_utils.h \
trx_if.h \
l1_if.h \
loops.h \
@@ -40,6 +41,12 @@
trx_if.c \
l1_if.c \
scheduler_trx.c \
+ sched_lchan_fcch_sch.c \
+ sched_lchan_rach.c \
+ sched_lchan_xcch.c \
+ sched_lchan_pdtch.c \
+ sched_lchan_tchf.c \
+ sched_lchan_tchh.c \
trx_vty.c \
loops.c \
$(NULL)
diff --git a/src/osmo-bts-trx/sched_lchan_fcch_sch.c b/src/osmo-bts-trx/sched_lchan_fcch_sch.c
new file mode 100644
index 0000000..63dd468
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_fcch_sch.c
@@ -0,0 +1,94 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+
+#include <sched_utils.h>
+
+/* obtain a to-be-transmitted FCCH (frequency correction channel) burst */
+ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting FCCH\n");
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ /* BURST BYPASS */
+
+ return (ubit_t *) _sched_fcch_burst;
+}
+
+/* obtain a to-be-transmitted SCH (synchronization channel) burst */
+ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ static ubit_t bits[GSM_BURST_LEN], burst[78];
+ uint8_t sb_info[4];
+ struct gsm_time t;
+ uint8_t t3p, bsic;
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting SCH\n");
+
+ /* BURST BYPASS */
+
+ /* create SB info from GSM time and BSIC */
+ gsm_fn2gsmtime(&t, fn);
+ t3p = t.t3 / 10;
+ bsic = l1t->trx->bts->bsic;
+ sb_info[0] =
+ ((bsic & 0x3f) << 2) |
+ ((t.t1 & 0x600) >> 9);
+ sb_info[1] =
+ ((t.t1 & 0x1fe) >> 1);
+ sb_info[2] =
+ ((t.t1 & 0x001) << 7) |
+ ((t.t2 & 0x1f) << 2) |
+ ((t3p & 0x6) >> 1);
+ sb_info[3] =
+ (t3p & 0x1);
+
+ /* encode bursts */
+ gsm0503_sch_encode(burst, sb_info);
+
+ /* compose burst */
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 39);
+ memcpy(bits + 42, _sched_sch_train, 64);
+ memcpy(bits + 106, burst + 39, 39);
+ memset(bits + 145, 0, 3);
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ return bits;
+}
diff --git a/src/osmo-bts-trx/sched_lchan_pdtch.c b/src/osmo-bts-trx/sched_lchan_pdtch.c
new file mode 100644
index 0000000..8b39797
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_pdtch.c
@@ -0,0 +1,249 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+
+#include <sched_utils.h>
+
+/* Maximum size of a EGPRS message in bytes */
+#define EGPRS_0503_MAX_BYTES 155
+
+/*! \brief a single PDTCH burst was received by the PHY, process it */
+int rx_pdtch_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
+ uint8_t bid, const struct trx_ul_burst_ind *bi)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint32_t *first_fn = &chan_state->ul_first_fn;
+ uint8_t *mask = &chan_state->ul_mask;
+ float *rssi_sum = &chan_state->rssi_sum;
+ uint8_t *rssi_num = &chan_state->rssi_num;
+ int32_t *toa256_sum = &chan_state->toa256_sum;
+ uint8_t *toa_num = &chan_state->toa_num;
+ int32_t *ci_cb_sum = &chan_state->ci_cb_sum;
+ uint8_t *ci_cb_num = &chan_state->ci_cb_num;
+ uint8_t l2[EGPRS_0503_MAX_BYTES];
+ int n_errors = 0;
+ int n_bursts_bits = 0;
+ int n_bits_total = 0;
+ int16_t lqual_cb;
+ uint16_t ber10k;
+ int rc;
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received PDTCH bid=%u\n", bid);
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx,
+ GSM0503_EGPRS_BURSTS_NBITS);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p, 0, GSM0503_EGPRS_BURSTS_NBITS);
+ *mask = 0x0;
+ *first_fn = bi->fn;
+ *rssi_sum = 0;
+ *rssi_num = 0;
+ *toa256_sum = 0;
+ *toa_num = 0;
+ *ci_cb_sum = 0;
+ *ci_cb_num = 0;
+ }
+
+ /* update mask + rssi */
+ *mask |= (1 << bid);
+ *rssi_sum += bi->rssi;
+ (*rssi_num)++;
+ *toa256_sum += bi->toa256;
+ (*toa_num)++;
+
+ /* C/I: Carrier-to-Interference ratio (in centiBels) */
+ if (bi->flags & TRX_BI_F_CI_CB) {
+ *ci_cb_sum += bi->ci_cb;
+ (*ci_cb_num)++;
+ }
+
+ /* copy burst to buffer of 4 bursts */
+ if (bi->burst_len == EGPRS_BURST_LEN) {
+ burst = *bursts_p + bid * 348;
+ memcpy(burst, bi->burst + 9, 174);
+ memcpy(burst + 174, bi->burst + 261, 174);
+ n_bursts_bits = GSM0503_EGPRS_BURSTS_NBITS;
+ } else {
+ burst = *bursts_p + bid * 116;
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+ n_bursts_bits = GSM0503_GPRS_BURSTS_NBITS;
+ }
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received incomplete frame (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ }
+ *mask = 0x0;
+
+ /*
+ * Attempt to decode EGPRS bursts first. For 8-PSK EGPRS this is all we
+ * do. Attempt GPRS decoding on EGPRS failure. If the burst is GPRS,
+ * then we incur decoding overhead of 31 bits on the Type 3 EGPRS
+ * header, which is tolerable.
+ */
+ rc = gsm0503_pdtch_egprs_decode(l2, *bursts_p, n_bursts_bits,
+ NULL, &n_errors, &n_bits_total);
+
+ if ((bi->burst_len == GSM_BURST_LEN) && (rc < 0)) {
+ rc = gsm0503_pdtch_decode(l2, *bursts_p, NULL,
+ &n_errors, &n_bits_total);
+ }
+
+ if (rc <= 0) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received bad PDTCH (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ return 0;
+ }
+
+ lqual_cb = *ci_cb_num ? (*ci_cb_sum / *ci_cb_num) : 0;
+ ber10k = compute_ber10k(n_bits_total, n_errors);
+ return _sched_compose_ph_data_ind(l1t, bi->tn,
+ *first_fn, chan, l2, rc,
+ *rssi_sum / *rssi_num,
+ *toa256_sum / *toa_num,
+ lqual_cb, ber10k,
+ PRES_INFO_BOTH);
+}
+
+/* obtain a to-be-transmitted PDTCH (packet data) burst */
+ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+ struct msgb *msg = NULL; /* make GCC happy */
+ ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
+ enum trx_burst_type *burst_type = &l1ts->chan_state[chan].dl_burst_type;
+ static ubit_t bits[EGPRS_BURST_LEN];
+ int rc = 0;
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get mac block from queue */
+ msg = _sched_dequeue_prim(l1t, tn, fn, chan);
+ if (msg)
+ goto got_msg;
+
+ LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No prim for transmit.\n");
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return NULL;
+
+got_msg:
+ /* BURST BYPASS */
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx,
+ GSM0503_EGPRS_BURSTS_NBITS);
+ if (!*bursts_p)
+ return NULL;
+ }
+
+ /* encode bursts */
+ rc = gsm0503_pdtch_egprs_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
+ if (rc < 0)
+ rc = gsm0503_pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
+
+ /* check validity of message */
+ if (rc < 0) {
+ LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim invalid length, please FIX! "
+ "(len=%ld)\n", (long)(msg->tail - msg->l2h));
+ /* free message */
+ msgb_free(msg);
+ goto no_msg;
+ } else if (rc == GSM0503_EGPRS_BURSTS_NBITS) {
+ *burst_type = TRX_BURST_8PSK;
+ } else {
+ *burst_type = TRX_BURST_GMSK;
+ }
+
+ /* free message */
+ msgb_free(msg);
+
+send_burst:
+ /* compose burst */
+ if (*burst_type == TRX_BURST_8PSK) {
+ burst = *bursts_p + bid * 348;
+ memset(bits, 1, 9);
+ memcpy(bits + 9, burst, 174);
+ memcpy(bits + 183, _sched_egprs_tsc[gsm_ts_tsc(ts)], 78);
+ memcpy(bits + 261, burst + 174, 174);
+ memset(bits + 435, 1, 9);
+
+ if (nbits)
+ *nbits = EGPRS_BURST_LEN;
+ } else {
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+ }
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
+
+ return bits;
+}
diff --git a/src/osmo-bts-trx/sched_lchan_rach.c b/src/osmo-bts-trx/sched_lchan_rach.c
new file mode 100644
index 0000000..fa50f14
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_rach.c
@@ -0,0 +1,212 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2019 by Vadim Yanitskiy <axilirator at gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+
+#include <sched_utils.h>
+
+/* 3GPP TS 05.02, section 5.2.7 */
+#define RACH_EXT_TAIL_LEN 8
+#define RACH_SYNCH_SEQ_LEN 41
+
+enum rach_synch_seq_t {
+ RACH_SYNCH_SEQ_UNKNOWN = -1,
+ RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */
+ RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */
+ RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */
+ RACH_SYNCH_SEQ_NUM
+};
+
+static struct value_string rach_synch_seq_names[] = {
+ { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" },
+ { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" },
+ { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" },
+ { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" },
+ { 0, NULL },
+};
+
+static enum rach_synch_seq_t rach_get_synch_seq(sbit_t *bits, int *best_score)
+{
+ sbit_t *synch_seq_burst = bits + RACH_EXT_TAIL_LEN;
+ enum rach_synch_seq_t seq = RACH_SYNCH_SEQ_TS0;
+ int score[RACH_SYNCH_SEQ_NUM] = { 0 };
+ int max_score = INT_MIN;
+ int i, j;
+
+ /* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
+ static const char synch_seq_ref[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
+ [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
+ [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
+ [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
+ };
+
+ /* Get a multiplier for j-th bit of i-th synch. sequence */
+#define RACH_SYNCH_SEQ_MULT \
+ (synch_seq_ref[i][j] == '1' ? -1 : 1)
+
+ /* For each synch. sequence, count the bit match score. Since we deal with
+ * soft-bits (-127...127), we sum the absolute values of matching ones,
+ * and subtract the absolute values of different ones, so the resulting
+ * score is more accurate than it could be with hard-bits. */
+ for (i = 0; i < RACH_SYNCH_SEQ_NUM; i++) {
+ for (j = 0; j < RACH_SYNCH_SEQ_LEN; j++)
+ score[i] += RACH_SYNCH_SEQ_MULT * synch_seq_burst[j];
+
+ /* Keep the maximum value updated */
+ if (score[i] > max_score) {
+ max_score = score[i];
+ seq = i;
+ }
+ }
+
+ /* Calculate an approximate level of our confidence */
+ if (best_score != NULL)
+ *best_score = max_score;
+
+ /* At least 1/3 of a synch. sequence shall match */
+ if (max_score < (127 * RACH_SYNCH_SEQ_LEN / 3))
+ return RACH_SYNCH_SEQ_UNKNOWN;
+
+ return seq;
+}
+
+int rx_rach_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
+ uint8_t bid, const struct trx_ul_burst_ind *bi)
+{
+ struct osmo_phsap_prim l1sap;
+ int n_errors = 0;
+ int n_bits_total = 0;
+ uint16_t ra11;
+ uint8_t ra;
+ int rc;
+
+ /* TSC (Training Sequence Code) is an optional parameter of the UL burst
+ * indication. We need this information in order to decide whether an
+ * Access Burst is 11-bit encoded or not (see OS#1854). If this information
+ * is absent, we try to correlate the received synch. sequence with the
+ * known ones (3GPP TS 05.02, section 5.2.7), and fall-back to the default
+ * TS0 if it fails. */
+ enum rach_synch_seq_t synch_seq = RACH_SYNCH_SEQ_TS0;
+ int best_score = 127 * RACH_SYNCH_SEQ_LEN;
+
+ /* If logical channel is not either of RACH, PDTCH or PTCCH, this is a
+ * handover Access Burst, which is always encoded as 8-bit and shall
+ * contain the generic training sequence (TS0). */
+ if (chan == TRXC_RACH || chan == TRXC_PDTCH || chan == TRXC_PTCCH) {
+ if (bi->flags & TRX_BI_F_TS_INFO)
+ synch_seq = (enum rach_synch_seq_t) bi->tsc;
+ else
+ synch_seq = rach_get_synch_seq((sbit_t *) bi->burst, &best_score);
+ }
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received%s RACH (%s): rssi=%d toa256=%d",
+ (chan != TRXC_RACH) ? " handover" : "",
+ get_value_string(rach_synch_seq_names, synch_seq),
+ bi->rssi, bi->toa256);
+ if (bi->flags & TRX_BI_F_CI_CB)
+ LOGPC(DL1P, LOGL_DEBUG, " C/I=%d cB", bi->ci_cb);
+ else
+ LOGPC(DL1P, LOGL_DEBUG, " match=%.1f%%",
+ best_score * 100.0 / (127 * RACH_SYNCH_SEQ_LEN));
+ LOGPC(DL1P, LOGL_DEBUG, "\n");
+
+ /* Compose a new L1SAP primitive */
+ memset(&l1sap, 0x00, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL);
+ l1sap.u.rach_ind.chan_nr = trx_chan_desc[chan].chan_nr | bi->tn;
+ l1sap.u.rach_ind.acc_delay = (bi->toa256 >= 0) ? bi->toa256 / 256 : 0;
+ l1sap.u.rach_ind.acc_delay_256bits = bi->toa256;
+ l1sap.u.rach_ind.rssi = bi->rssi;
+ l1sap.u.rach_ind.fn = bi->fn;
+
+ /* Link quality is defined by C/I (Carrier-to-Interference ratio),
+ * which has optional presence. If it's absent, report the
+ * minimum acceptable value to pass L1SAP checks. */
+ if (bi->flags & TRX_BI_F_CI_CB)
+ l1sap.u.rach_ind.lqual_cb = bi->ci_cb;
+ else
+ l1sap.u.rach_ind.lqual_cb = l1t->trx->bts->min_qual_rach;
+
+ /* Decode RACH depending on its synch. sequence */
+ switch (synch_seq) {
+ case RACH_SYNCH_SEQ_TS1:
+ case RACH_SYNCH_SEQ_TS2:
+ rc = gsm0503_rach_ext_decode_ber(&ra11, bi->burst + RACH_EXT_TAIL_LEN + RACH_SYNCH_SEQ_LEN,
+ l1t->trx->bts->bsic, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received bad Access Burst\n");
+ return 0;
+ }
+
+ if (synch_seq == RACH_SYNCH_SEQ_TS1)
+ l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_1;
+ else
+ l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_2;
+
+ l1sap.u.rach_ind.is_11bit = 1;
+ l1sap.u.rach_ind.ra = ra11;
+ break;
+
+ case RACH_SYNCH_SEQ_TS0:
+ default:
+ /* Fall-back to the default TS0 if needed */
+ if (synch_seq != RACH_SYNCH_SEQ_TS0) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Falling-back to the default TS0\n");
+ synch_seq = RACH_SYNCH_SEQ_TS0;
+ }
+
+ rc = gsm0503_rach_decode_ber(&ra, bi->burst + RACH_EXT_TAIL_LEN + RACH_SYNCH_SEQ_LEN,
+ l1t->trx->bts->bsic, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received bad Access Burst\n");
+ return 0;
+ }
+
+ l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_0;
+ l1sap.u.rach_ind.is_11bit = 0;
+ l1sap.u.rach_ind.ra = ra;
+ break;
+ }
+
+ l1sap.u.rach_ind.ber10k = compute_ber10k(n_bits_total, n_errors);
+
+ /* forward primitive */
+ l1sap_up(l1t->trx, &l1sap);
+
+ return 0;
+}
diff --git a/src/osmo-bts-trx/sched_lchan_tchf.c b/src/osmo-bts-trx/sched_lchan_tchf.c
new file mode 100644
index 0000000..fb11f45
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_tchf.c
@@ -0,0 +1,570 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/codec/codec.h>
+#include <osmocom/codec/ecu.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+#include <osmo-bts/msg_utils.h>
+
+#include <sched_utils.h>
+#include <loops.h>
+
+/*! \brief a single TCH/F burst was received by the PHY, process it */
+int rx_tchf_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
+ uint8_t bid, const struct trx_ul_burst_ind *bi)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint8_t *mask = &chan_state->ul_mask;
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ uint8_t tch_data[128]; /* just to be safe */
+ int rc, amr = 0;
+ int n_errors = 0;
+ int n_bits_total = 0;
+ bool bfi_flag = false;
+ struct gsm_lchan *lchan =
+ get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | bi->tn);
+ unsigned int fn_begin;
+ uint16_t ber10k;
+ uint8_t is_sub = 0;
+ uint8_t ft;
+
+ /* If handover RACH detection is turned on, treat this burst as an Access Burst.
+ * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
+ if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
+ return rx_rach_fn(l1t, chan, bid, bi);
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received TCH/F, bid=%u\n", bid);
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p + 464, 0, 464);
+ *mask = 0x0;
+ }
+
+ /* update mask */
+ *mask |= (1 << bid);
+
+ /* copy burst to end of buffer of 8 bursts */
+ burst = *bursts_p + bid * 116 + 464;
+ if (bi->burst_len > 0) {
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+ } else
+ memset(burst, 0, 116);
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received incomplete frame (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ }
+ *mask = 0x0;
+
+ /* decode
+ * also shift buffer by 4 bursts for interleaving */
+ switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
+ : tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ rc = gsm0503_tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total);
+ if (rc >= 0)
+ lchan_set_marker(osmo_fr_check_sid(tch_data, rc), lchan); /* DTXu */
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ rc = gsm0503_tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* the first FN 0,8,17 defines that CMI is included in frame,
+ * the first FN 4,13,21 defines that CMR is included in frame.
+ * NOTE: A frame ends 7 FN after start.
+ */
+
+ /* The AFS_ONSET frame itself does not result into an RTP frame
+ * since it only contains a recognition pattern that marks the
+ * end of the DTX interval. To mark the end of the DTX interval
+ * in the RTP stream as well, the voice frame after the
+ * AFS_ONSET frame is used. */
+ if (chan_state->amr_last_dtx == AFS_ONSET)
+ lchan_set_marker(false, lchan);
+
+ /* we store tch_data + 2 header bytes, the amr variable set to
+ * 2 will allow us to skip the first 2 bytes in case we did
+ * receive an FACCH frame instead of a voice frame (we do not
+ * know this before we actually decode the frame) */
+ amr = 2;
+ rc = gsm0503_tch_afs_decode_dtx(tch_data + amr, *bursts_p,
+ (((bi->fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
+ chan_state->codecs, &chan_state->ul_ft,
+ &chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
+
+ /* Tag all frames that are not regular AMR voice frames as
+ * SUB-Frames */
+ if (chan_state->amr_last_dtx != AMR_OTHER) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received AMR SID frame: %s\n",
+ gsm0503_amr_dtx_frame_name(chan_state->amr_last_dtx));
+ is_sub = 1;
+ }
+
+ /* The occurrence of the following frames indicates that we
+ * are either at the beginning or in the middle of a talk
+ * spurt. We update the SID status accordingly, but we do
+ * not want the marker to be set, since this must only
+ * happen when the talk spurt is over (see above) */
+ switch (chan_state->amr_last_dtx) {
+ case AFS_SID_FIRST:
+ case AFS_SID_UPDATE:
+ case AFS_SID_UPDATE_CN:
+ lchan_set_marker(true, lchan);
+ lchan->rtp_tx_marker = false;
+ break;
+ }
+
+ if (rc)
+ trx_loop_amr_input(l1t,
+ trx_chan_desc[chan].chan_nr | bi->tn, chan_state,
+ n_errors, n_bits_total);
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (chan_state->amr_last_dtx == AMR_OTHER) {
+ ft = chan_state->codec[chan_state->ul_cmr];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(tch_data,
+ chan_state->codec[chan_state->ul_cmr],
+ ft, AMR_GOOD);
+ }
+
+ break;
+ default:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "TCH mode %u invalid, please fix!\n",
+ tch_mode);
+ return -EINVAL;
+ }
+ memcpy(*bursts_p, *bursts_p + 464, 464);
+
+ /* Check if the frame is bad */
+ if (rc < 0) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received bad data (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ bfi_flag = true;
+ } else if (rc < 4) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received bad data (%u/%u) with invalid codec mode %d\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period, rc);
+ bfi_flag = true;
+ }
+
+ if (rc != GSM_MACBLOCK_LEN && lchan->ecu_state)
+ osmo_ecu_frame_in(lchan->ecu_state, bfi_flag, tch_data, rc);
+
+ ber10k = compute_ber10k(n_bits_total, n_errors);
+ if (bfi_flag)
+ goto bfi;
+
+ /* FACCH */
+ if (rc == GSM_MACBLOCK_LEN) {
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_F);
+ _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan,
+ tch_data + amr, GSM_MACBLOCK_LEN,
+ /* FIXME: AVG RSSI and ToA256 */
+ bi->rssi, bi->toa256,
+ 0 /* FIXME: AVG C/I */,
+ ber10k, PRES_INFO_UNKNOWN);
+bfi:
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
+ /* indicate bad frame */
+ if (lchan->tch.dtx.ul_sid) {
+ /* DTXu: pause in progress. Push empty payload to upper layers */
+ rc = 0;
+ goto compose_l1sap;
+ }
+
+ /* If there is an ECU active on this channel, use its output */
+ if (lchan->ecu_state) {
+ rc = osmo_ecu_frame_out(lchan->ecu_state, tch_data);
+ if (rc >= 0) /* Otherwise we send a BFI */
+ goto compose_l1sap;
+ }
+
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ memset(tch_data, 0, GSM_FR_BYTES);
+ tch_data[0] = 0xd0;
+ rc = GSM_FR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ memset(tch_data, 0, GSM_EFR_BYTES);
+ tch_data[0] = 0xc0;
+ rc = GSM_EFR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ rc = osmo_amr_rtp_enc(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft],
+ AMR_BAD);
+ if (rc < 2) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "Failed to encode AMR_BAD frame (rc=%d), "
+ "not sending BFI\n", rc);
+ return -EINVAL;
+ }
+ memset(tch_data + 2, 0, rc - 2);
+ break;
+ default:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "TCH mode %u invalid, please fix!\n", tch_mode);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
+ return 0;
+
+ /* TCH or BFI */
+compose_l1sap:
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_F);
+ return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan,
+ tch_data, rc, bi->toa256, ber10k, bi->rssi, is_sub);
+}
+
+/* common section for generation of TCH bursts (TCH/H and TCH/F).
+ * FIXME: this function is over-complicated, refactor / get rid of it. */
+void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid,
+ struct msgb **_msg_tch, struct msgb **_msg_facch)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ struct osmo_phsap_prim *l1sap;
+ int32_t *toa256_sum = &chan_state->toa256_sum;
+ uint8_t *toa_num = &chan_state->toa_num;
+ int16_t toa256;
+
+ /* handle loss detection of received TCH frames */
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH
+ && ++(chan_state->lost_frames) > 5) {
+ uint8_t tch_data[GSM_FR_BYTES];
+ int len;
+
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn,
+ "Missing TCH bursts detected, sending BFI\n");
+
+ /* indicate bad frame */
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ if (chan != TRXC_TCHF) { /* HR */
+ tch_data[0] = 0x70; /* F = 0, FT = 111 */
+ memset(tch_data + 1, 0, 14);
+ len = 15;
+ break;
+ }
+ memset(tch_data, 0, GSM_FR_BYTES);
+ len = GSM_FR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ if (chan != TRXC_TCHF)
+ goto inval_mode1;
+ memset(tch_data, 0, GSM_EFR_BYTES);
+ len = GSM_EFR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ len = osmo_amr_rtp_enc(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft], AMR_BAD);
+ if (len < 2) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn,
+ "Failed to encode AMR_BAD frame (rc=%d), "
+ "not sending BFI\n", len);
+ return;
+ }
+ memset(tch_data + 2, 0, len - 2);
+ break;
+ default:
+inval_mode1:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "TCH mode invalid, please fix!\n");
+ len = 0;
+ }
+
+ if (len) {
+ if (*toa_num == 0)
+ toa256 = 0;
+ else
+ toa256 = *toa256_sum / *toa_num;
+
+ /* Note: RSSI is set to 0 to indicate to the higher
+ * layers that this is a faked tch_ind */
+ _sched_compose_tch_ind(l1t, tn, fn, chan, tch_data, len, toa256, 10000, 0, 0);
+ }
+ }
+
+ /* get frame and unlink from queue */
+ msg1 = _sched_dequeue_prim(l1t, tn, fn, chan);
+ msg2 = _sched_dequeue_prim(l1t, tn, fn, chan);
+ if (msg1) {
+ l1sap = msgb_l1sap_prim(msg1);
+ if (l1sap->oph.primitive == PRIM_TCH) {
+ msg_tch = msg1;
+ if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive == PRIM_TCH) {
+ LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
+ "TCH twice, please FIX!\n");
+ msgb_free(msg2);
+ } else
+ msg_facch = msg2;
+ }
+ } else {
+ msg_facch = msg1;
+ if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive != PRIM_TCH) {
+ LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
+ "FACCH twice, please FIX!\n");
+ msgb_free(msg2);
+ } else
+ msg_tch = msg2;
+ }
+ }
+ } else if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive == PRIM_TCH)
+ msg_tch = msg2;
+ else
+ msg_facch = msg2;
+ }
+
+ /* check validity of message */
+ if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) {
+ LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim not 23 bytes, please FIX! "
+ "(len=%d)\n", msgb_l2len(msg_facch));
+ /* free message */
+ msgb_free(msg_facch);
+ msg_facch = NULL;
+ }
+
+ /* check validity of message, get AMR ft and cmr */
+ if (!msg_facch && msg_tch) {
+ int len;
+ uint8_t cmr_codec;
+ int cmr, ft, i;
+ enum osmo_amr_type ft_codec;
+ enum osmo_amr_quality bfi;
+ int8_t sti, cmi;
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Dropping speech frame, "
+ "because we are not in speech mode\n");
+ goto free_bad_msg;
+ }
+
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ if (chan != TRXC_TCHF) /* HR */
+ len = 15;
+ else
+ len = GSM_FR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ if (chan != TRXC_TCHF)
+ goto inval_mode2;
+ len = GSM_EFR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ len = osmo_amr_rtp_dec(msg_tch->l2h, msgb_l2len(msg_tch),
+ &cmr_codec, &cmi, &ft_codec,
+ &bfi, &sti);
+ cmr = -1;
+ ft = -1;
+ for (i = 0; i < chan_state->codecs; i++) {
+ if (chan_state->codec[i] == cmr_codec)
+ cmr = i;
+ if (chan_state->codec[i] == ft_codec)
+ ft = i;
+ }
+ if (cmr >= 0) { /* new request */
+ chan_state->dl_cmr = cmr;
+ /* disable AMR loop */
+ trx_loop_amr_set(chan_state, 0);
+ } else {
+ /* enable AMR loop */
+ trx_loop_amr_set(chan_state, 1);
+ }
+ if (ft < 0) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn,
+ "Codec (FT = %d) of RTP frame not in list\n", ft_codec);
+ goto free_bad_msg;
+ }
+ if (fn_is_codec_mode_request(fn) && chan_state->dl_ft != ft) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Codec (FT = %d) "
+ " of RTP cannot be changed now, but in next frame\n", ft_codec);
+ goto free_bad_msg;
+ }
+ chan_state->dl_ft = ft;
+ if (bfi == AMR_BAD) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn,
+ "Transmitting 'bad AMR frame'\n");
+ goto free_bad_msg;
+ }
+ break;
+ default:
+inval_mode2:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "TCH mode invalid, please fix!\n");
+ goto free_bad_msg;
+ }
+ if (len < 0) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot send invalid AMR payload\n");
+ goto free_bad_msg;
+ }
+ if (msgb_l2len(msg_tch) != len) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot send payload with "
+ "invalid length! (expecting %d, received %d)\n",
+ len, msgb_l2len(msg_tch));
+free_bad_msg:
+ /* free message */
+ msgb_free(msg_tch);
+ msg_tch = NULL;
+ goto send_frame;
+ }
+ }
+
+send_frame:
+ *_msg_tch = msg_tch;
+ *_msg_facch = msg_facch;
+}
+
+/* obtain a to-be-transmitted TCH/F (Full Traffic Channel) burst */
+ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ struct msgb *msg_tch = NULL, *msg_facch = NULL;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ uint8_t tch_mode = chan_state->tch_mode;
+ ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
+ static ubit_t bits[GSM_BURST_LEN];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch);
+
+ /* BURST BYPASS */
+
+ /* allocate burst memory, if not already,
+ * otherwise shift buffer by 4 bursts for interleaving */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ if (!*bursts_p)
+ return NULL;
+ } else {
+ memcpy(*bursts_p, *bursts_p + 464, 464);
+ memset(*bursts_p + 464, 0, 464);
+ }
+
+ /* no message at all */
+ if (!msg_tch && !msg_facch) {
+ LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No TCH or FACCH prim for transmit.\n");
+ goto send_burst;
+ }
+
+ /* encode bursts (prioritize FACCH) */
+ if (msg_facch)
+ gsm0503_tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
+ 1);
+ else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ /* the first FN 4,13,21 defines that CMI is included in frame,
+ * the first FN 0,8,17 defines that CMR is included in frame.
+ */
+ gsm0503_tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
+ msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(fn),
+ chan_state->codec, chan_state->codecs,
+ chan_state->dl_ft,
+ chan_state->dl_cmr);
+ else
+ gsm0503_tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1);
+
+ /* free message */
+ if (msg_tch)
+ msgb_free(msg_tch);
+ if (msg_facch)
+ msgb_free(msg_facch);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
+
+ return bits;
+}
diff --git a/src/osmo-bts-trx/sched_lchan_tchh.c b/src/osmo-bts-trx/sched_lchan_tchh.c
new file mode 100644
index 0000000..1895038
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_tchh.c
@@ -0,0 +1,408 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/codec/codec.h>
+#include <osmocom/codec/ecu.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+#include <osmo-bts/msg_utils.h>
+
+#include <sched_utils.h>
+#include <loops.h>
+
+/*! \brief a single TCH/H burst was received by the PHY, process it */
+int rx_tchh_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
+ uint8_t bid, const struct trx_ul_burst_ind *bi)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint8_t *mask = &chan_state->ul_mask;
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ uint8_t tch_data[128]; /* just to be safe */
+ int rc, amr = 0;
+ int n_errors = 0;
+ int n_bits_total = 0;
+ bool bfi_flag = false;
+ struct gsm_lchan *lchan =
+ get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | bi->tn);
+ /* Note on FN-10: If we are at FN 10, we decoded an even aligned
+ * TCH/FACCH frame, because our burst buffer carries 6 bursts.
+ * Even FN ending at: 10,11,19,20,2,3
+ */
+ int fn_is_odd = (((bi->fn + 26 - 10) % 26) >> 2) & 1;
+ unsigned int fn_begin;
+ uint16_t ber10k;
+ uint8_t is_sub = 0;
+ uint8_t ft;
+
+ /* If handover RACH detection is turned on, treat this burst as an Access Burst.
+ * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
+ if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
+ return rx_rach_fn(l1t, chan, bid, bi);
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received TCH/H, bid=%u\n", bid);
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p + 464, 0, 232);
+ *mask = 0x0;
+ }
+
+ /* update mask */
+ *mask |= (1 << bid);
+
+ /* copy burst to end of buffer of 6 bursts */
+ burst = *bursts_p + bid * 116 + 464;
+ if (bi->burst_len > 0) {
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+ } else
+ memset(burst, 0, 116);
+
+ /* wait until complete set of bursts */
+ if (bid != 1)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0x3) != 0x3) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received incomplete frame (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ }
+ *mask = 0x0;
+
+ /* skip second of two TCH frames of FACCH was received */
+ if (chan_state->ul_ongoing_facch) {
+ chan_state->ul_ongoing_facch = 0;
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+ ber10k = 0;
+ goto bfi;
+ }
+
+ /* decode
+ * also shift buffer by 4 bursts for interleaving */
+ switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
+ : tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* HR or signalling */
+ /* Note on FN-10: If we are at FN 10, we decoded an even aligned
+ * TCH/FACCH frame, because our burst buffer carries 6 bursts.
+ * Even FN ending at: 10,11,19,20,2,3
+ */
+ rc = gsm0503_tch_hr_decode(tch_data, *bursts_p,
+ fn_is_odd, &n_errors, &n_bits_total);
+ if (rc >= 0) /* DTXu */
+ lchan_set_marker(osmo_hr_check_sid(tch_data, rc), lchan);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
+ * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR
+ * is included in frame.
+ */
+
+ /* See comment in function rx_tchf_fn() */
+ switch (chan_state->amr_last_dtx) {
+ case AHS_ONSET:
+ case AHS_SID_FIRST_INH:
+ case AHS_SID_UPDATE_INH:
+ lchan_set_marker(false, lchan);
+ break;
+ }
+
+ /* See comment in function rx_tchf_fn() */
+ amr = 2;
+ rc = gsm0503_tch_ahs_decode_dtx(tch_data + amr, *bursts_p,
+ fn_is_odd, fn_is_odd, chan_state->codec,
+ chan_state->codecs, &chan_state->ul_ft,
+ &chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
+
+ /* Tag all frames that are not regular AMR voice frames
+ as SUB-Frames */
+ if (chan_state->amr_last_dtx != AMR_OTHER) {
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received AMR SID frame: %s\n",
+ gsm0503_amr_dtx_frame_name(chan_state->amr_last_dtx));
+ is_sub = 1;
+ }
+
+ /* See comment in function rx_tchf_fn() */
+ switch (chan_state->amr_last_dtx) {
+ case AHS_SID_FIRST_P1:
+ case AHS_SID_FIRST_P2:
+ case AHS_SID_UPDATE:
+ case AHS_SID_UPDATE_CN:
+ lchan_set_marker(true, lchan);
+ lchan->rtp_tx_marker = false;
+ break;
+ }
+
+ if (rc)
+ trx_loop_amr_input(l1t,
+ trx_chan_desc[chan].chan_nr | bi->tn, chan_state,
+ n_errors, n_bits_total);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (chan_state->amr_last_dtx == AMR_OTHER) {
+ ft = chan_state->codec[chan_state->ul_cmr];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(tch_data,
+ chan_state->codec[chan_state->ul_cmr],
+ ft, AMR_GOOD);
+ }
+
+ break;
+ default:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "TCH mode %u invalid, please fix!\n",
+ tch_mode);
+ return -EINVAL;
+ }
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+ ber10k = compute_ber10k(n_bits_total, n_errors);
+
+
+ /* Check if the frame is bad */
+ if (rc < 0) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received bad data (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ bfi_flag = true;
+ } else if (rc < 4) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received bad data (%u/%u) with invalid codec mode %d\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period, rc);
+ bfi_flag = true;
+ }
+
+ if (rc != GSM_MACBLOCK_LEN && lchan->ecu_state)
+ osmo_ecu_frame_in(lchan->ecu_state, bfi_flag, tch_data, rc);
+
+ if (bfi_flag)
+ goto bfi;
+
+ /* FACCH */
+ if (rc == GSM_MACBLOCK_LEN) {
+ chan_state->ul_ongoing_facch = 1;
+ uint16_t ber10k = compute_ber10k(n_bits_total, n_errors);
+ if (lchan->nr == 0)
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H0);
+ else
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H1);
+ _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan,
+ tch_data + amr, GSM_MACBLOCK_LEN,
+ /* FIXME: AVG both RSSI and ToA */
+ bi->rssi, bi->toa256,
+ 0 /* FIXME: AVG C/I */,
+ ber10k, PRES_INFO_UNKNOWN);
+bfi:
+ /* FIXME: a FACCH/H frame replaces two speech frames,
+ * so we actually need to send two bad frame indications! */
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
+ /* indicate bad frame */
+ if (lchan->tch.dtx.ul_sid) {
+ /* DTXu: pause in progress. Push empty payload to upper layers */
+ rc = 0;
+ goto compose_l1sap;
+ }
+
+ /* If there is an ECU active on this channel, use its output */
+ if (lchan->ecu_state) {
+ rc = osmo_ecu_frame_out(lchan->ecu_state, tch_data);
+ if (rc >= 0) /* Otherwise we send a BFI */
+ goto compose_l1sap;
+ }
+
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* HR */
+ tch_data[0] = 0x70; /* F = 0, FT = 111 */
+ memset(tch_data + 1, 0, 14);
+ rc = 15;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ rc = osmo_amr_rtp_enc(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft],
+ AMR_BAD);
+ if (rc < 2) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "Failed to encode AMR_BAD frame (rc=%d), "
+ "not sending BFI\n", rc);
+ return -EINVAL;
+ }
+ memset(tch_data + 2, 0, rc - 2);
+ break;
+ default:
+ LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
+ "TCH mode %u invalid, please fix!\n", tch_mode);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
+ return 0;
+
+compose_l1sap:
+ /* TCH or BFI */
+ /* Note on FN 19 or 20: If we received the last burst of a frame,
+ * it actually starts at FN 8 or 9. A burst starting there, overlaps
+ * with the slot 12, so an extra FN must be subtracted to get correct
+ * start of frame.
+ */
+ if (lchan->nr == 0)
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H0);
+ else
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H1);
+ return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan,
+ tch_data, rc, bi->toa256, ber10k, bi->rssi, is_sub);
+}
+
+/* common section for generation of TCH bursts (TCH/H and TCH/F).
+ * FIXME: this function is over-complicated, refactor / get rid of it. */
+extern void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid,
+ struct msgb **_msg_tch, struct msgb **_msg_facch);
+
+/* obtain a to-be-transmitted TCH/H (Half Traffic Channel) burst */
+ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ struct msgb *msg_tch = NULL, *msg_facch = NULL;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ uint8_t tch_mode = chan_state->tch_mode;
+ ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
+ static ubit_t bits[GSM_BURST_LEN];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get TCH and/or FACCH */
+ tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch);
+
+ /* check for FACCH alignment */
+ if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) {
+ LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot transmit FACCH starting on "
+ "even frames, please fix RTS!\n");
+ msgb_free(msg_facch);
+ msg_facch = NULL;
+ }
+
+ /* BURST BYPASS */
+
+ /* allocate burst memory, if not already,
+ * otherwise shift buffer by 2 bursts for interleaving */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ if (!*bursts_p)
+ return NULL;
+ } else {
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ if (chan_state->dl_ongoing_facch) {
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+ memset(*bursts_p + 464, 0, 232);
+ } else {
+ memset(*bursts_p + 232, 0, 232);
+ }
+ }
+
+ /* no message at all */
+ if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
+ LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No TCH or FACCH prim for transmit.\n");
+ goto send_burst;
+ }
+
+ /* encode bursts (prioritize FACCH) */
+ if (msg_facch) {
+ gsm0503_tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
+ chan_state->dl_ongoing_facch = 1; /* first of two TCH frames */
+ } else if (chan_state->dl_ongoing_facch) /* second of two TCH frames */
+ chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
+ else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ /* the first FN 4,13,21 or 5,14,22 defines that CMI is included
+ * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
+ * included in frame. */
+ gsm0503_tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
+ msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(fn),
+ chan_state->codec, chan_state->codecs,
+ chan_state->dl_ft,
+ chan_state->dl_cmr);
+ else
+ gsm0503_tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch));
+
+ /* free message */
+ if (msg_tch)
+ msgb_free(msg_tch);
+ if (msg_facch)
+ msgb_free(msg_facch);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
+
+ return bits;
+}
diff --git a/src/osmo-bts-trx/sched_lchan_xcch.c b/src/osmo-bts-trx/sched_lchan_xcch.c
new file mode 100644
index 0000000..a16553f
--- /dev/null
+++ b/src/osmo-bts-trx/sched_lchan_xcch.c
@@ -0,0 +1,234 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly at eversberg.eu>
+ * (C) 2015-2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmo-bts/bts.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+
+#include <sched_utils.h>
+
+/*! \brief a single (SDCCH/SACCH) burst was received by the PHY, process it */
+int rx_data_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
+ uint8_t bid, const struct trx_ul_burst_ind *bi)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint32_t *first_fn = &chan_state->ul_first_fn;
+ uint8_t *mask = &chan_state->ul_mask;
+ float *rssi_sum = &chan_state->rssi_sum;
+ uint8_t *rssi_num = &chan_state->rssi_num;
+ int32_t *toa256_sum = &chan_state->toa256_sum;
+ uint8_t *toa_num = &chan_state->toa_num;
+ int32_t *ci_cb_sum = &chan_state->ci_cb_sum;
+ uint8_t *ci_cb_num = &chan_state->ci_cb_num;
+ uint8_t l2[GSM_MACBLOCK_LEN], l2_len;
+ int n_errors = 0;
+ int n_bits_total = 0;
+ int16_t lqual_cb;
+ uint16_t ber10k;
+ int rc;
+
+ /* If handover RACH detection is turned on, treat this burst as an Access Burst.
+ * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
+ if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
+ return rx_rach_fn(l1t, chan, bid, bi);
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
+ "Received Data, bid=%u\n", bid);
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst & store frame number of first burst */
+ if (bid == 0) {
+ memset(*bursts_p, 0, 464);
+ *mask = 0x0;
+ *first_fn = bi->fn;
+ *rssi_sum = 0;
+ *rssi_num = 0;
+ *toa256_sum = 0;
+ *toa_num = 0;
+ *ci_cb_sum = 0;
+ *ci_cb_num = 0;
+ }
+
+ /* update mask + RSSI */
+ *mask |= (1 << bid);
+ *rssi_sum += bi->rssi;
+ (*rssi_num)++;
+ *toa256_sum += bi->toa256;
+ (*toa_num)++;
+
+ /* C/I: Carrier-to-Interference ratio (in centiBels) */
+ if (bi->flags & TRX_BI_F_CI_CB) {
+ *ci_cb_sum += bi->ci_cb;
+ (*ci_cb_num)++;
+ }
+
+ /* Copy burst to buffer of 4 bursts. If the burst indication contains
+ * no data, ensure that the buffer does not stay uninitialized */
+ burst = *bursts_p + bid * 116;
+ if (bi->burst_len > 0) {
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+ } else
+ memset(burst, 0, 58 * 2);
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received incomplete data (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+
+ /* we require first burst to have correct FN */
+ if (!(*mask & 0x1)) {
+ *mask = 0x0;
+ return 0;
+ }
+ }
+ *mask = 0x0;
+
+ /* decode */
+ rc = gsm0503_xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
+ "Received bad data (%u/%u)\n",
+ bi->fn % l1ts->mf_period, l1ts->mf_period);
+ l2_len = 0;
+ } else
+ l2_len = GSM_MACBLOCK_LEN;
+
+ lqual_cb = *ci_cb_num ? (*ci_cb_sum / *ci_cb_num) : 0;
+ ber10k = compute_ber10k(n_bits_total, n_errors);
+ return _sched_compose_ph_data_ind(l1t, bi->tn, *first_fn,
+ chan, l2, l2_len,
+ *rssi_sum / *rssi_num,
+ *toa256_sum / *toa_num,
+ lqual_cb, ber10k,
+ PRES_INFO_UNKNOWN);
+}
+
+/* obtain a to-be-transmitted xCCH (e.g SACCH or SDCCH) burst */
+ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
+{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+ struct msgb *msg = NULL; /* make GCC happy */
+ ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
+ static ubit_t bits[GSM_BURST_LEN];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get mac block from queue */
+ msg = _sched_dequeue_prim(l1t, tn, fn, chan);
+ if (msg)
+ goto got_msg;
+
+ LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No prim for transmit.\n");
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return NULL;
+
+got_msg:
+ /* check validity of message */
+ if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
+ LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim not 23 bytes, please FIX! "
+ "(len=%d)\n", msgb_l2len(msg));
+ /* free message */
+ msgb_free(msg);
+ goto no_msg;
+ }
+
+ /* BURST BYPASS */
+
+ /* handle loss detection of SACCH */
+ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
+ /* count and send BFI */
+ if (++(l1ts->chan_state[chan].lost_frames) > 1) {
+ /* TODO: Should we pass old TOA here? Otherwise we risk
+ * unnecessary decreasing TA */
+
+ /* Note: RSSI is set to 0 to indicate to the higher
+ * layers that this is a faked ph_data_ind */
+ _sched_compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0,
+ 0, 0, 0, 10000,
+ PRES_INFO_INVALID);
+ }
+ }
+
+ /* allocate burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return NULL;
+ }
+
+ /* encode bursts */
+ gsm0503_xcch_encode(*bursts_p, msg->l2h);
+
+ /* free message */
+ msgb_free(msg);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ if (nbits)
+ *nbits = GSM_BURST_LEN;
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
+
+ return bits;
+}
diff --git a/src/osmo-bts-trx/sched_utils.h b/src/osmo-bts-trx/sched_utils.h
new file mode 100644
index 0000000..4a1aaf5
--- /dev/null
+++ b/src/osmo-bts-trx/sched_utils.h
@@ -0,0 +1,42 @@
+/* Auxiliary scheduler utilities.
+ *
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <errno.h>
+
+extern void *tall_bts_ctx;
+
+/* Compute the bit error rate in 1/10000 units */
+static inline uint16_t compute_ber10k(int n_bits_total, int n_errors)
+{
+ if (n_bits_total == 0)
+ return 10000;
+ else
+ return 10000 * n_errors / n_bits_total;
+}
+
+/* determine if the FN is transmitting a CMR (1) or not (0) */
+static inline int fn_is_codec_mode_request(uint32_t fn)
+{
+ return (((fn + 4) % 26) >> 2) & 1;
+}
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
index 97b590d..4fb0e23 100644
--- a/src/osmo-bts-trx/scheduler_trx.c
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -32,12 +32,8 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer_compat.h>
-#include <osmocom/codec/codec.h>
-#include <osmocom/codec/ecu.h>
#include <osmocom/core/bits.h>
#include <osmocom/gsm/a5.h>
-#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/coding/gsm0503_amr_dtx.h>
#include <osmo-bts/gsm_data.h>
@@ -45,33 +41,11 @@
#include <osmo-bts/rsl.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/l1sap.h>
-#include <osmo-bts/msg_utils.h>
#include <osmo-bts/scheduler.h>
#include <osmo-bts/scheduler_backend.h>
-#include <osmocom/gsm/gsm0502.h>
#include "l1_if.h"
#include "trx_if.h"
-#include "loops.h"
-
-extern void *tall_bts_ctx;
-
-/* Maximum size of a EGPRS message in bytes */
-#define EGPRS_0503_MAX_BYTES 155
-
-
-/* Compute the bit error rate in 1/10000 units */
-static inline uint16_t compute_ber10k(int n_bits_total, int n_errors)
-{
- if (n_bits_total == 0)
- return 10000;
- else
- return 10000 * n_errors / n_bits_total;
-}
-
-/*
- * TX on downlink
- */
/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
@@ -85,1542 +59,6 @@
return NULL;
}
-/* obtain a to-be-transmitted FCCH (frequency correction channel) burst */
-ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting FCCH\n");
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
-
- /* BURST BYPASS */
-
- return (ubit_t *) _sched_fcch_burst;
-}
-
-/* obtain a to-be-transmitted SCH (synchronization channel) burst */
-ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- static ubit_t bits[GSM_BURST_LEN], burst[78];
- uint8_t sb_info[4];
- struct gsm_time t;
- uint8_t t3p, bsic;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting SCH\n");
-
- /* BURST BYPASS */
-
- /* create SB info from GSM time and BSIC */
- gsm_fn2gsmtime(&t, fn);
- t3p = t.t3 / 10;
- bsic = l1t->trx->bts->bsic;
- sb_info[0] =
- ((bsic & 0x3f) << 2) |
- ((t.t1 & 0x600) >> 9);
- sb_info[1] =
- ((t.t1 & 0x1fe) >> 1);
- sb_info[2] =
- ((t.t1 & 0x001) << 7) |
- ((t.t2 & 0x1f) << 2) |
- ((t3p & 0x6) >> 1);
- sb_info[3] =
- (t3p & 0x1);
-
- /* encode bursts */
- gsm0503_sch_encode(burst, sb_info);
-
- /* compose burst */
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 39);
- memcpy(bits + 42, _sched_sch_train, 64);
- memcpy(bits + 106, burst + 39, 39);
- memset(bits + 145, 0, 3);
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
-
- return bits;
-}
-
-/* obtain a to-be-transmitted data (SACCH/SDCCH) burst */
-ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
- struct msgb *msg = NULL; /* make GCC happy */
- ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
- static ubit_t bits[GSM_BURST_LEN];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- /* get mac block from queue */
- msg = _sched_dequeue_prim(l1t, tn, fn, chan);
- if (msg)
- goto got_msg;
-
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No prim for transmit.\n");
-
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
- }
- return NULL;
-
-got_msg:
- /* check validity of message */
- if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg));
- /* free message */
- msgb_free(msg);
- goto no_msg;
- }
-
- /* BURST BYPASS */
-
- /* handle loss detection of SACCH */
- if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
- /* count and send BFI */
- if (++(l1ts->chan_state[chan].lost_frames) > 1) {
- /* TODO: Should we pass old TOA here? Otherwise we risk
- * unnecessary decreasing TA */
-
- /* Note: RSSI is set to 0 to indicate to the higher
- * layers that this is a faked ph_data_ind */
- _sched_compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0,
- 0, 0, 0, 10000,
- PRES_INFO_INVALID);
- }
- }
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
- return NULL;
- }
-
- /* encode bursts */
- gsm0503_xcch_encode(*bursts_p, msg->l2h);
-
- /* free message */
- msgb_free(msg);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
-
- return bits;
-}
-
-/* obtain a to-be-transmitted PDTCH (packet data) burst */
-ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
- struct msgb *msg = NULL; /* make GCC happy */
- ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
- enum trx_burst_type *burst_type = &l1ts->chan_state[chan].dl_burst_type;
- static ubit_t bits[EGPRS_BURST_LEN];
- int rc = 0;
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- /* get mac block from queue */
- msg = _sched_dequeue_prim(l1t, tn, fn, chan);
- if (msg)
- goto got_msg;
-
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No prim for transmit.\n");
-
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
- }
- return NULL;
-
-got_msg:
- /* BURST BYPASS */
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx,
- GSM0503_EGPRS_BURSTS_NBITS);
- if (!*bursts_p)
- return NULL;
- }
-
- /* encode bursts */
- rc = gsm0503_pdtch_egprs_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
- if (rc < 0)
- rc = gsm0503_pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
-
- /* check validity of message */
- if (rc < 0) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim invalid length, please FIX! "
- "(len=%ld)\n", (long)(msg->tail - msg->l2h));
- /* free message */
- msgb_free(msg);
- goto no_msg;
- } else if (rc == GSM0503_EGPRS_BURSTS_NBITS) {
- *burst_type = TRX_BURST_8PSK;
- } else {
- *burst_type = TRX_BURST_GMSK;
- }
-
- /* free message */
- msgb_free(msg);
-
-send_burst:
- /* compose burst */
- if (*burst_type == TRX_BURST_8PSK) {
- burst = *bursts_p + bid * 348;
- memset(bits, 1, 9);
- memcpy(bits + 9, burst, 174);
- memcpy(bits + 183, _sched_egprs_tsc[gsm_ts_tsc(ts)], 78);
- memcpy(bits + 261, burst + 174, 174);
- memset(bits + 435, 1, 9);
-
- if (nbits)
- *nbits = EGPRS_BURST_LEN;
- } else {
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
- }
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
-
- return bits;
-}
-
-/* determine if the FN is transmitting a CMR (1) or not (0) */
-static inline int fn_is_codec_mode_request(uint32_t fn)
-{
- return (((fn + 4) % 26) >> 2) & 1;
-}
-
-/* common section for generation of TCH bursts (TCH/H and TCH/F) */
-static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
- struct msgb **_msg_facch)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- struct osmo_phsap_prim *l1sap;
- int32_t *toa256_sum = &chan_state->toa256_sum;
- uint8_t *toa_num = &chan_state->toa_num;
- int16_t toa256;
-
- /* handle loss detection of received TCH frames */
- if (rsl_cmode == RSL_CMOD_SPD_SPEECH
- && ++(chan_state->lost_frames) > 5) {
- uint8_t tch_data[GSM_FR_BYTES];
- int len;
-
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn,
- "Missing TCH bursts detected, sending BFI\n");
-
- /* indicate bad frame */
- switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR / HR */
- if (chan != TRXC_TCHF) { /* HR */
- tch_data[0] = 0x70; /* F = 0, FT = 111 */
- memset(tch_data + 1, 0, 14);
- len = 15;
- break;
- }
- memset(tch_data, 0, GSM_FR_BYTES);
- len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- if (chan != TRXC_TCHF)
- goto inval_mode1;
- memset(tch_data, 0, GSM_EFR_BYTES);
- len = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- len = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft], AMR_BAD);
- if (len < 2) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn,
- "Failed to encode AMR_BAD frame (rc=%d), "
- "not sending BFI\n", len);
- return;
- }
- memset(tch_data + 2, 0, len - 2);
- break;
- default:
-inval_mode1:
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "TCH mode invalid, please fix!\n");
- len = 0;
- }
-
- if (len) {
- if (*toa_num == 0)
- toa256 = 0;
- else
- toa256 = *toa256_sum / *toa_num;
-
- /* Note: RSSI is set to 0 to indicate to the higher
- * layers that this is a faked tch_ind */
- _sched_compose_tch_ind(l1t, tn, fn, chan, tch_data, len, toa256, 10000, 0, 0);
- }
- }
-
- /* get frame and unlink from queue */
- msg1 = _sched_dequeue_prim(l1t, tn, fn, chan);
- msg2 = _sched_dequeue_prim(l1t, tn, fn, chan);
- if (msg1) {
- l1sap = msgb_l1sap_prim(msg1);
- if (l1sap->oph.primitive == PRIM_TCH) {
- msg_tch = msg1;
- if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
- "TCH twice, please FIX!\n");
- msgb_free(msg2);
- } else
- msg_facch = msg2;
- }
- } else {
- msg_facch = msg1;
- if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive != PRIM_TCH) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
- "FACCH twice, please FIX!\n");
- msgb_free(msg2);
- } else
- msg_tch = msg2;
- }
- }
- } else if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH)
- msg_tch = msg2;
- else
- msg_facch = msg2;
- }
-
- /* check validity of message */
- if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn, "Prim not 23 bytes, please FIX! "
- "(len=%d)\n", msgb_l2len(msg_facch));
- /* free message */
- msgb_free(msg_facch);
- msg_facch = NULL;
- }
-
- /* check validity of message, get AMR ft and cmr */
- if (!msg_facch && msg_tch) {
- int len;
- uint8_t cmr_codec;
- int cmr, ft, i;
- enum osmo_amr_type ft_codec;
- enum osmo_amr_quality bfi;
- int8_t sti, cmi;
-
- if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Dropping speech frame, "
- "because we are not in speech mode\n");
- goto free_bad_msg;
- }
-
- switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR / HR */
- if (chan != TRXC_TCHF) /* HR */
- len = 15;
- else
- len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- if (chan != TRXC_TCHF)
- goto inval_mode2;
- len = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- len = osmo_amr_rtp_dec(msg_tch->l2h, msgb_l2len(msg_tch),
- &cmr_codec, &cmi, &ft_codec,
- &bfi, &sti);
- cmr = -1;
- ft = -1;
- for (i = 0; i < chan_state->codecs; i++) {
- if (chan_state->codec[i] == cmr_codec)
- cmr = i;
- if (chan_state->codec[i] == ft_codec)
- ft = i;
- }
- if (cmr >= 0) { /* new request */
- chan_state->dl_cmr = cmr;
- /* disable AMR loop */
- trx_loop_amr_set(chan_state, 0);
- } else {
- /* enable AMR loop */
- trx_loop_amr_set(chan_state, 1);
- }
- if (ft < 0) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn,
- "Codec (FT = %d) of RTP frame not in list\n", ft_codec);
- goto free_bad_msg;
- }
- if (fn_is_codec_mode_request(fn) && chan_state->dl_ft != ft) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn, "Codec (FT = %d) "
- " of RTP cannot be changed now, but in next frame\n", ft_codec);
- goto free_bad_msg;
- }
- chan_state->dl_ft = ft;
- if (bfi == AMR_BAD) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn,
- "Transmitting 'bad AMR frame'\n");
- goto free_bad_msg;
- }
- break;
- default:
-inval_mode2:
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "TCH mode invalid, please fix!\n");
- goto free_bad_msg;
- }
- if (len < 0) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot send invalid AMR payload\n");
- goto free_bad_msg;
- }
- if (msgb_l2len(msg_tch) != len) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot send payload with "
- "invalid length! (expecting %d, received %d)\n",
- len, msgb_l2len(msg_tch));
-free_bad_msg:
- /* free message */
- msgb_free(msg_tch);
- msg_tch = NULL;
- goto send_frame;
- }
- }
-
-send_frame:
- *_msg_tch = msg_tch;
- *_msg_facch = msg_facch;
-}
-
-/* obtain a to-be-transmitted TCH/F (Full Traffic Channel) burst */
-ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- struct msgb *msg_tch = NULL, *msg_facch = NULL;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- uint8_t tch_mode = chan_state->tch_mode;
- ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
- static ubit_t bits[GSM_BURST_LEN];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch);
-
- /* BURST BYPASS */
-
- /* allocate burst memory, if not already,
- * otherwise shift buffer by 4 bursts for interleaving */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
- if (!*bursts_p)
- return NULL;
- } else {
- memcpy(*bursts_p, *bursts_p + 464, 464);
- memset(*bursts_p + 464, 0, 464);
- }
-
- /* no message at all */
- if (!msg_tch && !msg_facch) {
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No TCH or FACCH prim for transmit.\n");
- goto send_burst;
- }
-
- /* encode bursts (prioritize FACCH) */
- if (msg_facch)
- gsm0503_tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
- 1);
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
- /* the first FN 4,13,21 defines that CMI is included in frame,
- * the first FN 0,8,17 defines that CMR is included in frame.
- */
- gsm0503_tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(fn),
- chan_state->codec, chan_state->codecs,
- chan_state->dl_ft,
- chan_state->dl_cmr);
- else
- gsm0503_tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1);
-
- /* free message */
- if (msg_tch)
- msgb_free(msg_tch);
- if (msg_facch)
- msgb_free(msg_facch);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
-
- return bits;
-}
-
-/* obtain a to-be-transmitted TCH/H (Half Traffic Channel) burst */
-ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, uint16_t *nbits)
-{
- struct msgb *msg_tch = NULL, *msg_facch = NULL;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- uint8_t tch_mode = chan_state->tch_mode;
- ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
- static ubit_t bits[GSM_BURST_LEN];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- /* get TCH and/or FACCH */
- tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch);
-
- /* check for FACCH alignment */
- if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Cannot transmit FACCH starting on "
- "even frames, please fix RTS!\n");
- msgb_free(msg_facch);
- msg_facch = NULL;
- }
-
- /* BURST BYPASS */
-
- /* allocate burst memory, if not already,
- * otherwise shift buffer by 2 bursts for interleaving */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
- if (!*bursts_p)
- return NULL;
- } else {
- memcpy(*bursts_p, *bursts_p + 232, 232);
- if (chan_state->dl_ongoing_facch) {
- memcpy(*bursts_p + 232, *bursts_p + 464, 232);
- memset(*bursts_p + 464, 0, 232);
- } else {
- memset(*bursts_p + 232, 0, 232);
- }
- }
-
- /* no message at all */
- if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "No TCH or FACCH prim for transmit.\n");
- goto send_burst;
- }
-
- /* encode bursts (prioritize FACCH) */
- if (msg_facch) {
- gsm0503_tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
- chan_state->dl_ongoing_facch = 1; /* first of two TCH frames */
- } else if (chan_state->dl_ongoing_facch) /* second of two TCH frames */
- chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
- /* the first FN 4,13,21 or 5,14,22 defines that CMI is included
- * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
- * included in frame. */
- gsm0503_tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(fn),
- chan_state->codec, chan_state->codecs,
- chan_state->dl_ft,
- chan_state->dl_cmr);
- else
- gsm0503_tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch));
-
- /* free message */
- if (msg_tch)
- msgb_free(msg_tch);
- if (msg_facch)
- msgb_free(msg_facch);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- if (nbits)
- *nbits = GSM_BURST_LEN;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, chan, fn, "Transmitting burst=%u.\n", bid);
-
- return bits;
-}
-
-
-/*
- * RX on uplink (indication to upper layer)
- */
-
-/* 3GPP TS 05.02, section 5.2.7 */
-#define RACH_EXT_TAIL_LEN 8
-#define RACH_SYNCH_SEQ_LEN 41
-
-enum rach_synch_seq_t {
- RACH_SYNCH_SEQ_UNKNOWN = -1,
- RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */
- RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */
- RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */
- RACH_SYNCH_SEQ_NUM
-};
-
-static struct value_string rach_synch_seq_names[] = {
- { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" },
- { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" },
- { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" },
- { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" },
- { 0, NULL },
-};
-
-static enum rach_synch_seq_t rach_get_synch_seq(sbit_t *bits, int *best_score)
-{
- sbit_t *synch_seq_burst = bits + RACH_EXT_TAIL_LEN;
- enum rach_synch_seq_t seq = RACH_SYNCH_SEQ_TS0;
- int score[RACH_SYNCH_SEQ_NUM] = { 0 };
- int max_score = INT_MIN;
- int i, j;
-
- /* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
- static const char synch_seq_ref[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
- [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
- [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
- [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
- };
-
- /* Get a multiplier for j-th bit of i-th synch. sequence */
-#define RACH_SYNCH_SEQ_MULT \
- (synch_seq_ref[i][j] == '1' ? -1 : 1)
-
- /* For each synch. sequence, count the bit match score. Since we deal with
- * soft-bits (-127...127), we sum the absolute values of matching ones,
- * and subtract the absolute values of different ones, so the resulting
- * score is more accurate than it could be with hard-bits. */
- for (i = 0; i < RACH_SYNCH_SEQ_NUM; i++) {
- for (j = 0; j < RACH_SYNCH_SEQ_LEN; j++)
- score[i] += RACH_SYNCH_SEQ_MULT * synch_seq_burst[j];
-
- /* Keep the maximum value updated */
- if (score[i] > max_score) {
- max_score = score[i];
- seq = i;
- }
- }
-
- /* Calculate an approximate level of our confidence */
- if (best_score != NULL)
- *best_score = max_score;
-
- /* At least 1/3 of a synch. sequence shall match */
- if (max_score < (127 * RACH_SYNCH_SEQ_LEN / 3))
- return RACH_SYNCH_SEQ_UNKNOWN;
-
- return seq;
-}
-
-int rx_rach_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
- uint8_t bid, const struct trx_ul_burst_ind *bi)
-{
- struct osmo_phsap_prim l1sap;
- int n_errors = 0;
- int n_bits_total = 0;
- uint16_t ra11;
- uint8_t ra;
- int rc;
-
- /* TSC (Training Sequence Code) is an optional parameter of the UL burst
- * indication. We need this information in order to decide whether an
- * Access Burst is 11-bit encoded or not (see OS#1854). If this information
- * is absent, we try to correlate the received synch. sequence with the
- * known ones (3GPP TS 05.02, section 5.2.7), and fall-back to the default
- * TS0 if it fails. */
- enum rach_synch_seq_t synch_seq = RACH_SYNCH_SEQ_TS0;
- int best_score = 127 * RACH_SYNCH_SEQ_LEN;
-
- /* If logical channel is not either of RACH, PDTCH or PTCCH, this is a
- * handover Access Burst, which is always encoded as 8-bit and shall
- * contain the generic training sequence (TS0). */
- if (chan == TRXC_RACH || chan == TRXC_PDTCH || chan == TRXC_PTCCH) {
- if (bi->flags & TRX_BI_F_TS_INFO)
- synch_seq = (enum rach_synch_seq_t) bi->tsc;
- else
- synch_seq = rach_get_synch_seq((sbit_t *) bi->burst, &best_score);
- }
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received%s RACH (%s): rssi=%d toa256=%d",
- (chan != TRXC_RACH) ? " handover" : "",
- get_value_string(rach_synch_seq_names, synch_seq),
- bi->rssi, bi->toa256);
- if (bi->flags & TRX_BI_F_CI_CB)
- LOGPC(DL1P, LOGL_DEBUG, " C/I=%d cB", bi->ci_cb);
- else
- LOGPC(DL1P, LOGL_DEBUG, " match=%.1f%%",
- best_score * 100.0 / (127 * RACH_SYNCH_SEQ_LEN));
- LOGPC(DL1P, LOGL_DEBUG, "\n");
-
- /* Compose a new L1SAP primitive */
- memset(&l1sap, 0x00, sizeof(l1sap));
- osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL);
- l1sap.u.rach_ind.chan_nr = trx_chan_desc[chan].chan_nr | bi->tn;
- l1sap.u.rach_ind.acc_delay = (bi->toa256 >= 0) ? bi->toa256 / 256 : 0;
- l1sap.u.rach_ind.acc_delay_256bits = bi->toa256;
- l1sap.u.rach_ind.rssi = bi->rssi;
- l1sap.u.rach_ind.fn = bi->fn;
-
- /* Link quality is defined by C/I (Carrier-to-Interference ratio),
- * which has optional presence. If it's absent, report the
- * minimum acceptable value to pass L1SAP checks. */
- if (bi->flags & TRX_BI_F_CI_CB)
- l1sap.u.rach_ind.lqual_cb = bi->ci_cb;
- else
- l1sap.u.rach_ind.lqual_cb = l1t->trx->bts->min_qual_rach;
-
- /* Decode RACH depending on its synch. sequence */
- switch (synch_seq) {
- case RACH_SYNCH_SEQ_TS1:
- case RACH_SYNCH_SEQ_TS2:
- rc = gsm0503_rach_ext_decode_ber(&ra11, bi->burst + RACH_EXT_TAIL_LEN + RACH_SYNCH_SEQ_LEN,
- l1t->trx->bts->bsic, &n_errors, &n_bits_total);
- if (rc) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received bad Access Burst\n");
- return 0;
- }
-
- if (synch_seq == RACH_SYNCH_SEQ_TS1)
- l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_1;
- else
- l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_2;
-
- l1sap.u.rach_ind.is_11bit = 1;
- l1sap.u.rach_ind.ra = ra11;
- break;
-
- case RACH_SYNCH_SEQ_TS0:
- default:
- /* Fall-back to the default TS0 if needed */
- if (synch_seq != RACH_SYNCH_SEQ_TS0) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Falling-back to the default TS0\n");
- synch_seq = RACH_SYNCH_SEQ_TS0;
- }
-
- rc = gsm0503_rach_decode_ber(&ra, bi->burst + RACH_EXT_TAIL_LEN + RACH_SYNCH_SEQ_LEN,
- l1t->trx->bts->bsic, &n_errors, &n_bits_total);
- if (rc) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received bad Access Burst\n");
- return 0;
- }
-
- l1sap.u.rach_ind.burst_type = GSM_L1_BURST_TYPE_ACCESS_0;
- l1sap.u.rach_ind.is_11bit = 0;
- l1sap.u.rach_ind.ra = ra;
- break;
- }
-
- l1sap.u.rach_ind.ber10k = compute_ber10k(n_bits_total, n_errors);
-
- /* forward primitive */
- l1sap_up(l1t->trx, &l1sap);
-
- return 0;
-}
-
-/*! \brief a single (SDCCH/SACCH) burst was received by the PHY, process it */
-int rx_data_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
- uint8_t bid, const struct trx_ul_burst_ind *bi)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint32_t *first_fn = &chan_state->ul_first_fn;
- uint8_t *mask = &chan_state->ul_mask;
- float *rssi_sum = &chan_state->rssi_sum;
- uint8_t *rssi_num = &chan_state->rssi_num;
- int32_t *toa256_sum = &chan_state->toa256_sum;
- uint8_t *toa_num = &chan_state->toa_num;
- int32_t *ci_cb_sum = &chan_state->ci_cb_sum;
- uint8_t *ci_cb_num = &chan_state->ci_cb_num;
- uint8_t l2[GSM_MACBLOCK_LEN], l2_len;
- int n_errors = 0;
- int n_bits_total = 0;
- int16_t lqual_cb;
- uint16_t ber10k;
- int rc;
-
- /* If handover RACH detection is turned on, treat this burst as an Access Burst.
- * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
- if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
- return rx_rach_fn(l1t, chan, bid, bi);
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received Data, bid=%u\n", bid);
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
- return -ENOMEM;
- }
-
- /* clear burst & store frame number of first burst */
- if (bid == 0) {
- memset(*bursts_p, 0, 464);
- *mask = 0x0;
- *first_fn = bi->fn;
- *rssi_sum = 0;
- *rssi_num = 0;
- *toa256_sum = 0;
- *toa_num = 0;
- *ci_cb_sum = 0;
- *ci_cb_num = 0;
- }
-
- /* update mask + RSSI */
- *mask |= (1 << bid);
- *rssi_sum += bi->rssi;
- (*rssi_num)++;
- *toa256_sum += bi->toa256;
- (*toa_num)++;
-
- /* C/I: Carrier-to-Interference ratio (in centiBels) */
- if (bi->flags & TRX_BI_F_CI_CB) {
- *ci_cb_sum += bi->ci_cb;
- (*ci_cb_num)++;
- }
-
- /* Copy burst to buffer of 4 bursts. If the burst indication contains
- * no data, ensure that the buffer does not stay uninitialized */
- burst = *bursts_p + bid * 116;
- if (bi->burst_len > 0) {
- memcpy(burst, bi->burst + 3, 58);
- memcpy(burst + 58, bi->burst + 87, 58);
- } else
- memset(burst, 0, 58 * 2);
-
- /* wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received incomplete data (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
-
- /* we require first burst to have correct FN */
- if (!(*mask & 0x1)) {
- *mask = 0x0;
- return 0;
- }
- }
- *mask = 0x0;
-
- /* decode */
- rc = gsm0503_xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total);
- if (rc) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received bad data (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- l2_len = 0;
- } else
- l2_len = GSM_MACBLOCK_LEN;
-
- lqual_cb = *ci_cb_num ? (*ci_cb_sum / *ci_cb_num) : 0;
- ber10k = compute_ber10k(n_bits_total, n_errors);
- return _sched_compose_ph_data_ind(l1t, bi->tn, *first_fn,
- chan, l2, l2_len,
- *rssi_sum / *rssi_num,
- *toa256_sum / *toa_num,
- lqual_cb, ber10k,
- PRES_INFO_UNKNOWN);
-}
-
-/*! \brief a single PDTCH burst was received by the PHY, process it */
-int rx_pdtch_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
- uint8_t bid, const struct trx_ul_burst_ind *bi)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint32_t *first_fn = &chan_state->ul_first_fn;
- uint8_t *mask = &chan_state->ul_mask;
- float *rssi_sum = &chan_state->rssi_sum;
- uint8_t *rssi_num = &chan_state->rssi_num;
- int32_t *toa256_sum = &chan_state->toa256_sum;
- uint8_t *toa_num = &chan_state->toa_num;
- int32_t *ci_cb_sum = &chan_state->ci_cb_sum;
- uint8_t *ci_cb_num = &chan_state->ci_cb_num;
- uint8_t l2[EGPRS_0503_MAX_BYTES];
- int n_errors = 0;
- int n_bursts_bits = 0;
- int n_bits_total = 0;
- int16_t lqual_cb;
- uint16_t ber10k;
- int rc;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received PDTCH bid=%u\n", bid);
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx,
- GSM0503_EGPRS_BURSTS_NBITS);
- if (!*bursts_p)
- return -ENOMEM;
- }
-
- /* clear burst */
- if (bid == 0) {
- memset(*bursts_p, 0, GSM0503_EGPRS_BURSTS_NBITS);
- *mask = 0x0;
- *first_fn = bi->fn;
- *rssi_sum = 0;
- *rssi_num = 0;
- *toa256_sum = 0;
- *toa_num = 0;
- *ci_cb_sum = 0;
- *ci_cb_num = 0;
- }
-
- /* update mask + rssi */
- *mask |= (1 << bid);
- *rssi_sum += bi->rssi;
- (*rssi_num)++;
- *toa256_sum += bi->toa256;
- (*toa_num)++;
-
- /* C/I: Carrier-to-Interference ratio (in centiBels) */
- if (bi->flags & TRX_BI_F_CI_CB) {
- *ci_cb_sum += bi->ci_cb;
- (*ci_cb_num)++;
- }
-
- /* copy burst to buffer of 4 bursts */
- if (bi->burst_len == EGPRS_BURST_LEN) {
- burst = *bursts_p + bid * 348;
- memcpy(burst, bi->burst + 9, 174);
- memcpy(burst + 174, bi->burst + 261, 174);
- n_bursts_bits = GSM0503_EGPRS_BURSTS_NBITS;
- } else {
- burst = *bursts_p + bid * 116;
- memcpy(burst, bi->burst + 3, 58);
- memcpy(burst + 58, bi->burst + 87, 58);
- n_bursts_bits = GSM0503_GPRS_BURSTS_NBITS;
- }
-
- /* wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received incomplete frame (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- }
- *mask = 0x0;
-
- /*
- * Attempt to decode EGPRS bursts first. For 8-PSK EGPRS this is all we
- * do. Attempt GPRS decoding on EGPRS failure. If the burst is GPRS,
- * then we incur decoding overhead of 31 bits on the Type 3 EGPRS
- * header, which is tolerable.
- */
- rc = gsm0503_pdtch_egprs_decode(l2, *bursts_p, n_bursts_bits,
- NULL, &n_errors, &n_bits_total);
-
- if ((bi->burst_len == GSM_BURST_LEN) && (rc < 0)) {
- rc = gsm0503_pdtch_decode(l2, *bursts_p, NULL,
- &n_errors, &n_bits_total);
- }
-
- if (rc <= 0) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received bad PDTCH (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- return 0;
- }
-
- lqual_cb = *ci_cb_num ? (*ci_cb_sum / *ci_cb_num) : 0;
- ber10k = compute_ber10k(n_bits_total, n_errors);
- return _sched_compose_ph_data_ind(l1t, bi->tn,
- *first_fn, chan, l2, rc,
- *rssi_sum / *rssi_num,
- *toa256_sum / *toa_num,
- lqual_cb, ber10k,
- PRES_INFO_BOTH);
-}
-
-/*! \brief a single TCH/F burst was received by the PHY, process it */
-int rx_tchf_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
- uint8_t bid, const struct trx_ul_burst_ind *bi)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint8_t *mask = &chan_state->ul_mask;
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- uint8_t tch_data[128]; /* just to be safe */
- int rc, amr = 0;
- int n_errors = 0;
- int n_bits_total = 0;
- bool bfi_flag = false;
- struct gsm_lchan *lchan =
- get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | bi->tn);
- unsigned int fn_begin;
- uint16_t ber10k;
- uint8_t is_sub = 0;
- uint8_t ft;
-
- /* If handover RACH detection is turned on, treat this burst as an Access Burst.
- * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
- if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
- return rx_rach_fn(l1t, chan, bid, bi);
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received TCH/F, bid=%u\n", bid);
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
- if (!*bursts_p)
- return -ENOMEM;
- }
-
- /* clear burst */
- if (bid == 0) {
- memset(*bursts_p + 464, 0, 464);
- *mask = 0x0;
- }
-
- /* update mask */
- *mask |= (1 << bid);
-
- /* copy burst to end of buffer of 8 bursts */
- burst = *bursts_p + bid * 116 + 464;
- if (bi->burst_len > 0) {
- memcpy(burst, bi->burst + 3, 58);
- memcpy(burst + 58, bi->burst + 87, 58);
- } else
- memset(burst, 0, 116);
-
- /* wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received incomplete frame (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- }
- *mask = 0x0;
-
- /* decode
- * also shift buffer by 4 bursts for interleaving */
- switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
- : tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR */
- rc = gsm0503_tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total);
- if (rc >= 0)
- lchan_set_marker(osmo_fr_check_sid(tch_data, rc), lchan); /* DTXu */
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- rc = gsm0503_tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /* the first FN 0,8,17 defines that CMI is included in frame,
- * the first FN 4,13,21 defines that CMR is included in frame.
- * NOTE: A frame ends 7 FN after start.
- */
-
- /* The AFS_ONSET frame itself does not result into an RTP frame
- * since it only contains a recognition pattern that marks the
- * end of the DTX interval. To mark the end of the DTX interval
- * in the RTP stream as well, the voice frame after the
- * AFS_ONSET frame is used. */
- if (chan_state->amr_last_dtx == AFS_ONSET)
- lchan_set_marker(false, lchan);
-
- /* we store tch_data + 2 header bytes, the amr variable set to
- * 2 will allow us to skip the first 2 bytes in case we did
- * receive an FACCH frame instead of a voice frame (we do not
- * know this before we actually decode the frame) */
- amr = 2;
- rc = gsm0503_tch_afs_decode_dtx(tch_data + amr, *bursts_p,
- (((bi->fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
- &chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
-
- /* Tag all frames that are not regular AMR voice frames as
- * SUB-Frames */
- if (chan_state->amr_last_dtx != AMR_OTHER) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received AMR SID frame: %s\n",
- gsm0503_amr_dtx_frame_name(chan_state->amr_last_dtx));
- is_sub = 1;
- }
-
- /* The occurrence of the following frames indicates that we
- * are either at the beginning or in the middle of a talk
- * spurt. We update the SID status accordingly, but we do
- * not want the marker to be set, since this must only
- * happen when the talk spurt is over (see above) */
- switch (chan_state->amr_last_dtx) {
- case AFS_SID_FIRST:
- case AFS_SID_UPDATE:
- case AFS_SID_UPDATE_CN:
- lchan_set_marker(true, lchan);
- lchan->rtp_tx_marker = false;
- break;
- }
-
- if (rc)
- trx_loop_amr_input(l1t,
- trx_chan_desc[chan].chan_nr | bi->tn, chan_state,
- n_errors, n_bits_total);
- /* only good speech frames get rtp header */
- if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
- if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
- } else {
- /* SID frames will always get Frame Type Index 8 (AMR_SID) */
- ft = AMR_SID;
- }
- rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->ul_cmr],
- ft, AMR_GOOD);
- }
-
- break;
- default:
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "TCH mode %u invalid, please fix!\n",
- tch_mode);
- return -EINVAL;
- }
- memcpy(*bursts_p, *bursts_p + 464, 464);
-
- /* Check if the frame is bad */
- if (rc < 0) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received bad data (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- bfi_flag = true;
- } else if (rc < 4) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received bad data (%u/%u) with invalid codec mode %d\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period, rc);
- bfi_flag = true;
- }
-
- if (rc != GSM_MACBLOCK_LEN && lchan->ecu_state)
- osmo_ecu_frame_in(lchan->ecu_state, bfi_flag, tch_data, rc);
-
- ber10k = compute_ber10k(n_bits_total, n_errors);
- if (bfi_flag)
- goto bfi;
-
- /* FACCH */
- if (rc == GSM_MACBLOCK_LEN) {
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_F);
- _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan,
- tch_data + amr, GSM_MACBLOCK_LEN,
- /* FIXME: AVG RSSI and ToA256 */
- bi->rssi, bi->toa256,
- 0 /* FIXME: AVG C/I */,
- ber10k, PRES_INFO_UNKNOWN);
-bfi:
- if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
- /* indicate bad frame */
- if (lchan->tch.dtx.ul_sid) {
- /* DTXu: pause in progress. Push empty payload to upper layers */
- rc = 0;
- goto compose_l1sap;
- }
-
- /* If there is an ECU active on this channel, use its output */
- if (lchan->ecu_state) {
- rc = osmo_ecu_frame_out(lchan->ecu_state, tch_data);
- if (rc >= 0) /* Otherwise we send a BFI */
- goto compose_l1sap;
- }
-
- switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR */
- memset(tch_data, 0, GSM_FR_BYTES);
- tch_data[0] = 0xd0;
- rc = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- memset(tch_data, 0, GSM_EFR_BYTES);
- tch_data[0] = 0xc0;
- rc = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
- AMR_BAD);
- if (rc < 2) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "Failed to encode AMR_BAD frame (rc=%d), "
- "not sending BFI\n", rc);
- return -EINVAL;
- }
- memset(tch_data + 2, 0, rc - 2);
- break;
- default:
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "TCH mode %u invalid, please fix!\n", tch_mode);
- return -EINVAL;
- }
- }
- }
-
- if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
- return 0;
-
- /* TCH or BFI */
-compose_l1sap:
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_F);
- return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan,
- tch_data, rc, bi->toa256, ber10k, bi->rssi, is_sub);
-}
-
-/*! \brief a single TCH/H burst was received by the PHY, process it */
-int rx_tchh_fn(struct l1sched_trx *l1t, enum trx_chan_type chan,
- uint8_t bid, const struct trx_ul_burst_ind *bi)
-{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
- struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint8_t *mask = &chan_state->ul_mask;
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- uint8_t tch_data[128]; /* just to be safe */
- int rc, amr = 0;
- int n_errors = 0;
- int n_bits_total = 0;
- bool bfi_flag = false;
- struct gsm_lchan *lchan =
- get_lchan_by_chan_nr(l1t->trx, trx_chan_desc[chan].chan_nr | bi->tn);
- /* Note on FN-10: If we are at FN 10, we decoded an even aligned
- * TCH/FACCH frame, because our burst buffer carries 6 bursts.
- * Even FN ending at: 10,11,19,20,2,3
- */
- int fn_is_odd = (((bi->fn + 26 - 10) % 26) >> 2) & 1;
- unsigned int fn_begin;
- uint16_t ber10k;
- uint8_t is_sub = 0;
- uint8_t ft;
-
- /* If handover RACH detection is turned on, treat this burst as an Access Burst.
- * Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
- if (chan_state->ho_rach_detect == 1 && ~bi->flags & TRX_BI_F_NOPE_IND)
- return rx_rach_fn(l1t, chan, bid, bi);
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received TCH/H, bid=%u\n", bid);
-
- /* allocate burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
- if (!*bursts_p)
- return -ENOMEM;
- }
-
- /* clear burst */
- if (bid == 0) {
- memset(*bursts_p + 464, 0, 232);
- *mask = 0x0;
- }
-
- /* update mask */
- *mask |= (1 << bid);
-
- /* copy burst to end of buffer of 6 bursts */
- burst = *bursts_p + bid * 116 + 464;
- if (bi->burst_len > 0) {
- memcpy(burst, bi->burst + 3, 58);
- memcpy(burst + 58, bi->burst + 87, 58);
- } else
- memset(burst, 0, 116);
-
- /* wait until complete set of bursts */
- if (bid != 1)
- return 0;
-
- /* check for complete set of bursts */
- if ((*mask & 0x3) != 0x3) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received incomplete frame (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- }
- *mask = 0x0;
-
- /* skip second of two TCH frames of FACCH was received */
- if (chan_state->ul_ongoing_facch) {
- chan_state->ul_ongoing_facch = 0;
- memcpy(*bursts_p, *bursts_p + 232, 232);
- memcpy(*bursts_p + 232, *bursts_p + 464, 232);
- ber10k = 0;
- goto bfi;
- }
-
- /* decode
- * also shift buffer by 4 bursts for interleaving */
- switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
- : tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* HR or signalling */
- /* Note on FN-10: If we are at FN 10, we decoded an even aligned
- * TCH/FACCH frame, because our burst buffer carries 6 bursts.
- * Even FN ending at: 10,11,19,20,2,3
- */
- rc = gsm0503_tch_hr_decode(tch_data, *bursts_p,
- fn_is_odd, &n_errors, &n_bits_total);
- if (rc >= 0) /* DTXu */
- lchan_set_marker(osmo_hr_check_sid(tch_data, rc), lchan);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
- * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR
- * is included in frame.
- */
-
- /* See comment in function rx_tchf_fn() */
- switch (chan_state->amr_last_dtx) {
- case AHS_ONSET:
- case AHS_SID_FIRST_INH:
- case AHS_SID_UPDATE_INH:
- lchan_set_marker(false, lchan);
- break;
- }
-
- /* See comment in function rx_tchf_fn() */
- amr = 2;
- rc = gsm0503_tch_ahs_decode_dtx(tch_data + amr, *bursts_p,
- fn_is_odd, fn_is_odd, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
- &chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
-
- /* Tag all frames that are not regular AMR voice frames
- as SUB-Frames */
- if (chan_state->amr_last_dtx != AMR_OTHER) {
- LOGL1S(DL1P, LOGL_DEBUG, l1t, bi->tn, chan, bi->fn,
- "Received AMR SID frame: %s\n",
- gsm0503_amr_dtx_frame_name(chan_state->amr_last_dtx));
- is_sub = 1;
- }
-
- /* See comment in function rx_tchf_fn() */
- switch (chan_state->amr_last_dtx) {
- case AHS_SID_FIRST_P1:
- case AHS_SID_FIRST_P2:
- case AHS_SID_UPDATE:
- case AHS_SID_UPDATE_CN:
- lchan_set_marker(true, lchan);
- lchan->rtp_tx_marker = false;
- break;
- }
-
- if (rc)
- trx_loop_amr_input(l1t,
- trx_chan_desc[chan].chan_nr | bi->tn, chan_state,
- n_errors, n_bits_total);
-
- /* only good speech frames get rtp header */
- if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
- if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
- } else {
- /* SID frames will always get Frame Type Index 8 (AMR_SID) */
- ft = AMR_SID;
- }
- rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->ul_cmr],
- ft, AMR_GOOD);
- }
-
- break;
- default:
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "TCH mode %u invalid, please fix!\n",
- tch_mode);
- return -EINVAL;
- }
- memcpy(*bursts_p, *bursts_p + 232, 232);
- memcpy(*bursts_p + 232, *bursts_p + 464, 232);
- ber10k = compute_ber10k(n_bits_total, n_errors);
-
-
- /* Check if the frame is bad */
- if (rc < 0) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received bad data (%u/%u)\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period);
- bfi_flag = true;
- } else if (rc < 4) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, bi->tn, chan, bi->fn,
- "Received bad data (%u/%u) with invalid codec mode %d\n",
- bi->fn % l1ts->mf_period, l1ts->mf_period, rc);
- bfi_flag = true;
- }
-
- if (rc != GSM_MACBLOCK_LEN && lchan->ecu_state)
- osmo_ecu_frame_in(lchan->ecu_state, bfi_flag, tch_data, rc);
-
- if (bfi_flag)
- goto bfi;
-
- /* FACCH */
- if (rc == GSM_MACBLOCK_LEN) {
- chan_state->ul_ongoing_facch = 1;
- uint16_t ber10k = compute_ber10k(n_bits_total, n_errors);
- if (lchan->nr == 0)
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H0);
- else
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_FACCH_H1);
- _sched_compose_ph_data_ind(l1t, bi->tn, fn_begin, chan,
- tch_data + amr, GSM_MACBLOCK_LEN,
- /* FIXME: AVG both RSSI and ToA */
- bi->rssi, bi->toa256,
- 0 /* FIXME: AVG C/I */,
- ber10k, PRES_INFO_UNKNOWN);
-bfi:
- /* FIXME: a FACCH/H frame replaces two speech frames,
- * so we actually need to send two bad frame indications! */
- if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
- /* indicate bad frame */
- if (lchan->tch.dtx.ul_sid) {
- /* DTXu: pause in progress. Push empty payload to upper layers */
- rc = 0;
- goto compose_l1sap;
- }
-
- /* If there is an ECU active on this channel, use its output */
- if (lchan->ecu_state) {
- rc = osmo_ecu_frame_out(lchan->ecu_state, tch_data);
- if (rc >= 0) /* Otherwise we send a BFI */
- goto compose_l1sap;
- }
-
- switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* HR */
- tch_data[0] = 0x70; /* F = 0, FT = 111 */
- memset(tch_data + 1, 0, 14);
- rc = 15;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
- AMR_BAD);
- if (rc < 2) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "Failed to encode AMR_BAD frame (rc=%d), "
- "not sending BFI\n", rc);
- return -EINVAL;
- }
- memset(tch_data + 2, 0, rc - 2);
- break;
- default:
- LOGL1S(DL1P, LOGL_ERROR, l1t, bi->tn, chan, bi->fn,
- "TCH mode %u invalid, please fix!\n", tch_mode);
- return -EINVAL;
- }
- }
- }
-
- if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
- return 0;
-
-compose_l1sap:
- /* TCH or BFI */
- /* Note on FN 19 or 20: If we received the last burst of a frame,
- * it actually starts at FN 8 or 9. A burst starting there, overlaps
- * with the slot 12, so an extra FN must be subtracted to get correct
- * start of frame.
- */
- if (lchan->nr == 0)
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H0);
- else
- fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_H1);
- return _sched_compose_tch_ind(l1t, bi->tn, fn_begin, chan,
- tch_data, rc, bi->toa256, ber10k, bi->rssi, is_sub);
-}
-
/* schedule all frames of all TRX for given FN */
static int trx_sched_fn(struct gsm_bts *bts, uint32_t fn)
{
--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/18820
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: Ie5663fd90596b4800a4546675a323250bbb24c80
Gerrit-Change-Number: 18820
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200613/1ccf3b66/attachment.htm>