summaryrefslogtreecommitdiffstats
path: root/src/host/trxcon
diff options
context:
space:
mode:
Diffstat (limited to 'src/host/trxcon')
-rw-r--r--src/host/trxcon/.gitignore9
-rw-r--r--src/host/trxcon/Makefile.am57
-rw-r--r--src/host/trxcon/configure.ac38
-rw-r--r--src/host/trxcon/include/Makefile.am3
-rw-r--r--src/host/trxcon/include/osmocom/Makefile.am3
-rw-r--r--src/host/trxcon/include/osmocom/bb/Makefile.am9
l---------src/host/trxcon/include/osmocom/bb/l1ctl_proto.h1
l---------src/host/trxcon/include/osmocom/bb/l1gprs.h1
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am5
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h417
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/logging.h37
-rw-r--r--src/host/trxcon/include/osmocom/bb/l1sched/prim.h132
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am9
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h22
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h67
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/logging.h16
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/phyif.h120
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h61
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h64
-rw-r--r--src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h169
-rw-r--r--src/host/trxcon/l1ctl.c905
-rw-r--r--src/host/trxcon/l1ctl.h26
-rw-r--r--src/host/trxcon/l1ctl_link.c319
-rw-r--r--src/host/trxcon/l1ctl_link.h48
l---------src/host/trxcon/l1ctl_proto.h1
-rw-r--r--src/host/trxcon/logging.h17
-rw-r--r--src/host/trxcon/m4/.gitkeep0
-rw-r--r--src/host/trxcon/sched_clck.c206
-rw-r--r--src/host/trxcon/sched_lchan_common.c181
-rw-r--r--src/host/trxcon/sched_lchan_pdtch.c204
-rw-r--r--src/host/trxcon/sched_lchan_rach.c181
-rw-r--r--src/host/trxcon/sched_lchan_tchf.c297
-rw-r--r--src/host/trxcon/sched_lchan_tchh.c502
-rw-r--r--src/host/trxcon/sched_lchan_xcch.c213
-rw-r--r--src/host/trxcon/sched_mframe.c2101
-rw-r--r--src/host/trxcon/sched_prim.c611
-rw-r--r--src/host/trxcon/sched_trx.c704
-rw-r--r--src/host/trxcon/sched_trx.h365
-rw-r--r--src/host/trxcon/scheduler.h54
-rw-r--r--src/host/trxcon/src/Makefile.am64
-rw-r--r--src/host/trxcon/src/l1ctl.c849
-rw-r--r--src/host/trxcon/src/l1ctl_server.c282
l---------src/host/trxcon/src/l1gprs.c1
-rw-r--r--src/host/trxcon/src/logging.c (renamed from src/host/trxcon/logging.c)41
-rw-r--r--src/host/trxcon/src/sched_lchan_common.c137
-rw-r--r--src/host/trxcon/src/sched_lchan_desc.c (renamed from src/host/trxcon/sched_lchan_desc.c)387
-rw-r--r--src/host/trxcon/src/sched_lchan_pdtch.c195
-rw-r--r--src/host/trxcon/src/sched_lchan_rach.c136
-rw-r--r--src/host/trxcon/src/sched_lchan_sch.c (renamed from src/host/trxcon/sched_lchan_sch.c)84
-rw-r--r--src/host/trxcon/src/sched_lchan_tchf.c441
-rw-r--r--src/host/trxcon/src/sched_lchan_tchh.c622
-rw-r--r--src/host/trxcon/src/sched_lchan_xcch.c181
-rw-r--r--src/host/trxcon/src/sched_mframe.c2102
-rw-r--r--src/host/trxcon/src/sched_prim.c410
-rw-r--r--src/host/trxcon/src/sched_trx.c894
-rw-r--r--src/host/trxcon/src/trx_if.c (renamed from src/host/trxcon/trx_if.c)480
-rw-r--r--src/host/trxcon/src/trxcon_fsm.c822
-rw-r--r--src/host/trxcon/src/trxcon_inst.c107
-rw-r--r--src/host/trxcon/src/trxcon_main.c423
-rw-r--r--src/host/trxcon/src/trxcon_shim.c262
-rw-r--r--src/host/trxcon/trx_if.h80
-rw-r--r--src/host/trxcon/trxcon.c343
-rw-r--r--src/host/trxcon/trxcon.h20
63 files changed, 9677 insertions, 7861 deletions
diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore
index fe90e43c..b5c3a99d 100644
--- a/src/host/trxcon/.gitignore
+++ b/src/host/trxcon/.gitignore
@@ -9,6 +9,11 @@ install-sh
missing
compile
+# libtool by-products
+ltmain.sh
+libtool
+m4/*.m4
+
# configure by-products
.deps/
Makefile
@@ -18,9 +23,11 @@ version.h
# build by-products
*.o
+*.lo
*.a
+*.la
-trxcon
+/src/trxcon
# various
.version
diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am
index b51db02f..5b1002c8 100644
--- a/src/host/trxcon/Makefile.am
+++ b/src/host/trxcon/Makefile.am
@@ -1,52 +1,21 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
-# versioning magic
-BUILT_SOURCES = $(top_srcdir)/.version
-$(top_srcdir)/.version:
- echo $(VERSION) > $@-t && mv $@-t $@
-dist-hook:
- echo $(VERSION) > $(distdir)/.tarball-version
-
-AM_CPPFLAGS = \
- $(all_includes) \
- -I$(top_srcdir)/include \
- $(NULL)
-
-AM_CFLAGS = \
- -Wall \
- $(LIBOSMOCORE_CFLAGS) \
- $(LIBOSMOCODING_CFLAGS) \
- $(LIBOSMOGSM_CFLAGS) \
+SUBDIRS = \
+ include \
+ src \
$(NULL)
-bin_PROGRAMS = trxcon
+ACLOCAL_AMFLAGS = -I m4
-trxcon_SOURCES = \
- l1ctl_link.c \
- l1ctl.c \
- trx_if.c \
- logging.c \
- trxcon.c \
+BUILT_SOURCES = \
+ $(top_srcdir)/.version \
$(NULL)
-
-# Scheduler
-trxcon_SOURCES += \
- sched_lchan_common.c \
- sched_lchan_pdtch.c \
- sched_lchan_desc.c \
- sched_lchan_xcch.c \
- sched_lchan_tchf.c \
- sched_lchan_tchh.c \
- sched_lchan_rach.c \
- sched_lchan_sch.c \
- sched_mframe.c \
- sched_clck.c \
- sched_prim.c \
- sched_trx.c \
+EXTRA_DIST = \
+ .version \
$(NULL)
-trxcon_LDADD = \
- $(LIBOSMOCORE_LIBS) \
- $(LIBOSMOCODING_LIBS) \
- $(LIBOSMOGSM_LIBS) \
- $(NULL)
+# versioning magic
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac
index 1f24260d..6508689b 100644
--- a/src/host/trxcon/configure.ac
+++ b/src/host/trxcon/configure.ac
@@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script
AC_INIT([trxcon], [0.0.0])
AM_INIT_AUTOMAKE
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -18,6 +20,9 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm)
dnl checks for header files
AC_HEADER_STDC
+dnl init libtool
+LT_INIT
+
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
[--enable-sanitize],
@@ -29,7 +34,36 @@ then
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
fi
+AC_ARG_ENABLE(werror,
+ [AS_HELP_STRING(
+ [--enable-werror],
+ [Turn all compiler warnings into errors, with exceptions:
+ a) deprecation (allow upstream to mark deprecation without breaking builds);
+ b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
+ ]
+ )],
+ [werror=$enableval], [werror="no"])
+if test x"$werror" = x"yes"
+then
+ WERROR_FLAGS="-Werror"
+ WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition"
+ WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
+ WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
+ CFLAGS="$CFLAGS $WERROR_FLAGS"
+ CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
+fi
+
+AC_MSG_RESULT([CFLAGS="$CFLAGS"])
+AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
+
dnl Checks for typedefs, structures and compiler characteristics
-AC_OUTPUT(
- Makefile)
+AC_CONFIG_MACRO_DIRS([m4])
+AC_CONFIG_FILES([include/Makefile
+ include/osmocom/Makefile
+ include/osmocom/bb/Makefile
+ include/osmocom/bb/l1sched/Makefile
+ include/osmocom/bb/trxcon/Makefile
+ src/Makefile
+ Makefile])
+AC_OUTPUT
diff --git a/src/host/trxcon/include/Makefile.am b/src/host/trxcon/include/Makefile.am
new file mode 100644
index 00000000..9d963a02
--- /dev/null
+++ b/src/host/trxcon/include/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ osmocom \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/Makefile.am b/src/host/trxcon/include/osmocom/Makefile.am
new file mode 100644
index 00000000..83c6385c
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ bb \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/Makefile.am b/src/host/trxcon/include/osmocom/bb/Makefile.am
new file mode 100644
index 00000000..4a575ffb
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = \
+ l1sched \
+ trxcon \
+ $(NULL)
+
+noinst_HEADERS = \
+ l1ctl_proto.h \
+ l1gprs.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h
new file mode 120000
index 00000000..ee19b80e
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h
@@ -0,0 +1 @@
+../../../../../../include/l1ctl_proto.h \ No newline at end of file
diff --git a/src/host/trxcon/include/osmocom/bb/l1gprs.h b/src/host/trxcon/include/osmocom/bb/l1gprs.h
new file mode 120000
index 00000000..3bf85176
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1gprs.h
@@ -0,0 +1 @@
+../../../../../../include/l1gprs.h \ No newline at end of file
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
new file mode 100644
index 00000000..39c32ba0
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = \
+ l1sched.h \
+ logging.h \
+ prim.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
new file mode 100644
index 00000000..6c5a31e8
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
@@ -0,0 +1,417 @@
+#pragma once
+
+#include <time.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
+#include <osmocom/bb/l1sched/prim.h>
+
+#define GPRS_L2_MAX_LEN 54
+#define EDGE_L2_MAX_LEN 155
+
+#define L1SCHED_CH_LID_DEDIC 0x00
+#define L1SCHED_CH_LID_SACCH 0x40
+
+/* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2).
+ * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */
+#define L1SCHED_CH_LID_PTCCH 0x80
+
+/* Is a channel related to PDCH (GPRS) */
+#define L1SCHED_CH_FLAG_PDCH (1 << 0)
+/* Should a channel be activated automatically */
+#define L1SCHED_CH_FLAG_AUTO (1 << 1)
+
+#define MAX_A5_KEY_LEN (128 / 8)
+#define TRX_TS_COUNT 8
+
+struct l1sched_lchan_state;
+struct l1sched_meas_set;
+struct l1sched_state;
+struct l1sched_ts;
+
+enum l1sched_burst_type {
+ L1SCHED_BURST_GMSK,
+ L1SCHED_BURST_8PSK,
+};
+
+/**
+ * These types define the different channels on a multiframe.
+ * Each channel has queues and can be activated individually.
+ */
+enum l1sched_lchan_type {
+ L1SCHED_IDLE = 0,
+ L1SCHED_FCCH,
+ L1SCHED_SCH,
+ L1SCHED_BCCH,
+ L1SCHED_RACH,
+ L1SCHED_CCCH,
+ L1SCHED_TCHF,
+ L1SCHED_TCHH_0,
+ L1SCHED_TCHH_1,
+ L1SCHED_SDCCH4_0,
+ L1SCHED_SDCCH4_1,
+ L1SCHED_SDCCH4_2,
+ L1SCHED_SDCCH4_3,
+ L1SCHED_SDCCH8_0,
+ L1SCHED_SDCCH8_1,
+ L1SCHED_SDCCH8_2,
+ L1SCHED_SDCCH8_3,
+ L1SCHED_SDCCH8_4,
+ L1SCHED_SDCCH8_5,
+ L1SCHED_SDCCH8_6,
+ L1SCHED_SDCCH8_7,
+ L1SCHED_SACCHTF,
+ L1SCHED_SACCHTH_0,
+ L1SCHED_SACCHTH_1,
+ L1SCHED_SACCH4_0,
+ L1SCHED_SACCH4_1,
+ L1SCHED_SACCH4_2,
+ L1SCHED_SACCH4_3,
+ L1SCHED_SACCH8_0,
+ L1SCHED_SACCH8_1,
+ L1SCHED_SACCH8_2,
+ L1SCHED_SACCH8_3,
+ L1SCHED_SACCH8_4,
+ L1SCHED_SACCH8_5,
+ L1SCHED_SACCH8_6,
+ L1SCHED_SACCH8_7,
+ L1SCHED_PDTCH,
+ L1SCHED_PTCCH,
+ L1SCHED_SDCCH4_CBCH,
+ L1SCHED_SDCCH8_CBCH,
+ _L1SCHED_CHAN_MAX
+};
+
+/* Represents a burst to be transmitted */
+struct l1sched_burst_req {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t pwr;
+
+ /* Internally used by the scheduler */
+ uint8_t bid;
+
+ ubit_t burst[GSM_NBITS_NB_8PSK_BURST];
+ size_t burst_len;
+};
+
+/* Represents a received burst */
+struct l1sched_burst_ind {
+ uint32_t fn;
+ uint8_t tn;
+
+ /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */
+ int16_t toa256;
+ /*! RSSI (Received Signal Strength Indication) */
+ int8_t rssi;
+
+ /* Internally used by the scheduler */
+ uint8_t bid;
+
+ sbit_t burst[GSM_NBITS_NB_8PSK_BURST];
+ size_t burst_len;
+};
+
+/* Probed lchan is active */
+#define L1SCHED_PROBE_F_ACTIVE (1 << 0)
+
+/* RTR (Ready-to-Receive) probe */
+struct l1sched_probe {
+ uint32_t flags; /* see L1SCHED_PROBE_F_* above */
+ uint32_t fn;
+ uint8_t tn;
+};
+
+typedef int l1sched_lchan_rx_func(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
+
+typedef int l1sched_lchan_tx_func(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
+
+struct l1sched_lchan_desc {
+ /*! Human-readable name */
+ const char *name;
+ /*! Human-readable description */
+ const char *desc;
+
+ /*! Channel Number (like in RSL) */
+ uint8_t chan_nr;
+ /*! Link ID (like in RSL) */
+ uint8_t link_id;
+
+ /*! How much memory do we need to store bursts */
+ size_t burst_buf_size;
+ /*! Channel specific flags */
+ uint8_t flags;
+
+ /*! Function to call when burst received from PHY */
+ l1sched_lchan_rx_func *rx_fn;
+ /*! Function to call when data received from L2 */
+ l1sched_lchan_tx_func *tx_fn;
+};
+
+struct l1sched_tdma_frame {
+ /*! Downlink channel (slot) type */
+ enum l1sched_lchan_type dl_chan;
+ /*! Downlink block ID */
+ uint8_t dl_bid;
+ /*! Uplink channel (slot) type */
+ enum l1sched_lchan_type ul_chan;
+ /*! Uplink block ID */
+ uint8_t ul_bid;
+};
+
+struct l1sched_tdma_multiframe {
+ /*! Channel combination */
+ enum gsm_phys_chan_config chan_config;
+ /*! Human-readable name */
+ const char *name;
+ /*! Repeats how many frames */
+ uint8_t period;
+ /*! Applies to which timeslots */
+ uint8_t slotmask;
+ /*! Contains which lchans */
+ uint64_t lchan_mask;
+ /*! Pointer to scheduling structure */
+ const struct l1sched_tdma_frame *frames;
+};
+
+struct l1sched_meas_set {
+ /*! TDMA frame number of the first burst this set belongs to */
+ uint32_t fn;
+ /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */
+ int16_t toa256;
+ /*! RSSI (Received Signal Strength Indication) */
+ int8_t rssi;
+};
+
+/* Simple ring buffer (up to 24 unique measurements) */
+struct l1sched_lchan_meas_hist {
+ struct l1sched_meas_set buf[24];
+ struct l1sched_meas_set *head;
+};
+
+/* States each channel on a multiframe */
+struct l1sched_lchan_state {
+ /*! Channel type */
+ enum l1sched_lchan_type type;
+ /*! Channel status */
+ uint8_t active;
+ /*! Link to a list of channels */
+ struct llist_head list;
+
+ /*! Burst type: GMSK or 8PSK */
+ enum l1sched_burst_type burst_type;
+ /*! Mask of received bursts */
+ uint32_t rx_burst_mask;
+ /*! Mask of transmitted bursts */
+ uint32_t tx_burst_mask;
+ /*! Burst buffer for RX */
+ sbit_t *rx_bursts;
+ /*! Burst buffer for TX */
+ ubit_t *tx_bursts;
+
+ /*! Queue of Tx primitives */
+ struct llist_head tx_prims;
+ /*! Tx primitive being sent */
+ struct msgb *prim;
+
+ /*! Mode for TCH channels (see GSM48_CMODE_*) */
+ uint8_t tch_mode;
+ /*! Training Sequence Code */
+ uint8_t tsc;
+
+ /*! FACCH/H on downlink */
+ bool dl_ongoing_facch;
+ /*! pending FACCH/H blocks on Uplink */
+ uint8_t ul_facch_blocks;
+
+ /*! Downlink measurements history */
+ struct l1sched_lchan_meas_hist meas_hist;
+ /*! AVG measurements of the last received block */
+ struct l1sched_meas_set meas_avg;
+
+ /*! TDMA loss detection state */
+ struct {
+ /*! Last processed TDMA frame number */
+ uint32_t last_proc;
+ /*! Number of processed TDMA frames */
+ unsigned long num_proc;
+ /*! Number of lost TDMA frames */
+ unsigned long num_lost;
+ } tdma;
+
+ /*! SACCH state */
+ struct {
+ /*! Cached measurement report (last received) */
+ uint8_t mr_cache[GSM_MACBLOCK_LEN];
+ /*! Cache usage counter */
+ uint8_t mr_cache_usage;
+ /*! Was a MR transmitted last time? */
+ bool mr_tx_last;
+ } sacch;
+
+ /* AMR specific */
+ struct {
+ /*! 4 possible codecs for AMR */
+ uint8_t codec[4];
+ /*! Number of possible codecs */
+ uint8_t codecs;
+ /*! Current uplink FT index */
+ uint8_t ul_ft;
+ /*! Current downlink FT index */
+ uint8_t dl_ft;
+ /*! Current uplink CMR index */
+ uint8_t ul_cmr;
+ /*! Current downlink CMR index */
+ uint8_t dl_cmr;
+ /*! If AMR loop is enabled */
+ uint8_t amr_loop;
+ /*! Number of bit error rates */
+ uint8_t ber_num;
+ /*! Sum of bit error rates */
+ float ber_sum;
+ /* last received dtx frame type */
+ uint8_t last_dtx;
+ } amr;
+
+ /*! A5/X encryption state */
+ struct {
+ uint8_t key[MAX_A5_KEY_LEN];
+ uint8_t key_len;
+ uint8_t algo;
+ } a5;
+
+ /* TS that this lchan belongs to */
+ struct l1sched_ts *ts;
+};
+
+struct l1sched_ts {
+ /*! Timeslot index within a frame (0..7) */
+ uint8_t index;
+
+ /*! Pointer to multiframe layout */
+ const struct l1sched_tdma_multiframe *mf_layout;
+ /*! Channel states for logical channels */
+ struct llist_head lchans;
+ /*! Backpointer to the scheduler */
+ struct l1sched_state *sched;
+};
+
+/*! Scheduler configuration */
+struct l1sched_cfg {
+ /*! Logging context (used as prefix for messages) */
+ const char *log_prefix;
+};
+
+/*! One scheduler instance */
+struct l1sched_state {
+ /*! List of timeslots maintained by this scheduler */
+ struct l1sched_ts *ts[TRX_TS_COUNT];
+ /*! SACCH cache (common for all lchans) */
+ uint8_t sacch_cache[GSM_MACBLOCK_LEN];
+ /*! BSIC value learned from SCH bursts */
+ uint8_t bsic;
+ /*! Logging context (used as prefix for messages) */
+ const char *log_prefix;
+ /*! Some private data */
+ void *priv;
+};
+
+extern const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX];
+const struct l1sched_tdma_multiframe *
+l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn);
+
+/* Scheduler management functions */
+struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv);
+void l1sched_reset(struct l1sched_state *sched, bool reset_clock);
+void l1sched_free(struct l1sched_state *sched);
+
+/* Timeslot management functions */
+struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn);
+void l1sched_del_ts(struct l1sched_state *sched, int tn);
+int l1sched_reset_ts(struct l1sched_state *sched, int tn);
+int l1sched_configure_ts(struct l1sched_state *sched, int tn,
+ enum gsm_phys_chan_config config);
+int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo,
+ const uint8_t *key, uint8_t key_len);
+
+/* Logical channel management functions */
+enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr);
+
+void l1sched_deactivate_all_lchans(struct l1sched_ts *ts);
+int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr,
+ int active, uint8_t tch_mode, uint8_t tsc);
+int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan,
+ uint8_t codecs_bitmask, uint8_t start_codec);
+int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan);
+int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan);
+struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts,
+ enum l1sched_lchan_type type);
+struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
+ uint8_t chan_nr, uint8_t link_id);
+
+#define L1SCHED_TCH_MODE_IS_SPEECH(mode) \
+ (mode == GSM48_CMODE_SPEECH_V1 \
+ || mode == GSM48_CMODE_SPEECH_EFR \
+ || mode == GSM48_CMODE_SPEECH_AMR)
+
+#define L1SCHED_TCH_MODE_IS_DATA(mode) \
+ (mode == GSM48_CMODE_DATA_14k5 \
+ || mode == GSM48_CMODE_DATA_12k0 \
+ || mode == GSM48_CMODE_DATA_6k0 \
+ || mode == GSM48_CMODE_DATA_3k6)
+
+#define L1SCHED_CHAN_IS_TCH(chan) \
+ (chan == L1SCHED_TCHF || chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1)
+
+#define L1SCHED_CHAN_IS_SACCH(chan) \
+ (l1sched_lchan_desc[chan].link_id & L1SCHED_CH_LID_SACCH)
+
+int l1sched_handle_rx_burst(struct l1sched_state *sched,
+ struct l1sched_burst_ind *bi);
+int l1sched_handle_rx_probe(struct l1sched_state *sched,
+ struct l1sched_probe *probe);
+int l1sched_handle_burst_req(struct l1sched_state *sched,
+ const struct l1sched_burst_req *br);
+
+/* Shared declarations for lchan handlers */
+extern const uint8_t l1sched_nb_training_bits[8][26];
+
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits);
+
+/* Interleaved TCH/H block TDMA frame mapping */
+bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan,
+ uint32_t fn, bool ul, bool facch, bool start);
+
+#define l1sched_tchh_traffic_start(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 1)
+#define l1sched_tchh_traffic_end(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 0, 0)
+
+#define l1sched_tchh_facch_start(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
+#define l1sched_tchh_facch_end(chan, fn, ul) \
+ l1sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
+
+/* Measurement history */
+void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
+void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n);
+
+/* Clock and Downlink scheduling trigger */
+int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn);
+void l1sched_clck_reset(struct l1sched_state *sched);
+
+void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br);
+void l1sched_pull_send_frame(struct l1sched_state *sched);
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/logging.h b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
new file mode 100644
index 00000000..21be9955
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
@@ -0,0 +1,37 @@
+#pragma once
+
+extern int l1sched_log_cat_common;
+extern int l1sched_log_cat_data;
+
+void l1sched_logging_init(int log_cat_common, int log_cat_data);
+
+/* Messages using l1sched_state as the context */
+#define LOGP_SCHED_CAT(sched, cat, level, fmt, args...) \
+ LOGP(l1sched_log_cat_##cat, level, "%s" fmt, \
+ (sched)->log_prefix, ## args)
+
+/* Common messages using l1sched_state as the context */
+#define LOGP_SCHEDC(sched, level, fmt, args...) \
+ LOGP_SCHED_CAT(sched, common, level, fmt, ## args)
+
+/* Data messages using l1sched_state as the context */
+#define LOGP_SCHEDD(sched, level, fmt, args...) \
+ LOGP_SCHED_CAT(sched, data, level, fmt, ## args)
+
+
+#define LOGP_LCHAN_NAME_FMT "TS%u-%s"
+#define LOGP_LCHAN_NAME_ARGS(lchan) \
+ (lchan)->ts->index, l1sched_lchan_desc[(lchan)->type].name
+
+/* Messages using l1sched_lchan_state as the context */
+#define LOGP_LCHAN_CAT(lchan, cat, level, fmt, args...) \
+ LOGP_SCHED_CAT((lchan)->ts->sched, cat, level, LOGP_LCHAN_NAME_FMT " " fmt, \
+ LOGP_LCHAN_NAME_ARGS(lchan), ## args)
+
+/* Common messages using l1sched_lchan_state as the context */
+#define LOGP_LCHANC(lchan, level, fmt, args...) \
+ LOGP_LCHAN_CAT(lchan, common, level, fmt, ## args)
+
+/* Data messages using l1sched_lchan_state as the context */
+#define LOGP_LCHAND(lchan, level, fmt, args...) \
+ LOGP_LCHAN_CAT(lchan, data, level, fmt, ## args)
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/prim.h b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
new file mode 100644
index 00000000..a9187c2a
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+
+#define l1sched_prim_from_msgb(msg) \
+ ((struct l1sched_prim *)(msg)->l1h)
+
+#define l1sched_prim_data_from_msgb(msg) \
+ ((uint8_t *)msgb_l2(msg))
+
+#define l1sched_prim_type_from_msgb(msg) \
+ l1sched_prim_from_msgb(msg)->oph.primitive
+
+#define L1SCHED_PRIM_STR_FMT "%s.%s"
+#define L1SCHED_PRIM_STR_ARGS(prim) \
+ l1sched_prim_type_name((prim)->oph.primitive), \
+ osmo_prim_operation_name((prim)->oph.operation)
+
+enum l1sched_prim_type {
+ L1SCHED_PRIM_T_DATA, /* Req | Ind | Cnf */
+ L1SCHED_PRIM_T_RACH, /* Req | Cnf */
+ L1SCHED_PRIM_T_SCH, /* Ind */
+ L1SCHED_PRIM_T_PCHAN_COMB, /* Ind */
+};
+
+extern const struct value_string l1sched_prim_type_names[];
+static inline const char *l1sched_prim_type_name(enum l1sched_prim_type val)
+{
+ return get_value_string(l1sched_prim_type_names, val);
+}
+
+/*! Common header for L1SCHED_PRIM_T_{DATA,RACH} */
+struct l1sched_prim_chdr {
+ /*! TDMA Frame Number */
+ uint32_t frame_nr;
+ /*! RSL Channel Number */
+ uint8_t chan_nr;
+ /*! RSL Link Identifier */
+ uint8_t link_id;
+ /*! Traffic or signalling */
+ bool traffic;
+};
+
+/*! Payload of L1SCHED_PRIM_T_DATA | Ind */
+struct l1sched_prim_data_ind {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ int16_t toa256;
+ int8_t rssi;
+ int n_errors;
+ int n_bits_total;
+};
+
+/*! Payload of L1SCHED_PRIM_T_RACH | {Req,Cnf} */
+struct l1sched_prim_rach {
+ /*! Common sub-header */
+ struct l1sched_prim_chdr chdr;
+ /*! Training Sequence (only for 11-bit RA) */
+ uint8_t synch_seq;
+ /*! Transmission offset (how many frames to skip) */
+ uint8_t offset;
+ /*! RA value is 11 bit */
+ bool is_11bit;
+ /*! RA value */
+ uint16_t ra;
+};
+
+struct l1sched_prim {
+ /*! Primitive header */
+ struct osmo_prim_hdr oph;
+ /*! Type specific header */
+ union {
+ /*! L1SCHED_PRIM_T_DATA | Req */
+ struct l1sched_prim_chdr data_req;
+ /*! L1SCHED_PRIM_T_DATA | Cnf */
+ struct l1sched_prim_chdr data_cnf;
+ /*! L1SCHED_PRIM_T_DATA | Ind */
+ struct l1sched_prim_data_ind data_ind;
+
+ /*! L1SCHED_PRIM_T_RACH | Req */
+ struct l1sched_prim_rach rach_req;
+ /*! L1SCHED_PRIM_T_RACH | Cnf */
+ struct l1sched_prim_rach rach_cnf;
+
+ /*! L1SCHED_PRIM_T_SCH | Ind */
+ struct {
+ /*! TDMA frame number */
+ uint32_t frame_nr;
+ /*! BSIC */
+ uint8_t bsic;
+ } sch_ind;
+
+ /*! L1SCHED_PRIM_T_PCHAN_COMB | Ind */
+ struct {
+ /*! Timeslot number */
+ uint8_t tn;
+ /*! Channel combination for a timeslot */
+ enum gsm_phys_chan_config pchan;
+ } pchan_comb_ind;
+ };
+};
+
+
+struct l1sched_state;
+struct l1sched_lchan_state;
+
+void l1sched_prim_init(struct msgb *msg,
+ enum l1sched_prim_type type,
+ enum osmo_prim_operation op);
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op);
+
+bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, bool is_cmr);
+struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan);
+struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch);
+struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan);
+
+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total, bool traffic);
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, uint32_t fn);
+
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg);
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am
new file mode 100644
index 00000000..ad1e89a0
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am
@@ -0,0 +1,9 @@
+noinst_HEADERS = \
+ l1ctl_server.h \
+ l1ctl.h \
+ phyif.h \
+ trx_if.h \
+ logging.h \
+ trxcon.h \
+ trxcon_fsm.h \
+ $(NULL)
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h
new file mode 100644
index 00000000..7e2fa6a5
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <stdint.h>
+
+struct msgb;
+struct trxcon_param_rx_data_ind;
+struct trxcon_param_tx_data_cnf;
+struct trxcon_param_tx_access_burst_cnf;
+
+int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic);
+int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn);
+int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode);
+int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last);
+int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type);
+int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type);
+
+int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon,
+ const struct trxcon_param_rx_data_ind *ind);
+int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_data_cnf *cnf);
+int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_access_burst_cnf *cnf);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
new file mode 100644
index 00000000..83c61f02
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/msgb.h>
+
+#define L1CTL_LENGTH 512
+#define L1CTL_HEADROOM 32
+
+/**
+ * Each L1CTL message gets its own length pushed
+ * as two bytes in front before sending.
+ */
+#define L1CTL_MSG_LEN_FIELD 2
+
+struct l1ctl_client;
+
+typedef int l1ctl_conn_data_func(struct l1ctl_client *, struct msgb *);
+typedef void l1ctl_conn_state_func(struct l1ctl_client *);
+
+struct l1ctl_server_cfg {
+ /* UNIX socket path to listen on */
+ const char *sock_path;
+ /* maximum number of connected clients */
+ unsigned int num_clients_max;
+ /* functions to be called on various events */
+ l1ctl_conn_data_func *conn_read_cb; /* mandatory */
+ l1ctl_conn_state_func *conn_accept_cb; /* optional */
+ l1ctl_conn_state_func *conn_close_cb; /* optional */
+};
+
+struct l1ctl_server {
+ /* list of connected clients */
+ struct llist_head clients;
+ /* number of connected clients */
+ unsigned int num_clients;
+ /* used for client ID generation */
+ unsigned int next_client_id;
+ /* socket on which we listen for connections */
+ struct osmo_fd ofd;
+ /* server configuration */
+ const struct l1ctl_server_cfg *cfg;
+};
+
+struct l1ctl_client {
+ /* list head in l1ctl_server.clients */
+ struct llist_head list;
+ /* struct l1ctl_server we belong to */
+ struct l1ctl_server *server;
+ /* client's write queue */
+ struct osmo_wqueue wq;
+ /* logging context (used as prefix for messages) */
+ const char *log_prefix;
+ /* unique client ID */
+ unsigned int id;
+ /* some private data */
+ void *priv;
+};
+
+struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg);
+void l1ctl_server_free(struct l1ctl_server *server);
+
+int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg);
+void l1ctl_client_conn_close(struct l1ctl_client *client);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/logging.h b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h
new file mode 100644
index 00000000..ce149926
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <osmocom/core/logging.h>
+
+enum {
+ DAPP,
+ DL1C,
+ DL1D,
+ DTRXC,
+ DTRXD,
+ DSCH,
+ DSCHD,
+ DGPRS,
+};
+
+int trxcon_logging_init(void *tall_ctx, const char *category_mask);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h
new file mode 100644
index 00000000..2ad7a678
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h
@@ -0,0 +1,120 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/* PHYIF command type */
+enum trxcon_phyif_cmd_type {
+ TRXCON_PHYIF_CMDT_RESET,
+ TRXCON_PHYIF_CMDT_POWERON,
+ TRXCON_PHYIF_CMDT_POWEROFF,
+ TRXCON_PHYIF_CMDT_MEASURE,
+ TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ TRXCON_PHYIF_CMDT_SETFREQ_H1,
+ TRXCON_PHYIF_CMDT_SETSLOT,
+ TRXCON_PHYIF_CMDT_SETTA,
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETFREQ_H0 */
+struct trxcon_phyif_cmdp_setfreq_h0 {
+ uint16_t band_arfcn;
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETFREQ_H1 */
+struct trxcon_phyif_cmdp_setfreq_h1 {
+ uint8_t hsn;
+ uint8_t maio;
+ const uint16_t *ma;
+ unsigned int ma_len;
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETSLOT */
+struct trxcon_phyif_cmdp_setslot {
+ uint8_t tn;
+ uint8_t pchan; /* enum gsm_phys_chan_config */
+};
+
+/* param of TRXCON_PHYIF_CMDT_SETTA */
+struct trxcon_phyif_cmdp_setta {
+ int8_t ta; /* intentionally signed */
+};
+
+/* param of TRXCON_PHYIF_CMDT_MEASURE (command) */
+struct trxcon_phyif_cmdp_measure {
+ uint16_t band_arfcn;
+};
+
+/* param of TRXCON_PHYIF_CMDT_MEASURE (response) */
+struct trxcon_phyif_rspp_measure {
+ uint16_t band_arfcn;
+ int dbm;
+};
+
+struct trxcon_phyif_cmd {
+ enum trxcon_phyif_cmd_type type;
+ union {
+ struct trxcon_phyif_cmdp_setfreq_h0 setfreq_h0;
+ struct trxcon_phyif_cmdp_setfreq_h1 setfreq_h1;
+ struct trxcon_phyif_cmdp_setslot setslot;
+ struct trxcon_phyif_cmdp_setta setta;
+ struct trxcon_phyif_cmdp_measure measure;
+ } param;
+};
+
+struct trxcon_phyif_rsp {
+ enum trxcon_phyif_cmd_type type;
+ union {
+ struct trxcon_phyif_rspp_measure measure;
+ } param;
+};
+
+/* RTS.ind - Ready-to-Send indication */
+struct trxcon_phyif_rts_ind {
+ uint32_t fn;
+ uint8_t tn;
+};
+
+/* RTR.ind - Ready-to-Receive indicaton */
+struct trxcon_phyif_rtr_ind {
+ uint32_t fn;
+ uint8_t tn;
+};
+
+/* The probed lchan is active */
+#define TRXCON_PHYIF_RTR_F_ACTIVE (1 << 0)
+
+/* RTR.rsp - Ready-to-Receive response */
+struct trxcon_phyif_rtr_rsp {
+ uint32_t flags; /* see TRXCON_PHYIF_RTR_F_* above */
+};
+
+/* BURST.req - a burst to be transmitted */
+struct trxcon_phyif_burst_req {
+ uint32_t fn;
+ uint8_t tn;
+ uint8_t pwr;
+ const ubit_t *burst;
+ unsigned int burst_len;
+};
+
+/* BURST.ind - a received burst */
+struct trxcon_phyif_burst_ind {
+ uint32_t fn;
+ uint8_t tn;
+ int16_t toa256;
+ int8_t rssi;
+ const sbit_t *burst;
+ unsigned int burst_len;
+};
+
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br);
+int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *bi);
+
+int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts);
+int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind,
+ struct trxcon_phyif_rtr_rsp *rsp);
+
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd);
+int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp);
+void trxcon_phyif_close(void *phyif);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h
new file mode 100644
index 00000000..e564fd8e
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bb/trxcon/phyif.h>
+
+#define TRXC_BUF_SIZE 1024
+#define TRXD_BUF_SIZE 512
+
+enum trx_fsm_states {
+ TRX_STATE_OFFLINE = 0,
+ TRX_STATE_IDLE,
+ TRX_STATE_ACTIVE,
+ TRX_STATE_RSP_WAIT,
+};
+
+struct trx_instance {
+ struct osmo_fd trx_ofd_ctrl;
+ struct osmo_fd trx_ofd_data;
+
+ struct osmo_timer_list trx_ctrl_timer;
+ struct llist_head trx_ctrl_list;
+ struct osmo_fsm_inst *fi;
+ uint32_t fn_advance;
+
+ /* HACK: we need proper state machines */
+ uint32_t prev_state;
+ bool powered_up;
+
+ /* Some private data */
+ void *priv;
+};
+
+struct trx_ctrl_msg {
+ struct llist_head list;
+ char cmd[TRXC_BUF_SIZE];
+ int retry_cnt;
+ int critical;
+ int cmd_len;
+};
+
+struct trx_if_params {
+ const char *local_host;
+ const char *remote_host;
+ uint16_t base_port;
+ uint32_t fn_advance;
+ uint8_t instance;
+
+ struct osmo_fsm_inst *parent_fi;
+ uint32_t parent_term_event;
+ void *priv;
+};
+
+struct trx_instance *trx_if_open(const struct trx_if_params *params);
+void trx_if_close(struct trx_instance *trx);
+
+int trx_if_handle_phyif_burst_req(struct trx_instance *trx, const struct trxcon_phyif_burst_req *br);
+int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
new file mode 100644
index 00000000..ff54e785
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <stdint.h>
+
+struct osmo_fsm_inst;
+struct l1sched_state;
+struct l1gprs_state;
+struct msgb;
+
+struct trxcon_inst {
+ struct osmo_fsm_inst *fi;
+ unsigned int id;
+
+ /* Logging context for sched and l1c */
+ const char *log_prefix;
+
+ /* GSMTAP instance (optional) */
+ struct gsmtap_inst *gsmtap;
+
+ /* The L1 scheduler */
+ struct l1sched_state *sched;
+ /* GPRS state (MAC layer) */
+ struct l1gprs_state *gprs;
+
+ /* PHY interface (e.g. TRXC/TRXD) */
+ void *phyif;
+ /* L2 interface (e.g. L1CTL) */
+ void *l2if;
+
+ /* State specific data of trxcon_fsm */
+ void *fi_data;
+
+ /* L1 parameters */
+ struct {
+ uint16_t band_arfcn;
+ uint8_t tx_power;
+ uint8_t tsc; /* only valid for DCCH/PDCH */
+ int8_t ta;
+ } l1p;
+
+ /* PHY specific quirks */
+ struct {
+ /* FBSB timeout extension (in TDMA FNs) */
+ unsigned int fbsb_extend_fns;
+ } phy_quirks;
+};
+
+enum trxcon_log_cat {
+ TRXCON_LOGC_FSM, /* trxcon_fsm */
+ TRXCON_LOGC_L1C, /* L1CTL control */
+ TRXCON_LOGC_L1D, /* L1CTL data */
+ TRXCON_LOGC_SCHC, /* l1sched control */
+ TRXCON_LOGC_SCHD, /* l1sched data */
+ TRXCON_LOGC_GPRS, /* l1gprs logging */
+};
+
+void trxcon_set_log_cfg(const int *logc, unsigned int logc_num);
+
+struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id);
+void trxcon_inst_free(struct trxcon_inst *trxcon);
+
+int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg);
+int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg);
+void trxcon_l1ctl_close(struct trxcon_inst *trxcon);
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h
new file mode 100644
index 00000000..9eba4fde
--- /dev/null
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h
@@ -0,0 +1,169 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/fsm.h>
+
+extern struct osmo_fsm trxcon_fsm_def;
+
+enum trxcon_fsm_states {
+ TRXCON_ST_RESET,
+ TRXCON_ST_FULL_POWER_SCAN,
+ TRXCON_ST_FBSB_SEARCH,
+ TRXCON_ST_BCCH_CCCH,
+ TRXCON_ST_DEDICATED,
+ TRXCON_ST_PACKET_DATA,
+};
+
+enum trxcon_fsm_events {
+ TRXCON_EV_PHYIF_FAILURE,
+ TRXCON_EV_L2IF_FAILURE,
+ TRXCON_EV_RESET_FULL_REQ,
+ TRXCON_EV_RESET_SCHED_REQ,
+ TRXCON_EV_FULL_POWER_SCAN_REQ,
+ TRXCON_EV_FULL_POWER_SCAN_RES,
+ TRXCON_EV_FBSB_SEARCH_REQ,
+ TRXCON_EV_FBSB_SEARCH_RES,
+ TRXCON_EV_SET_CCCH_MODE_REQ,
+ TRXCON_EV_SET_TCH_MODE_REQ,
+ TRXCON_EV_SET_PHY_CONFIG_REQ,
+ TRXCON_EV_TX_ACCESS_BURST_REQ,
+ TRXCON_EV_TX_ACCESS_BURST_CNF,
+ TRXCON_EV_UPDATE_SACCH_CACHE_REQ,
+ TRXCON_EV_DCH_EST_REQ,
+ TRXCON_EV_DCH_REL_REQ,
+ TRXCON_EV_TX_DATA_REQ,
+ TRXCON_EV_TX_DATA_CNF,
+ TRXCON_EV_RX_DATA_IND,
+ TRXCON_EV_CRYPTO_REQ,
+ TRXCON_EV_GPRS_UL_TBF_CFG_REQ, /* param: L1CTL msgb */
+ TRXCON_EV_GPRS_DL_TBF_CFG_REQ, /* param: L1CTL msgb */
+ TRXCON_EV_GPRS_UL_BLOCK_REQ, /* param: L1CTL msgb */
+};
+
+/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */
+struct trxcon_param_full_power_scan_req {
+ uint16_t band_arfcn_start;
+ uint16_t band_arfcn_stop;
+};
+
+/* param of TRXCON_EV_FULL_POWER_SCAN_RES */
+struct trxcon_param_full_power_scan_res {
+ uint16_t band_arfcn;
+ int dbm;
+};
+
+/* param of TRXCON_EV_FBSB_SEARCH_REQ */
+struct trxcon_param_fbsb_search_req {
+ uint16_t band_arfcn;
+ uint16_t timeout_fns; /* in TDMA Fn periods */
+ uint8_t pchan_config;
+};
+
+/* param of TRXCON_EV_SET_{CCCH,TCH}_MODE_REQ */
+struct trxcon_param_set_ccch_tch_mode_req {
+ uint8_t mode;
+ struct {
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
+ bool applied;
+};
+
+/* param of TRXCON_EV_SET_PHY_CONFIG_REQ */
+struct trxcon_param_set_phy_config_req {
+ enum {
+ TRXCON_PHY_CFGT_PCHAN_COMB,
+ TRXCON_PHY_CFGT_TX_PARAMS,
+ } type;
+ union {
+ struct {
+ uint8_t tn;
+ uint8_t pchan;
+ } pchan_comb;
+ struct {
+ uint8_t timing_advance;
+ uint8_t tx_power;
+ } tx_params;
+ };
+};
+
+/* param of TRXCON_EV_TX_DATA_REQ */
+struct trxcon_param_tx_data_req {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_TX_DATA_CNF */
+struct trxcon_param_tx_data_cnf {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_RX_DATA_IND */
+struct trxcon_param_rx_data_ind {
+ bool traffic;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+ int16_t toa256;
+ int8_t rssi;
+ int n_errors;
+ int n_bits_total;
+ size_t data_len;
+ const uint8_t *data;
+};
+
+/* param of TRXCON_EV_TX_ACCESS_BURST_REQ */
+struct trxcon_param_tx_access_burst_req {
+ uint8_t chan_nr;
+ uint8_t link_id;
+ uint8_t offset;
+ uint8_t synch_seq;
+ uint16_t ra;
+ bool is_11bit;
+};
+
+/* param of TRXCON_EV_TX_ACCESS_BURST_CNF */
+struct trxcon_param_tx_access_burst_cnf {
+ uint16_t band_arfcn;
+ uint32_t frame_nr;
+};
+
+/* param of TRXCON_EV_DCH_EST_REQ */
+struct trxcon_param_dch_est_req {
+ uint8_t chan_nr;
+ uint8_t tch_mode;
+ uint8_t tsc;
+
+ bool hopping;
+ union {
+ struct { /* hopping=false */
+ uint16_t band_arfcn;
+ } h0;
+ struct { /* hopping=true */
+ uint8_t hsn;
+ uint8_t maio;
+ uint8_t n;
+ uint16_t ma[64];
+ } h1;
+ };
+};
+
+/* param of TRXCON_EV_CRYPTO_REQ */
+struct trxcon_param_crypto_req {
+ uint8_t chan_nr;
+ uint8_t a5_algo; /* 0 is A5/0 */
+ uint8_t key_len;
+ const uint8_t *key;
+};
diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c
deleted file mode 100644
index e722624c..00000000
--- a/src/host/trxcon/l1ctl.c
+++ /dev/null
@@ -1,905 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * GSM L1 control interface handlers
- *
- * (C) 2014 by Sylvain Munaut <tnt@246tNt.com>
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-
-#include "logging.h"
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-
-#include "trx_if.h"
-#include "sched_trx.h"
-
-static const char *arfcn2band_name(uint16_t arfcn)
-{
- enum gsm_band band;
-
- if (gsm_arfcn2band_rc(arfcn, &band) < 0)
- return "(invalid)";
-
- return gsm_band_name(band);
-}
-
-static struct msgb *l1ctl_alloc_msg(uint8_t msg_type)
-{
- struct l1ctl_hdr *l1h;
- struct msgb *msg;
-
- /**
- * Each L1CTL message gets its own length pushed in front
- * before sending. This is why we need this small headroom.
- */
- msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_MSG_LEN_FIELD,
- L1CTL_MSG_LEN_FIELD, "l1ctl_tx_msg");
- if (!msg) {
- LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n");
- return NULL;
- }
-
- msg->l1h = msgb_put(msg, sizeof(*l1h));
- l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->msg_type = msg_type;
-
- return msg;
-}
-
-int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn,
- int dbm, int last)
-{
- struct l1ctl_pm_conf *pmc;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_PM_CONF);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send PM Conf (%s %d = %d dBm)\n",
- arfcn2band_name(band_arfcn),
- band_arfcn &~ ARFCN_FLAG_MASK, dbm);
-
- pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
- pmc->band_arfcn = htons(band_arfcn);
- pmc->pm[0] = dbm2rxlev(dbm);
- pmc->pm[1] = 0;
-
- if (last) {
- struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->flags |= L1CTL_F_DONE;
- }
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type)
-{
- struct msgb *msg;
- struct l1ctl_reset *res;
-
- msg = l1ctl_alloc_msg(L1CTL_RESET_IND);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send Reset Ind (%u)\n", type);
-
- res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
- res->type = type;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type)
-{
- struct msgb *msg;
- struct l1ctl_reset *res;
-
- msg = l1ctl_alloc_msg(L1CTL_RESET_CONF);
- if (!msg)
- return -ENOMEM;
-
- LOGP(DL1C, LOGL_DEBUG, "Send Reset Conf (%u)\n", type);
- res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
- res->type = type;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg, struct l1ctl_info_dl *dl_info)
-{
- size_t len = sizeof(struct l1ctl_info_dl);
- struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
-
- if (dl_info) /* Copy DL info provided by handler */
- memcpy(dl, dl_info, len);
- else /* Init DL info header */
- memset(dl, 0x00, len);
-
- return dl;
-}
-
-/* Fill in FBSB payload: BSIC and sync result */
-static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic)
-{
- struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
-
- LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n", result, bsic);
-
- conf->result = result;
- conf->bsic = bsic;
-
- return conf;
-}
-
-int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result,
- struct l1ctl_info_dl *dl_info, uint8_t bsic)
-{
- struct l1ctl_fbsb_conf *conf;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- put_dl_info_hdr(msg, dl_info);
- talloc_free(dl_info);
-
- conf = fbsb_conf_make(msg, result, bsic);
-
- /* FIXME: set proper value */
- conf->initial_freq_err = 0;
-
- /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
- l1l->fbsb_conf_sent = true;
-
- /* Abort FBSB expire timer */
- if (osmo_timer_pending(&l1l->fbsb_timer))
- osmo_timer_del(&l1l->fbsb_timer);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode)
-{
- struct l1ctl_ccch_mode_conf *conf;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf));
- conf->ccch_mode = mode;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-/**
- * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND.
- */
-int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
- uint8_t *l2, size_t l2_len, bool traffic)
-{
- struct msgb *msg;
- uint8_t *msg_l2;
-
- msg = l1ctl_alloc_msg(traffic ?
- L1CTL_TRAFFIC_IND : L1CTL_DATA_IND);
- if (msg == NULL)
- return -ENOMEM;
-
- put_dl_info_hdr(msg, data);
-
- /* Copy the L2 payload if preset */
- if (l2 && l2_len > 0) {
- msg_l2 = (uint8_t *) msgb_put(msg, l2_len);
- memcpy(msg_l2, l2, l2_len);
- }
-
- /* Put message to upper layers */
- return l1ctl_link_send(l1l, msg);
-}
-
-int l1ctl_tx_rach_conf(struct l1ctl_link *l1l,
- uint16_t band_arfcn, uint32_t fn)
-{
- struct l1ctl_info_dl *dl;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_RACH_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- dl = put_dl_info_hdr(msg, NULL);
- memset(dl, 0x00, sizeof(*dl));
-
- dl->band_arfcn = htons(band_arfcn);
- dl->frame_nr = htonl(fn);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-
-/**
- * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF.
- */
-int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
- struct l1ctl_info_dl *data, bool traffic)
-{
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(traffic ?
- L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF);
- if (msg == NULL)
- return -ENOMEM;
-
- /* Copy DL frame header from source message */
- put_dl_info_hdr(msg, data);
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode)
-{
- switch (mode) {
- /* TODO: distinguish extended BCCH */
- case CCCH_MODE_NON_COMBINED:
- case CCCH_MODE_NONE:
- return GSM_PCHAN_CCCH;
-
- case CCCH_MODE_COMBINED:
- return GSM_PCHAN_CCCH_SDCCH4;
- case CCCH_MODE_COMBINED_CBCH:
- return GSM_PCHAN_CCCH_SDCCH4_CBCH;
-
- default:
- LOGP(DL1C, LOGL_NOTICE, "Undandled CCCH mode (%u), "
- "assuming non-combined configuration\n", mode);
- return GSM_PCHAN_CCCH;
- }
-}
-
-/* FBSB expire timer */
-static void fbsb_timer_cb(void *data)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) data;
- struct l1ctl_info_dl *dl;
- struct msgb *msg;
-
- msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
- if (msg == NULL)
- return;
-
- LOGP(DL1C, LOGL_NOTICE, "FBSB timer fired for ARFCN %u\n", l1l->trx->band_arfcn &~ ARFCN_FLAG_MASK);
-
- dl = put_dl_info_hdr(msg, NULL);
-
- /* Fill in current ARFCN */
- dl->band_arfcn = htons(l1l->trx->band_arfcn);
-
- fbsb_conf_make(msg, 255, 0);
-
- /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */
- l1l->fbsb_conf_sent = true;
-
- l1ctl_link_send(l1l, msg);
-}
-
-static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config ch_config;
- struct l1ctl_fbsb_req *fbsb;
- uint16_t band_arfcn;
- uint16_t timeout;
- int rc = 0;
-
- fbsb = (struct l1ctl_fbsb_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*fbsb)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- ch_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode);
- band_arfcn = ntohs(fbsb->band_arfcn);
- timeout = ntohs(fbsb->timeout);
-
- LOGP(DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n",
- arfcn2band_name(band_arfcn),
- band_arfcn &~ ARFCN_FLAG_MASK);
-
- /* Reset scheduler and clock counter */
- sched_trx_reset(l1l->trx, true);
-
- /* Configure a single timeslot */
- sched_trx_configure_ts(l1l->trx, 0, ch_config);
-
- /* Ask SCH handler to send L1CTL_FBSB_CONF */
- l1l->fbsb_conf_sent = false;
-
- /* Only if current ARFCN differs */
- if (l1l->trx->band_arfcn != band_arfcn) {
- /* Update current ARFCN */
- l1l->trx->band_arfcn = band_arfcn;
-
- /* Tune transceiver to required ARFCN */
- trx_if_cmd_rxtune(l1l->trx, band_arfcn);
- trx_if_cmd_txtune(l1l->trx, band_arfcn);
- }
-
- /* Transceiver might have been powered on before, e.g.
- * in case of sending L1CTL_FBSB_REQ due to signal loss. */
- if (!l1l->trx->powered_up)
- trx_if_cmd_poweron(l1l->trx);
-
- /* Start FBSB expire timer */
- l1l->fbsb_timer.data = l1l;
- l1l->fbsb_timer.cb = fbsb_timer_cb;
- LOGP(DL1C, LOGL_INFO, "Starting FBSB timer %u ms\n", timeout * FRAME_DURATION_uS / 1000);
- osmo_timer_schedule(&l1l->fbsb_timer, 0,
- timeout * FRAME_DURATION_uS);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- uint16_t band_arfcn_start, band_arfcn_stop;
- struct l1ctl_pm_req *pmr;
- int rc = 0;
-
- pmr = (struct l1ctl_pm_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*pmr)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short PM Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- band_arfcn_start = ntohs(pmr->range.band_arfcn_from);
- band_arfcn_stop = ntohs(pmr->range.band_arfcn_to);
-
- LOGP(DL1C, LOGL_NOTICE, "Received power measurement "
- "request (%s: %d -> %d)\n",
- arfcn2band_name(band_arfcn_start),
- band_arfcn_start &~ ARFCN_FLAG_MASK,
- band_arfcn_stop &~ ARFCN_FLAG_MASK);
-
- /* Send measurement request to transceiver */
- rc = trx_if_cmd_measure(l1l->trx, band_arfcn_start, band_arfcn_stop);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_reset *res;
- int rc = 0;
-
- res = (struct l1ctl_reset *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*res)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- LOGP(DL1C, LOGL_NOTICE, "Received reset request (%u)\n",
- res->type);
-
- switch (res->type) {
- case L1CTL_RES_T_FULL:
- /* TODO: implement trx_if_reset() */
- trx_if_cmd_poweroff(l1l->trx);
- trx_if_cmd_echo(l1l->trx);
-
- /* Fall through */
- case L1CTL_RES_T_SCHED:
- sched_trx_reset(l1l->trx, true);
- break;
- default:
- LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n");
- goto exit;
- }
-
- /* Confirm */
- rc = l1ctl_tx_reset_conf(l1l, res->type);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h;
-
- LOGP(DL1C, LOGL_NOTICE, "Recv Echo Req\n");
- LOGP(DL1C, LOGL_NOTICE, "Send Echo Conf\n");
-
- /* Nothing to do, just send it back */
- l1h = (struct l1ctl_hdr *) msg->l1h;
- l1h->msg_type = L1CTL_ECHO_CONF;
- msg->data = msg->l1h;
-
- return l1ctl_link_send(l1l, msg);
-}
-
-static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config ch_config;
- struct l1ctl_ccch_mode_req *req;
- struct trx_ts *ts;
- int rc = 0;
-
- req = (struct l1ctl_ccch_mode_req *) msg->l1h;
- if (msgb_l1len(msg) < sizeof(*req)) {
- LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n",
- msgb_l1len(msg));
- rc = -EINVAL;
- goto exit;
- }
-
- LOGP(DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n",
- req->ccch_mode); /* TODO: add value-string for ccch_mode */
-
- /* Make sure that TS0 is allocated and configured */
- ts = l1l->trx->ts_list[0];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DL1C, LOGL_ERROR, "TS0 is not configured");
- rc = -EINVAL;
- goto exit;
- }
-
- /* Choose corresponding channel combination */
- ch_config = l1ctl_ccch_mode2pchan_config(req->ccch_mode);
-
- /* Do nothing if the current mode matches required */
- if (ts->mf_layout->chan_config != ch_config)
- rc = sched_trx_configure_ts(l1l->trx, 0, ch_config);
-
- /* Confirm reconfiguration */
- if (!rc)
- rc = l1ctl_tx_ccch_mode_conf(l1l, req->ccch_mode);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg, bool ext)
-{
- struct l1ctl_ext_rach_req *ext_req;
- struct l1ctl_rach_req *req;
- struct l1ctl_info_ul *ul;
- struct trx_ts_prim *prim;
- size_t len;
- int rc;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
-
- /* Is it extended (11-bit) RACH or not? */
- if (ext) {
- ext_req = (struct l1ctl_ext_rach_req *) ul->payload;
- ext_req->offset = ntohs(ext_req->offset);
- ext_req->ra11 = ntohs(ext_req->ra11);
- len = sizeof(*ext_req);
-
- LOGP(DL1C, LOGL_NOTICE, "Received extended (11-bit) RACH request "
- "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n",
- ext_req->offset, ext_req->synch_seq, ext_req->ra11);
- } else {
- req = (struct l1ctl_rach_req *) ul->payload;
- req->offset = ntohs(req->offset);
- len = sizeof(*req);
-
- LOGP(DL1C, LOGL_NOTICE, "Received regular (8-bit) RACH request "
- "(offset=%u, ra=0x%02x)\n", req->offset, req->ra);
- }
-
- /* The controlling L1CTL side always does include the UL info header,
- * but may leave it empty. We assume RACH is on TS0 in this case. */
- if (ul->chan_nr == 0x00) {
- LOGP(DL1C, LOGL_NOTICE, "The UL info header is empty, "
- "assuming RACH is on TS0\n");
- ul->chan_nr = RSL_CHAN_RACH;
- }
-
- /* Init a new primitive */
- rc = sched_prim_init(l1l->trx, &prim, len, ul->chan_nr, ul->link_id);
- if (rc)
- goto exit;
-
- /**
- * Push this primitive to the transmit queue.
- * Indicated timeslot needs to be configured.
- */
- rc = sched_prim_push(l1l->trx, prim, ul->chan_nr);
- if (rc) {
- talloc_free(prim);
- goto exit;
- }
-
- /* Fill in the payload */
- memcpy(prim->payload, ul->payload, len);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_proc_est_req_h0(struct trx_instance *trx, struct l1ctl_h0 *h)
-{
- uint16_t band_arfcn;
- int rc = 0;
-
- band_arfcn = ntohs(h->band_arfcn);
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a single "
- "ARFCN=%u channel\n", band_arfcn &~ ARFCN_FLAG_MASK);
-
- /* Do we need to retune? */
- if (trx->band_arfcn == band_arfcn)
- return 0;
-
- /* Tune transceiver to required ARFCN */
- rc |= trx_if_cmd_rxtune(trx, band_arfcn);
- rc |= trx_if_cmd_txtune(trx, band_arfcn);
- if (rc)
- return rc;
-
- /* Update current ARFCN */
- trx->band_arfcn = band_arfcn;
-
- return 0;
-}
-
-static int l1ctl_proc_est_req_h1(struct trx_instance *trx, struct l1ctl_h1 *h)
-{
- int rc;
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a Frequency "
- "Hopping (hsn=%u, maio=%u, chans=%u) channel\n",
- h->hsn, h->maio, h->n);
-
- /* No channels?!? */
- if (!h->n) {
- LOGP(DL1C, LOGL_ERROR, "No channels in mobile allocation?!?\n");
- return -EINVAL;
- }
-
- /* Forward hopping parameters to TRX */
- rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, h->ma, h->n);
- if (rc)
- return rc;
-
- /**
- * TODO: update the state of trx_instance somehow
- * in order to indicate that it is in hopping mode...
- */
- return 0;
-}
-
-static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- enum gsm_phys_chan_config config;
- struct l1ctl_dm_est_req *est_req;
- struct l1ctl_info_ul *ul;
- struct trx_ts *ts;
- uint8_t chan_nr, tn;
- int rc;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- est_req = (struct l1ctl_dm_est_req *) ul->payload;
-
- chan_nr = ul->chan_nr;
- tn = chan_nr & 0x07;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ "
- "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n",
- tn, chan_nr, est_req->tsc, est_req->tch_mode);
-
- /* Determine channel config */
- config = sched_trx_chan_nr2pchan_config(chan_nr);
- if (config == GSM_PCHAN_NONE) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't determine channel config\n");
- rc = -EINVAL;
- goto exit;
- }
-
- /* Frequency hopping? */
- if (est_req->h)
- rc = l1ctl_proc_est_req_h1(l1l->trx, &est_req->h1);
- else /* Single ARFCN */
- rc = l1ctl_proc_est_req_h0(l1l->trx, &est_req->h0);
- if (rc)
- goto exit;
-
- /* Update TSC (Training Sequence Code) */
- l1l->trx->tsc = est_req->tsc;
-
- /* Configure requested TS */
- rc = sched_trx_configure_ts(l1l->trx, tn, config);
- ts = l1l->trx->ts_list[tn];
- if (rc) {
- rc = -EINVAL;
- goto exit;
- }
-
- /* Deactivate all lchans */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Activate only requested lchans */
- rc = sched_trx_set_lchans(ts, chan_nr, 1, est_req->tch_mode);
- if (rc) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n");
- rc = -EINVAL;
- goto exit;
- }
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ, "
- "switching back to CCCH\n");
-
- /* Reset scheduler */
- sched_trx_reset(l1l->trx, false);
-
- msgb_free(msg);
- return 0;
-}
-
-/**
- * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ.
- */
-static int l1ctl_rx_dt_req(struct l1ctl_link *l1l,
- struct msgb *msg, bool traffic)
-{
- struct l1ctl_info_ul *ul;
- struct trx_ts_prim *prim;
- uint8_t chan_nr, link_id;
- size_t payload_len;
- int rc;
-
- /* Extract UL frame header */
- ul = (struct l1ctl_info_ul *) msg->l1h;
-
- /* Calculate the payload len */
- msg->l2h = ul->payload;
- payload_len = msgb_l2len(msg);
-
- /* Obtain channel description */
- chan_nr = ul->chan_nr;
- link_id = ul->link_id & 0x40;
-
- LOGP(DL1D, LOGL_DEBUG, "Recv %s Req (chan_nr=0x%02x, "
- "link_id=0x%02x, len=%zu)\n", traffic ? "TRAFFIC" : "DATA",
- chan_nr, link_id, payload_len);
-
- /* Init a new primitive */
- rc = sched_prim_init(l1l->trx, &prim, payload_len,
- chan_nr, link_id);
- if (rc)
- goto exit;
-
- /* Push this primitive to transmit queue */
- rc = sched_prim_push(l1l->trx, prim, chan_nr);
- if (rc) {
- talloc_free(prim);
- goto exit;
- }
-
- /* Fill in the payload */
- memcpy(prim->payload, ul->payload, payload_len);
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_par_req *par_req;
- struct l1ctl_info_ul *ul;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- par_req = (struct l1ctl_par_req *) ul->payload;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_PARAM_REQ "
- "(ta=%d, tx_power=%u)\n", par_req->ta, par_req->tx_power);
-
- /* Instruct TRX to use new TA value */
- if (l1l->trx->ta != par_req->ta) {
- trx_if_cmd_setta(l1l->trx, par_req->ta);
- l1l->trx->ta = par_req->ta;
- }
-
- l1l->trx->tx_power = par_req->tx_power;
-
- msgb_free(msg);
- return 0;
-}
-
-static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_tch_mode_req *req;
- struct trx_lchan_state *lchan;
- struct trx_ts *ts;
- int i;
-
- req = (struct l1ctl_tch_mode_req *) msg->l1h;
-
- LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_TCH_MODE_REQ "
- "(tch_mode=%u, audio_mode=%u)\n", req->tch_mode, req->audio_mode);
-
- /* Iterate over timeslot list */
- for (i = 0; i < TRX_TS_COUNT; i++) {
- /* Timeslot is not allocated */
- ts = l1l->trx->ts_list[i];
- if (ts == NULL)
- continue;
-
- /* Timeslot is not configured */
- if (ts->mf_layout == NULL)
- continue;
-
- /* Iterate over all allocated lchans */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Set TCH mode */
- lchan->tch_mode = req->tch_mode;
- }
- }
-
- /* TODO: do we need to care about audio_mode? */
-
- msgb_free(msg);
- return 0;
-}
-
-static int l1ctl_rx_crypto_req(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_crypto_req *req;
- struct l1ctl_info_ul *ul;
- struct trx_ts *ts;
- uint8_t tn;
- int rc = 0;
-
- ul = (struct l1ctl_info_ul *) msg->l1h;
- req = (struct l1ctl_crypto_req *) ul->payload;
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n",
- req->algo, req->key_len);
-
- /* Determine TS index */
- tn = ul->chan_nr & 0x7;
-
- /* Make sure that required TS is allocated and configured */
- ts = l1l->trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DL1C, LOGL_ERROR, "TS %u is not configured\n", tn);
- rc = -EINVAL;
- goto exit;
- }
-
- /* Poke scheduler */
- rc = sched_trx_start_ciphering(ts, req->algo, req->key, req->key_len);
- if (rc) {
- LOGP(DL1C, LOGL_ERROR, "Couldn't configure ciphering\n");
- rc = -EINVAL;
- goto exit;
- }
-
-exit:
- msgb_free(msg);
- return rc;
-}
-
-int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg)
-{
- struct l1ctl_hdr *l1h;
-
- l1h = (struct l1ctl_hdr *) msg->l1h;
- msg->l1h = l1h->data;
-
- switch (l1h->msg_type) {
- case L1CTL_FBSB_REQ:
- return l1ctl_rx_fbsb_req(l1l, msg);
- case L1CTL_PM_REQ:
- return l1ctl_rx_pm_req(l1l, msg);
- case L1CTL_RESET_REQ:
- return l1ctl_rx_reset_req(l1l, msg);
- case L1CTL_ECHO_REQ:
- return l1ctl_rx_echo_req(l1l, msg);
- case L1CTL_CCCH_MODE_REQ:
- return l1ctl_rx_ccch_mode_req(l1l, msg);
- case L1CTL_RACH_REQ:
- return l1ctl_rx_rach_req(l1l, msg, false);
- case L1CTL_EXT_RACH_REQ:
- return l1ctl_rx_rach_req(l1l, msg, true);
- case L1CTL_DM_EST_REQ:
- return l1ctl_rx_dm_est_req(l1l, msg);
- case L1CTL_DM_REL_REQ:
- return l1ctl_rx_dm_rel_req(l1l, msg);
- case L1CTL_DATA_REQ:
- return l1ctl_rx_dt_req(l1l, msg, false);
- case L1CTL_TRAFFIC_REQ:
- return l1ctl_rx_dt_req(l1l, msg, true);
- case L1CTL_PARAM_REQ:
- return l1ctl_rx_param_req(l1l, msg);
- case L1CTL_TCH_MODE_REQ:
- return l1ctl_rx_tch_mode_req(l1l, msg);
- case L1CTL_CRYPTO_REQ:
- return l1ctl_rx_crypto_req(l1l, msg);
-
- /* Not (yet) handled messages */
- case L1CTL_NEIGH_PM_REQ:
- case L1CTL_DATA_TBF_REQ:
- case L1CTL_TBF_CFG_REQ:
- case L1CTL_DM_FREQ_REQ:
- case L1CTL_SIM_REQ:
- LOGP(DL1C, LOGL_NOTICE, "Ignoring unsupported message "
- "(type=%u)\n", l1h->msg_type);
- msgb_free(msg);
- return -ENOTSUP;
- default:
- LOGP(DL1C, LOGL_ERROR, "Unknown MSG type %u: %s\n", l1h->msg_type,
- osmo_hexdump(msgb_data(msg), msgb_length(msg)));
- msgb_free(msg);
- return -EINVAL;
- }
-}
-
-void l1ctl_shutdown_cb(struct l1ctl_link *l1l)
-{
- /* Abort FBSB expire timer */
- if (osmo_timer_pending(&l1l->fbsb_timer))
- osmo_timer_del(&l1l->fbsb_timer);
-}
diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h
deleted file mode 100644
index 48bbe097..00000000
--- a/src/host/trxcon/l1ctl.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <osmocom/core/msgb.h>
-
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-
-/* Event handlers */
-int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg);
-void l1ctl_shutdown_cb(struct l1ctl_link *l1l);
-
-int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result,
- struct l1ctl_info_dl *dl_info, uint8_t bsic);
-int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode);
-int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn,
- int dbm, int last);
-int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type);
-int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type);
-
-int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data,
- uint8_t *l2, size_t l2_len, bool traffic);
-int l1ctl_tx_dt_conf(struct l1ctl_link *l1l,
- struct l1ctl_info_dl *data, bool traffic);
-int l1ctl_tx_rach_conf(struct l1ctl_link *l1l,
- uint16_t band_arfcn, uint32_t fn);
diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c
deleted file mode 100644
index b7ea262a..00000000
--- a/src/host/trxcon/l1ctl_link.c
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * GSM L1 control socket (/tmp/osmocom_l2) handlers
- *
- * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <sys/un.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/write_queue.h>
-
-#include "trxcon.h"
-#include "logging.h"
-#include "l1ctl_link.h"
-#include "l1ctl.h"
-
-static struct value_string l1ctl_evt_names[] = {
- { 0, NULL } /* no events? */
-};
-
-static struct osmo_fsm_state l1ctl_fsm_states[] = {
- [L1CTL_STATE_IDLE] = {
- .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED),
- .name = "IDLE",
- },
- [L1CTL_STATE_CONNECTED] = {
- .out_state_mask = GEN_MASK(L1CTL_STATE_IDLE),
- .name = "CONNECTED",
- },
-};
-
-static struct osmo_fsm l1ctl_fsm = {
- .name = "l1ctl_link_fsm",
- .states = l1ctl_fsm_states,
- .num_states = ARRAY_SIZE(l1ctl_fsm_states),
- .log_subsys = DL1C,
- .event_names = l1ctl_evt_names,
-};
-
-static int l1ctl_link_read_cb(struct osmo_fd *bfd)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
- struct msgb *msg;
- uint16_t len;
- int rc;
-
- /* Attempt to read from socket */
- rc = read(bfd->fd, &len, L1CTL_MSG_LEN_FIELD);
- if (rc < L1CTL_MSG_LEN_FIELD) {
- LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n");
- if (rc >= 0)
- rc = -EIO;
- l1ctl_link_close_conn(l1l);
- return rc;
- }
-
- /* Check message length */
- len = ntohs(len);
- if (len > L1CTL_LENGTH) {
- LOGP(DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
- return -EINVAL;
- }
-
- /* Allocate a new msg */
- msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
- L1CTL_HEADROOM, "l1ctl_rx_msg");
- if (!msg) {
- LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n");
- return -ENOMEM;
- }
-
- msg->l1h = msgb_put(msg, len);
- rc = read(bfd->fd, msg->l1h, msgb_l1len(msg));
- if (rc != len) {
- LOGP(DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: "
- "%s\n", len, rc, strerror(errno));
- msgb_free(msg);
- return rc;
- }
-
- /* Debug print */
- LOGP(DL1D, LOGL_DEBUG, "RX: '%s'\n",
- osmo_hexdump(msg->data, msg->len));
-
- /* Call L1CTL handler */
- l1ctl_rx_cb(l1l, msg);
-
- return 0;
-}
-
-static int l1ctl_link_write_cb(struct osmo_fd *bfd, struct msgb *msg)
-{
- int len;
-
- if (bfd->fd <= 0)
- return -EINVAL;
-
- len = write(bfd->fd, msg->data, msg->len);
- if (len != msg->len) {
- LOGP(DL1D, LOGL_ERROR, "Failed to write data: "
- "written (%d) < msg_len (%d)\n", len, msg->len);
- return -1;
- }
-
- return 0;
-}
-
-/* Connection handler */
-static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags)
-{
- struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data;
- struct osmo_fd *conn_bfd = &l1l->wq.bfd;
- struct sockaddr_un un_addr;
- socklen_t len;
- int cfd;
-
- len = sizeof(un_addr);
- cfd = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
- if (cfd < 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to accept a new connection\n");
- return -1;
- }
-
- /* Check if we already have an active connection */
- if (conn_bfd->fd != -1) {
- LOGP(DL1C, LOGL_NOTICE, "A new connection rejected: "
- "we already have another active\n");
- close(cfd);
- return 0;
- }
-
- osmo_wqueue_init(&l1l->wq, 100);
- INIT_LLIST_HEAD(&conn_bfd->list);
-
- l1l->wq.write_cb = l1ctl_link_write_cb;
- l1l->wq.read_cb = l1ctl_link_read_cb;
- conn_bfd->when = BSC_FD_READ;
- conn_bfd->data = l1l;
- conn_bfd->fd = cfd;
-
- if (osmo_fd_register(conn_bfd) != 0) {
- LOGP(DL1C, LOGL_ERROR, "Failed to register new connection fd\n");
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
- return -1;
- }
-
- osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_CONNECT, l1l);
- osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_CONNECTED, 0, 0);
-
- LOGP(DL1C, LOGL_NOTICE, "L1CTL has a new connection\n");
-
- return 0;
-}
-
-int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg)
-{
- uint16_t *len;
-
- /* Debug print */
- LOGP(DL1D, LOGL_DEBUG, "TX: '%s'\n",
- osmo_hexdump(msg->data, msg->len));
-
- if (msg->l1h != msg->data)
- LOGP(DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
-
- /* Prepend 16-bit length before sending */
- len = (uint16_t *) msgb_push(msg, L1CTL_MSG_LEN_FIELD);
- *len = htons(msg->len - L1CTL_MSG_LEN_FIELD);
-
- if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) {
- LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
- msgb_free(msg);
- return -EIO;
- }
-
- return 0;
-}
-
-int l1ctl_link_close_conn(struct l1ctl_link *l1l)
-{
- struct osmo_fd *conn_bfd = &l1l->wq.bfd;
-
- if (conn_bfd->fd <= 0)
- return -EINVAL;
-
- /* Close connection socket */
- osmo_fd_unregister(conn_bfd);
- close(conn_bfd->fd);
- conn_bfd->fd = -1;
-
- /* Clear pending messages */
- osmo_wqueue_clear(&l1l->wq);
-
- osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_DISCONNECT, l1l);
- osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_IDLE, 0, 0);
-
- return 0;
-}
-
-struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path)
-{
- struct l1ctl_link *l1l;
- struct osmo_fd *bfd;
- int rc;
-
- LOGP(DL1C, LOGL_NOTICE, "Init L1CTL link (%s)\n", sock_path);
-
- l1l = talloc_zero(tall_ctx, struct l1ctl_link);
- if (!l1l) {
- LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n");
- return NULL;
- }
-
- /* Allocate a new dedicated state machine */
- l1l->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l,
- NULL, LOGL_DEBUG, "l1ctl_link");
- if (l1l->fsm == NULL) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance "
- "of FSM '%s'\n", l1ctl_fsm.name);
- talloc_free(l1l);
- return NULL;
- }
-
- /* Create a socket and bind handlers */
- bfd = &l1l->listen_bfd;
- rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path,
- OSMO_SOCK_F_BIND);
- if (rc < 0) {
- LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n",
- strerror(errno));
- osmo_fsm_inst_free(l1l->fsm);
- talloc_free(l1l);
- return NULL;
- }
-
- /* Bind shutdown handler */
- l1l->shutdown_cb = l1ctl_shutdown_cb;
-
- /* Bind connection handler */
- bfd->cb = l1ctl_link_accept;
- bfd->when = BSC_FD_READ;
- bfd->data = l1l;
-
- /**
- * To be able to accept first connection and
- * drop others, it should be set to -1
- */
- l1l->wq.bfd.fd = -1;
-
- return l1l;
-}
-
-void l1ctl_link_shutdown(struct l1ctl_link *l1l)
-{
- struct osmo_fd *listen_bfd;
-
- /* May be unallocated due to init error */
- if (!l1l)
- return;
-
- LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n");
-
- /* Call shutdown callback */
- if (l1l->shutdown_cb != NULL)
- l1l->shutdown_cb(l1l);
-
- listen_bfd = &l1l->listen_bfd;
-
- /* Check if we have an established connection */
- if (l1l->wq.bfd.fd != -1)
- l1ctl_link_close_conn(l1l);
-
- /* Unbind listening socket */
- if (listen_bfd->fd != -1) {
- osmo_fd_unregister(listen_bfd);
- close(listen_bfd->fd);
- listen_bfd->fd = -1;
- }
-
- osmo_fsm_inst_free(l1l->fsm);
- talloc_free(l1l);
-}
-
-static __attribute__((constructor)) void on_dso_load(void)
-{
- OSMO_ASSERT(osmo_fsm_register(&l1ctl_fsm) == 0);
-}
diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h
deleted file mode 100644
index a333e407..00000000
--- a/src/host/trxcon/l1ctl_link.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/fsm.h>
-
-#define L1CTL_LENGTH 256
-#define L1CTL_HEADROOM 32
-
-/**
- * Each L1CTL message gets its own length pushed
- * as two bytes in front before sending.
- */
-#define L1CTL_MSG_LEN_FIELD 2
-
-/* Forward declaration to avoid mutual include */
-struct trx_instance;
-
-enum l1ctl_fsm_states {
- L1CTL_STATE_IDLE = 0,
- L1CTL_STATE_CONNECTED,
-};
-
-struct l1ctl_link {
- struct osmo_fsm_inst *fsm;
- struct osmo_fd listen_bfd;
- struct osmo_wqueue wq;
-
- /* Bind TRX instance */
- struct trx_instance *trx;
-
- /* L1CTL handlers specific */
- struct osmo_timer_list fbsb_timer;
- bool fbsb_conf_sent;
-
- /* Shutdown callback */
- void (*shutdown_cb)(struct l1ctl_link *l1l);
-};
-
-struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path);
-void l1ctl_link_shutdown(struct l1ctl_link *l1l);
-
-int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg);
-int l1ctl_link_close_conn(struct l1ctl_link *l1l);
diff --git a/src/host/trxcon/l1ctl_proto.h b/src/host/trxcon/l1ctl_proto.h
deleted file mode 120000
index 75862bae..00000000
--- a/src/host/trxcon/l1ctl_proto.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../include/l1ctl_proto.h \ No newline at end of file
diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h
deleted file mode 100644
index 152c3467..00000000
--- a/src/host/trxcon/logging.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include <osmocom/core/logging.h>
-
-#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH:DSCHD"
-
-enum {
- DAPP,
- DL1C,
- DL1D,
- DTRX,
- DTRXD,
- DSCH,
- DSCHD,
-};
-
-int trx_log_init(void *tall_ctx, const char *category_mask);
diff --git a/src/host/trxcon/m4/.gitkeep b/src/host/trxcon/m4/.gitkeep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/host/trxcon/m4/.gitkeep
diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c
deleted file mode 100644
index 66477b24..00000000
--- a/src/host/trxcon/sched_clck.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: clock synchronization
- *
- * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
- * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * (C) 2015 by Harald Welte <laforge@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 <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/timer_compat.h>
-#include <osmocom/gsm/a5.h>
-
-#include "scheduler.h"
-#include "logging.h"
-#include "trx_if.h"
-
-#define MAX_FN_SKEW 50
-#define TRX_LOSS_FRAMES 400
-
-static void sched_clck_tick(void *data)
-{
- struct trx_sched *sched = (struct trx_sched *) data;
- struct timespec tv_now, *tv_clock, elapsed;
- int64_t elapsed_us;
- const struct timespec frame_duration = { .tv_sec = 0, .tv_nsec = FRAME_DURATION_uS * 1000 };
-
- /* Check if transceiver is still alive */
- if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) {
- LOGP(DSCH, LOGL_DEBUG, "No more clock from transceiver\n");
- sched->state = SCH_CLCK_STATE_WAIT;
-
- return;
- }
-
- /* Get actual / previous frame time */
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- tv_clock = &sched->clock;
-
- timespecsub(&tv_now, tv_clock, &elapsed);
- elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000);
-
- /* If someone played with clock, or if the process stalled */
- if (elapsed_us > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) {
- LOGP(DSCH, LOGL_NOTICE, "PC clock skew: "
- "elapsed uS %" PRId64 "\n", elapsed_us);
-
- sched->state = SCH_CLCK_STATE_WAIT;
-
- return;
- }
-
- /* Schedule next FN clock */
- while (elapsed_us > FRAME_DURATION_uS / 2) {
- timespecadd(tv_clock, &frame_duration, tv_clock);
- elapsed_us -= FRAME_DURATION_uS;
-
- sched->fn_counter_proc = TDMA_FN_INC(sched->fn_counter_proc);
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
- }
-
- osmo_timer_schedule(&sched->clock_timer, 0,
- FRAME_DURATION_uS - elapsed_us);
-}
-
-static void sched_clck_correct(struct trx_sched *sched,
- struct timespec *tv_now, uint32_t fn)
-{
- sched->fn_counter_proc = fn;
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
-
- /* Schedule first FN clock */
- sched->clock = *tv_now;
- memset(&sched->clock_timer, 0, sizeof(sched->clock_timer));
-
- sched->clock_timer.cb = sched_clck_tick;
- sched->clock_timer.data = sched;
- osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
-}
-
-int sched_clck_handle(struct trx_sched *sched, uint32_t fn)
-{
- struct timespec tv_now, *tv_clock, elapsed;
- int64_t elapsed_us, elapsed_fn;
-
- /* Reset lost counter */
- sched->fn_counter_lost = 0;
-
- /* Get actual / previous frame time */
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- tv_clock = &sched->clock;
-
- /* If this is the first CLCK IND */
- if (sched->state == SCH_CLCK_STATE_WAIT) {
- sched_clck_correct(sched, &tv_now, fn);
-
- LOGP(DSCH, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn);
- sched->state = SCH_CLCK_STATE_OK;
-
- return 0;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Clock indication: fn=%u\n", fn);
-
- osmo_timer_del(&sched->clock_timer);
-
- /* Calculate elapsed time / frames since last processed fn */
- timespecsub(&tv_now, tv_clock, &elapsed);
- elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000);
- elapsed_fn = TDMA_FN_SUB(fn, sched->fn_counter_proc);
-
- if (elapsed_fn >= 135774)
- elapsed_fn -= GSM_HYPERFRAME;
-
- /* Check for max clock skew */
- if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
- LOGP(DSCH, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
- "new fn=%u\n", sched->fn_counter_proc, fn);
-
- sched_clck_correct(sched, &tv_now, fn);
- return 0;
- }
-
- LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n",
- elapsed_fn * FRAME_DURATION_uS - elapsed_us);
-
- /* Too many frames have been processed already */
- if (elapsed_fn < 0) {
- struct timespec duration;
- /**
- * Set clock to the time or last FN should
- * have been transmitted
- */
- duration.tv_nsec = (0 - elapsed_fn) * FRAME_DURATION_uS * 1000;
- duration.tv_sec = duration.tv_nsec / 1000000000;
- duration.tv_nsec = duration.tv_nsec % 1000000000;
- timespecadd(&tv_now, &duration, tv_clock);
-
- /* Set time to the time our next FN has to be transmitted */
- osmo_timer_schedule(&sched->clock_timer, 0,
- FRAME_DURATION_uS * (1 - elapsed_fn));
-
- return 0;
- }
-
- /* Transmit what we still need to transmit */
- while (fn != sched->fn_counter_proc) {
- sched->fn_counter_proc = TDMA_FN_INC(sched->fn_counter_proc);
-
- /* Call frame callback */
- if (sched->clock_cb)
- sched->clock_cb(sched);
- }
-
- /* Schedule next FN to be transmitted */
- *tv_clock = tv_now;
- osmo_timer_schedule(&sched->clock_timer, 0, FRAME_DURATION_uS);
-
- return 0;
-}
-
-void sched_clck_reset(struct trx_sched *sched)
-{
- /* Reset internal state */
- sched->state = SCH_CLCK_STATE_WAIT;
-
- /* Stop clock timer */
- osmo_timer_del(&sched->clock_timer);
-
- /* Flush counters */
- sched->fn_counter_proc = 0;
- sched->fn_counter_lost = 0;
-}
diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c
deleted file mode 100644
index 615d81c9..00000000
--- a/src/host/trxcon/sched_lchan_common.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: common routines for lchan handlers
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <talloc.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/codec/codec.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
-const uint8_t sched_nb_training_bits[8][26] = {
- {
- 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
- 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1,
- },
- {
- 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
- 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
- },
- {
- 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
- 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
- },
- {
- 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
- 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
- },
- {
- 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
- 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
- },
- {
- 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
- 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
- },
- {
- 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
- 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
- },
- {
- 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,
- 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
- },
-};
-
-int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
- int bit_error_count, bool dec_failed, bool traffic)
-{
- const struct trx_lchan_desc *lchan_desc;
- struct l1ctl_info_dl dl_hdr;
- int dbm_avg;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- /* Fill in known downlink info */
- dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index;
- dl_hdr.link_id = lchan_desc->link_id;
- dl_hdr.band_arfcn = htons(trx->band_arfcn);
- dl_hdr.frame_nr = htonl(lchan->rx_first_fn);
- dl_hdr.num_biterr = bit_error_count;
-
- /* Convert average RSSI to RX level */
- if (lchan->meas.num) {
- /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */
- dbm_avg = lchan->meas.rssi_sum / lchan->meas.num;
- dl_hdr.rx_level = dbm2rxlev(dbm_avg);
- } else {
- /* No measurements, assuming the worst */
- dl_hdr.rx_level = 0;
- }
-
- /* FIXME: set proper values */
- dl_hdr.snr = 0;
-
- /* Mark frame as broken if so */
- dl_hdr.fire_crc = dec_failed ? 2 : 0;
-
- /* Put a packet to higher layers */
- l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, traffic);
-
- return 0;
-}
-
-int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, bool traffic)
-{
- const struct trx_lchan_desc *lchan_desc;
- struct l1ctl_info_dl dl_hdr;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- /* Zero-initialize DL header, because we don't set all fields */
- memset(&dl_hdr, 0x00, sizeof(struct l1ctl_info_dl));
-
- /* Fill in known downlink info */
- dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index;
- dl_hdr.link_id = lchan_desc->link_id;
- dl_hdr.band_arfcn = htons(trx->band_arfcn);
- dl_hdr.frame_nr = htonl(fn);
-
- l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, traffic);
-
- return 0;
-}
-
-/**
- * Composes a bad frame indication message
- * according to the current tch_mode.
- *
- * @param l2 Caller-allocated byte array
- * @param lchan Logical channel to generate BFI for
- * @return How much bytes were written
- */
-size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan)
-{
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- if (lchan->type == TRXC_TCHF) { /* Full Rate */
- memset(l2, 0x00, GSM_FR_BYTES);
- l2[0] = 0xd0;
- return GSM_FR_BYTES;
- } else { /* Half Rate */
- memset(l2 + 1, 0x00, GSM_HR_BYTES);
- l2[0] = 0x70; /* F = 0, FT = 111 */
- return GSM_HR_BYTES + 1;
- }
- case GSM48_CMODE_SPEECH_EFR: /* Enhanced Full Rate */
- memset(l2, 0x00, GSM_EFR_BYTES);
- l2[0] = 0xc0;
- return GSM_EFR_BYTES;
- case GSM48_CMODE_SPEECH_AMR: /* Adaptive Multi Rate */
- /* FIXME: AMR is not implemented yet */
- return 0;
- case GSM48_CMODE_SIGN:
- LOGP(DSCH, LOGL_ERROR, "BFI is not allowed in signalling mode\n");
- return 0;
- default:
- LOGP(DSCH, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return 0;
- }
-}
diff --git a/src/host/trxcon/sched_lchan_pdtch.c b/src/host/trxcon/sched_lchan_pdtch.c
deleted file mode 100644
index 733e5741..00000000
--- a/src/host/trxcon/sched_lchan_pdtch.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- const struct trx_lchan_desc *lchan_desc;
- uint8_t l2[GPRS_L2_MAX_LEN], *mask;
- int n_errors, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint32_t *first_fn;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Packet data received on %s: "
- "fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x0;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update measurements */
- lchan->meas.toa256_sum += toa256;
- lchan->meas.rssi_sum += rssi;
- lchan->meas.num++;
-
- /* Copy burst to buffer of 4 bursts */
- offset = buffer + bid * 116;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- return -1;
- }
-
- /* Attempt to decode */
- rc = gsm0503_pdtch_decode(l2, buffer,
- NULL, &n_errors, &n_bits_total);
- if (rc < 0) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad packet data frame "
- "at fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- }
-
- /* Determine L2 length */
- l2_len = rc > 0 ? rc : 0;
-
- /* Send a L2 frame to the higher layers */
- sched_send_dt_ind(trx, ts, lchan,
- l2, l2_len, n_errors, rc < 0, true);
-
- return 0;
-}
-
-
-int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (bid > 0) {
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
- else
- return 0;
- }
-
- /* Encode payload */
- rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload,
- lchan->prim->payload_len);
- if (rc < 0) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, fn, true);
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c
deleted file mode 100644
index 5d1f3ab9..00000000
--- a/src/host/trxcon/sched_lchan_rach.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */
-#define RACH_EXT_TAIL_BITS_LEN 8
-#define RACH_SYNCH_SEQ_LEN 41
-#define RACH_PAYLOAD_LEN 36
-
-/* Extended tail bits (BN0..BN7) */
-static const ubit_t rach_ext_tail_bits[] = {
- 0, 0, 1, 1, 1, 0, 1, 0,
-};
-
-/* Synchronization (training) sequence types */
-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
-};
-
-/* Synchronization (training) sequence bits */
-static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
- [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
- [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
- [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
-};
-
-/* Synchronization (training) sequence names */
-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 },
-};
-
-/* Obtain a to-be-transmitted RACH burst */
-int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)
-{
- struct l1ctl_ext_rach_req *ext_req = NULL;
- struct l1ctl_rach_req *req = NULL;
- enum rach_synch_seq_t synch_seq;
- uint8_t burst[GSM_BURST_LEN];
- uint8_t *burst_ptr = burst;
- uint8_t payload[36];
- int i, rc;
-
- /* Is it extended (11-bit) RACH or not? */
- if (PRIM_IS_RACH11(lchan->prim)) {
- ext_req = (struct l1ctl_ext_rach_req *) lchan->prim->payload;
- synch_seq = ext_req->synch_seq;
-
- /* Check requested synch. sequence */
- if (synch_seq >= RACH_SYNCH_SEQ_NUM) {
- LOGP(DSCHD, LOGL_ERROR, "Unknown RACH synch. sequence=0x%02x\n", synch_seq);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -ENOTSUP;
- }
-
- /* Delay sending according to offset value */
- if (ext_req->offset-- > 0)
- return 0;
-
- /* Encode extended (11-bit) payload */
- rc = gsm0503_rach_ext_encode(payload, ext_req->ra11, trx->bsic, true);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not encode extended RACH burst "
- "(ra=%u bsic=%u)\n", ext_req->ra11, trx->bsic);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return rc;
- }
- } else if (PRIM_IS_RACH8(lchan->prim)) {
- req = (struct l1ctl_rach_req *) lchan->prim->payload;
- synch_seq = RACH_SYNCH_SEQ_TS0;
-
- /* Delay sending according to offset value */
- if (req->offset-- > 0)
- return 0;
-
- /* Encode regular (8-bit) payload */
- rc = gsm0503_rach_ext_encode(payload, req->ra, trx->bsic, false);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst "
- "(ra=%u bsic=%u)\n", req->ra, trx->bsic);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return rc;
- }
- } else {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %zu or %zu), "
- "so dropping...\n", lchan->prim->payload_len,
- sizeof(*req), sizeof(*ext_req));
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
-
- /* BN0-7: extended tail bits */
- memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
- burst_ptr += RACH_EXT_TAIL_BITS_LEN;
-
- /* BN8-48: chosen synch. (training) sequence */
- for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
- *(burst_ptr++) = rach_synch_seq_bits[synch_seq][i] == '1';
-
- /* BN49-84: encrypted bits (the payload) */
- memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
- burst_ptr += RACH_PAYLOAD_LEN;
-
- /* BN85-156: tail bits & extended guard period */
- memset(burst_ptr, 0, burst + GSM_BURST_LEN - burst_ptr);
-
- LOGP(DSCHD, LOGL_NOTICE, "Transmitting %s RACH (%s) on fn=%u, tn=%u, lchan=%s\n",
- PRIM_IS_RACH11(lchan->prim) ? "extended (11-bit)" : "regular (8-bit)",
- get_value_string(rach_synch_seq_names, synch_seq), fn,
- ts->index, trx_lchan_desc[lchan->type].name);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return rc;
- }
-
- /* Confirm RACH request */
- l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, fn);
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c
deleted file mode 100644
index f2ecdcc6..00000000
--- a/src/host/trxcon/sched_lchan_tchf.c
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/codec/codec.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- const struct trx_lchan_desc *lchan_desc;
- int n_errors = -1, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint8_t l2[128], *mask;
- uint32_t *first_fn;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x00;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update mask and RSSI */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* Copy burst to end of buffer of 8 bursts */
- offset = buffer + bid * 116 + 464;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete traffic frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- /* Send BFI */
- goto bfi;
- }
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* FR */
- rc = gsm0503_tch_fr_decode(l2, buffer,
- 1, 0, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- rc = gsm0503_tch_fr_decode(l2, buffer,
- 1, 1, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n");
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts for interleaving */
- memcpy(buffer, buffer + 464, 464);
-
- /* Check decoding result */
- if (rc < 4) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at "
- "fn=%u for %s\n", fn, lchan_desc->name);
-
- /* Send BFI */
- goto bfi;
- } else if (rc == GSM_MACBLOCK_LEN) {
- /* FACCH received, forward it to the higher layers */
- sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-
- /* Send BFI instead of stolen TCH frame */
- goto bfi;
- } else {
- /* A good TCH frame received */
- l2_len = rc;
- }
-
- /* Send a traffic frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, false, true);
-
-bfi:
- /* Didn't try to decode */
- if (n_errors < 0)
- n_errors = 116 * 4;
-
- /* BFI is not applicable in signalling mode */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
-
- /* Bad frame indication */
- l2_len = sched_bad_frame_ind(l2, lchan);
-
- /* Send a BFI frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, true, true);
-}
-
-int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- size_t l2_len;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
-
- /* Wait until a first burst in period */
- if (bid > 0)
- return 0;
-
- /* Check the current TCH mode */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* FR */
- l2_len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- l2_len = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, "
- "dropping frame...\n");
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, "
- "dropping frame...\n", lchan->tch_mode);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
- /* Determine and check the payload length */
- if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) {
- l2_len = GSM_MACBLOCK_LEN; /* FACCH */
- } else if (lchan->prim->payload_len != l2_len) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu "
- "(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
-
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts back for interleaving */
- memcpy(buffer, buffer + 464, 464);
-
- /* Encode payload */
- rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if (*mask == 0x0f) {
- /* Confirm data / traffic sending */
- sched_send_dt_conf(trx, ts, lchan, fn, PRIM_IS_TCH(lchan->prim));
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c
deleted file mode 100644
index 0201ee35..00000000
--- a/src/host/trxcon/sched_lchan_tchh.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
- * (C) 2018 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/gsm/gsm_utils.h>
-
-#include <osmocom/coding/gsm0503_coding.h>
-#include <osmocom/codec/codec.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-static const uint8_t tch_h0_traffic_block_map[3][4] = {
- /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */
- { 0, 2, 4, 6 },
- { 4, 6, 8, 10 },
- { 8, 10, 0, 2 },
-};
-
-static const uint8_t tch_h1_traffic_block_map[3][4] = {
- /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */
- { 1, 3, 5, 7 },
- { 5, 7, 9, 11 },
- { 9, 11, 1, 3 },
-};
-
-static const uint8_t tch_h0_dl_facch_block_map[3][6] = {
- /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */
- { 4, 6, 8, 10, 13, 15 },
- { 13, 15, 17, 19, 21, 23 },
- { 21, 23, 0, 2, 4, 6 },
-};
-
-static const uint8_t tch_h0_ul_facch_block_map[3][6] = {
- /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */
- { 0, 2, 4, 6, 8, 10 },
- { 8, 10, 13, 15, 17, 19 },
- { 17, 19, 21, 23, 0, 2 },
-};
-
-static const uint8_t tch_h1_dl_facch_block_map[3][6] = {
- /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */
- { 5, 7, 9, 11, 14, 16 },
- { 14, 16, 18, 20, 22, 24 },
- { 22, 24, 1, 3, 5, 7 },
-};
-
-const uint8_t tch_h1_ul_facch_block_map[3][6] = {
- /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */
- { 1, 3, 5, 7, 9, 11 },
- { 9, 11, 14, 16, 18, 20 },
- { 18, 20, 22, 24, 1, 3 },
-};
-
-/**
- * Can a TCH/H block transmission be initiated / finished
- * on a given frame number and a given channel type?
- *
- * See GSM 05.02, clause 7, table 1
- *
- * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
- * @param fn the current frame number
- * @param ul Uplink or Downlink?
- * @param facch FACCH/H or traffic?
- * @param start init or end of transmission?
- * @return true (yes) or false (no)
- */
-bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
- uint32_t fn, bool ul, bool facch, bool start)
-{
- uint8_t fn_mf;
- int i = 0;
-
- /* Just to be sure */
- OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1);
-
- /* Calculate a modulo */
- fn_mf = facch ? (fn % 26) : (fn % 13);
-
-#define MAP_GET_POS(map) \
- (start ? 0 : ARRAY_SIZE(map[i]) - 1)
-
-#define BLOCK_MAP_FN(map) \
- do { \
- if (map[i][MAP_GET_POS(map)] == fn_mf) \
- return true; \
- } while (++i < ARRAY_SIZE(map))
-
- /* Choose a proper block map */
- if (facch) {
- if (ul) {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_ul_facch_block_map);
- else
- BLOCK_MAP_FN(tch_h1_ul_facch_block_map);
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_dl_facch_block_map);
- else
- BLOCK_MAP_FN(tch_h1_dl_facch_block_map);
- }
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_MAP_FN(tch_h0_traffic_block_map);
- else
- BLOCK_MAP_FN(tch_h1_traffic_block_map);
- }
-
- return false;
-}
-
-/**
- * Calculates a frame number of the first burst
- * using given frame number of the last burst.
- *
- * See GSM 05.02, clause 7, table 1
- *
- * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
- * @param last_fn frame number of the last burst
- * @param facch FACCH/H or traffic?
- * @return either frame number of the first burst,
- * or fn=last_fn if calculation failed
- */
-uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
- uint32_t last_fn, bool facch)
-{
- uint8_t fn_mf, fn_diff;
- int i = 0;
-
- /* Just to be sure */
- OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1);
-
- /* Calculate a modulo */
- fn_mf = facch ? (last_fn % 26) : (last_fn % 13);
-
-#define BLOCK_FIRST_FN(map) \
- do { \
- if (map[i][ARRAY_SIZE(map[i]) - 1] == fn_mf) { \
- fn_diff = TDMA_FN_DIFF(fn_mf, map[i][0]); \
- return TDMA_FN_SUB(last_fn, fn_diff); \
- } \
- } while (++i < ARRAY_SIZE(map))
-
- /* Choose a proper block map */
- if (facch) {
- if (chan == TRXC_TCHH_0)
- BLOCK_FIRST_FN(tch_h0_dl_facch_block_map);
- else
- BLOCK_FIRST_FN(tch_h1_dl_facch_block_map);
- } else {
- if (chan == TRXC_TCHH_0)
- BLOCK_FIRST_FN(tch_h0_traffic_block_map);
- else
- BLOCK_FIRST_FN(tch_h1_traffic_block_map);
- }
-
- LOGP(DSCHD, LOGL_ERROR, "Failed to calculate TDMA "
- "frame number of the first burst of %s block, "
- "using the current fn=%u\n", facch ?
- "FACCH/H" : "TCH/H", last_fn);
-
- /* Couldn't calculate the first fn, return the last */
- return last_fn;
-}
-
-int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- const struct trx_lchan_desc *lchan_desc;
- int n_errors = -1, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint8_t l2[128], *mask;
- size_t l2_len;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- if (*mask == 0x00) {
- /* Align to the first burst */
- if (bid > 0)
- return 0;
-
- /* Align reception of the first FACCH/H frame */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- if (!sched_tchh_facch_start(lchan->type, fn, 0))
- return 0;
- } else { /* or TCH/H traffic frame */
- if (!sched_tchh_traffic_start(lchan->type, fn, 0))
- return 0;
- }
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /**
- * FIXME: properly update measurements
- *
- * Since TCH/H channel is using block-diagonal interleaving,
- * a single burst may carry 57 bits of one encoded frame,
- * and 57 bits of another. This should be taken into account.
- */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* Copy burst to the end of buffer of 6 bursts */
- offset = buffer + bid * 116 + 464;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until the second burst */
- if (bid != 1)
- return 0;
-
- /* Wait for complete set of bursts */
- if (lchan->tch_mode == GSM48_CMODE_SIGN) {
- /* FACCH/H is interleaved over 6 bursts */
- if ((*mask & 0x3f) != 0x3f)
- goto bfi_shift;
- } else {
- /* Traffic is interleaved over 4 bursts */
- if ((*mask & 0x0f) != 0x0f)
- goto bfi_shift;
- }
-
- /* Skip decoding attempt in case of FACCH/H */
- if (lchan->dl_ongoing_facch) {
- lchan->dl_ongoing_facch = false;
- goto bfi_shift; /* 2/2 BFI */
- }
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* HR */
- rc = gsm0503_tch_hr_decode(l2, buffer,
- !sched_tchh_facch_end(lchan->type, fn, 0),
- &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n");
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
- return -EINVAL;
- }
-
- /* Shift buffer by 4 bursts for interleaving */
- memcpy(buffer, buffer + 232, 232);
- memcpy(buffer + 232, buffer + 464, 232);
-
- /* Shift burst mask */
- *mask = *mask << 2;
-
- /* Check decoding result */
- if (rc < 4) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at "
- "fn=%u on %s (rc=%d)\n", fn, lchan_desc->name, rc);
-
- /* Send BFI */
- goto bfi;
- } else if (rc == GSM_MACBLOCK_LEN) {
- /* Skip decoding of the next 2 stolen bursts */
- lchan->dl_ongoing_facch = true;
-
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, true); /* FACCH/H */
-
- /* FACCH/H received, forward to the higher layers */
- sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-
- /* 1/2 BFI */
- goto bfi;
- } else {
- /* A good TCH frame received */
- l2_len = rc;
- }
-
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, false); /* TCH/H */
-
- /* Send a traffic frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, false, true);
-
-bfi_shift:
- /* Shift buffer */
- memcpy(buffer, buffer + 232, 232);
- memcpy(buffer + 232, buffer + 464, 232);
-
- /* Shift burst mask */
- *mask = *mask << 2;
-
-bfi:
- /* Didn't try to decode */
- if (n_errors < 0)
- n_errors = 116 * 2;
-
- /* Calculate TDMA frame number of the first burst */
- lchan->rx_first_fn = sched_tchh_block_dl_first_fn(lchan->type,
- fn, false); /* TCH/H */
-
- /* BFI is not applicable in signalling mode */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
-
- /* Bad frame indication */
- l2_len = sched_bad_frame_ind(l2, lchan);
-
- /* Send a BFI frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, l2_len,
- n_errors, true, true);
-}
-
-int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- size_t l2_len;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (bid > 0) {
- /* Align to the first burst */
- if (*mask == 0x00)
- return 0;
- goto send_burst;
- }
-
- if (*mask == 0x00) {
- /* Align transmission of the first FACCH/H frame */
- if (lchan->tch_mode == GSM48_CMODE_SIGN)
- if (!sched_tchh_facch_start(lchan->type, fn, 1))
- return 0;
- }
-
- /* Shift buffer by 2 bursts back for interleaving */
- memcpy(buffer, buffer + 232, 232);
-
- /* Also shift TX burst mask */
- *mask = *mask << 2;
-
- /* If FACCH/H blocks are still pending */
- if (lchan->ul_facch_blocks > 2) {
- memcpy(buffer + 232, buffer + 464, 232);
- goto send_burst;
- }
-
- /* Check the current TCH mode */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* HR */
- l2_len = GSM_HR_BYTES + 1;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, "
- "dropping frame...\n");
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -ENOTSUP;
- default:
- LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, "
- "dropping frame...\n", lchan->tch_mode);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Determine payload length */
- if (PRIM_IS_FACCH(lchan->prim)) {
- l2_len = GSM_MACBLOCK_LEN; /* FACCH */
- } else if (lchan->prim->payload_len != l2_len) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu "
- "(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Encode the payload */
- rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* A FACCH/H frame occupies 6 bursts */
- if (PRIM_IS_FACCH(lchan->prim))
- lchan->ul_facch_blocks = 6;
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to transceiver */
- sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
-
- /* In case of a FACCH/H frame, one block less */
- if (lchan->ul_facch_blocks)
- lchan->ul_facch_blocks--;
-
- if ((*mask & 0x0f) == 0x0f) {
- /**
- * If no more FACCH/H blocks pending,
- * confirm data / traffic sending
- */
- if (!lchan->ul_facch_blocks)
- sched_send_dt_conf(trx, ts, lchan, fn,
- PRIM_IS_TCH(lchan->prim));
-
- /* Forget processed primitive */
- sched_prim_drop(lchan);
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c
deleted file mode 100644
index 196f949c..00000000
--- a/src/host/trxcon/sched_lchan_xcch.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: handlers for DL / UL bursts on logical channels
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/core/bits.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-#include <osmocom/coding/gsm0503_coding.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
-
-int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256)
-{
- const struct trx_lchan_desc *lchan_desc;
- uint8_t l2[GSM_MACBLOCK_LEN], *mask;
- int n_errors, n_bits_total, rc;
- sbit_t *buffer, *offset;
- uint32_t *first_fn;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- first_fn = &lchan->rx_first_fn;
- mask = &lchan->rx_burst_mask;
- buffer = lchan->rx_bursts;
-
- LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Reset internal state */
- if (bid == 0) {
- /* Clean up old measurements */
- memset(&lchan->meas, 0x00, sizeof(lchan->meas));
-
- *first_fn = fn;
- *mask = 0x0;
- }
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Update measurements */
- lchan->meas.rssi_sum += rssi;
- lchan->meas.toa256_sum += toa256;
- lchan->meas.num++;
-
- /* Copy burst to buffer of 4 bursts */
- offset = buffer + bid * 116;
- memcpy(offset, bits + 3, 58);
- memcpy(offset + 58, bits + 87, 58);
-
- /* Wait until complete set of bursts */
- if (bid != 3)
- return 0;
-
- /* Check for complete set of bursts */
- if ((*mask & 0xf) != 0xf) {
- LOGP(DSCHD, LOGL_ERROR, "Received incomplete data frame at "
- "fn=%u (%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
- /* NOTE: xCCH has an insane amount of redundancy for error
- * correction, so even just 2 valid bursts might be enough
- * to reconstruct some L2 frames. This is why we do not
- * abort here. */
- }
-
- /* Attempt to decode */
- rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad data frame at fn=%u "
- "(%u/%u) for %s\n", *first_fn,
- (*first_fn) % ts->mf_layout->period,
- ts->mf_layout->period,
- lchan_desc->name);
-
- /**
- * We should anyway send dummy frame for
- * proper measurement reporting...
- */
- return sched_send_dt_ind(trx, ts, lchan, NULL, 0,
- n_errors, true, false);
- }
-
- /* Send a L2 frame to the higher layers */
- return sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN,
- n_errors, false, false);
-}
-
-int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid)
-{
- const struct trx_lchan_desc *lchan_desc;
- ubit_t burst[GSM_BURST_LEN];
- ubit_t *buffer, *offset;
- const uint8_t *tsc;
- uint8_t *mask;
- int rc;
-
- /* Set up pointers */
- lchan_desc = &trx_lchan_desc[lchan->type];
- mask = &lchan->tx_burst_mask;
- buffer = lchan->tx_bursts;
-
- if (bid > 0) {
- /* If we have encoded bursts */
- if (*mask)
- goto send_burst;
- else
- return 0;
- }
-
- /* Check the prim payload length */
- if (lchan->prim->payload_len != GSM_MACBLOCK_LEN) {
- LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %u), "
- "so dropping...\n", lchan->prim->payload_len, GSM_MACBLOCK_LEN);
-
- sched_prim_drop(lchan);
- return -EINVAL;
- }
-
- /* Encode payload */
- rc = gsm0503_xcch_encode(buffer, lchan->prim->payload);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n",
- lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
- lchan->prim->payload_len));
-
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
-send_burst:
- /* Determine which burst should be sent */
- offset = buffer + bid * 116;
-
- /* Update mask */
- *mask |= (1 << bid);
-
- /* Choose proper TSC */
- tsc = sched_nb_training_bits[trx->tsc];
-
- /* Compose a new burst */
- memset(burst, 0, 3); /* TB */
- memcpy(burst + 3, offset, 58); /* Payload 1/2 */
- memcpy(burst + 61, tsc, 26); /* TSC */
- memcpy(burst + 87, offset + 58, 58); /* Payload 2/2 */
- memset(burst + 145, 0, 3); /* TB */
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u burst=%u\n",
- lchan_desc->name, fn, ts->index, bid);
-
- /* Forward burst to scheduler */
- rc = sched_trx_handle_tx_burst(trx, ts, lchan, fn, burst);
- if (rc) {
- /* Forget this primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- return rc;
- }
-
- /* If we have sent the last (4/4) burst */
- if ((*mask & 0x0f) == 0x0f) {
- /* Forget processed primitive */
- sched_prim_drop(lchan);
-
- /* Reset mask */
- *mask = 0x00;
-
- /* Confirm data sending */
- sched_send_dt_conf(trx, ts, lchan, fn, false);
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_mframe.c b/src/host/trxcon/sched_mframe.c
deleted file mode 100644
index 9b759af3..00000000
--- a/src/host/trxcon/sched_mframe.c
+++ /dev/null
@@ -1,2101 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: channel combinations, burst mapping
- *
- * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
- * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
- * (C) 2015 by Harald Welte <laforge@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 <osmocom/gsm/gsm_utils.h>
-
-#include "sched_trx.h"
-
-/* Non-combined CCCH */
-static const struct trx_frame frame_bcch[51] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_BCCH, 0, TRXC_RACH, 0 },
- { TRXC_BCCH, 1, TRXC_RACH, 0 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_IDLE, 0, TRXC_RACH, 0 },
-};
-
-/* Combined CCCH+SDCCH4 */
-static const struct trx_frame frame_bcch_sdcch4[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_2, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_2, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_2, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_2, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_3, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 1, TRXC_SDCCH4_2, 0 },
- { TRXC_SACCH4_1, 2, TRXC_SDCCH4_2, 1 },
- { TRXC_SACCH4_1, 3, TRXC_SDCCH4_2, 2 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 },
-
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_1, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_2, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_2, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_2, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_2, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 1, TRXC_SDCCH4_2, 0 },
- { TRXC_SACCH4_3, 2, TRXC_SDCCH4_2, 1 },
- { TRXC_SACCH4_3, 3, TRXC_SDCCH4_2, 2 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 },
-};
-
-static const struct trx_frame frame_bcch_sdcch4_cbch[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_IDLE, 0 },
- { TRXC_CCCH, 1, TRXC_IDLE, 1 },
- { TRXC_CCCH, 2, TRXC_IDLE, 2 },
- { TRXC_CCCH, 3, TRXC_IDLE, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_3, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_SACCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_1, 1, TRXC_IDLE, 0 },
- { TRXC_SACCH4_1, 2, TRXC_IDLE, 1 },
- { TRXC_SACCH4_1, 3, TRXC_IDLE, 2 },
- { TRXC_IDLE, 0, TRXC_IDLE, 3 },
-
- { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 },
- { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 },
- { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 },
- { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 },
- { TRXC_BCCH, 2, TRXC_RACH, 0 },
- { TRXC_BCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 },
- { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 },
- { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 },
- { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 },
- { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 },
- { TRXC_SCH, 0, TRXC_SACCH4_1, 1 },
- { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 },
- { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_CCCH, 0, TRXC_RACH, 0 },
- { TRXC_CCCH, 1, TRXC_RACH, 0 },
- { TRXC_CCCH, 2, TRXC_RACH, 0 },
- { TRXC_CCCH, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 },
- { TRXC_FCCH, 0, TRXC_RACH, 0 },
- { TRXC_SCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 },
- { TRXC_SDCCH4_CBCH, 3, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 },
- { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 },
- { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 },
- { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 },
- { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 },
- { TRXC_IDLE, 0, TRXC_SDCCH4_1, 1 },
- { TRXC_IDLE, 1, TRXC_SDCCH4_1, 2 },
- { TRXC_IDLE, 2, TRXC_SDCCH4_1, 3 },
- { TRXC_IDLE, 3, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 0, TRXC_RACH, 0 },
- { TRXC_SACCH4_3, 1, TRXC_IDLE, 0 },
- { TRXC_SACCH4_3, 2, TRXC_IDLE, 1 },
- { TRXC_SACCH4_3, 3, TRXC_IDLE, 2 },
- { TRXC_IDLE, 0, TRXC_IDLE, 3 },
-};
-
-static const struct trx_frame frame_sdcch8[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 },
- { TRXC_SDCCH8_2, 0, TRXC_SACCH8_7, 0 },
- { TRXC_SDCCH8_2, 1, TRXC_SACCH8_7, 1 },
- { TRXC_SDCCH8_2, 2, TRXC_SACCH8_7, 2 },
- { TRXC_SDCCH8_2, 3, TRXC_SACCH8_7, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_2, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_2, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_2, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_2, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 },
-
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_2, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_2, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_2, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_2, 3 },
- { TRXC_SDCCH8_2, 0, TRXC_SACCH8_3, 0 },
- { TRXC_SDCCH8_2, 1, TRXC_SACCH8_3, 1 },
- { TRXC_SDCCH8_2, 2, TRXC_SACCH8_3, 2 },
- { TRXC_SDCCH8_2, 3, TRXC_SACCH8_3, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 },
-};
-
-static const struct trx_frame frame_sdcch8_cbch[102] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 },
- { TRXC_SDCCH8_CBCH, 0, TRXC_SACCH8_7, 0 },
- { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_7, 1 },
- { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_7, 2 },
- { TRXC_SDCCH8_CBCH, 3, TRXC_SACCH8_7, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_IDLE, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_IDLE, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_IDLE, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_IDLE, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 },
-
- { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 },
- { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 },
- { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 },
- { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 },
- { TRXC_SDCCH8_1, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_1, 1, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_1, 2, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_1, 3, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_CBCH, 0, TRXC_SACCH8_3, 0 },
- { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_3, 1 },
- { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_3, 2 },
- { TRXC_SDCCH8_CBCH, 3, TRXC_SACCH8_3, 3 },
- { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 },
- { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 },
- { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 },
- { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 },
- { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 },
- { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 },
- { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 },
- { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 },
- { TRXC_SDCCH8_5, 3, TRXC_IDLE, 0 },
- { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 },
- { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 },
- { TRXC_SDCCH8_6, 2, TRXC_IDLE, 3 },
- { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 },
- { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 },
- { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 },
- { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 },
- { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 },
- { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 },
- { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 },
- { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 },
- { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 },
- { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 },
- { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 },
- { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 },
- { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 },
- { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 },
- { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 },
- { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 },
- { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 },
- { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 },
- { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 },
- { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 },
- { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 },
- { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 },
-};
-
-static const struct trx_frame frame_tchf_ts0[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts1[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
-};
-
-static const struct trx_frame frame_tchf_ts2[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts3[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
-};
-
-static const struct trx_frame frame_tchf_ts4[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts5[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
-};
-
-static const struct trx_frame frame_tchf_ts6[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-static const struct trx_frame frame_tchf_ts7[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_TCHF, 0, TRXC_TCHF, 0 },
- { TRXC_TCHF, 1, TRXC_TCHF, 1 },
- { TRXC_TCHF, 2, TRXC_TCHF, 2 },
- { TRXC_TCHF, 3, TRXC_TCHF, 3 },
- { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 },
-};
-
-static const struct trx_frame frame_tchh_ts01[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
-};
-
-static const struct trx_frame frame_tchh_ts23[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
-};
-
-static const struct trx_frame frame_tchh_ts45[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
-};
-
-static const struct trx_frame frame_tchh_ts67[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 },
- { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 },
- { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 },
- { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 },
- { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 },
-};
-
-static const struct trx_frame frame_pdch[104] = {
- /* dl_chan dl_bid ul_chan ul_bid */
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 0, TRXC_PTCCH, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 1, TRXC_PTCCH, 1 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 2, TRXC_PTCCH, 2 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PTCCH, 3, TRXC_PTCCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_PDTCH, 0, TRXC_PDTCH, 0 },
- { TRXC_PDTCH, 1, TRXC_PDTCH, 1 },
- { TRXC_PDTCH, 2, TRXC_PDTCH, 2 },
- { TRXC_PDTCH, 3, TRXC_PDTCH, 3 },
- { TRXC_IDLE, 0, TRXC_IDLE, 0 },
-};
-
-/* Logical channel mask for a single channel */
-#define M64(x) \
- ((uint64_t) 0x01 << x)
-
-/* Logical channel mask for BCCH+CCCH */
-#define M64_BCCH_CCCH \
- (uint64_t) 0x00 \
- | M64(TRXC_FCCH) \
- | M64(TRXC_SCH) \
- | M64(TRXC_BCCH) \
- | M64(TRXC_RACH) \
- | M64(TRXC_CCCH)
-
-/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */
-#define M64_SDCCH4 \
- (uint64_t) 0x00 \
- | M64(TRXC_SDCCH4_0) | M64(TRXC_SACCH4_0) \
- | M64(TRXC_SDCCH4_1) | M64(TRXC_SACCH4_1) \
- | M64(TRXC_SDCCH4_2) | M64(TRXC_SACCH4_2) \
- | M64(TRXC_SDCCH4_3) | M64(TRXC_SACCH4_3)
-
-/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */
-#define M64_SDCCH8 \
- (uint64_t) 0x00 \
- | M64(TRXC_SDCCH8_0) | M64(TRXC_SACCH8_0) \
- | M64(TRXC_SDCCH8_1) | M64(TRXC_SACCH8_1) \
- | M64(TRXC_SDCCH8_2) | M64(TRXC_SACCH8_2) \
- | M64(TRXC_SDCCH8_3) | M64(TRXC_SACCH8_3) \
- | M64(TRXC_SDCCH8_4) | M64(TRXC_SACCH8_4) \
- | M64(TRXC_SDCCH8_5) | M64(TRXC_SACCH8_5) \
- | M64(TRXC_SDCCH8_6) | M64(TRXC_SACCH8_6) \
- | M64(TRXC_SDCCH8_7) | M64(TRXC_SACCH8_7)
-
-/* Logical channel mask for TCH/F (with SACCH) */
-#define M64_TCHF \
- (uint64_t) 0x00 \
- | M64(TRXC_TCHF) | M64(TRXC_SACCHTF)
-
-/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */
-#define M64_TCHH \
- (uint64_t) 0x00 \
- | M64(TRXC_TCHH_0) | M64(TRXC_SACCHTH_0) \
- | M64(TRXC_TCHH_1) | M64(TRXC_SACCHTH_1)
-
-/**
- * A few notes about frame count:
- *
- * 26 frame multiframe - traffic multiframe
- * 51 frame multiframe - control multiframe
- *
- * 102 = 2 x 51 frame multiframe
- * 104 = 4 x 26 frame multiframe
- */
-static const struct trx_multiframe layouts[] = {
- {
- GSM_PCHAN_NONE, "NONE",
- 0, 0xff,
- 0x00,
- NULL
- },
- {
- GSM_PCHAN_CCCH, "BCCH+CCCH",
- 51, 0xff,
- M64_BCCH_CCCH,
- frame_bcch
- },
- {
- GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4",
- 102, 0xff,
- M64_BCCH_CCCH | M64_SDCCH4,
- frame_bcch_sdcch4
- },
- {
- GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH",
- 102, 0xff,
- M64_BCCH_CCCH | M64_SDCCH4 | M64(TRXC_SDCCH4_CBCH),
- frame_bcch_sdcch4_cbch
- },
- {
- GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8",
- 102, 0xff,
- M64_SDCCH8,
- frame_sdcch8
- },
- {
- GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH",
- 102, 0xff,
- M64_SDCCH8 | M64(TRXC_SDCCH8_CBCH),
- frame_sdcch8_cbch
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x01,
- M64_TCHF,
- frame_tchf_ts0
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x02,
- M64_TCHF,
- frame_tchf_ts1
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x04,
- M64_TCHF,
- frame_tchf_ts2
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x08,
- M64_TCHF,
- frame_tchf_ts3
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x10,
- M64_TCHF,
- frame_tchf_ts4
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x20,
- M64_TCHF,
- frame_tchf_ts5
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x40,
- M64_TCHF,
- frame_tchf_ts6
- },
- {
- GSM_PCHAN_TCH_F, "TCH/F+SACCH",
- 104, 0x80,
- M64_TCHF,
- frame_tchf_ts7
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x03,
- M64_TCHH,
- frame_tchh_ts01
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x0c,
- M64_TCHH,
- frame_tchh_ts23
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0x30,
- M64_TCHH,
- frame_tchh_ts45
- },
- {
- GSM_PCHAN_TCH_H, "TCH/H+SACCH",
- 104, 0xc0,
- M64_TCHH,
- frame_tchh_ts67
- },
- {
- GSM_PCHAN_PDCH, "PDCH",
- 104, 0xff,
- M64(TRXC_PDTCH) | M64(TRXC_PTCCH),
- frame_pdch
- },
-};
-
-const struct trx_multiframe *sched_mframe_layout(
- enum gsm_phys_chan_config config, int tn)
-{
- int i, ts_allowed;
-
- for (i = 0; i < ARRAY_SIZE(layouts); i++) {
- ts_allowed = layouts[i].slotmask & (0x01 << tn);
- if (layouts[i].chan_config == config && ts_allowed)
- return &layouts[i];
- }
-
- return NULL;
-}
diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c
deleted file mode 100644
index 50dfd6e9..00000000
--- a/src/host/trxcon/sched_prim.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: primitive management
- *
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <talloc.h>
-
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-
-#include <osmocom/gsm/protocol/gsm_04_08.h>
-
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "trx_if.h"
-#include "logging.h"
-
-/**
- * Initializes a new primitive by allocating memory
- * and filling some meta-information (e.g. lchan type).
- *
- * @param ctx parent talloc context
- * @param prim external prim pointer (will point to the allocated prim)
- * @param pl_len prim payload length
- * @param chan_nr RSL channel description (used to set a proper chan)
- * @param link_id RSL link description (used to set a proper chan)
- * @return zero in case of success, otherwise a error number
- */
-int sched_prim_init(void *ctx, struct trx_ts_prim **prim,
- size_t pl_len, uint8_t chan_nr, uint8_t link_id)
-{
- enum trx_lchan_type lchan_type;
- struct trx_ts_prim *new_prim;
- uint8_t len;
-
- /* Determine lchan type */
- lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id);
- if (!lchan_type) {
- LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type "
- "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id);
- return -EINVAL;
- }
-
- /* How much memory do we need? */
- len = sizeof(struct trx_ts_prim); /* Primitive header */
- len += pl_len; /* Requested payload size */
-
- /* Allocate a new primitive */
- new_prim = talloc_zero_size(ctx, len);
- if (new_prim == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n");
- return -ENOMEM;
- }
-
- /* Init primitive header */
- new_prim->payload_len = pl_len;
- new_prim->chan = lchan_type;
-
- /* Set external pointer */
- *prim = new_prim;
-
- return 0;
-}
-
-/**
- * Adds a primitive to the end of transmit queue of a particular
- * timeslot, whose index is parsed from chan_nr.
- *
- * @param trx TRX instance
- * @param prim to be enqueued primitive
- * @param chan_nr RSL channel description
- * @return zero in case of success, otherwise a error number
- */
-int sched_prim_push(struct trx_instance *trx,
- struct trx_ts_prim *prim, uint8_t chan_nr)
-{
- struct trx_ts *ts;
- uint8_t tn;
-
- /* Determine TS index */
- tn = chan_nr & 0x7;
-
- /* Check whether required timeslot is allocated and configured */
- ts = trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn);
- return -EINVAL;
- }
-
- /**
- * Change talloc context of primitive
- * from trx to the parent ts
- */
- talloc_steal(ts, prim);
-
- /* Add primitive to TS transmit queue */
- llist_add_tail(&prim->list, &ts->tx_prims);
-
- return 0;
-}
-
-/**
- * Composes a new primitive using either cached (if populated),
- * or "dummy" Measurement Report message.
- *
- * @param lchan lchan to assign a primitive
- * @return SACCH primitive to be transmitted
- */
-static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan)
-{
- struct trx_ts_prim *prim;
- uint8_t *mr_src_ptr;
- bool cached;
- int rc;
-
- /* "Dummy" Measurement Report */
- static const uint8_t meas_rep_dummy[] = {
- /* L1 SACCH pseudo-header */
- 0x0f, 0x00,
-
- /* LAPDm header */
- 0x01, 0x03, 0x49,
-
- /* Measurement report */
- 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0,
-
- /* TODO: Padding? Randomize if so */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- };
-
- /* Allocate a new primitive */
- rc = sched_prim_init(lchan, &prim, GSM_MACBLOCK_LEN,
- trx_lchan_desc[lchan->type].chan_nr, TRX_CH_LID_SACCH);
- OSMO_ASSERT(rc == 0);
-
- /* Check if the MR cache is populated (verify LAPDm header) */
- cached = (lchan->sacch.mr_cache[2] != 0x00
- && lchan->sacch.mr_cache[3] != 0x00
- && lchan->sacch.mr_cache[4] != 0x00);
- if (cached) { /* Use the cached one */
- mr_src_ptr = lchan->sacch.mr_cache;
- lchan->sacch.mr_cache_usage++;
- } else { /* Use "dummy" one */
- mr_src_ptr = (uint8_t *) meas_rep_dummy;
- }
-
- /* Compose a new Measurement Report primitive */
- memcpy(prim->payload, mr_src_ptr, GSM_MACBLOCK_LEN);
-
-#if 0
- /**
- * Update the L1 SACCH pseudo-header (only for cached MRs)
- *
- * FIXME: this would require having access to the trx_instance,
- * what can be achieved either by chain-passing the pointer
- * through sched_prim_dequeue(), or by adding some
- * back-pointers to the logical channel state.
- *
- * TODO: filling of the actual values into cached Measurement
- * Reports would break the distance spoofing feature. If it
- * were known whether the spoofing is enabled or not, we could
- * decide whether to update the cached L1 SACCH header here.
- */
- if (!cached) {
- prim->payload[0] = trx->tx_power;
- prim->payload[1] = trx->ta;
- }
-#endif
-
- /* Inform about the cache usage count */
- if (cached && lchan->sacch.mr_cache_usage > 5) {
- LOGP(DSCHD, LOGL_NOTICE, "SACCH MR cache usage count=%u > 5 "
- "on lchan=%s => ancient measurements, please fix!\n",
- lchan->sacch.mr_cache_usage,
- trx_lchan_desc[lchan->type].name);
- }
-
- LOGP(DSCHD, LOGL_NOTICE, "Using a %s Measurement Report "
- "on lchan=%s\n", (cached ? "cached" : "dummy"),
- trx_lchan_desc[lchan->type].name);
-
- return prim;
-}
-
-/**
- * Dequeues a SACCH primitive from transmit queue, if present.
- * Otherwise dequeues a cached Measurement Report (the last
- * received one). Finally, if the cache is empty, a "dummy"
- * measurement report is used.
- *
- * According to 3GPP TS 04.08, section 3.4.1, SACCH channel
- * accompanies either a traffic or a signaling channel. It
- * has the particularity that continuous transmission must
- * occur in both directions, so on the Uplink direction
- * measurement result messages are sent at each possible
- * occasion when nothing else has to be sent. The LAPDm
- * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not
- * applicable on SACCH channels!
- *
- * Unfortunately, 3GPP TS 04.08 doesn't clearly state
- * which "else messages" besides Measurement Reports
- * can be send by the MS on SACCH channels. However,
- * in sub-clause 3.4.1 it's stated that the interval
- * between two successive measurement result messages
- * shall not exceed one L2 frame.
- *
- * @param queue transmit queue to take a prim from
- * @param lchan lchan to assign a primitive
- * @return SACCH primitive to be transmitted
- */
-static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue,
- struct trx_lchan_state *lchan)
-{
- struct trx_ts_prim *prim_nmr = NULL;
- struct trx_ts_prim *prim_mr = NULL;
- struct trx_ts_prim *prim;
- bool mr_now;
-
- /* Shall we transmit MR now? */
- mr_now = !lchan->sacch.mr_tx_last;
-
-#define PRIM_IS_MR(prim) \
- (prim->payload[5] == GSM48_PDISC_RR \
- && prim->payload[6] == GSM48_MT_RR_MEAS_REP)
-
- /* Iterate over all primitives in the queue */
- llist_for_each_entry(prim, queue, list) {
- /* We are looking for particular channel */
- if (prim->chan != lchan->type)
- continue;
-
- /* Look for a Measurement Report */
- if (!prim_mr && PRIM_IS_MR(prim))
- prim_mr = prim;
-
- /* Look for anything else */
- if (!prim_nmr && !PRIM_IS_MR(prim))
- prim_nmr = prim;
-
- /* Should we look further? */
- if (mr_now && prim_mr)
- break; /* MR was found */
- else if (!mr_now && prim_nmr)
- break; /* something else was found */
- }
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH MR selection on lchan=%s: "
- "mr_tx_last=%d prim_mr=%p prim_nmr=%p\n",
- trx_lchan_desc[lchan->type].name,
- lchan->sacch.mr_tx_last,
- prim_mr, prim_nmr);
-
- /* Prioritize non-MR prim if possible */
- if (mr_now && prim_mr)
- prim = prim_mr;
- else if (!mr_now && prim_nmr)
- prim = prim_nmr;
- else if (!mr_now && prim_mr)
- prim = prim_mr;
- else /* Nothing was found */
- prim = NULL;
-
- /* Have we found what we were looking for? */
- if (prim) /* Dequeue if so */
- llist_del(&prim->list);
- else /* Otherwise compose a new MR */
- prim = prim_compose_mr(lchan);
-
- /* Update the cached report */
- if (prim == prim_mr) {
- memcpy(lchan->sacch.mr_cache,
- prim->payload, GSM_MACBLOCK_LEN);
- lchan->sacch.mr_cache_usage = 0;
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH MR cache has been updated "
- "for lchan=%s\n", trx_lchan_desc[lchan->type].name);
- }
-
- /* Update the MR transmission state */
- lchan->sacch.mr_tx_last = PRIM_IS_MR(prim);
-
- LOGP(DSCHD, LOGL_DEBUG, "SACCH decision on lchan=%s: %s\n",
- trx_lchan_desc[lchan->type].name, PRIM_IS_MR(prim) ?
- "Measurement Report" : "data frame");
-
- return prim;
-}
-
-/* Dequeues a primitive of a given channel type */
-static struct trx_ts_prim *prim_dequeue_one(struct llist_head *queue,
- enum trx_lchan_type lchan_type)
-{
- struct trx_ts_prim *prim;
-
- /**
- * There is no need to use the 'safe' list iteration here
- * as an item removal is immediately followed by return.
- */
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan == lchan_type) {
- llist_del(&prim->list);
- return prim;
- }
- }
-
- return NULL;
-}
-
-/**
- * Dequeues either a FACCH, or a speech TCH primitive
- * of a given channel type (Lm or Bm).
- *
- * Note: we could avoid 'lchan_type' parameter and just
- * check the prim's channel type using CHAN_IS_TCH(),
- * but the current approach is a bit more flexible,
- * and allows one to have both sub-slots of TCH/H
- * enabled on same timeslot e.g. for testing...
- *
- * @param queue transmit queue to take a prim from
- * @param lchan_type required channel type of a primitive,
- * e.g. TRXC_TCHF, TRXC_TCHH_0, or TRXC_TCHH_1
- * @param facch FACCH (true) or speech (false) prim?
- * @return either a FACCH, or a TCH primitive if found,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch(struct llist_head *queue,
- enum trx_lchan_type lchan_type, bool facch)
-{
- struct trx_ts_prim *prim;
-
- /**
- * There is no need to use the 'safe' list iteration here
- * as an item removal is immediately followed by return.
- */
- llist_for_each_entry(prim, queue, list) {
- if (prim->chan != lchan_type)
- continue;
-
- /* Either FACCH, or not FACCH */
- if (PRIM_IS_FACCH(prim) != facch)
- continue;
-
- llist_del(&prim->list);
- return prim;
- }
-
- return NULL;
-}
-
-/**
- * Dequeues either a TCH/F, or a FACCH/F prim (preferred).
- * If a FACCH/F prim is found, one TCH/F prim is being
- * dropped (i.e. replaced).
- *
- * @param queue a transmit queue to take a prim from
- * @return either a FACCH/F, or a TCH/F primitive,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch_f(struct llist_head *queue)
-{
- struct trx_ts_prim *facch;
- struct trx_ts_prim *tch;
-
- /* Attempt to find a pair of both FACCH/F and TCH/F frames */
- facch = prim_dequeue_tch(queue, TRXC_TCHF, true);
- tch = prim_dequeue_tch(queue, TRXC_TCHF, false);
-
- /* Prioritize FACCH/F, if found */
- if (facch) {
- /* One TCH/F prim is replaced */
- if (tch)
- talloc_free(tch);
- return facch;
- } else if (tch) {
- /* Only TCH/F prim was found */
- return tch;
- } else {
- /* Nothing was found, e.g. when only SACCH frames are in queue */
- return NULL;
- }
-}
-
-/**
- * Dequeues either a TCH/H, or a FACCH/H prim (preferred).
- * If a FACCH/H prim is found, two TCH/H prims are being
- * dropped (i.e. replaced).
- *
- * According to GSM 05.02, the following blocks can be used
- * to carry FACCH/H data (see clause 7, table 1 of 9):
- *
- * UL FACCH/H0:
- * B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2)
- *
- * UL FACCH/H1:
- * B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3)
- *
- * where the numbers within brackets are fn % 26.
- *
- * @param queue transmit queue to take a prim from
- * @param fn the current frame number
- * @param lchan_type required channel type of a primitive,
- * @return either a FACCH/H, or a TCH/H primitive,
- * otherwise NULL
- */
-static struct trx_ts_prim *prim_dequeue_tch_h(struct llist_head *queue,
- uint32_t fn, enum trx_lchan_type lchan_type)
-{
- struct trx_ts_prim *facch;
- struct trx_ts_prim *tch;
- bool facch_now;
-
- /* May we initiate an UL FACCH/H frame transmission now? */
- facch_now = sched_tchh_facch_start(lchan_type, fn, true);
- if (!facch_now) /* Just dequeue a TCH/H prim */
- goto no_facch;
-
- /* If there are no FACCH/H prims in the queue */
- facch = prim_dequeue_tch(queue, lchan_type, true);
- if (!facch) /* Just dequeue a TCH/H prim */
- goto no_facch;
-
- /* FACCH/H prim replaces two TCH/F prims */
- tch = prim_dequeue_tch(queue, lchan_type, false);
- if (tch) {
- /* At least one TCH/H prim is dropped */
- talloc_free(tch);
-
- /* Attempt to find another */
- tch = prim_dequeue_tch(queue, lchan_type, false);
- if (tch) /* Drop the second TCH/H prim */
- talloc_free(tch);
- }
-
- return facch;
-
-no_facch:
- return prim_dequeue_tch(queue, lchan_type, false);
-}
-
-/**
- * Dequeues a single primitive of required type
- * from a specified transmit queue.
- *
- * @param queue a transmit queue to take a prim from
- * @param fn the current frame number (used for FACCH/H)
- * @param lchan logical channel state
- * @return a primitive or NULL if not found
- */
-struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct trx_lchan_state *lchan)
-{
- /* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */
- if (CHAN_IS_SACCH(lchan->type))
- return prim_dequeue_sacch(queue, lchan);
-
- /* There is nothing to dequeue */
- if (llist_empty(queue))
- return NULL;
-
- switch (lchan->type) {
- /* TCH/F requires FACCH/F prioritization */
- case TRXC_TCHF:
- return prim_dequeue_tch_f(queue);
-
- /* FACCH/H prioritization is a bit more complex */
- case TRXC_TCHH_0:
- case TRXC_TCHH_1:
- return prim_dequeue_tch_h(queue, fn, lchan->type);
-
- /* Other kinds of logical channels */
- default:
- return prim_dequeue_one(queue, lchan->type);
- }
-}
-
-/**
- * Drops the current primitive of specified logical channel
- *
- * @param lchan a logical channel to drop prim from
- */
-void sched_prim_drop(struct trx_lchan_state *lchan)
-{
- /* Forget this primitive */
- talloc_free(lchan->prim);
- lchan->prim = NULL;
-}
-
-/**
- * Assigns a dummy primitive to a lchan depending on its type.
- * Could be used when there is nothing to transmit, but
- * CBTX (Continuous Burst Transmission) is assumed.
- *
- * @param lchan lchan to assign a primitive
- * @return zero in case of success, otherwise a error code
- */
-int sched_prim_dummy(struct trx_lchan_state *lchan)
-{
- enum trx_lchan_type chan = lchan->type;
- uint8_t tch_mode = lchan->tch_mode;
- struct trx_ts_prim *prim;
- uint8_t prim_buffer[40];
- size_t prim_len = 0;
- int i;
-
- /**
- * TS 144.006, section 8.4.2.3 "Fill frames"
- * A fill frame is a UI command frame for SAPI 0, P=0
- * and with an information field of 0 octet length.
- */
- static const uint8_t lapdm_fill_frame[] = {
- 0x01, 0x03, 0x01, 0x2b,
- /* Pending part is to be randomized */
- };
-
- /* Make sure that there is no existing primitive */
- OSMO_ASSERT(lchan->prim == NULL);
- /* Not applicable for SACCH! */
- OSMO_ASSERT(!CHAN_IS_SACCH(lchan->type));
-
- /**
- * Determine what actually should be generated:
- * TCH in GSM48_CMODE_SIGN: LAPDm fill frame;
- * TCH in other modes: silence frame;
- * other channels: LAPDm fill frame.
- */
- if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) {
- /* Bad frame indication */
- prim_len = sched_bad_frame_ind(prim_buffer, lchan);
- } else if (CHAN_IS_TCH(chan) && TCH_MODE_IS_DATA(tch_mode)) {
- /* FIXME: should we do anything for CSD? */
- return 0;
- } else {
- /* Copy LAPDm fill frame's header */
- memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame));
-
- /**
- * TS 144.006, section 5.2 "Frame delimitation and fill bits"
- * Except for the first octet containing fill bits which shall
- * be set to the binary value "00101011", each fill bit should
- * be set to a random value when sent by the network.
- */
- for (i = sizeof(lapdm_fill_frame); i < GSM_MACBLOCK_LEN; i++)
- prim_buffer[i] = (uint8_t) rand();
-
- /* Define a prim length */
- prim_len = GSM_MACBLOCK_LEN;
- }
-
- /* Nothing to allocate / assign */
- if (!prim_len)
- return 0;
-
- /* Allocate a new primitive */
- prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len);
- if (prim == NULL)
- return -ENOMEM;
-
- /* Init primitive header */
- prim->payload_len = prim_len;
- prim->chan = lchan->type;
-
- /* Fill in the payload */
- memcpy(prim->payload, prim_buffer, prim_len);
-
- /* Assign the current prim */
- lchan->prim = prim;
-
- LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame "
- "on lchan=%s\n", trx_lchan_desc[chan].name);
-
- return 0;
-}
-
-/**
- * Flushes a queue of primitives
- *
- * @param list list of prims going to be flushed
- */
-void sched_prim_flush_queue(struct llist_head *list)
-{
- struct trx_ts_prim *prim, *prim_next;
-
- llist_for_each_entry_safe(prim, prim_next, list, list) {
- llist_del(&prim->list);
- talloc_free(prim);
- }
-}
diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c
deleted file mode 100644
index 37d1acf6..00000000
--- a/src/host/trxcon/sched_trx.c
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- * TDMA scheduler: GSM PHY routines
- *
- * (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <error.h>
-#include <errno.h>
-#include <string.h>
-#include <talloc.h>
-#include <stdbool.h>
-
-#include <osmocom/gsm/a5.h>
-#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include <osmocom/core/bits.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/linuxlist.h>
-
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "trx_if.h"
-#include "logging.h"
-
-static void sched_frame_clck_cb(struct trx_sched *sched)
-{
- struct trx_instance *trx = (struct trx_instance *) sched->data;
- const struct trx_frame *frame;
- struct trx_lchan_state *lchan;
- trx_lchan_tx_func *handler;
- enum trx_lchan_type chan;
- uint8_t offset, bid;
- struct trx_ts *ts;
- uint32_t fn;
- int i;
-
- /* Iterate over timeslot list */
- for (i = 0; i < TRX_TS_COUNT; i++) {
- /* Timeslot is not allocated */
- ts = trx->ts_list[i];
- if (ts == NULL)
- continue;
-
- /* Timeslot is not configured */
- if (ts->mf_layout == NULL)
- continue;
-
- /**
- * Advance frame number, giving the transceiver more
- * time until a burst must be transmitted...
- */
- fn = TDMA_FN_SUM(sched->fn_counter_proc,
- sched->fn_counter_advance);
-
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- bid = frame->ul_bid;
- chan = frame->ul_chan;
- handler = trx_lchan_desc[chan].tx_fn;
-
- /* Omit lchans without handler */
- if (!handler)
- continue;
-
- /* Make sure that lchan was allocated and activated */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- continue;
-
- /* Omit inactive lchans */
- if (!lchan->active)
- continue;
-
- /**
- * If we aren't processing any primitive yet,
- * attempt to obtain a new one from queue
- */
- if (lchan->prim == NULL)
- lchan->prim = sched_prim_dequeue(&ts->tx_prims, fn, lchan);
-
- /* TODO: report TX buffers health to the higher layers */
-
- /* If CBTX (Continuous Burst Transmission) is assumed */
- if (trx_lchan_desc[chan].flags & TRX_CH_FLAG_CBTX) {
- /**
- * Probably, a TX buffer is empty. Nevertheless,
- * we shall continuously transmit anything on
- * CBTX channels.
- */
- if (lchan->prim == NULL)
- sched_prim_dummy(lchan);
- }
-
- /* If there is no primitive, do nothing */
- if (lchan->prim == NULL)
- continue;
-
- /* Handover RACH needs to be handled regardless of the
- * current channel type and the associated handler. */
- if (PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != TRXC_RACH)
- handler = trx_lchan_desc[TRXC_RACH].tx_fn;
-
- /* Poke lchan handler */
- handler(trx, ts, lchan, fn, bid);
- }
-}
-
-int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance)
-{
- struct trx_sched *sched;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Init scheduler\n");
-
- /* Obtain a scheduler instance from TRX */
- sched = &trx->sched;
-
- /* Register frame clock callback */
- sched->clock_cb = sched_frame_clck_cb;
-
- /* Set pointers */
- sched = &trx->sched;
- sched->data = trx;
-
- /* Set frame counter advance */
- sched->fn_counter_advance = fn_advance;
-
- return 0;
-}
-
-int sched_trx_shutdown(struct trx_instance *trx)
-{
- int i;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Shutdown scheduler\n");
-
- /* Free all potentially allocated timeslots */
- for (i = 0; i < TRX_TS_COUNT; i++)
- sched_trx_del_ts(trx, i);
-
- return 0;
-}
-
-int sched_trx_reset(struct trx_instance *trx, bool reset_clock)
-{
- int i;
-
- if (!trx)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "Reset scheduler %s\n",
- reset_clock ? "and clock counter" : "");
-
- /* Free all potentially allocated timeslots */
- for (i = 0; i < TRX_TS_COUNT; i++)
- sched_trx_del_ts(trx, i);
-
- /* Stop and reset clock counter if required */
- if (reset_clock)
- sched_clck_reset(&trx->sched);
-
- return 0;
-}
-
-struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn)
-{
- /* Make sure that ts isn't allocated yet */
- if (trx->ts_list[tn] != NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot #%u already allocated\n", tn);
- return NULL;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn);
-
- /* Allocate a new one */
- trx->ts_list[tn] = talloc_zero(trx, struct trx_ts);
-
- /* Assign TS index */
- trx->ts_list[tn]->index = tn;
-
- return trx->ts_list[tn];
-}
-
-void sched_trx_del_ts(struct trx_instance *trx, int tn)
-{
- struct trx_lchan_state *lchan, *lchan_next;
- struct trx_ts *ts;
-
- /* Find ts in list */
- ts = trx->ts_list[tn];
- if (ts == NULL)
- return;
-
- LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn);
-
- /* Deactivate all logical channels */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Free channel states */
- llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
- llist_del(&lchan->list);
- talloc_free(lchan);
- }
-
- /* Flush queue primitives for TX */
- sched_prim_flush_queue(&ts->tx_prims);
-
- /* Remove ts from list and free memory */
- trx->ts_list[tn] = NULL;
- talloc_free(ts);
-
- /* Notify transceiver about that */
- trx_if_cmd_setslot(trx, tn, 0);
-}
-
-#define LAYOUT_HAS_LCHAN(layout, lchan) \
- (layout->lchan_mask & ((uint64_t) 0x01 << lchan))
-
-int sched_trx_configure_ts(struct trx_instance *trx, int tn,
- enum gsm_phys_chan_config config)
-{
- struct trx_lchan_state *lchan;
- enum trx_lchan_type type;
- struct trx_ts *ts;
-
- /* Try to find specified ts */
- ts = trx->ts_list[tn];
- if (ts != NULL) {
- /* Reconfiguration of existing one */
- sched_trx_reset_ts(trx, tn);
- } else {
- /* Allocate a new one if doesn't exist */
- ts = sched_trx_add_ts(trx, tn);
- if (ts == NULL)
- return -ENOMEM;
- }
-
- /* Choose proper multiframe layout */
- ts->mf_layout = sched_mframe_layout(config, tn);
- if (ts->mf_layout->chan_config != config)
- return -EINVAL;
-
- LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n",
- tn, ts->mf_layout->name);
-
- /* Init queue primitives for TX */
- INIT_LLIST_HEAD(&ts->tx_prims);
- /* Init logical channels list */
- INIT_LLIST_HEAD(&ts->lchans);
-
- /* Allocate channel states */
- for (type = 0; type < _TRX_CHAN_MAX; type++) {
- if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type))
- continue;
-
- /* Allocate a channel state */
- lchan = talloc_zero(ts, struct trx_lchan_state);
- if (!lchan)
- return -ENOMEM;
-
- /* Set channel type */
- lchan->type = type;
-
- /* Add to the list of channel states */
- llist_add_tail(&lchan->list, &ts->lchans);
-
- /* Enable channel automatically if required */
- if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO)
- sched_trx_activate_lchan(ts, type);
- }
-
- /* Notify transceiver about TS activation */
- /* FIXME: set proper channel type */
- trx_if_cmd_setslot(trx, tn, 1);
-
- return 0;
-}
-
-int sched_trx_reset_ts(struct trx_instance *trx, int tn)
-{
- struct trx_lchan_state *lchan, *lchan_next;
- struct trx_ts *ts;
-
- /* Try to find specified ts */
- ts = trx->ts_list[tn];
- if (ts == NULL)
- return -EINVAL;
-
- /* Flush TS frame counter */
- ts->mf_last_fn = 0;
-
- /* Undefine multiframe layout */
- ts->mf_layout = NULL;
-
- /* Flush queue primitives for TX */
- sched_prim_flush_queue(&ts->tx_prims);
-
- /* Deactivate all logical channels */
- sched_trx_deactivate_all_lchans(ts);
-
- /* Free channel states */
- llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
- llist_del(&lchan->list);
- talloc_free(lchan);
- }
-
- /* Notify transceiver about that */
- trx_if_cmd_setslot(trx, tn, 0);
-
- return 0;
-}
-
-int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo,
- uint8_t *key, uint8_t key_len)
-{
- struct trx_lchan_state *lchan;
-
- /* Prevent NULL-pointer deference */
- if (!ts)
- return -EINVAL;
-
- /* Make sure we can store this key */
- if (key_len > MAX_A5_KEY_LEN)
- return -ERANGE;
-
- /* Iterate over all allocated logical channels */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Set key length and algorithm */
- lchan->a5.key_len = key_len;
- lchan->a5.algo = algo;
-
- /* Copy requested key */
- if (key_len)
- memcpy(lchan->a5.key, key, key_len);
- }
-
- return 0;
-}
-
-struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts,
- enum trx_lchan_type chan)
-{
- struct trx_lchan_state *lchan;
-
- llist_for_each_entry(lchan, &ts->lchans, list)
- if (lchan->type == chan)
- return lchan;
-
- return NULL;
-}
-
-int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode)
-{
- const struct trx_lchan_desc *lchan_desc;
- struct trx_lchan_state *lchan;
- int rc = 0;
-
- /* Prevent NULL-pointer deference */
- if (ts == NULL) {
- LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n");
- return -EINVAL;
- }
-
- /* Iterate over all allocated lchans */
- llist_for_each_entry(lchan, &ts->lchans, list) {
- lchan_desc = &trx_lchan_desc[lchan->type];
-
- if (lchan_desc->chan_nr == (chan_nr & 0xf8)) {
- if (active) {
- rc |= sched_trx_activate_lchan(ts, lchan->type);
- lchan->tch_mode = tch_mode;
- } else
- rc |= sched_trx_deactivate_lchan(ts, lchan->type);
- }
- }
-
- return rc;
-}
-
-int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
-{
- const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan];
- struct trx_lchan_state *lchan;
-
- /* Try to find requested logical channel */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- return -EINVAL;
-
- if (lchan->active) {
- LOGP(DSCH, LOGL_ERROR, "Logical channel %s already activated "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
- return -EINVAL;
- }
-
- LOGP(DSCH, LOGL_NOTICE, "Activating lchan=%s "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
-
- /* Conditionally allocate memory for bursts */
- if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) {
- lchan->rx_bursts = talloc_zero_size(lchan,
- lchan_desc->burst_buf_size);
- if (lchan->rx_bursts == NULL)
- return -ENOMEM;
- }
-
- if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) {
- lchan->tx_bursts = talloc_zero_size(lchan,
- lchan_desc->burst_buf_size);
- if (lchan->tx_bursts == NULL)
- return -ENOMEM;
- }
-
- /* Finally, update channel status */
- lchan->active = 1;
-
- return 0;
-}
-
-static void sched_trx_reset_lchan(struct trx_lchan_state *lchan)
-{
- /* Prevent NULL-pointer deference */
- OSMO_ASSERT(lchan != NULL);
-
- /* Reset internal state variables */
- lchan->rx_burst_mask = 0x00;
- lchan->tx_burst_mask = 0x00;
- lchan->rx_first_fn = 0;
-
- /* Free burst memory */
- talloc_free(lchan->rx_bursts);
- talloc_free(lchan->tx_bursts);
-
- lchan->rx_bursts = NULL;
- lchan->tx_bursts = NULL;
-
- /* Forget the current prim */
- sched_prim_drop(lchan);
-
- /* Channel specific stuff */
- if (CHAN_IS_TCH(lchan->type)) {
- lchan->dl_ongoing_facch = 0;
- lchan->ul_facch_blocks = 0;
-
- lchan->tch_mode = GSM48_CMODE_SIGN;
-
- /* Reset AMR state */
- memset(&lchan->amr, 0x00, sizeof(lchan->amr));
- } else if (CHAN_IS_SACCH(lchan->type)) {
- /* Reset SACCH state */
- memset(&lchan->sacch, 0x00, sizeof(lchan->sacch));
- }
-
- /* Reset ciphering state */
- memset(&lchan->a5, 0x00, sizeof(lchan->a5));
-}
-
-int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan)
-{
- struct trx_lchan_state *lchan;
-
- /* Try to find requested logical channel */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- return -EINVAL;
-
- if (!lchan->active) {
- LOGP(DSCH, LOGL_ERROR, "Logical channel %s already deactivated "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
- return -EINVAL;
- }
-
- LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s "
- "on ts=%d\n", trx_lchan_desc[chan].name, ts->index);
-
- /* Reset internal state, free memory */
- sched_trx_reset_lchan(lchan);
-
- /* Update activation flag */
- lchan->active = 0;
-
- return 0;
-}
-
-void sched_trx_deactivate_all_lchans(struct trx_ts *ts)
-{
- struct trx_lchan_state *lchan;
-
- LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels "
- "on ts=%d\n", ts->index);
-
- llist_for_each_entry(lchan, &ts->lchans, list) {
- /* Omit inactive channels */
- if (!lchan->active)
- continue;
-
- /* Reset internal state, free memory */
- sched_trx_reset_lchan(lchan);
-
- /* Update activation flag */
- lchan->active = 0;
- }
-}
-
-enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr)
-{
- uint8_t cbits = chan_nr >> 3;
-
- if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
- return GSM_PCHAN_TCH_F;
- else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
- return GSM_PCHAN_TCH_H;
- else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
- return GSM_PCHAN_CCCH_SDCCH4;
- else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
- return GSM_PCHAN_SDCCH8_SACCH8C;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4)
- return GSM_PCHAN_CCCH_SDCCH4_CBCH;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
- return GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
- else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH)
- return GSM_PCHAN_PDCH;
-
- return GSM_PCHAN_NONE;
-}
-
-enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id)
-{
- int i;
-
- /* Iterate over all known lchan types */
- for (i = 0; i < _TRX_CHAN_MAX; i++)
- if (trx_lchan_desc[i].chan_nr == (chan_nr & 0xf8))
- if (trx_lchan_desc[i].link_id == link_id)
- return i;
-
- return TRXC_IDLE;
-}
-
-static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan,
- uint32_t fn, sbit_t *burst)
-{
- ubit_t ks[114];
- int i;
-
- /* Generate keystream for a DL burst */
- osmo_a5(lchan->a5.algo, lchan->a5.key, fn, ks, NULL);
-
- /* Apply keystream over ciphertext */
- for (i = 0; i < 57; i++) {
- if (ks[i])
- burst[i + 3] *= -1;
- if (ks[i + 57])
- burst[i + 88] *= -1;
- }
-}
-
-static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan,
- uint32_t fn, ubit_t *burst)
-{
- ubit_t ks[114];
- int i;
-
- /* Generate keystream for an UL burst */
- osmo_a5(lchan->a5.algo, lchan->a5.key, fn, NULL, ks);
-
- /* Apply keystream over plaintext */
- for (i = 0; i < 57; i++) {
- burst[i + 3] ^= ks[i];
- burst[i + 88] ^= ks[i + 57];
- }
-}
-
-int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
- int8_t rssi, int16_t toa256)
-{
- struct trx_lchan_state *lchan;
- const struct trx_frame *frame;
- struct trx_ts *ts;
-
- trx_lchan_rx_func *handler;
- enum trx_lchan_type chan;
- uint32_t fn, elapsed;
- uint8_t offset, bid;
-
- /* Check whether required timeslot is allocated and configured */
- ts = trx->ts_list[tn];
- if (ts == NULL || ts->mf_layout == NULL) {
- LOGP(DSCHD, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, "
- "ignoring burst...\n", tn);
- return -EINVAL;
- }
-
- /* Calculate how many frames have been elapsed */
- elapsed = TDMA_FN_SUB(burst_fn, ts->mf_last_fn);
-
- /**
- * If not too many frames have been elapsed,
- * start counting from last fn + 1
- */
- if (elapsed < 10)
- fn = TDMA_FN_INC(ts->mf_last_fn);
- else
- fn = burst_fn;
-
- while (1) {
- /* Get frame from multiframe */
- offset = fn % ts->mf_layout->period;
- frame = ts->mf_layout->frames + offset;
-
- /* Get required info from frame */
- bid = frame->dl_bid;
- chan = frame->dl_chan;
- handler = trx_lchan_desc[chan].rx_fn;
-
- /* Omit bursts which have no handler, like IDLE bursts */
- if (!handler)
- goto next_frame;
-
- /* Find required channel state */
- lchan = sched_trx_find_lchan(ts, chan);
- if (lchan == NULL)
- goto next_frame;
-
- /* Ensure that channel is active */
- if (!lchan->active)
- goto next_frame;
-
- /* Reached current fn */
- if (fn == burst_fn) {
- /* Perform A5/X decryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_dec(lchan, fn, bits);
-
- /* Put burst to handler */
- handler(trx, ts, lchan, fn, bid, bits, rssi, toa256);
- }
-
-next_frame:
- /* Reached current fn */
- if (fn == burst_fn)
- break;
-
- fn = TDMA_FN_INC(fn);
- }
-
- /* Set last processed frame number */
- ts->mf_last_fn = fn;
-
- return 0;
-}
-
-int sched_trx_handle_tx_burst(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, ubit_t *bits)
-{
- int rc;
-
- /* Perform A5/X burst encryption if required */
- if (lchan->a5.algo)
- sched_trx_a5_burst_enc(lchan, fn, bits);
-
- /* Forward burst to transceiver */
- rc = trx_if_tx_burst(trx, ts->index, fn, trx->tx_power, bits);
- if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Could not send burst to transceiver\n");
- return rc;
- }
-
- return 0;
-}
diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h
deleted file mode 100644
index 6ef9ce47..00000000
--- a/src/host/trxcon/sched_trx.h
+++ /dev/null
@@ -1,365 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <osmocom/core/bits.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/gsm/gsm_utils.h>
-#include <osmocom/core/linuxlist.h>
-
-#include "logging.h"
-#include "scheduler.h"
-
-#define GSM_BURST_LEN 148
-#define GSM_BURST_PL_LEN 116
-
-#define GPRS_BURST_LEN GSM_BURST_LEN
-#define EDGE_BURST_LEN 444
-
-#define GPRS_L2_MAX_LEN 54
-#define EDGE_L2_MAX_LEN 155
-
-#define TRX_CH_LID_DEDIC 0x00
-#define TRX_CH_LID_SACCH 0x40
-
-/* Is a channel related to PDCH (GPRS) */
-#define TRX_CH_FLAG_PDCH (1 << 0)
-/* Should a channel be activated automatically */
-#define TRX_CH_FLAG_AUTO (1 << 1)
-/* Is continuous burst transmission assumed */
-#define TRX_CH_FLAG_CBTX (1 << 2)
-
-#define MAX_A5_KEY_LEN (128 / 8)
-#define TRX_TS_COUNT 8
-
-/* Forward declaration to avoid mutual include */
-struct trx_lchan_state;
-struct trx_instance;
-struct trx_ts;
-
-enum trx_burst_type {
- TRX_BURST_GMSK,
- TRX_BURST_8PSK,
-};
-
-/**
- * These types define the different channels on a multiframe.
- * Each channel has queues and can be activated individually.
- */
-enum trx_lchan_type {
- TRXC_IDLE = 0,
- TRXC_FCCH,
- TRXC_SCH,
- TRXC_BCCH,
- TRXC_RACH,
- TRXC_CCCH,
- TRXC_TCHF,
- TRXC_TCHH_0,
- TRXC_TCHH_1,
- TRXC_SDCCH4_0,
- TRXC_SDCCH4_1,
- TRXC_SDCCH4_2,
- TRXC_SDCCH4_3,
- TRXC_SDCCH8_0,
- TRXC_SDCCH8_1,
- TRXC_SDCCH8_2,
- TRXC_SDCCH8_3,
- TRXC_SDCCH8_4,
- TRXC_SDCCH8_5,
- TRXC_SDCCH8_6,
- TRXC_SDCCH8_7,
- TRXC_SACCHTF,
- TRXC_SACCHTH_0,
- TRXC_SACCHTH_1,
- TRXC_SACCH4_0,
- TRXC_SACCH4_1,
- TRXC_SACCH4_2,
- TRXC_SACCH4_3,
- TRXC_SACCH8_0,
- TRXC_SACCH8_1,
- TRXC_SACCH8_2,
- TRXC_SACCH8_3,
- TRXC_SACCH8_4,
- TRXC_SACCH8_5,
- TRXC_SACCH8_6,
- TRXC_SACCH8_7,
- TRXC_PDTCH,
- TRXC_PTCCH,
- TRXC_SDCCH4_CBCH,
- TRXC_SDCCH8_CBCH,
- _TRX_CHAN_MAX
-};
-
-typedef int trx_lchan_rx_func(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, uint8_t bid, sbit_t *bits,
- int8_t rssi, int16_t toa256);
-
-typedef int trx_lchan_tx_func(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, uint8_t bid);
-
-struct trx_lchan_desc {
- /*! \brief Human-readable name */
- const char *name;
- /*! \brief Human-readable description */
- const char *desc;
-
- /*! \brief Channel Number (like in RSL) */
- uint8_t chan_nr;
- /*! \brief Link ID (like in RSL) */
- uint8_t link_id;
-
- /*! \brief How much memory do we need to store bursts */
- size_t burst_buf_size;
- /*! \brief Channel specific flags */
- uint8_t flags;
-
- /*! \brief Function to call when burst received from PHY */
- trx_lchan_rx_func *rx_fn;
- /*! \brief Function to call when data received from L2 */
- trx_lchan_tx_func *tx_fn;
-};
-
-struct trx_frame {
- /*! \brief Downlink TRX channel type */
- enum trx_lchan_type dl_chan;
- /*! \brief Downlink block ID */
- uint8_t dl_bid;
- /*! \brief Uplink TRX channel type */
- enum trx_lchan_type ul_chan;
- /*! \brief Uplink block ID */
- uint8_t ul_bid;
-};
-
-struct trx_multiframe {
- /*! \brief Channel combination */
- enum gsm_phys_chan_config chan_config;
- /*! \brief Human-readable name */
- const char *name;
- /*! \brief Repeats how many frames */
- uint8_t period;
- /*! \brief Applies to which timeslots */
- uint8_t slotmask;
- /*! \brief Contains which lchans */
- uint64_t lchan_mask;
- /*! \brief Pointer to scheduling structure */
- const struct trx_frame *frames;
-};
-
-/* States each channel on a multiframe */
-struct trx_lchan_state {
- /*! \brief Channel type */
- enum trx_lchan_type type;
- /*! \brief Channel status */
- uint8_t active;
- /*! \brief Link to a list of channels */
- struct llist_head list;
-
- /*! \brief Burst type: GMSK or 8PSK */
- enum trx_burst_type burst_type;
- /*! \brief Frame number of first burst */
- uint32_t rx_first_fn;
- /*! \brief Mask of received bursts */
- uint8_t rx_burst_mask;
- /*! \brief Mask of transmitted bursts */
- uint8_t tx_burst_mask;
- /*! \brief Burst buffer for RX */
- sbit_t *rx_bursts;
- /*! \brief Burst buffer for TX */
- ubit_t *tx_bursts;
-
- /*! \brief A primitive being sent */
- struct trx_ts_prim *prim;
-
- /*! \brief Mode for TCH channels (see GSM48_CMODE_*) */
- uint8_t tch_mode;
-
- /*! \brief FACCH/H on downlink */
- bool dl_ongoing_facch;
- /*! \brief pending FACCH/H blocks on Uplink */
- uint8_t ul_facch_blocks;
-
- struct {
- /*! \brief Number of measurements */
- unsigned int num;
- /*! \brief Sum of RSSI values */
- float rssi_sum;
- /*! \brief Sum of TOA values */
- int32_t toa256_sum;
- } meas;
-
- /*! \brief SACCH state */
- struct {
- /*! \brief Cached measurement report (last received) */
- uint8_t mr_cache[GSM_MACBLOCK_LEN];
- /*! \brief Cache usage counter */
- uint8_t mr_cache_usage;
- /*! \brief Was a MR transmitted last time? */
- bool mr_tx_last;
- } sacch;
-
- /* AMR specific */
- struct {
- /*! \brief 4 possible codecs for AMR */
- uint8_t codec[4];
- /*! \brief Number of possible codecs */
- uint8_t codecs;
- /*! \brief Current uplink FT index */
- uint8_t ul_ft;
- /*! \brief Current downlink FT index */
- uint8_t dl_ft;
- /*! \brief Current uplink CMR index */
- uint8_t ul_cmr;
- /*! \brief Current downlink CMR index */
- uint8_t dl_cmr;
- /*! \brief If AMR loop is enabled */
- uint8_t amr_loop;
- /*! \brief Number of bit error rates */
- uint8_t ber_num;
- /*! \brief Sum of bit error rates */
- float ber_sum;
- } amr;
-
- /*! \brief A5/X encryption state */
- struct {
- uint8_t key[MAX_A5_KEY_LEN];
- uint8_t key_len;
- uint8_t algo;
- } a5;
-};
-
-struct trx_ts {
- /*! \brief Timeslot index within a frame (0..7) */
- uint8_t index;
- /*! \brief Last received frame number */
- uint32_t mf_last_fn;
-
- /*! \brief Pointer to multiframe layout */
- const struct trx_multiframe *mf_layout;
- /*! \brief Channel states for logical channels */
- struct llist_head lchans;
- /*! \brief Queue primitives for TX */
- struct llist_head tx_prims;
-};
-
-/* Represents one TX primitive in the queue of trx_ts */
-struct trx_ts_prim {
- /*! \brief Link to queue of TS */
- struct llist_head list;
- /*! \brief Logical channel type */
- enum trx_lchan_type chan;
- /*! \brief Payload length */
- size_t payload_len;
- /*! \brief Payload */
- uint8_t payload[0];
-};
-
-extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX];
-const struct trx_multiframe *sched_mframe_layout(
- enum gsm_phys_chan_config config, int tn);
-
-/* Scheduler management functions */
-int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance);
-int sched_trx_reset(struct trx_instance *trx, bool reset_clock);
-int sched_trx_shutdown(struct trx_instance *trx);
-
-/* Timeslot management functions */
-struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn);
-void sched_trx_del_ts(struct trx_instance *trx, int tn);
-int sched_trx_reset_ts(struct trx_instance *trx, int tn);
-int sched_trx_configure_ts(struct trx_instance *trx, int tn,
- enum gsm_phys_chan_config config);
-int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo,
- uint8_t *key, uint8_t key_len);
-
-/* Logical channel management functions */
-enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr);
-enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr,
- uint8_t link_id);
-
-void sched_trx_deactivate_all_lchans(struct trx_ts *ts);
-int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode);
-int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan);
-int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan);
-struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts,
- enum trx_lchan_type chan);
-
-/* Primitive management functions */
-int sched_prim_init(void *ctx, struct trx_ts_prim **prim,
- size_t pl_len, uint8_t chan_nr, uint8_t link_id);
-int sched_prim_push(struct trx_instance *trx,
- struct trx_ts_prim *prim, uint8_t chan_nr);
-
-#define TCH_MODE_IS_SPEECH(mode) \
- (mode == GSM48_CMODE_SPEECH_V1 \
- || mode == GSM48_CMODE_SPEECH_EFR \
- || mode == GSM48_CMODE_SPEECH_AMR)
-
-#define TCH_MODE_IS_DATA(mode) \
- (mode == GSM48_CMODE_DATA_14k5 \
- || mode == GSM48_CMODE_DATA_12k0 \
- || mode == GSM48_CMODE_DATA_6k0 \
- || mode == GSM48_CMODE_DATA_3k6)
-
-#define CHAN_IS_TCH(chan) \
- (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1)
-
-#define CHAN_IS_SACCH(chan) \
- (trx_lchan_desc[chan].link_id & TRX_CH_LID_SACCH)
-
-/* FIXME: we need a better way to identify / distinguish primitives */
-#define PRIM_IS_RACH11(prim) \
- (prim->payload_len == sizeof(struct l1ctl_ext_rach_req))
-
-#define PRIM_IS_RACH8(prim) \
- (prim->payload_len == sizeof(struct l1ctl_rach_req))
-
-#define PRIM_IS_RACH(prim) \
- (PRIM_IS_RACH8(prim) || PRIM_IS_RACH11(prim))
-
-#define PRIM_IS_TCH(prim) \
- (CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN)
-
-#define PRIM_IS_FACCH(prim) \
- (CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN)
-
-struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue,
- uint32_t fn, struct trx_lchan_state *lchan);
-int sched_prim_dummy(struct trx_lchan_state *lchan);
-void sched_prim_drop(struct trx_lchan_state *lchan);
-void sched_prim_flush_queue(struct llist_head *list);
-
-int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn,
- uint32_t burst_fn, sbit_t *bits, uint16_t nbits,
- int8_t rssi, int16_t toa256);
-int sched_trx_handle_tx_burst(struct trx_instance *trx,
- struct trx_ts *ts, struct trx_lchan_state *lchan,
- uint32_t fn, ubit_t *bits);
-
-/* Shared declarations for lchan handlers */
-extern const uint8_t sched_nb_training_bits[8][26];
-
-size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan);
-int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len,
- int bit_error_count, bool dec_failed, bool traffic);
-int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, bool traffic);
-
-/* Interleaved TCH/H block TDMA frame mapping */
-uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan,
- uint32_t last_fn, bool facch);
-bool sched_tchh_block_map_fn(enum trx_lchan_type chan,
- uint32_t fn, bool ul, bool facch, bool start);
-
-#define sched_tchh_traffic_start(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 0, 1)
-#define sched_tchh_traffic_end(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 0, 0)
-
-#define sched_tchh_facch_start(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 1, 1)
-#define sched_tchh_facch_end(chan, fn, ul) \
- sched_tchh_block_map_fn(chan, fn, ul, 1, 0)
diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h
deleted file mode 100644
index 7ab17ab5..00000000
--- a/src/host/trxcon/scheduler.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <time.h>
-
-#include <osmocom/core/timer.h>
-
-#define FRAME_DURATION_uS 4615
-
-#define GSM_SUPERFRAME (26 * 51)
-#define GSM_HYPERFRAME (2048 * GSM_SUPERFRAME)
-
-/* TDMA frame number arithmetics */
-#define TDMA_FN_SUM(a, b) \
- ((a + b) % GSM_HYPERFRAME)
-#define TDMA_FN_SUB(a, b) \
- ((a + GSM_HYPERFRAME - b) % GSM_HYPERFRAME)
-#define TDMA_FN_INC(fn) \
- TDMA_FN_SUM(fn, 1)
-#define TDMA_FN_MIN(a, b) \
- (a < b ? a : b)
-#define TDMA_FN_DIFF(a, b) \
- TDMA_FN_MIN(TDMA_FN_SUB(a, b), TDMA_FN_SUB(b, a))
-
-enum tdma_sched_clck_state {
- SCH_CLCK_STATE_WAIT,
- SCH_CLCK_STATE_OK,
-};
-
-/* Forward structure declaration */
-struct trx_sched;
-
-/*! \brief One scheduler instance */
-struct trx_sched {
- /*! \brief Clock state */
- uint8_t state;
- /*! \brief Local clock source */
- struct timespec clock;
- /*! \brief Count of processed frames */
- uint32_t fn_counter_proc;
- /*! \brief Local frame counter advance */
- uint32_t fn_counter_advance;
- /*! \brief Frame counter */
- uint32_t fn_counter_lost;
- /*! \brief Frame callback timer */
- struct osmo_timer_list clock_timer;
- /*! \brief Frame callback */
- void (*clock_cb)(struct trx_sched *sched);
- /*! \brief Private data (e.g. pointer to trx instance) */
- void *data;
-};
-
-int sched_clck_handle(struct trx_sched *sched, uint32_t fn);
-void sched_clck_reset(struct trx_sched *sched);
diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am
new file mode 100644
index 00000000..7be7de62
--- /dev/null
+++ b/src/host/trxcon/src/Makefile.am
@@ -0,0 +1,64 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOCODING_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(NULL)
+
+
+noinst_LTLIBRARIES = libl1sched.la
+
+libl1sched_la_SOURCES = \
+ sched_lchan_common.c \
+ sched_lchan_pdtch.c \
+ sched_lchan_desc.c \
+ sched_lchan_xcch.c \
+ sched_lchan_tchf.c \
+ sched_lchan_tchh.c \
+ sched_lchan_rach.c \
+ sched_lchan_sch.c \
+ sched_mframe.c \
+ sched_prim.c \
+ sched_trx.c \
+ $(NULL)
+
+
+noinst_LTLIBRARIES += libl1gprs.la
+
+libl1gprs_la_SOURCES = \
+ l1gprs.c \
+ $(NULL)
+
+
+noinst_LTLIBRARIES += libtrxcon.la
+
+libtrxcon_la_SOURCES = \
+ trxcon_inst.c \
+ trxcon_fsm.c \
+ trxcon_shim.c \
+ l1ctl.c \
+ $(NULL)
+
+
+bin_PROGRAMS = trxcon
+
+trxcon_SOURCES = \
+ l1ctl_server.c \
+ trxcon_main.c \
+ logging.c \
+ trx_if.c \
+ $(NULL)
+
+trxcon_LDADD = \
+ libtrxcon.la \
+ libl1sched.la \
+ libl1gprs.la \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOCODING_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(NULL)
diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c
new file mode 100644
index 00000000..0f6bd1be
--- /dev/null
+++ b/src/host/trxcon/src/l1ctl.c
@@ -0,0 +1,849 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * GSM L1 control interface handlers
+ *
+ * (C) 2014 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/l1ctl_proto.h>
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+
+#define L1CTL_LENGTH 512
+#define L1CTL_HEADROOM 32
+
+/* Logging categories configurable via trxcon_set_log_cfg() */
+int g_logc_l1c = DLGLOBAL;
+int g_logc_l1d = DLGLOBAL;
+
+static const struct value_string l1ctl_ccch_mode_names[] = {
+ { CCCH_MODE_NONE, "NONE" },
+ { CCCH_MODE_NON_COMBINED, "NON_COMBINED" },
+ { CCCH_MODE_COMBINED, "COMBINED" },
+ { CCCH_MODE_COMBINED_CBCH, "COMBINED_CBCH" },
+ { 0, NULL },
+};
+
+static const struct value_string l1ctl_reset_names[] = {
+ { L1CTL_RES_T_BOOT, "BOOT" },
+ { L1CTL_RES_T_FULL, "FULL" },
+ { L1CTL_RES_T_SCHED, "SCHED" },
+ { 0, NULL },
+};
+
+static const char *arfcn2band_name(uint16_t arfcn)
+{
+ enum gsm_band band;
+
+ if (gsm_arfcn2band_rc(arfcn, &band) < 0)
+ return "(invalid)";
+
+ return gsm_band_name(band);
+}
+
+static struct msgb *l1ctl_alloc_msg(uint8_t msg_type)
+{
+ struct l1ctl_hdr *l1h;
+ struct msgb *msg;
+
+ /**
+ * Each L1CTL message gets its own length pushed in front
+ * before sending. This is why we need this small headroom.
+ */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
+ L1CTL_HEADROOM, "l1ctl_tx_msg");
+ if (!msg) {
+ LOGP(g_logc_l1c, LOGL_ERROR, "Failed to allocate memory\n");
+ return NULL;
+ }
+
+ msg->l1h = msgb_put(msg, sizeof(*l1h));
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = msg_type;
+
+ return msg;
+}
+
+int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_pm_conf *pmc;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_PM_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG,
+ "Send PM Conf (%s %d = %d dBm)\n",
+ arfcn2band_name(band_arfcn),
+ band_arfcn & ~ARFCN_FLAG_MASK, dbm);
+
+ pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc));
+ pmc->band_arfcn = htons(band_arfcn);
+ pmc->pm[0] = dbm2rxlev(dbm);
+ pmc->pm[1] = 0;
+
+ if (last) {
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->flags |= L1CTL_F_DONE;
+ }
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = l1ctl_alloc_msg(L1CTL_RESET_IND);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Ind (%s)\n",
+ get_value_string(l1ctl_reset_names, type));
+
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct msgb *msg;
+ struct l1ctl_reset *res;
+
+ msg = l1ctl_alloc_msg(L1CTL_RESET_CONF);
+ if (!msg)
+ return -ENOMEM;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Conf (%s)\n",
+ get_value_string(l1ctl_reset_names, type));
+ res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res));
+ res->type = type;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg,
+ const struct l1ctl_info_dl *dl_info)
+{
+ size_t len = sizeof(struct l1ctl_info_dl);
+ struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len);
+
+ if (dl_info) /* Copy DL info provided by handler */
+ memcpy(dl, dl_info, len);
+ else /* Init DL info header */
+ memset(dl, 0x00, len);
+
+ return dl;
+}
+
+/* Fill in FBSB payload: BSIC and sync result */
+static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic)
+{
+ struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf));
+
+ conf->result = result;
+ conf->bsic = bsic;
+
+ return conf;
+}
+
+int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_info_dl *dl;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ dl = put_dl_info_hdr(msg, NULL);
+
+ /* Fill in current ARFCN */
+ dl->band_arfcn = htons(band_arfcn);
+
+ fbsb_conf_make(msg, 255, 0);
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send FBSB Conf (timeout)\n");
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_fbsb_conf *conf;
+ struct l1ctl_info_dl *dl;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ dl = put_dl_info_hdr(msg, NULL);
+
+ /* Fill in current ARFCN */
+ dl->band_arfcn = htons(band_arfcn);
+
+ conf = fbsb_conf_make(msg, 0, bsic);
+
+ /* FIXME: set proper value */
+ conf->initial_freq_err = 0;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG,
+ "Send FBSB Conf (result=%u, bsic=%u)\n",
+ conf->result, conf->bsic);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode)
+{
+ struct l1ctl_ccch_mode_conf *conf;
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf));
+ conf->ccch_mode = mode;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+/**
+ * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND.
+ */
+int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon,
+ const struct trxcon_param_rx_data_ind *ind)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(ind->traffic ? L1CTL_TRAFFIC_IND : L1CTL_DATA_IND);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .chan_nr = ind->chan_nr,
+ .link_id = ind->link_id,
+ .frame_nr = htonl(ind->frame_nr),
+ .band_arfcn = htons(ind->band_arfcn),
+ .fire_crc = ind->data_len > 0 ? 0 : 2,
+ .rx_level = dbm2rxlev(ind->rssi),
+ .num_biterr = ind->n_errors,
+ /* TODO: set proper .snr */
+ };
+
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ /* Copy the L2 payload if preset */
+ if (ind->data && ind->data_len > 0)
+ memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len);
+
+ /* Put message to upper layers */
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_access_burst_cnf *cnf)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(L1CTL_RACH_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .band_arfcn = htons(cnf->band_arfcn),
+ .frame_nr = htonl(cnf->frame_nr),
+ };
+
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+
+/**
+ * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF.
+ */
+int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon,
+ const struct trxcon_param_tx_data_cnf *cnf)
+{
+ struct msgb *msg;
+
+ msg = l1ctl_alloc_msg(cnf->traffic ? L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF);
+ if (msg == NULL)
+ return -ENOMEM;
+
+ const struct l1ctl_info_dl dl_hdr = {
+ .chan_nr = cnf->chan_nr,
+ .link_id = cnf->link_id,
+ .frame_nr = htonl(cnf->frame_nr),
+ .band_arfcn = htons(cnf->band_arfcn),
+ };
+
+ /* Copy DL frame header from source message */
+ put_dl_info_hdr(msg, &dl_hdr);
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode)
+{
+ switch (mode) {
+ /* TODO: distinguish extended BCCH */
+ case CCCH_MODE_NON_COMBINED:
+ case CCCH_MODE_NONE:
+ return GSM_PCHAN_CCCH;
+
+ case CCCH_MODE_COMBINED:
+ return GSM_PCHAN_CCCH_SDCCH4;
+ case CCCH_MODE_COMBINED_CBCH:
+ return GSM_PCHAN_CCCH_SDCCH4_CBCH;
+
+ default:
+ LOGP(g_logc_l1c, LOGL_NOTICE, "Undandled CCCH mode (%u), "
+ "assuming non-combined configuration\n", mode);
+ return GSM_PCHAN_CCCH;
+ }
+}
+
+static int l1ctl_rx_fbsb_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_fbsb_req *fbsb;
+ int rc = 0;
+
+ fbsb = (const struct l1ctl_fbsb_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*fbsb)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short FBSB Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ struct trxcon_param_fbsb_search_req req = {
+ .pchan_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode),
+ .timeout_fns = ntohs(fbsb->timeout),
+ .band_arfcn = ntohs(fbsb->band_arfcn),
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received FBSB request (%s %d, timeout %u TDMA FNs)\n",
+ arfcn2band_name(req.band_arfcn),
+ req.band_arfcn & ~ARFCN_FLAG_MASK,
+ req.timeout_fns);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_FBSB_SEARCH_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_pm_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_pm_req *pmr;
+ int rc = 0;
+
+ pmr = (const struct l1ctl_pm_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*pmr)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short PM Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ struct trxcon_param_full_power_scan_req req = {
+ .band_arfcn_start = ntohs(pmr->range.band_arfcn_from),
+ .band_arfcn_stop = ntohs(pmr->range.band_arfcn_to),
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received power measurement request (%s: %d -> %d)\n",
+ arfcn2band_name(req.band_arfcn_start),
+ req.band_arfcn_start & ~ARFCN_FLAG_MASK,
+ req.band_arfcn_stop & ~ARFCN_FLAG_MASK);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_FULL_POWER_SCAN_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_reset_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_reset *res;
+ int rc = 0;
+
+ res = (const struct l1ctl_reset *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*res)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short Reset Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received reset request (%s)\n",
+ get_value_string(l1ctl_reset_names, res->type));
+
+ switch (res->type) {
+ case L1CTL_RES_T_FULL:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_FULL_REQ, NULL);
+ break;
+ case L1CTL_RES_T_SCHED:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_SCHED_REQ, NULL);
+ break;
+ default:
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "Unknown L1CTL_RESET_REQ type\n");
+ goto exit;
+ }
+
+ /* Confirm */
+ rc = l1ctl_tx_reset_conf(trxcon, res->type);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_echo_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ struct l1ctl_hdr *l1h;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Recv Echo Req\n");
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Send Echo Conf\n");
+
+ /* Nothing to do, just send it back */
+ l1h = (struct l1ctl_hdr *) msg->l1h;
+ l1h->msg_type = L1CTL_ECHO_CONF;
+ msg->data = msg->l1h;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static int l1ctl_rx_ccch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_ccch_mode_req *mode_req;
+ int rc;
+
+ mode_req = (const struct l1ctl_ccch_mode_req *)msg->l1h;
+ if (msgb_l1len(msg) < sizeof(*mode_req)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "MSG too short Reset Req: %u\n",
+ msgb_l1len(msg));
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received CCCH mode request (%s)\n",
+ get_value_string(l1ctl_ccch_mode_names, mode_req->ccch_mode));
+
+ struct trxcon_param_set_ccch_tch_mode_req req = {
+ /* Choose corresponding channel combination */
+ .mode = l1ctl_ccch_mode2pchan_config(mode_req->ccch_mode),
+ };
+
+ rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_CCCH_MODE_REQ, &req);
+ if (rc == 0 && req.applied)
+ l1ctl_tx_ccch_mode_conf(trxcon, mode_req->ccch_mode);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_rach_req(struct trxcon_inst *trxcon, struct msgb *msg, bool is_11bit)
+{
+ struct trxcon_param_tx_access_burst_req req;
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+
+ if (is_11bit) {
+ const struct l1ctl_ext_rach_req *rr = (void *)ul->payload;
+
+ req = (struct trxcon_param_tx_access_burst_req) {
+ .offset = ntohs(rr->offset),
+ .synch_seq = rr->synch_seq,
+ .ra = ntohs(rr->ra11),
+ .is_11bit = true,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received 11-bit RACH request "
+ "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n",
+ req.offset, req.synch_seq, req.ra);
+ } else {
+ const struct l1ctl_rach_req *rr = (void *)ul->payload;
+
+ req = (struct trxcon_param_tx_access_burst_req) {
+ .offset = ntohs(rr->offset),
+ .ra = rr->ra,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received 8-bit RACH request "
+ "(offset=%u, ra=0x%02x)\n", req.offset, req.ra);
+ }
+
+ /* The controlling L1CTL side always does include the UL info header,
+ * but may leave it empty. We assume RACH is on TS0 in this case. */
+ if (ul->chan_nr == 0x00) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "The UL info header is empty, assuming RACH is on TS0\n");
+ req.chan_nr = RSL_CHAN_RACH;
+ req.link_id = 0x00;
+ } else {
+ req.chan_nr = ul->chan_nr;
+ req.link_id = ul->link_id;
+ }
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_ACCESS_BURST_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_proc_est_req_h0(struct osmo_fsm_inst *fi,
+ struct trxcon_param_dch_est_req *req,
+ const struct l1ctl_h0 *h)
+{
+ req->h0.band_arfcn = ntohs(h->band_arfcn);
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_DM_EST_REQ indicates single ARFCN %s %u\n",
+ arfcn2band_name(req->h0.band_arfcn),
+ req->h0.band_arfcn & ~ARFCN_FLAG_MASK);
+
+ return 0;
+}
+
+static int l1ctl_proc_est_req_h1(struct osmo_fsm_inst *fi,
+ struct trxcon_param_dch_est_req *req,
+ const struct l1ctl_h1 *h)
+{
+ unsigned int i;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_DM_EST_REQ indicates a Frequency "
+ "Hopping (hsn=%u, maio=%u, chans=%u) channel\n",
+ h->hsn, h->maio, h->n);
+
+ /* No channels?!? */
+ if (!h->n) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "No channels in mobile allocation?!?\n");
+ return -EINVAL;
+ } else if (h->n > ARRAY_SIZE(h->ma)) {
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR,
+ "More than 64 channels in mobile allocation?!?\n");
+ return -EINVAL;
+ }
+
+ /* Convert from network to host byte order */
+ for (i = 0; i < h->n; i++)
+ req->h1.ma[i] = ntohs(h->ma[i]);
+ req->h1.n = h->n;
+ req->h1.hsn = h->hsn;
+ req->h1.maio = h->maio;
+
+ return 0;
+}
+
+static int l1ctl_rx_dm_est_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_dm_est_req *est_req;
+ const struct l1ctl_info_ul *ul;
+ int rc;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ est_req = (const struct l1ctl_dm_est_req *)ul->payload;
+
+ struct trxcon_param_dch_est_req req = {
+ .chan_nr = ul->chan_nr,
+ .tch_mode = est_req->tch_mode,
+ .tsc = est_req->tsc,
+ .hopping = est_req->h,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_DM_EST_REQ "
+ "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=%s)\n",
+ req.chan_nr & 0x07, req.chan_nr, req.tsc,
+ gsm48_chan_mode_name(est_req->tch_mode));
+
+ /* Frequency hopping? */
+ if (est_req->h)
+ rc = l1ctl_proc_est_req_h1(fi, &req, &est_req->h1);
+ else /* Single ARFCN */
+ rc = l1ctl_proc_est_req_h0(fi, &req, &est_req->h0);
+ if (rc)
+ goto exit;
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_EST_REQ, &req);
+
+exit:
+ msgb_free(msg);
+ return rc;
+}
+
+static int l1ctl_rx_dm_rel_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ\n");
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_REL_REQ, NULL);
+
+ msgb_free(msg);
+ return 0;
+}
+
+/**
+ * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ.
+ */
+static int l1ctl_rx_dt_req(struct trxcon_inst *trxcon, struct msgb *msg, bool traffic)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_info_ul *ul;
+
+ /* Extract UL frame header */
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ msg->l2h = (uint8_t *)ul->payload;
+
+ struct trxcon_param_tx_data_req req = {
+ .traffic = traffic,
+ .chan_nr = ul->chan_nr,
+ .link_id = ul->link_id & 0x40,
+ .data_len = msgb_l2len(msg),
+ .data = ul->payload,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1d, LOGL_DEBUG,
+ "Recv %s Req (chan_nr=0x%02x, link_id=0x%02x, len=%zu)\n",
+ traffic ? "TRAFFIC" : "DATA", req.chan_nr, req.link_id, req.data_len);
+
+ switch (fi->state) {
+ case TRXCON_ST_DEDICATED:
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_DATA_REQ, &req);
+ break;
+ default:
+ if (!traffic && req.link_id == 0x40) /* only for SACCH */
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_UPDATE_SACCH_CACHE_REQ, &req);
+ /* TODO: log an error about uhnandled DATA.req / TRAFFIC.req */
+ }
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_rx_param_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_par_req *par_req;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ par_req = (const struct l1ctl_par_req *)ul->payload;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_PARAM_REQ (ta=%d, tx_power=%u)\n",
+ par_req->ta, par_req->tx_power);
+
+ struct trxcon_param_set_phy_config_req req = {
+ .type = TRXCON_PHY_CFGT_TX_PARAMS,
+ .tx_params = {
+ .timing_advance = par_req->ta,
+ .tx_power = par_req->tx_power,
+ }
+ };
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+static int l1ctl_rx_tch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_tch_mode_req *mode_req;
+ int rc;
+
+ mode_req = (const struct l1ctl_tch_mode_req *)msg->l1h;
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "Received L1CTL_TCH_MODE_REQ (tch_mode=%s, audio_mode=%u)\n",
+ gsm48_chan_mode_name(mode_req->tch_mode), mode_req->audio_mode);
+
+ /* TODO: do we need to care about audio_mode? */
+
+ struct trxcon_param_set_ccch_tch_mode_req req = {
+ .mode = mode_req->tch_mode,
+ };
+ if (mode_req->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ req.amr.start_codec = mode_req->amr.start_codec;
+ req.amr.codecs_bitmask = mode_req->amr.codecs_bitmask;
+ }
+
+ rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_TCH_MODE_REQ, &req);
+ if (rc != 0 || !req.applied) {
+ talloc_free(msg);
+ return rc;
+ }
+
+ /* Re-use the original message as confirmation */
+ struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
+ l1h->msg_type = L1CTL_TCH_MODE_CONF;
+
+ return trxcon_l1ctl_send(trxcon, msg);
+}
+
+static int l1ctl_rx_crypto_req(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct osmo_fsm_inst *fi = trxcon->fi;
+ const struct l1ctl_crypto_req *cr;
+ const struct l1ctl_info_ul *ul;
+
+ ul = (const struct l1ctl_info_ul *)msg->l1h;
+ cr = (const struct l1ctl_crypto_req *)ul->payload;
+
+ struct trxcon_param_crypto_req req = {
+ .chan_nr = ul->chan_nr,
+ .a5_algo = cr->algo,
+ .key_len = cr->key_len,
+ .key = cr->key,
+ };
+
+ LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE,
+ "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n",
+ req.a5_algo, req.key_len);
+
+ osmo_fsm_inst_dispatch(fi, TRXCON_EV_CRYPTO_REQ, &req);
+
+ msgb_free(msg);
+ return 0;
+}
+
+int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1ctl_hdr *l1h;
+ int rc;
+
+ l1h = (const struct l1ctl_hdr *)msg->l1h;
+ msg->l1h = (uint8_t *)l1h->data;
+
+ switch (l1h->msg_type) {
+ case L1CTL_FBSB_REQ:
+ return l1ctl_rx_fbsb_req(trxcon, msg);
+ case L1CTL_PM_REQ:
+ return l1ctl_rx_pm_req(trxcon, msg);
+ case L1CTL_RESET_REQ:
+ return l1ctl_rx_reset_req(trxcon, msg);
+ case L1CTL_ECHO_REQ:
+ return l1ctl_rx_echo_req(trxcon, msg);
+ case L1CTL_CCCH_MODE_REQ:
+ return l1ctl_rx_ccch_mode_req(trxcon, msg);
+ case L1CTL_RACH_REQ:
+ return l1ctl_rx_rach_req(trxcon, msg, false);
+ case L1CTL_EXT_RACH_REQ:
+ return l1ctl_rx_rach_req(trxcon, msg, true);
+ case L1CTL_DM_EST_REQ:
+ return l1ctl_rx_dm_est_req(trxcon, msg);
+ case L1CTL_DM_REL_REQ:
+ return l1ctl_rx_dm_rel_req(trxcon, msg);
+ case L1CTL_DATA_REQ:
+ return l1ctl_rx_dt_req(trxcon, msg, false);
+ case L1CTL_TRAFFIC_REQ:
+ return l1ctl_rx_dt_req(trxcon, msg, true);
+ case L1CTL_PARAM_REQ:
+ return l1ctl_rx_param_req(trxcon, msg);
+ case L1CTL_TCH_MODE_REQ:
+ return l1ctl_rx_tch_mode_req(trxcon, msg);
+ case L1CTL_CRYPTO_REQ:
+ return l1ctl_rx_crypto_req(trxcon, msg);
+ case L1CTL_GPRS_UL_TBF_CFG_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_TBF_CFG_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ case L1CTL_GPRS_DL_TBF_CFG_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_DL_TBF_CFG_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ case L1CTL_GPRS_UL_BLOCK_REQ:
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg);
+ msgb_free(msg);
+ return rc;
+ /* Not (yet) handled messages */
+ case L1CTL_NEIGH_PM_REQ:
+ case L1CTL_DM_FREQ_REQ:
+ case L1CTL_SIM_REQ:
+ LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE,
+ "Ignoring unsupported message (type=%u)\n",
+ l1h->msg_type);
+ msgb_free(msg);
+ return -ENOTSUP;
+ default:
+ LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_ERROR, "Unknown MSG type %u: %s\n",
+ l1h->msg_type, osmo_hexdump(msgb_data(msg), msgb_length(msg)));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+}
diff --git a/src/host/trxcon/src/l1ctl_server.c b/src/host/trxcon/src/l1ctl_server.c
new file mode 100644
index 00000000..c0f10158
--- /dev/null
+++ b/src/host/trxcon/src/l1ctl_server.c
@@ -0,0 +1,282 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * UNIX socket server for L1CTL
+ *
+ * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2022 by by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/write_queue.h>
+
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+
+#define LOGP_CLI(cli, cat, level, fmt, args...) \
+ LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ## args)
+
+static int l1ctl_client_read_cb(struct osmo_fd *ofd)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ struct msgb *msg;
+ uint16_t len;
+ int rc;
+
+ /* Attempt to read from socket */
+ rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD);
+ if (rc != L1CTL_MSG_LEN_FIELD) {
+ if (rc <= 0) {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE,
+ "L1CTL connection error: read() failed (rc=%d): %s\n",
+ rc, strerror(errno));
+ } else {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE,
+ "L1CTL connection error: short read\n");
+ rc = -EIO;
+ }
+ l1ctl_client_conn_close(client);
+ return -EBADF; /* client fd is gone, avoid processing any other events. */
+ }
+
+ /* Check message length */
+ len = ntohs(len);
+ if (len > L1CTL_LENGTH) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
+ return -EINVAL;
+ }
+
+ /* Allocate a new msg */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM,
+ L1CTL_HEADROOM, "l1ctl_rx_msg");
+ if (!msg) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n");
+ return -ENOMEM;
+ }
+
+ msg->l1h = msgb_put(msg, len);
+ rc = read(ofd->fd, msg->l1h, msgb_l1len(msg));
+ if (rc != len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR,
+ "Can not read data: len=%d < rc=%d: %s\n",
+ len, rc, strerror(errno));
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ /* Call L1CTL handler */
+ client->server->cfg->conn_read_cb(client, msg);
+
+ return 0;
+}
+
+static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ int len;
+
+ if (ofd->fd <= 0)
+ return -EINVAL;
+
+ len = write(ofd->fd, msg->data, msg->len);
+ if (len != msg->len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR,
+ "Failed to write data: written (%d) < msg_len (%d)\n",
+ len, msg->len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Connection handler */
+static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags)
+{
+ struct l1ctl_server *server = (struct l1ctl_server *)sfd->data;
+ struct l1ctl_client *client;
+ int rc, client_fd;
+
+ client_fd = accept(sfd->fd, NULL, NULL);
+ if (client_fd < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to accept() a new connection: "
+ "%s\n", strerror(errno));
+ return client_fd;
+ }
+
+ if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ &&
+ server->num_clients >= server->cfg->num_clients_max) {
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL server cannot accept more "
+ "than %u connection(s)\n", server->cfg->num_clients_max);
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ client = talloc_zero(server, struct l1ctl_client);
+ if (client == NULL) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n");
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ /* Init the client's write queue */
+ osmo_wqueue_init(&client->wq, 100);
+ INIT_LLIST_HEAD(&client->wq.bfd.list);
+
+ client->wq.write_cb = &l1ctl_client_write_cb;
+ client->wq.read_cb = &l1ctl_client_read_cb;
+ osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0);
+
+ /* Register the client's write queue */
+ rc = osmo_fd_register(&client->wq.bfd);
+ if (rc != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n");
+ close(client->wq.bfd.fd);
+ talloc_free(client);
+ return rc;
+ }
+
+ llist_add_tail(&client->list, &server->clients);
+ client->id = server->next_client_id++;
+ client->server = server;
+ server->num_clients++;
+
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id);
+
+ if (client->server->cfg->conn_accept_cb != NULL)
+ client->server->cfg->conn_accept_cb(client);
+
+ return 0;
+}
+
+int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg)
+{
+ uint8_t *len;
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ if (msg->l1h != msg->data)
+ LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
+
+ /* Prepend 16-bit length before sending */
+ len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
+ osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
+
+ if (osmo_wqueue_enqueue(&client->wq, msg) != 0) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void l1ctl_client_conn_close(struct l1ctl_client *client)
+{
+ struct l1ctl_server *server = client->server;
+
+ LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n");
+
+ if (server->cfg->conn_close_cb != NULL)
+ server->cfg->conn_close_cb(client);
+
+ /* Close connection socket */
+ osmo_fd_unregister(&client->wq.bfd);
+ close(client->wq.bfd.fd);
+ client->wq.bfd.fd = -1;
+
+ /* Clear pending messages */
+ osmo_wqueue_clear(&client->wq);
+
+ client->server->num_clients--;
+ llist_del(&client->list);
+ talloc_free(client);
+
+ /* If this was the last client, reset the client IDs generator to 0.
+ * This way avoid assigning huge unreadable client IDs like 26545. */
+ if (llist_empty(&server->clients))
+ server->next_client_id = 0;
+}
+
+struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg)
+{
+ struct l1ctl_server *server;
+ int rc;
+
+ LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path);
+
+ server = talloc(ctx, struct l1ctl_server);
+ OSMO_ASSERT(server != NULL);
+
+ *server = (struct l1ctl_server) {
+ .clients = LLIST_HEAD_INIT(server->clients),
+ .cfg = cfg,
+ };
+
+ /* conn_read_cb shall not be NULL */
+ OSMO_ASSERT(cfg->conn_read_cb != NULL);
+
+ /* Bind connection handler */
+ osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0);
+
+ rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0,
+ cfg->sock_path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n",
+ strerror(errno));
+ talloc_free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void l1ctl_server_free(struct l1ctl_server *server)
+{
+ LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n");
+
+ /* Close all client connections */
+ while (!llist_empty(&server->clients)) {
+ struct l1ctl_client *client = llist_entry(server->clients.next,
+ struct l1ctl_client,
+ list);
+ l1ctl_client_conn_close(client);
+ }
+
+ /* Unbind listening socket */
+ if (server->ofd.fd != -1) {
+ osmo_fd_unregister(&server->ofd);
+ close(server->ofd.fd);
+ server->ofd.fd = -1;
+ }
+
+ talloc_free(server);
+}
diff --git a/src/host/trxcon/src/l1gprs.c b/src/host/trxcon/src/l1gprs.c
new file mode 120000
index 00000000..0185f68b
--- /dev/null
+++ b/src/host/trxcon/src/l1gprs.c
@@ -0,0 +1 @@
+../../../shared/l1gprs.c \ No newline at end of file
diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/src/logging.c
index 78915f21..e8730450 100644
--- a/src/host/trxcon/logging.c
+++ b/src/host/trxcon/src/logging.c
@@ -15,19 +15,16 @@
* 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
-#include "logging.h"
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/logging.h>
-static struct log_info_cat trx_log_info_cat[] = {
+static struct log_info_cat trxcon_log_info_cat[] = {
[DAPP] = {
.name = "DAPP",
.description = "Application",
@@ -46,8 +43,8 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
- [DTRX] = {
- .name = "DTRX",
+ [DTRXC] = {
+ .name = "DTRXC",
.description = "Transceiver control interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
@@ -70,19 +67,37 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
+ [DGPRS] = {
+ .name = "DGPRS",
+ .description = "L1 GPRS (MAC layer)",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
};
-static const struct log_info trx_log_info = {
- .cat = trx_log_info_cat,
- .num_cat = ARRAY_SIZE(trx_log_info_cat),
+static const struct log_info trxcon_log_info = {
+ .cat = trxcon_log_info_cat,
+ .num_cat = ARRAY_SIZE(trxcon_log_info_cat),
};
-int trx_log_init(void *tall_ctx, const char *category_mask)
+static const int trxcon_log_cfg[] = {
+ [TRXCON_LOGC_FSM] = DAPP,
+ [TRXCON_LOGC_L1C] = DL1C,
+ [TRXCON_LOGC_L1D] = DL1D,
+ [TRXCON_LOGC_SCHC] = DSCH,
+ [TRXCON_LOGC_SCHD] = DSCHD,
+ [TRXCON_LOGC_GPRS] = DGPRS,
+};
+
+int trxcon_logging_init(void *tall_ctx, const char *category_mask)
{
- osmo_init_logging2(tall_ctx, &trx_log_info);
+ osmo_init_logging2(tall_ctx, &trxcon_log_info);
+ log_target_file_switch_to_wqueue(osmo_stderr_target);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
+ trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
+
return 0;
}
diff --git a/src/host/trxcon/src/sched_lchan_common.c b/src/host/trxcon/src/sched_lchan_common.c
new file mode 100644
index 00000000..2b1729ae
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_common.c
@@ -0,0 +1,137 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: common routines for lchan handlers
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <talloc.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */
+const uint8_t l1sched_nb_training_bits[8][26] = {
+ {
+ 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0,
+ 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1,
+ 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
+ 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0,
+ },
+ {
+ 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
+ 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
+ },
+ {
+ 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
+ 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1,
+ },
+ {
+ 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
+ },
+ {
+ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1,
+ 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
+ },
+ {
+ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0,
+ 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
+ },
+};
+
+/* Get a string representation of the burst buffer's completeness.
+ * Examples: " ****.." (incomplete, 4/6 bursts)
+ * " ****" (complete, all 4 bursts)
+ * "**.***.." (incomplete, 5/8 bursts) */
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits)
+{
+ static char buf[32 + 1];
+ char *ptr = buf;
+
+ OSMO_ASSERT(bits <= 32 && bits > 0);
+
+ while (--bits >= 0)
+ *(ptr++) = (*mask & (1 << bits)) ? '*' : '.';
+ *ptr = '\0';
+
+ return buf;
+}
+
+bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, bool is_cmr)
+{
+ enum osmo_amr_type ft_codec;
+ uint8_t cmr_codec;
+ int ft, cmr, len;
+
+ len = osmo_amr_rtp_dec(msgb_l2(msg), msgb_l2len(msg),
+ &cmr_codec, NULL, &ft_codec, NULL, NULL);
+ if (len < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ return false;
+ }
+ ft = -1;
+ cmr = -1;
+ for (unsigned int i = 0; i < lchan->amr.codecs; i++) {
+ if (lchan->amr.codec[i] == ft_codec)
+ ft = i;
+ if (lchan->amr.codec[i] == cmr_codec)
+ cmr = i;
+ }
+ if (ft < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (FT = %d) of RTP frame not in list\n", ft_codec);
+ return false;
+ }
+ if (is_cmr && lchan->amr.ul_ft != ft) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (FT = %d) of RTP cannot be changed now, but in next frame\n",
+ ft_codec);
+ return false;
+ }
+ lchan->amr.ul_ft = ft;
+ if (cmr < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Codec (CMR = %d) of RTP frame not in list\n", cmr_codec);
+ } else {
+ lchan->amr.ul_cmr = cmr;
+ }
+
+ return true;
+}
diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/src/sched_lchan_desc.c
index 667a88d6..db5446e3 100644
--- a/src/host/trxcon/sched_lchan_desc.c
+++ b/src/host/trxcon/src/sched_lchan_desc.c
@@ -5,6 +5,7 @@
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
* (C) 2015 by Harald Welte <laforge@gnumonks.org>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -24,66 +25,62 @@
*/
#include <osmocom/gsm/protocol/gsm_08_58.h>
-#include "sched_trx.h"
+
+#include <osmocom/bb/l1sched/l1sched.h>
/* Forward declaration of handlers */
-int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+int rx_data_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
+int tx_data_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+int rx_sch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
+int tx_rach_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+int rx_tchf_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
+int tx_tchf_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+int rx_tchh_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
+int tx_tchh_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256);
+int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi);
-int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid);
+int tx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
-const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
- [TRXC_IDLE] = {
+const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = {
+ [L1SCHED_IDLE] = {
.name = "IDLE",
.desc = "Idle channel",
/* The MS needs to perform neighbour measurements during
* IDLE slots, however this is not implemented (yet). */
},
- [TRXC_FCCH] = {
+ [L1SCHED_FCCH] = {
.name = "FCCH", /* 3GPP TS 05.02, section 3.3.2.1 */
.desc = "Frequency correction channel",
/* Handled by transceiver, nothing to do. */
},
- [TRXC_SCH] = {
+ [L1SCHED_SCH] = {
.name = "SCH", /* 3GPP TS 05.02, section 3.3.2.2 */
.desc = "Synchronization channel",
/* 3GPP TS 05.03, section 4.7. Handled by transceiver,
* however we still need to parse BSIC (BCC / NCC). */
- .flags = TRX_CH_FLAG_AUTO,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_sch_fn,
},
- [TRXC_BCCH] = {
+ [L1SCHED_BCCH] = {
.name = "BCCH", /* 3GPP TS 05.02, section 3.3.2.3 */
.desc = "Broadcast control channel",
.chan_nr = RSL_CHAN_BCCH,
@@ -91,20 +88,20 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_RACH] = {
+ [L1SCHED_RACH] = {
.name = "RACH", /* 3GPP TS 05.02, section 3.3.3.1 */
.desc = "Random access channel",
.chan_nr = RSL_CHAN_RACH,
/* Tx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */
- .flags = TRX_CH_FLAG_AUTO,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.tx_fn = tx_rach_fn,
},
- [TRXC_CCCH] = {
+ [L1SCHED_CCCH] = {
.name = "CCCH", /* 3GPP TS 05.02, section 3.3.3.1 */
.desc = "Common control channel",
.chan_nr = RSL_CHAN_PCH_AGCH,
@@ -112,15 +109,15 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
/* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_TCHF] = {
+ [L1SCHED_TCHF] = {
.name = "TCH/F", /* 3GPP TS 05.02, section 3.2 */
.desc = "Full Rate traffic channel",
.chan_nr = RSL_CHAN_Bm_ACCHs,
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
@@ -133,21 +130,23 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
*
* The MS shall continuously transmit bursts, even if there is nothing
* to send, unless DTX (Discontinuous Transmission) is used. */
- .burst_buf_size = 8 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchf_fn,
.tx_fn = tx_tchf_fn,
},
- [TRXC_TCHH_0] = {
+ [L1SCHED_TCHH_0] = {
.name = "TCH/H(0)", /* 3GPP TS 05.02, section 3.2 */
.desc = "Half Rate traffic channel (sub-channel 0)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
*
- * - a traffic frame is interleaved over 6 consecutive bursts
+ * - a traffic frame is interleaved over 4 non-consecutive bursts
+ * using the even numbered bits of the first 2 bursts,
+ * and odd numbered bits of the last 2 bursts;
+ * - a FACCH/H frame is interleaved over 6 non-consecutive bursts
* using the even numbered bits of the first 2 bursts,
* all bits of the middle two 2 bursts,
* and odd numbered bits of the last 2 bursts;
@@ -157,389 +156,365 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = {
*
* The MS shall continuously transmit bursts, even if there is nothing
* to send, unless DTX (Discontinuous Transmission) is used. */
- .burst_buf_size = 6 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchh_fn,
.tx_fn = tx_tchh_fn,
},
- [TRXC_TCHH_1] = {
+ [L1SCHED_TCHH_1] = {
.name = "TCH/H(1)", /* 3GPP TS 05.02, section 3.2 */
.desc = "Half Rate traffic channel (sub-channel 1)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_TCHH_0, see above. */
- .burst_buf_size = 6 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_TCHH_0, see above. */
+ .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_tchh_fn,
.tx_fn = tx_tchh_fn,
},
- [TRXC_SDCCH4_0] = {
+ [L1SCHED_SDCCH4_0] = {
.name = "SDCCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_1] = {
+ [L1SCHED_SDCCH4_1] = {
.name = "SDCCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_2] = {
+ [L1SCHED_SDCCH4_2] = {
.name = "SDCCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH4_3] = {
+ [L1SCHED_SDCCH4_3] = {
.name = "SDCCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_0] = {
+ [L1SCHED_SDCCH8_0] = {
.name = "SDCCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 0)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_1] = {
+ [L1SCHED_SDCCH8_1] = {
.name = "SDCCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 1)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_2] = {
+ [L1SCHED_SDCCH8_2] = {
.name = "SDCCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 2)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_3] = {
+ [L1SCHED_SDCCH8_3] = {
.name = "SDCCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 3)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_4] = {
+ [L1SCHED_SDCCH8_4] = {
.name = "SDCCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 4)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_5] = {
+ [L1SCHED_SDCCH8_5] = {
.name = "SDCCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 5)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_6] = {
+ [L1SCHED_SDCCH8_6] = {
.name = "SDCCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 6)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SDCCH8_7] = {
+ [L1SCHED_SDCCH8_7] = {
.name = "SDCCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Stand-alone dedicated control channel (sub-channel 7)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_DEDIC,
+ .link_id = L1SCHED_CH_LID_DEDIC,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTF] = {
+ [L1SCHED_SACCHTF] = {
.name = "SACCH/TF", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/F associated control channel",
.chan_nr = RSL_CHAN_Bm_ACCHs,
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTH_0] = {
+ [L1SCHED_SACCHTH_0] = {
.name = "SACCH/TH(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/H associated control channel (sub-channel 0)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCHTH_1] = {
+ [L1SCHED_SACCHTH_1] = {
.name = "SACCH/TH(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow TCH/H associated control channel (sub-channel 1)",
.chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_0] = {
+ [L1SCHED_SACCH4_0] = {
.name = "SACCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 0)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_1] = {
+ [L1SCHED_SACCH4_1] = {
.name = "SACCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 1)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_2] = {
+ [L1SCHED_SACCH4_2] = {
.name = "SACCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 2)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH4_3] = {
+ [L1SCHED_SACCH4_3] = {
.name = "SACCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/4 associated control channel (sub-channel 3)",
.chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_0] = {
+ [L1SCHED_SACCH8_0] = {
.name = "SACCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 0)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_1] = {
+ [L1SCHED_SACCH8_1] = {
.name = "SACCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 1)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_2] = {
+ [L1SCHED_SACCH8_2] = {
.name = "SACCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 2)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_3] = {
+ [L1SCHED_SACCH8_3] = {
.name = "SACCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 3)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_4] = {
+ [L1SCHED_SACCH8_4] = {
.name = "SACCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 4)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_5] = {
+ [L1SCHED_SACCH8_5] = {
.name = "SACCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 5)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_6] = {
+ [L1SCHED_SACCH8_6] = {
.name = "SACCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 6)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_SACCH8_7] = {
+ [L1SCHED_SACCH8_7] = {
.name = "SACCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */
.desc = "Slow SDCCH/8 associated control channel (sub-channel 7)",
.chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3),
- .link_id = TRX_CH_LID_SACCH,
+ .link_id = L1SCHED_CH_LID_SACCH,
- /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_CBTX,
+ /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
.tx_fn = tx_data_fn,
},
- [TRXC_PDTCH] = {
+ [L1SCHED_PDTCH] = {
.name = "PDTCH", /* 3GPP TS 05.02, sections 3.2.4, 3.3.2.4 */
.desc = "Packet data traffic & control channel",
.chan_nr = RSL_CHAN_OSMO_PDCH,
- /* Rx and Tx, multiple coding schemes: CS-2..4 and MCS-1..9 (3GPP TS
+ /* Rx and Tx, multiple coding schemes: CS-1..4 and MCS-1..9 (3GPP TS
* 05.03, chapter 5), regular interleaving as specified for xCCH.
* NOTE: the burst buffer is three times bigger because the
* payload of EDGE bursts is three times longer. */
- .burst_buf_size = 3 * 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_PDCH,
+ .burst_buf_size = 4 * GSM_NBITS_NB_8PSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_PDCH,
.rx_fn = rx_pdtch_fn,
.tx_fn = tx_pdtch_fn,
},
- [TRXC_PTCCH] = {
+ [L1SCHED_PTCCH] = {
.name = "PTCCH", /* 3GPP TS 05.02, section 3.3.4.2 */
.desc = "Packet Timing advance control channel",
.chan_nr = RSL_CHAN_OSMO_PDCH,
-
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_PDCH,
- .rx_fn = rx_data_fn,
- .tx_fn = tx_data_fn,
+ .link_id = L1SCHED_CH_LID_PTCCH,
+
+ /* On the Uplink, mobile stations transmit random Access Bursts
+ * to allow estimation of the timing advance for one MS in packet
+ * transfer mode. On Downlink, the network sends timing advance
+ * updates for several mobile stations. The coding scheme used
+ * for PTCCH/D messages is the same as for PDTCH CS-1. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_PDCH,
+ .rx_fn = rx_pdtch_fn,
+ .tx_fn = tx_rach_fn,
},
- [TRXC_SDCCH4_CBCH] = {
+ [L1SCHED_SDCCH4_CBCH] = {
.name = "SDCCH/4(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
.desc = "Cell Broadcast channel on SDCCH/4",
.chan_nr = RSL_CHAN_OSMO_CBCH4,
- /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
- .flags = TRX_CH_FLAG_AUTO,
+ /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
+ .flags = L1SCHED_CH_FLAG_AUTO,
.rx_fn = rx_data_fn,
},
- [TRXC_SDCCH8_CBCH] = {
+ [L1SCHED_SDCCH8_CBCH] = {
.name = "SDCCH/8(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */
.desc = "Cell Broadcast channel on SDCCH/8",
.chan_nr = RSL_CHAN_OSMO_CBCH8,
- /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */
- .burst_buf_size = 4 * GSM_BURST_PL_LEN,
+ /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */
+ .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD,
.rx_fn = rx_data_fn,
},
};
diff --git a/src/host/trxcon/src/sched_lchan_pdtch.c b/src/host/trxcon/src/sched_lchan_pdtch.c
new file mode 100644
index 00000000..5b884ddc
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_pdtch.c
@@ -0,0 +1,195 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ uint8_t l2[GPRS_L2_MAX_LEN];
+ int n_errors, n_bits_total, rc;
+ sbit_t *bursts_p, *burst;
+ size_t l2_len;
+ uint32_t *mask;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Packet data received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bi->bid != 0)
+ return 0;
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to buffer of 4 bursts */
+ burst = bursts_p + bi->bid * 116;
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) packet data at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
+ }
+
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
+ /* Attempt to decode */
+ rc = gsm0503_pdtch_decode(l2, bursts_p,
+ NULL, &n_errors, &n_bits_total);
+ if (rc < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+ }
+
+ /* Determine L2 length */
+ l2_len = rc > 0 ? rc : 0;
+
+ /* Send a L2 frame to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, true);
+
+ return 0;
+}
+
+static struct msgb *prim_dequeue_pdtch(struct l1sched_lchan_state *lchan, uint32_t fn)
+{
+ while (!llist_empty(&lchan->tx_prims)) {
+ struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list);
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ int ret = gsm0502_fncmp(prim->data_req.frame_nr, fn);
+
+ if (OSMO_LIKELY(ret == 0)) { /* it's a match! */
+ llist_del(&msg->list);
+ return msg;
+ } else if (ret > 0) { /* not now, come back later */
+ break;
+ } /* else: the ship has sailed, drop your ticket */
+
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "%s(): dropping stale Tx prim (current Fn=%u, prim Fn=%u): %s\n",
+ __func__, fn, prim->data_req.frame_nr, msgb_hexdump_l2(msg));
+ llist_del(&msg->list);
+ msgb_free(msg);
+ }
+
+ return NULL;
+}
+
+int tx_pdtch_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ *mask = *mask << 4;
+
+ struct msgb *msg = prim_dequeue_pdtch(lchan, br->fn);
+ if (msg == NULL)
+ return -ENOENT;
+
+ /* Encode payload */
+ rc = gsm0503_pdtch_encode(bursts_p, msgb_l2(msg), msgb_l2len(msg));
+ if (rc < 0) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* Cache the prim, so that we can confirm it later (see below) */
+ OSMO_ASSERT(lchan->prim == NULL);
+ lchan->prim = msg;
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = bursts_p + br->bid * 116;
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled at fn=%u burst=%u\n", br->fn, br->bid);
+
+ if (br->bid == 3) {
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, lchan->prim,
+ GSM_TDMA_FN_SUB(br->fn, 3));
+ lchan->prim = NULL;
+ }
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_rach.c b/src/host/trxcon/src/sched_lchan_rach.c
new file mode 100644
index 00000000..905f1d57
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_rach.c
@@ -0,0 +1,136 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */
+#define RACH_EXT_TAIL_BITS_LEN 8
+#define RACH_SYNCH_SEQ_LEN 41
+#define RACH_PAYLOAD_LEN 36
+
+/* Extended tail bits (BN0..BN7) */
+static const ubit_t rach_ext_tail_bits[] = {
+ 0, 0, 1, 1, 1, 0, 1, 0,
+};
+
+/* Synchronization (training) sequence types */
+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
+};
+
+/* Synchronization (training) sequence bits */
+static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = {
+ [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000",
+ [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101",
+ [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111",
+};
+
+/* Synchronization (training) sequence names */
+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 },
+};
+
+/* Obtain a to-be-transmitted RACH burst */
+int tx_rach_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ const uint8_t bsic = lchan->ts->sched->bsic;
+ uint8_t *burst_ptr = br->burst;
+ uint8_t payload[36];
+ int i, rc;
+
+ if (llist_empty(&lchan->tx_prims))
+ return 0;
+
+ struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list);
+ struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ /* Delay sending according to offset value */
+ if (prim->rach_req.offset-- > 0)
+ return 0;
+ llist_del(&msg->list);
+
+ /* Check requested synch. sequence */
+ if (prim->rach_req.synch_seq >= RACH_SYNCH_SEQ_NUM) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Unknown RACH synch. sequence=0x%02x\n",
+ prim->rach_req.synch_seq);
+ msgb_free(msg);
+ return -ENOTSUP;
+ }
+
+ /* Encode the payload */
+ rc = gsm0503_rach_ext_encode(payload, prim->rach_req.ra,
+ bsic, prim->rach_req.is_11bit);
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Could not encode %s-bit RACH burst (ra=%u bsic=%u)\n",
+ prim->rach_req.is_11bit ? "11" : "8",
+ prim->rach_req.ra, bsic);
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* BN0-7: extended tail bits */
+ memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN);
+ burst_ptr += RACH_EXT_TAIL_BITS_LEN;
+
+ /* BN8-48: chosen synch. (training) sequence */
+ for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++)
+ *(burst_ptr++) = rach_synch_seq_bits[prim->rach_req.synch_seq][i] == '1';
+
+ /* BN49-84: encrypted bits (the payload) */
+ memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN);
+ burst_ptr += RACH_PAYLOAD_LEN;
+
+ /* BN85-156: tail bits & extended guard period */
+ memset(burst_ptr, 0, br->burst + GSM_NBITS_NB_GMSK_BURST - burst_ptr);
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n",
+ prim->rach_req.is_11bit ? "11" : "8",
+ get_value_string(rach_synch_seq_names, prim->rach_req.synch_seq), br->fn);
+
+ /* Confirm RACH request (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+
+ return 0;
+}
diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/src/sched_lchan_sch.c
index 9eed506b..e2420050 100644
--- a/src/host/trxcon/sched_lchan_sch.c
+++ b/src/host/trxcon/src/sched_lchan_sch.c
@@ -2,7 +2,8 @@
* OsmocomBB <-> SDR connection bridge
* TDMA scheduler: handlers for DL / UL bursts on logical channels
*
- * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -16,10 +17,6 @@
* 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <errno.h>
@@ -35,12 +32,8 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/coding/gsm0503_coding.h>
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-#include "logging.h"
-#include "trx_if.h"
-#include "l1ctl.h"
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
{
@@ -68,9 +61,23 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info)
time->fn = gsm_gsmtime2fn(time);
}
-int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
- struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid,
- sbit_t *bits, int8_t rssi, int16_t toa256)
+static int handle_sch_ind(struct l1sched_state *sched, uint32_t fn, uint8_t bsic)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->sch_ind.frame_nr = fn;
+ prim->sch_ind.bsic = bsic;
+
+ return l1sched_prim_to_user(sched, msg);
+}
+
+int rx_sch_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
{
sbit_t payload[2 * 39];
struct gsm_time time;
@@ -79,55 +86,34 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts,
int rc;
/* Obtain payload from burst */
- memcpy(payload, bits + 3, 39);
- memcpy(payload + 39, bits + 3 + 39 + 64, 39);
+ memcpy(payload, bi->burst + 3, 39);
+ memcpy(payload + 39, bi->burst + 3 + 39 + 64, 39);
/* Attempt to decode */
rc = gsm0503_sch_decode(sb_info, payload);
if (rc) {
- LOGP(DSCHD, LOGL_ERROR, "Received bad SCH burst at fn=%u\n", fn);
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad SCH burst at fn=%u\n", bi->fn);
return rc;
}
/* Decode BSIC and TDMA frame number */
decode_sb(&time, &bsic, sb_info);
- LOGP(DSCHD, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n",
- bsic, time.fn, trx->sched.fn_counter_proc);
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n",
+ bsic, time.fn, bi->fn);
/* Check if decoded frame number matches */
- if (time.fn != fn) {
- LOGP(DSCHD, LOGL_ERROR, "Decoded fn=%u does not match "
- "fn=%u provided by scheduler\n", time.fn, fn);
+ if (time.fn != bi->fn) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Decoded fn=%u does not match sched_fn=%u\n",
+ time.fn, bi->fn);
return -EINVAL;
}
- /* We don't need to send L1CTL_FBSB_CONF */
- if (trx->l1l->fbsb_conf_sent)
- return 0;
-
- /* Send L1CTL_FBSB_CONF to higher layers */
- struct l1ctl_info_dl *data;
- data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl));
- if (data == NULL)
- return -ENOMEM;
-
- /* Fill in some downlink info */
- data->chan_nr = trx_lchan_desc[lchan->type].chan_nr | ts->index;
- data->link_id = trx_lchan_desc[lchan->type].link_id;
- data->band_arfcn = htons(trx->band_arfcn);
- data->frame_nr = htonl(fn);
- data->rx_level = -rssi;
-
- /* FIXME: set proper values */
- data->num_biterr = 0;
- data->fire_crc = 0;
- data->snr = 0;
-
- l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic);
-
- /* Update BSIC value of trx_instance */
- trx->bsic = bsic;
+ /* Update BSIC value in the scheduler state */
+ lchan->ts->sched->bsic = bsic;
- return 0;
+ return handle_sch_ind(lchan->ts->sched, time.fn, bsic);
}
diff --git a/src/host/trxcon/src/sched_lchan_tchf.c b/src/host/trxcon/src/sched_lchan_tchf.c
new file mode 100644
index 00000000..37e0cea3
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_tchf.c
@@ -0,0 +1,441 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2021-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
+/* 3GPP TS 45.009, table 3.2.1.3-{1,3}: AMR on Downlink TCH/F.
+ *
+ * +---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | Burst 'a' received first
+ * +---+---+---+---+---+---+---+---+
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Speech/FACCH frame (bursts 'a' .. 'h')
+ *
+ * TDMA frame number of burst 'h' is always used as the table index. */
+static const uint8_t sched_tchf_dl_amr_cmi_map[26] = {
+ [11] = 1, /* TCH/F: a=4 / h=11 */
+ [20] = 1, /* TCH/F: a=13 / h=20 */
+ [3] = 1, /* TCH/F: a=21 / h=3 (21+7=28, 25 is idle -> 29. 29%26=3) */
+};
+
+/* TDMA frame number of burst 'a' should be used as the table index. */
+static const uint8_t sched_tchf_ul_amr_cmi_map[26] = {
+ [0] = 1, /* TCH/F: a=0 */
+ [8] = 1, /* TCH/F: a=8 */
+ [17] = 1, /* TCH/F: a=17 */
+};
+
+static int decode_fr_facch(struct l1sched_lchan_state *lchan)
+{
+ uint8_t data[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+ int rc;
+
+ rc = gsm0503_tch_fr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+ &n_errors, &n_bits_total);
+ if (rc != GSM_MACBLOCK_LEN)
+ return rc;
+
+ /* calculate AVG of the measurements (FACCH/F takes 8 bursts) */
+ l1sched_lchan_meas_avg(lchan, 8);
+
+ l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+int rx_tchf_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ int n_errors = -1, n_bits_total = 0, rc;
+ sbit_t *bursts_p, *burst;
+ uint8_t tch_data[290];
+ size_t tch_data_len;
+ uint32_t *mask;
+ int amr = 0;
+ uint8_t ft;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ if (bi->bid == 0) {
+ /* Shift the burst buffer by 4 bursts leftwards */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
+ *mask = *mask << 4;
+ } else {
+ /* Align to the first burst of a block */
+ if (*mask == 0x00)
+ return 0;
+ }
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to end of buffer of 24 bursts */
+ burst = BUFPOS(bursts_p, 20 + bi->bid);
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 8); // XXX
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xff) != 0xff) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) traffic frame at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 8), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */
+
+ }
+
+ /* TCH/F: speech and signalling frames are interleaved over 8 bursts, while
+ * CSD frames are interleaved over 22 bursts. Unless we're in CSD mode,
+ * decode only the last 8 bursts to avoid introducing additional delays. */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ 1, 0, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ 1, 1, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* 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], BUFTAIL8(bursts_p),
+ !sched_tchf_dl_amr_cmi_map[bi->fn % 26],
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ &lchan->amr.dl_ft,
+ &lchan->amr.dl_cmr,
+ &n_errors, &n_bits_total,
+ &lchan->amr.last_dtx);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (lchan->amr.last_dtx == AMR_OTHER) {
+ ft = lchan->amr.codec[lchan->amr.dl_ft];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(&tch_data[0],
+ lchan->amr.codec[lchan->amr.dl_cmr],
+ ft, AMR_GOOD);
+ if (rc < 0)
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "osmo_amr_rtp_enc() returned rc=%d\n", rc);
+ }
+ break;
+ /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_14k5:
+ /* FACCH/F does not steal TCH/F14.4 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr144_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_12k0:
+ /* FACCH/F does not steal TCH/F9.6 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr96_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ /* FACCH/F does not steal TCH/F4.8 frames, but only disturbs some bits */
+ decode_fr_facch(lchan);
+ rc = gsm0503_tch_fr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ /* TCH/F2.4 employs the same interleaving as TCH/FS (8 bursts),
+ * so FACCH/F *does* steal TCH/F2.4 frames completely. */
+ if (decode_fr_facch(lchan) == GSM_MACBLOCK_LEN)
+ return 0; /* TODO: emit BFI? */
+ rc = gsm0503_tch_fr24_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ &n_errors, &n_bits_total);
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
+ return -EINVAL;
+ }
+
+ /* Check decoding result */
+ if (rc < 4) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+
+ /* Send BFI (DATA.ind without payload) */
+ tch_data_len = 0;
+ } else if (rc == GSM_MACBLOCK_LEN) {
+ /* FACCH received, forward it to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ /* Send BFI (DATA.ind without payload) */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN)
+ return 0;
+ tch_data_len = 0;
+ } else {
+ /* A good TCH frame received */
+ tch_data_len = rc;
+ }
+
+ /* Send a traffic frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len,
+ n_errors, n_bits_total, true);
+}
+
+int tx_tchf_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ struct msgb *msg_facch, *msg_tch, *msg;
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ /* Shift the burst buffer by 4 bursts leftwards for interleaving */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
+ *mask = *mask << 4;
+
+ /* dequeue a pair of TCH and FACCH frames */
+ msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+ /* prioritize FACCH over TCH */
+ msg = (msg_facch != NULL) ? msg_facch : msg_tch;
+
+ /* populate the buffer with bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ /* fall-through */
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ if (msg == NULL) {
+ /* transmit a dummy speech block with inverted CRC3 */
+ gsm0503_tch_fr_encode(bursts_p, NULL, 0, 1);
+ goto send_burst;
+ }
+ rc = gsm0503_tch_fr_encode(BUFPOS(bursts_p, 0),
+ msgb_l2(msg),
+ msgb_l2len(msg), 1);
+ break;
+ case GSM48_CMODE_SPEECH_AMR:
+ {
+ bool amr_fn_is_cmr = !sched_tchf_ul_amr_cmi_map[br->fn % 26];
+ const uint8_t *data = msg ? msgb_l2(msg) : NULL;
+ size_t data_len = msg ? msgb_l2len(msg) : 0;
+
+ if (msg == NULL) {
+ /* TODO: It's not clear what to do for TCH/AFS.
+ * TODO: Send dummy FACCH maybe? */
+ goto send_burst; /* send something */
+ }
+
+ if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AFS: speech */
+ if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr))
+ goto free_bad_msg;
+ /* pull the AMR header - sizeof(struct amr_hdr) */
+ data_len -= 2;
+ data += 2;
+ }
+
+ rc = gsm0503_tch_afs_encode(BUFPOS(bursts_p, 0),
+ data, data_len,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
+ break;
+ }
+ /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_14k5:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 290);
+ gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[290];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_12k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+ gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 2 * 60);
+ gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[2 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ if ((msg = msg_facch) != NULL) {
+ /* FACCH/F does steal a TCH/F2.4 frame completely */
+ rc = gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ } else if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 2 * 36);
+ rc = gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ } else {
+ ubit_t idle[2 * 36];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ goto send_burst;
+ }
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "TCH mode %s is unknown or not supported\n",
+ gsm48_chan_mode_name(lchan->tch_mode));
+ goto free_bad_msg;
+ }
+
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+free_bad_msg:
+ msgb_free(msg_facch);
+ msgb_free(msg_tch);
+ return -EINVAL;
+ }
+
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ msgb_free((msg == msg_facch) ? msg_tch : msg_facch);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = BUFPOS(bursts_p, br->bid);
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_tchh.c b/src/host/trxcon/src/sched_lchan_tchh.c
new file mode 100644
index 00000000..99e26808
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_tchh.c
@@ -0,0 +1,622 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2018 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2020-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
+/* 3GPP TS 45.009, table 3.2.1.3-{2,4}: AMR on Downlink TCH/H.
+ *
+ * +---+---+---+---+---+---+
+ * | a | b | c | d | e | f | Burst 'a' received first
+ * +---+---+---+---+---+---+
+ * ^^^^^^^^^^^^^^^^^^^^^^^ FACCH frame (bursts 'a' .. 'f')
+ * ^^^^^^^^^^^^^^^ Speech frame (bursts 'a' .. 'd')
+ *
+ * TDMA frame number of burst 'f' is always used as the table index. */
+static const uint8_t sched_tchh_dl_amr_cmi_map[26] = {
+ [15] = 1, /* TCH/H(0): a=4 / d=10 / f=15 */
+ [23] = 1, /* TCH/H(0): a=13 / d=19 / f=23 */
+ [6] = 1, /* TCH/H(0): a=21 / d=2 / f=6 */
+
+ [16] = 1, /* TCH/H(1): a=5 / d=11 / f=16 */
+ [24] = 1, /* TCH/H(1): a=14 / d=20 / f=24 */
+ [7] = 1, /* TCH/H(1): a=22 / d=3 / f=7 */
+};
+
+/* TDMA frame number of burst 'a' is always used as the table index. */
+static const uint8_t sched_tchh_ul_amr_cmi_map[26] = {
+ [0] = 1, /* TCH/H(0): a=0 */
+ [8] = 1, /* TCH/H(0): a=8 */
+ [17] = 1, /* TCH/H(0): a=17 */
+
+ [1] = 1, /* TCH/H(1): a=1 */
+ [9] = 1, /* TCH/H(1): a=9 */
+ [18] = 1, /* TCH/H(1): a=18 */
+};
+
+static const uint8_t tch_h0_traffic_block_map[3][4] = {
+ /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */
+ { 0, 2, 4, 6 },
+ { 4, 6, 8, 10 },
+ { 8, 10, 0, 2 },
+};
+
+static const uint8_t tch_h1_traffic_block_map[3][4] = {
+ /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */
+ { 1, 3, 5, 7 },
+ { 5, 7, 9, 11 },
+ { 9, 11, 1, 3 },
+};
+
+static const uint8_t tch_h0_dl_facch_block_map[3][6] = {
+ /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */
+ { 4, 6, 8, 10, 13, 15 },
+ { 13, 15, 17, 19, 21, 23 },
+ { 21, 23, 0, 2, 4, 6 },
+};
+
+static const uint8_t tch_h0_ul_facch_block_map[3][6] = {
+ /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */
+ { 0, 2, 4, 6, 8, 10 },
+ { 8, 10, 13, 15, 17, 19 },
+ { 17, 19, 21, 23, 0, 2 },
+};
+
+static const uint8_t tch_h1_dl_facch_block_map[3][6] = {
+ /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */
+ { 5, 7, 9, 11, 14, 16 },
+ { 14, 16, 18, 20, 22, 24 },
+ { 22, 24, 1, 3, 5, 7 },
+};
+
+const uint8_t tch_h1_ul_facch_block_map[3][6] = {
+ /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */
+ { 1, 3, 5, 7, 9, 11 },
+ { 9, 11, 14, 16, 18, 20 },
+ { 18, 20, 22, 24, 1, 3 },
+};
+
+/* FACCH/H channel mapping for Downlink (see 3GPP TS 45.002, table 1).
+ * This mapping is valid for both FACCH/H(0) and FACCH/H(1).
+ * TDMA frame number of burst 'f' is used as the table index. */
+static const uint8_t sched_tchh_dl_facch_map[26] = {
+ [15] = 1, /* FACCH/H(0): B0(4,6,8,10,13,15) */
+ [16] = 1, /* FACCH/H(1): B0(5,7,9,11,14,16) */
+ [23] = 1, /* FACCH/H(0): B1(13,15,17,19,21,23) */
+ [24] = 1, /* FACCH/H(1): B1(14,16,18,20,22,24) */
+ [6] = 1, /* FACCH/H(0): B2(21,23,0,2,4,6) */
+ [7] = 1, /* FACCH/H(1): B2(22,24,1,3,5,7) */
+};
+
+/* 3GPP TS 45.002, table 2 in clause 7: Mapping tables for TCH/H2.4 and TCH/H4.8.
+ *
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v |
+ * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * TCH/H(0): B0(0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19)
+ * TCH/H(1): B0(1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20)
+ * TCH/H(0): B1(8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2)
+ * TCH/H(1): B1(9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3)
+ * TCH/H(0): B2(17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10)
+ * TCH/H(1): B2(18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11)
+ *
+ * TDMA frame number of burst 'a' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_ul_csd_map[26] = {
+ [0] = 1, /* TCH/H(0): B0(0 ... 19) */
+ [1] = 1, /* TCH/H(1): B0(1 ... 20) */
+ [8] = 1, /* TCH/H(0): B1(8 ... 2) */
+ [9] = 1, /* TCH/H(1): B1(9 ... 3) */
+ [17] = 1, /* TCH/H(0): B2(17 ... 10) */
+ [18] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
+/* TDMA frame number of burst 'v' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_dl_csd_map[26] = {
+ [19] = 1, /* TCH/H(0): B0(0 ... 19) */
+ [20] = 1, /* TCH/H(1): B0(1 ... 20) */
+ [2] = 1, /* TCH/H(0): B1(8 ... 2) */
+ [3] = 1, /* TCH/H(1): B1(9 ... 3) */
+ [10] = 1, /* TCH/H(0): B2(17 ... 10) */
+ [11] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
+/**
+ * Can a TCH/H block transmission be initiated / finished
+ * on a given frame number and a given channel type?
+ *
+ * See GSM 05.02, clause 7, table 1
+ *
+ * @param chan channel type (L1SCHED_TCHH_0 or L1SCHED_TCHH_1)
+ * @param fn the current frame number
+ * @param ul Uplink or Downlink?
+ * @param facch FACCH/H or traffic?
+ * @param start init or end of transmission?
+ * @return true (yes) or false (no)
+ */
+bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan,
+ uint32_t fn, bool ul, bool facch, bool start)
+{
+ uint8_t fn_mf;
+ int i = 0;
+
+ /* Just to be sure */
+ OSMO_ASSERT(chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1);
+
+ /* Calculate a modulo */
+ fn_mf = facch ? (fn % 26) : (fn % 13);
+
+#define MAP_GET_POS(map) \
+ (start ? 0 : ARRAY_SIZE(map[i]) - 1)
+
+#define BLOCK_MAP_FN(map) \
+ do { \
+ if (map[i][MAP_GET_POS(map)] == fn_mf) \
+ return true; \
+ } while (++i < ARRAY_SIZE(map))
+
+ /* Choose a proper block map */
+ if (facch) {
+ if (ul) {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_ul_facch_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_ul_facch_block_map);
+ } else {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_dl_facch_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_dl_facch_block_map);
+ }
+ } else {
+ if (chan == L1SCHED_TCHH_0)
+ BLOCK_MAP_FN(tch_h0_traffic_block_map);
+ else
+ BLOCK_MAP_FN(tch_h1_traffic_block_map);
+ }
+
+ return false;
+}
+
+static int decode_hr_facch(struct l1sched_lchan_state *lchan)
+{
+ uint8_t data[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total;
+ int rc;
+
+ rc = gsm0503_tch_hr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+ &n_errors, &n_bits_total);
+ if (rc != GSM_MACBLOCK_LEN)
+ return rc;
+
+ /* calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+ l1sched_lchan_meas_avg(lchan, 6);
+
+ l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+int rx_tchh_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ int n_errors = -1, n_bits_total = 0, rc;
+ sbit_t *bursts_p, *burst;
+ uint8_t tch_data[240];
+ size_t tch_data_len;
+ uint32_t *mask;
+ int amr = 0;
+ uint8_t ft;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ if (bi->bid == 0) {
+ /* Shift the burst buffer by 2 bursts leftwards */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
+ *mask = *mask << 2;
+ }
+
+ if (*mask == 0x00) {
+ /* Align to the first burst */
+ if (bi->bid > 0)
+ return 0;
+
+ /* Align reception of the first FACCH/H frame */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN) {
+ if (!l1sched_tchh_facch_start(lchan->type, bi->fn, 0))
+ return 0;
+ }
+ }
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to the end of buffer of 24 bursts */
+ burst = BUFPOS(bursts_p, 20 + bi->bid);
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until the second burst */
+ if (bi->bid != 1)
+ return 0;
+
+ /* Wait for complete set of bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ /* FACCH/H is interleaved over 6 bursts */
+ if ((*mask & 0x3f) != 0x3f)
+ return 0;
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ /* Data (CSD) is interleaved over 22 bursts */
+ if ((*mask & 0x3fffff) != 0x3fffff)
+ return 0;
+ if (!sched_tchh_dl_csd_map[bi->fn % 26])
+ return 0; /* CSD: skip decoding attempt, need 2 more bursts */
+ break;
+ default:
+ /* Speech is interleaved over 4 bursts */
+ if ((*mask & 0x0f) != 0x0f)
+ return 0;
+ break;
+ }
+
+ /* Skip decoding attempt in case of FACCH/H */
+ if (lchan->dl_ongoing_facch) {
+ /* Send BFI (DATA.ind without payload) for the 2nd stolen TCH frame */
+ l1sched_lchan_meas_avg(lchan, 4);
+ l1sched_lchan_emit_data_ind(lchan, NULL, 0, 0, 0, true);
+ lchan->dl_ongoing_facch = false;
+ return 0;
+ }
+
+ /* TCH/H: speech and signalling frames are interleaved over 4 and 6 bursts,
+ * respectively, while CSD frames are interleaved over 22 bursts. Unless
+ * we're in CSD mode, decode only the last 6 bursts to avoid introducing
+ * additional delays. */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ case GSM48_CMODE_SPEECH_V1: /* HR */
+ rc = gsm0503_tch_hr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+ !sched_tchh_dl_facch_map[bi->fn % 26],
+ &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* See comment in function rx_tchf_fn() */
+ amr = 2;
+ rc = gsm0503_tch_ahs_decode_dtx(&tch_data[amr], BUFTAIL8(bursts_p),
+ !sched_tchh_dl_facch_map[bi->fn % 26],
+ !sched_tchh_dl_amr_cmi_map[bi->fn % 26],
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ &lchan->amr.dl_ft,
+ &lchan->amr.dl_cmr,
+ &n_errors, &n_bits_total,
+ &lchan->amr.last_dtx);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (lchan->amr.last_dtx == AMR_OTHER) {
+ ft = lchan->amr.codec[lchan->amr.dl_ft];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(&tch_data[0],
+ lchan->amr.codec[lchan->amr.dl_cmr],
+ ft, AMR_GOOD);
+ if (rc < 0)
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "osmo_amr_rtp_enc() returned rc=%d\n", rc);
+ }
+ break;
+ /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ /* FACCH/H does not steal TCH/H4.8 frames, but only disturbs some bits */
+ decode_hr_facch(lchan);
+ rc = gsm0503_tch_hr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ /* FACCH/H does not steal TCH/H2.4 frames, but only disturbs some bits */
+ decode_hr_facch(lchan);
+ rc = gsm0503_tch_hr24_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+ &n_errors, &n_bits_total);
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
+ return -EINVAL;
+ }
+
+ /* Check decoding result */
+ if (rc < 4) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+
+ /* Send BFI (DATA.ind without payload) */
+ tch_data_len = 0;
+ } else if (rc == GSM_MACBLOCK_LEN) {
+ /* Skip decoding of the next 2 stolen bursts */
+ lchan->dl_ongoing_facch = true;
+
+ /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+ l1sched_lchan_meas_avg(lchan, 6);
+
+ /* FACCH/H received, forward to the higher layers */
+ l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+
+ /* Send BFI (DATA.ind without payload) for the 1st stolen TCH frame */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN)
+ return 0;
+ tch_data_len = 0;
+ } else {
+ /* A good TCH frame received */
+ tch_data_len = rc;
+ }
+
+ /* Calculate AVG of the measurements (traffic takes 4 bursts) */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Send a traffic frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len,
+ n_errors, n_bits_total, true);
+}
+
+int tx_tchh_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ struct msgb *msg_facch, *msg_tch, *msg;
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ if (*mask == 0x00) {
+ /* Align transmission of the first frame */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1))
+ return 0;
+ break;
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ if (!sched_tchh_ul_csd_map[br->fn % 26])
+ return 0;
+ break;
+ }
+ }
+
+ /* Shift the burst buffer by 2 bursts leftwards for interleaving */
+ memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+ memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
+ *mask = *mask << 2;
+
+ /* If FACCH/H blocks are still pending */
+ if (lchan->ul_facch_blocks > 2) {
+ struct msgb *msg = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ msgb_free(msg); /* drop 2nd TCH/HS block */
+ goto send_burst;
+ }
+
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_DATA_6k0:
+ case GSM48_CMODE_DATA_3k6:
+ /* CSD: skip dequeueing/encoding, send 2 more bursts */
+ if (!sched_tchh_ul_csd_map[br->fn % 26])
+ goto send_burst;
+ break;
+ }
+
+ /* dequeue a pair of TCH and FACCH frames */
+ msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+ if (l1sched_tchh_facch_start(lchan->type, br->fn, true))
+ msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+ else
+ msg_facch = NULL;
+ /* prioritize FACCH over TCH */
+ msg = (msg_facch != NULL) ? msg_facch : msg_tch;
+
+ /* populate the buffer with bursts */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1))
+ goto send_burst; /* XXX: should not happen */
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ /* fall-through */
+ case GSM48_CMODE_SPEECH_V1:
+ if (msg == NULL) {
+ /* transmit a dummy speech block with inverted CRC3 */
+ gsm0503_tch_hr_encode(bursts_p, NULL, 0);
+ goto send_burst;
+ }
+ rc = gsm0503_tch_hr_encode(BUFPOS(bursts_p, 0),
+ msgb_l2(msg),
+ msgb_l2len(msg));
+ break;
+ case GSM48_CMODE_SPEECH_AMR:
+ {
+ bool amr_fn_is_cmr = !sched_tchh_ul_amr_cmi_map[br->fn % 26];
+ const uint8_t *data = msg ? msgb_l2(msg) : NULL;
+ size_t data_len = msg ? msgb_l2len(msg) : 0;
+
+ if (msg == NULL) {
+ /* TODO: It's not clear what to do for TCH/AHS.
+ * TODO: Send dummy FACCH maybe? */
+ goto send_burst; /* send garbage */
+ }
+
+ if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AHS: speech */
+ if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr))
+ goto free_bad_msg;
+ /* pull the AMR header - sizeof(struct amr_hdr) */
+ data_len -= 2;
+ data += 2;
+ }
+
+ rc = gsm0503_tch_ahs_encode(BUFPOS(bursts_p, 0),
+ data, data_len,
+ amr_fn_is_cmr,
+ lchan->amr.codec,
+ lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
+ break;
+ }
+ /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_6k0:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+ gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 60];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+ case GSM48_CMODE_DATA_3k6:
+ if ((msg = msg_tch) != NULL) {
+ OSMO_ASSERT(msgb_l2len(msg) == 4 * 36);
+ gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ } else {
+ ubit_t idle[4 * 36];
+ memset(&idle[0], 0x01, sizeof(idle));
+ gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), &idle[0]);
+ }
+ if ((msg = msg_facch) != NULL) {
+ gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg));
+ /* Confirm FACCH sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ }
+ goto send_burst;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "TCH mode %s is unknown or not supported\n",
+ gsm48_chan_mode_name(lchan->tch_mode));
+ goto free_bad_msg;
+ }
+
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+free_bad_msg:
+ msgb_free(msg_facch);
+ msgb_free(msg_tch);
+ return -EINVAL;
+ }
+
+ if (msgb_l2len(msg) == GSM_MACBLOCK_LEN)
+ lchan->ul_facch_blocks = 6;
+
+ /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+ msgb_free((msg == msg_facch) ? msg_tch : msg_facch);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = BUFPOS(bursts_p, br->bid);
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ /* In case of a FACCH/H frame, one block less */
+ if (lchan->ul_facch_blocks)
+ lchan->ul_facch_blocks--;
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_lchan_xcch.c b/src/host/trxcon/src/sched_lchan_xcch.c
new file mode 100644
index 00000000..52b5d1e6
--- /dev/null
+++ b/src/host/trxcon/src/sched_lchan_xcch.c
@@ -0,0 +1,181 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: handlers for DL / UL bursts on logical channels
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/bits.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/coding/gsm0503_coding.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+int rx_data_fn(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ uint8_t l2[GSM_MACBLOCK_LEN];
+ int n_errors, n_bits_total, rc;
+ sbit_t *bursts_p, *burst;
+ uint32_t *mask;
+
+ /* Set up pointers */
+ mask = &lchan->rx_burst_mask;
+ bursts_p = lchan->rx_bursts;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Data received: fn=%u bid=%u\n", bi->fn, bi->bid);
+
+ /* Align to the first burst of a block */
+ if (*mask == 0x00 && bi->bid != 0)
+ return 0;
+
+ /* Update mask */
+ *mask |= (1 << bi->bid);
+
+ /* Store the measurements */
+ l1sched_lchan_meas_push(lchan, bi);
+
+ /* Copy burst to buffer of 4 bursts */
+ burst = bursts_p + bi->bid * 116;
+ memcpy(burst, bi->burst + 3, 58);
+ memcpy(burst + 58, bi->burst + 87, 58);
+
+ /* Wait until complete set of bursts */
+ if (bi->bid != 3)
+ return 0;
+
+ /* Calculate AVG of the measurements */
+ l1sched_lchan_meas_avg(lchan, 4);
+
+ /* Check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received incomplete (%s) data frame at fn=%u (%u/%u)\n",
+ l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn,
+ lchan->meas_avg.fn % lchan->ts->mf_layout->period,
+ lchan->ts->mf_layout->period);
+ /* NOTE: xCCH has an insane amount of redundancy for error
+ * correction, so even just 2 valid bursts might be enough
+ * to reconstruct some L2 frames. This is why we do not
+ * abort here. */
+ }
+
+ /* Keep the mask updated */
+ *mask = *mask << 4;
+
+ /* Attempt to decode */
+ rc = gsm0503_xcch_decode(l2, bursts_p, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n",
+ rc, n_errors, n_bits_total, lchan->meas_avg.fn);
+ }
+
+ /* Send a L2 frame to the higher layers */
+ return l1sched_lchan_emit_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN,
+ n_errors, n_bits_total, false);
+}
+
+static struct msgb *prim_dequeue_xcch(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg;
+
+ if (L1SCHED_CHAN_IS_SACCH(lchan->type))
+ return l1sched_lchan_prim_dequeue_sacch(lchan);
+ if ((msg = msgb_dequeue(&lchan->tx_prims)) == NULL)
+ return NULL;
+
+ /* Check the prim payload length */
+ if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) {
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Primitive has odd length %u (expected %u), so dropping...\n",
+ msgb_l2len(msg), GSM_MACBLOCK_LEN);
+ msgb_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+int tx_data_fn(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t *bursts_p, *burst;
+ const uint8_t *tsc;
+ uint32_t *mask;
+ int rc;
+
+ /* Set up pointers */
+ mask = &lchan->tx_burst_mask;
+ bursts_p = lchan->tx_bursts;
+
+ if (br->bid > 0) {
+ if ((*mask & 0x01) != 0x01)
+ return -ENOENT;
+ goto send_burst;
+ }
+
+ *mask = *mask << 4;
+
+ struct msgb *msg = prim_dequeue_xcch(lchan);
+ if (msg == NULL)
+ msg = l1sched_lchan_prim_dummy_lapdm(lchan);
+ OSMO_ASSERT(msg != NULL);
+
+ /* Encode payload */
+ rc = gsm0503_xcch_encode(bursts_p, msgb_l2(msg));
+ if (rc) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n",
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ /* Confirm data sending (pass ownership of the msgb/prim) */
+ l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+
+send_burst:
+ /* Determine which burst should be sent */
+ burst = bursts_p + br->bid * 116;
+
+ /* Update mask */
+ *mask |= (1 << br->bid);
+
+ /* Choose proper TSC */
+ tsc = l1sched_nb_training_bits[lchan->tsc];
+
+ /* Compose a new burst */
+ memset(br->burst, 0, 3); /* TB */
+ memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */
+ memcpy(br->burst + 61, tsc, 26); /* TSC */
+ memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */
+ memset(br->burst + 145, 0, 3); /* TB */
+ br->burst_len = GSM_NBITS_NB_GMSK_BURST;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid);
+
+ return 0;
+}
diff --git a/src/host/trxcon/src/sched_mframe.c b/src/host/trxcon/src/sched_mframe.c
new file mode 100644
index 00000000..0d95e0ac
--- /dev/null
+++ b/src/host/trxcon/src/sched_mframe.c
@@ -0,0 +1,2102 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: channel combinations, burst mapping
+ *
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2015 by Harald Welte <laforge@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 <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Non-combined CCCH */
+static const struct l1sched_tdma_frame frame_bcch[51] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_RACH, 0 },
+};
+
+/* Combined CCCH+SDCCH4 */
+static const struct l1sched_tdma_frame frame_bcch_sdcch4[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_2, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_2, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_2, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_2, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 1, L1SCHED_SDCCH4_2, 0 },
+ { L1SCHED_SACCH4_1, 2, L1SCHED_SDCCH4_2, 1 },
+ { L1SCHED_SACCH4_1, 3, L1SCHED_SDCCH4_2, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 },
+
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_2, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_2, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_2, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_2, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 1, L1SCHED_SDCCH4_2, 0 },
+ { L1SCHED_SACCH4_3, 2, L1SCHED_SDCCH4_2, 1 },
+ { L1SCHED_SACCH4_3, 3, L1SCHED_SDCCH4_2, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_bcch_sdcch4_cbch[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_IDLE, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_IDLE, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_IDLE, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_1, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SACCH4_1, 2, L1SCHED_IDLE, 1 },
+ { L1SCHED_SACCH4_1, 3, L1SCHED_IDLE, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 },
+
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 },
+ { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 },
+ { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 },
+ { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 },
+ { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 },
+ { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 },
+ { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 },
+ { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 },
+ { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 },
+ { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 },
+ { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 },
+ { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 },
+ { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_1, 1 },
+ { L1SCHED_IDLE, 1, L1SCHED_SDCCH4_1, 2 },
+ { L1SCHED_IDLE, 2, L1SCHED_SDCCH4_1, 3 },
+ { L1SCHED_IDLE, 3, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 },
+ { L1SCHED_SACCH4_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SACCH4_3, 2, L1SCHED_IDLE, 1 },
+ { L1SCHED_SACCH4_3, 3, L1SCHED_IDLE, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_sdcch8[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 },
+ { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_7, 0 },
+ { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_7, 1 },
+ { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_7, 2 },
+ { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_7, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_2, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_2, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_2, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_2, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 },
+
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_2, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_2, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_2, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_2, 3 },
+ { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_3, 0 },
+ { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_3, 1 },
+ { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_3, 2 },
+ { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_3, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_sdcch8_cbch[102] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 },
+ { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_7, 0 },
+ { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_7, 1 },
+ { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_7, 2 },
+ { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_7, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_IDLE, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_IDLE, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_IDLE, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 },
+
+ { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 },
+ { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 },
+ { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 },
+ { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 },
+ { L1SCHED_SDCCH8_1, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_1, 1, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_1, 2, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_1, 3, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_3, 0 },
+ { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_3, 1 },
+ { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_3, 2 },
+ { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_3, 3 },
+ { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 },
+ { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 },
+ { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 },
+ { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 },
+ { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 },
+ { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 },
+ { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 },
+ { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 },
+ { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 },
+ { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 },
+ { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 },
+ { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 },
+ { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 },
+ { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 },
+ { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 },
+ { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 },
+ { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 },
+ { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 },
+ { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 },
+ { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 },
+ { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 },
+ { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 },
+ { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 },
+ { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 },
+ { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 },
+ { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 },
+ { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 },
+ { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 },
+ { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 },
+ { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 },
+ { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 },
+ { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 },
+ { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 },
+ { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts0[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts1[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts2[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts3[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts4[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts5[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts6[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchf_ts7[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 },
+ { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 },
+ { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 },
+ { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 },
+ { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts01[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts23[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts45[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+};
+
+static const struct l1sched_tdma_frame frame_tchh_ts67[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 },
+ { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 },
+ { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 },
+ { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 },
+ { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 },
+};
+
+static const struct l1sched_tdma_frame frame_pdch[104] = {
+ /* dl_chan dl_bid ul_chan ul_bid */
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 0, L1SCHED_PTCCH, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 1, L1SCHED_PTCCH, 1 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 2, L1SCHED_PTCCH, 2 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PTCCH, 3, L1SCHED_PTCCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 },
+ { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 },
+ { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 },
+ { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 },
+ { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 },
+};
+
+/* Logical channel mask for a single channel */
+#define M64(x) \
+ ((uint64_t) 0x01 << x)
+
+/* Logical channel mask for BCCH+CCCH */
+#define M64_BCCH_CCCH \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_FCCH) \
+ | M64(L1SCHED_SCH) \
+ | M64(L1SCHED_BCCH) \
+ | M64(L1SCHED_RACH) \
+ | M64(L1SCHED_CCCH)
+
+/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */
+#define M64_SDCCH4 \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_SDCCH4_0) | M64(L1SCHED_SACCH4_0) \
+ | M64(L1SCHED_SDCCH4_1) | M64(L1SCHED_SACCH4_1) \
+ | M64(L1SCHED_SDCCH4_2) | M64(L1SCHED_SACCH4_2) \
+ | M64(L1SCHED_SDCCH4_3) | M64(L1SCHED_SACCH4_3)
+
+/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */
+#define M64_SDCCH8 \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_SDCCH8_0) | M64(L1SCHED_SACCH8_0) \
+ | M64(L1SCHED_SDCCH8_1) | M64(L1SCHED_SACCH8_1) \
+ | M64(L1SCHED_SDCCH8_2) | M64(L1SCHED_SACCH8_2) \
+ | M64(L1SCHED_SDCCH8_3) | M64(L1SCHED_SACCH8_3) \
+ | M64(L1SCHED_SDCCH8_4) | M64(L1SCHED_SACCH8_4) \
+ | M64(L1SCHED_SDCCH8_5) | M64(L1SCHED_SACCH8_5) \
+ | M64(L1SCHED_SDCCH8_6) | M64(L1SCHED_SACCH8_6) \
+ | M64(L1SCHED_SDCCH8_7) | M64(L1SCHED_SACCH8_7)
+
+/* Logical channel mask for TCH/F (with SACCH) */
+#define M64_TCHF \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_TCHF) | M64(L1SCHED_SACCHTF)
+
+/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */
+#define M64_TCHH \
+ (uint64_t) 0x00 \
+ | M64(L1SCHED_TCHH_0) | M64(L1SCHED_SACCHTH_0) \
+ | M64(L1SCHED_TCHH_1) | M64(L1SCHED_SACCHTH_1)
+
+/**
+ * A few notes about frame count:
+ *
+ * 26 frame multiframe - traffic multiframe
+ * 51 frame multiframe - control multiframe
+ *
+ * 102 = 2 x 51 frame multiframe
+ * 104 = 4 x 26 frame multiframe
+ */
+static const struct l1sched_tdma_multiframe layouts[] = {
+ {
+ GSM_PCHAN_NONE, "NONE",
+ 0, 0xff,
+ 0x00,
+ NULL
+ },
+ {
+ GSM_PCHAN_CCCH, "BCCH+CCCH",
+ 51, 0xff,
+ M64_BCCH_CCCH,
+ frame_bcch
+ },
+ {
+ GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4",
+ 102, 0xff,
+ M64_BCCH_CCCH | M64_SDCCH4,
+ frame_bcch_sdcch4
+ },
+ {
+ GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH",
+ 102, 0xff,
+ M64_BCCH_CCCH | M64_SDCCH4 | M64(L1SCHED_SDCCH4_CBCH),
+ frame_bcch_sdcch4_cbch
+ },
+ {
+ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8",
+ 102, 0xff,
+ M64_SDCCH8,
+ frame_sdcch8
+ },
+ {
+ GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH",
+ 102, 0xff,
+ M64_SDCCH8 | M64(L1SCHED_SDCCH8_CBCH),
+ frame_sdcch8_cbch
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x01,
+ M64_TCHF,
+ frame_tchf_ts0
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x02,
+ M64_TCHF,
+ frame_tchf_ts1
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x04,
+ M64_TCHF,
+ frame_tchf_ts2
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x08,
+ M64_TCHF,
+ frame_tchf_ts3
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x10,
+ M64_TCHF,
+ frame_tchf_ts4
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x20,
+ M64_TCHF,
+ frame_tchf_ts5
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x40,
+ M64_TCHF,
+ frame_tchf_ts6
+ },
+ {
+ GSM_PCHAN_TCH_F, "TCH/F+SACCH",
+ 104, 0x80,
+ M64_TCHF,
+ frame_tchf_ts7
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x03,
+ M64_TCHH,
+ frame_tchh_ts01
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x0c,
+ M64_TCHH,
+ frame_tchh_ts23
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0x30,
+ M64_TCHH,
+ frame_tchh_ts45
+ },
+ {
+ GSM_PCHAN_TCH_H, "TCH/H+SACCH",
+ 104, 0xc0,
+ M64_TCHH,
+ frame_tchh_ts67
+ },
+ {
+ GSM_PCHAN_PDCH, "PDCH",
+ 104, 0xff,
+ M64(L1SCHED_PDTCH) | M64(L1SCHED_PTCCH),
+ frame_pdch
+ },
+};
+
+const struct l1sched_tdma_multiframe *
+l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(layouts); i++) {
+ if (layouts[i].chan_config != config)
+ continue;
+ if (~layouts[i].slotmask & (1 << tn))
+ continue;
+ return &layouts[i];
+ }
+
+ return NULL;
+}
diff --git a/src/host/trxcon/src/sched_prim.c b/src/host/trxcon/src/sched_prim.c
new file mode 100644
index 00000000..67be75e5
--- /dev/null
+++ b/src/host/trxcon/src/sched_prim.c
@@ -0,0 +1,410 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: primitive management
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/prim.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+#define L1SCHED_PRIM_HEADROOM 64
+#define L1SCHED_PRIM_TAILROOM 512
+
+osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, l1sched_prim_size);
+
+const struct value_string l1sched_prim_type_names[] = {
+ { L1SCHED_PRIM_T_DATA, "DATA" },
+ { L1SCHED_PRIM_T_RACH, "RACH" },
+ { L1SCHED_PRIM_T_SCH, "SCH" },
+ { L1SCHED_PRIM_T_PCHAN_COMB, "PCHAN_COMB" },
+ { 0, NULL },
+};
+
+void l1sched_prim_init(struct msgb *msg,
+ enum l1sched_prim_type type,
+ enum osmo_prim_operation op)
+{
+ struct l1sched_prim *prim;
+
+ msg->l2h = msg->data;
+ msg->l1h = msgb_push(msg, sizeof(*prim));
+
+ prim = l1sched_prim_from_msgb(msg);
+ osmo_prim_init(&prim->oph, 0, type, op, msg);
+}
+
+struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type,
+ enum osmo_prim_operation op)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(L1SCHED_PRIM_HEADROOM + L1SCHED_PRIM_TAILROOM,
+ L1SCHED_PRIM_HEADROOM, "l1sched_prim");
+ if (msg == NULL)
+ return NULL;
+
+ l1sched_prim_init(msg, type, op);
+
+ return msg;
+}
+
+/**
+ * Composes a new primitive from cached RR Measurement Report.
+ *
+ * @param lchan lchan to assign a primitive
+ * @return SACCH primitive to be transmitted
+ */
+static struct msgb *prim_compose_mr(struct l1sched_lchan_state *lchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+ bool cached;
+
+ /* Allocate a new primitive */
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index,
+ .link_id = L1SCHED_CH_LID_SACCH,
+ };
+
+ /* Check if the MR cache is populated (verify LAPDm header) */
+ cached = (lchan->sacch.mr_cache[2] != 0x00
+ && lchan->sacch.mr_cache[3] != 0x00
+ && lchan->sacch.mr_cache[4] != 0x00);
+ if (!cached) {
+ memcpy(&lchan->sacch.mr_cache[0],
+ &lchan->ts->sched->sacch_cache[0],
+ sizeof(lchan->sacch.mr_cache));
+ }
+
+ /* Compose a new Measurement Report primitive */
+ memcpy(msgb_put(msg, GSM_MACBLOCK_LEN),
+ &lchan->sacch.mr_cache[0],
+ GSM_MACBLOCK_LEN);
+
+ /* Inform about the cache usage count */
+ if (++lchan->sacch.mr_cache_usage > 5) {
+ LOGP_LCHAND(lchan, LOGL_NOTICE,
+ "SACCH MR cache usage count=%u > 5 "
+ "=> ancient measurements, please fix!\n",
+ lchan->sacch.mr_cache_usage);
+ }
+
+ LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n");
+
+ return msg;
+}
+
+/**
+ * Dequeues a SACCH primitive from transmit queue, if present.
+ * Otherwise dequeues a cached Measurement Report (the last
+ * received one). Finally, if the cache is empty, a "dummy"
+ * measurement report is used.
+ *
+ * According to 3GPP TS 04.08, section 3.4.1, SACCH channel
+ * accompanies either a traffic or a signaling channel. It
+ * has the particularity that continuous transmission must
+ * occur in both directions, so on the Uplink direction
+ * measurement result messages are sent at each possible
+ * occasion when nothing else has to be sent. The LAPDm
+ * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not
+ * applicable on SACCH channels!
+ *
+ * Unfortunately, 3GPP TS 04.08 doesn't clearly state
+ * which "else messages" besides Measurement Reports
+ * can be send by the MS on SACCH channels. However,
+ * in sub-clause 3.4.1 it's stated that the interval
+ * between two successive measurement result messages
+ * shall not exceed one L2 frame.
+ *
+ * @param lchan lchan to assign a primitive
+ * @return SACCH primitive to be transmitted
+ */
+struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg_nmr = NULL;
+ struct msgb *msg_mr = NULL;
+ struct msgb *msg;
+ bool mr_now;
+
+ /* Shall we transmit MR now? */
+ mr_now = !lchan->sacch.mr_tx_last;
+
+#define PRIM_MSGB_IS_MR(msg) \
+ (l1sched_prim_data_from_msgb(msg)[5] == GSM48_PDISC_RR && \
+ l1sched_prim_data_from_msgb(msg)[6] == GSM48_MT_RR_MEAS_REP)
+
+ /* Iterate over all primitives in the queue */
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
+ /* Look for a Measurement Report */
+ if (!msg_mr && PRIM_MSGB_IS_MR(msg))
+ msg_mr = msg;
+
+ /* Look for anything else */
+ if (!msg_nmr && !PRIM_MSGB_IS_MR(msg))
+ msg_nmr = msg;
+
+ /* Should we look further? */
+ if (mr_now && msg_mr)
+ break; /* MR was found */
+ else if (!mr_now && msg_nmr)
+ break; /* something else was found */
+ }
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "SACCH MR selection: mr_tx_last=%d msg_mr=%p msg_nmr=%p\n",
+ lchan->sacch.mr_tx_last, msg_mr, msg_nmr);
+
+ /* Prioritize non-MR prim if possible */
+ if (mr_now && msg_mr)
+ msg = msg_mr;
+ else if (!mr_now && msg_nmr)
+ msg = msg_nmr;
+ else if (!mr_now && msg_mr)
+ msg = msg_mr;
+ else /* Nothing was found */
+ msg = NULL;
+
+ /* Have we found what we were looking for? */
+ if (msg) /* Dequeue if so */
+ llist_del(&msg->list);
+ else /* Otherwise compose a new MR */
+ msg = prim_compose_mr(lchan);
+
+ /* Update the cached report */
+ if (msg == msg_mr) {
+ memcpy(lchan->sacch.mr_cache, msgb_l2(msg), GSM_MACBLOCK_LEN);
+ lchan->sacch.mr_cache_usage = 0;
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n");
+ }
+
+ /* Update the MR transmission state */
+ lchan->sacch.mr_tx_last = PRIM_MSGB_IS_MR(msg);
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n",
+ PRIM_MSGB_IS_MR(msg) ? "Measurement Report" : "data frame");
+
+ return msg;
+}
+
+/**
+ * Dequeues either a FACCH, or a speech TCH primitive
+ * of a given channel type (Lm or Bm).
+ *
+ * @param lchan logical channel state
+ * @param facch FACCH (true) or speech (false) prim?
+ * @return either a FACCH, or a TCH primitive if found,
+ * otherwise NULL
+ */
+struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch)
+{
+ struct msgb *msg;
+
+ /**
+ * There is no need to use the 'safe' list iteration here
+ * as an item removal is immediately followed by return.
+ */
+ llist_for_each_entry(msg, &lchan->tx_prims, list) {
+ bool is_facch = msgb_l2len(msg) == GSM_MACBLOCK_LEN;
+ if (is_facch != facch)
+ continue;
+
+ llist_del(&msg->list);
+ return msg;
+ }
+
+ return NULL;
+}
+
+/**
+ * Allocate a DATA.req with dummy LAPDm func=UI frame for the given logical channel.
+ * To be used when no suitable DATA.req is present in the Tx queue.
+ *
+ * @param lchan lchan to allocate a dummy primitive for
+ * @return an msgb with DATA.req primitive, or NULL
+ */
+struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+ uint8_t *ptr;
+
+ /* LAPDm func=UI is not applicable for SACCH */
+ OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type));
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index,
+ .link_id = l1sched_lchan_desc[lchan->type].link_id,
+ };
+
+ ptr = msgb_put(msg, GSM_MACBLOCK_LEN);
+
+ /**
+ * TS 144.006, section 8.4.2.3 "Fill frames"
+ * A fill frame is a UI command frame for SAPI 0, P=0
+ * and with an information field of 0 octet length.
+ */
+ *(ptr++) = 0x01;
+ *(ptr++) = 0x03;
+ *(ptr++) = 0x01;
+
+ /**
+ * TS 144.006, section 5.2 "Frame delimitation and fill bits"
+ * Except for the first octet containing fill bits which shall
+ * be set to the binary value "00101011", each fill bit should
+ * be set to a random value when sent by the network.
+ */
+ *(ptr++) = 0x2b;
+ while (ptr < msg->tail)
+ *(ptr++) = (uint8_t)rand();
+
+ return msg;
+}
+
+int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan,
+ const uint8_t *data, size_t data_len,
+ int n_errors, int n_bits_total,
+ bool traffic)
+{
+ const struct l1sched_meas_set *meas = &lchan->meas_avg;
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_ind = (struct l1sched_prim_data_ind) {
+ .chdr = {
+ .frame_nr = meas->fn,
+ .chan_nr = lchan_desc->chan_nr | lchan->ts->index,
+ .link_id = lchan_desc->link_id,
+ .traffic = traffic,
+ },
+ .toa256 = meas->toa256,
+ .rssi = meas->rssi,
+ .n_errors = n_errors,
+ .n_bits_total = n_bits_total,
+ };
+
+ if (data_len > 0)
+ memcpy(msgb_put(msg, data_len), data, data_len);
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan,
+ struct msgb *msg, uint32_t fn)
+{
+ struct l1sched_prim *prim;
+
+ OSMO_ASSERT(msg != NULL);
+
+ /* convert from DATA.req to DATA.cnf */
+ prim = l1sched_prim_from_msgb(msg);
+ prim->oph.operation = PRIM_OP_CONFIRM;
+
+ switch (prim->oph.primitive) {
+ case L1SCHED_PRIM_T_DATA:
+ prim->data_cnf.frame_nr = fn;
+ break;
+ case L1SCHED_PRIM_T_RACH:
+ prim->rach_cnf.chdr.frame_nr = fn;
+ break;
+ default:
+ /* shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ return l1sched_prim_to_user(lchan->ts->sched, msg);
+}
+
+static int prim_enqeue(struct l1sched_state *sched, struct msgb *msg,
+ const struct l1sched_prim_chdr *chdr)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct l1sched_lchan_state *lchan;
+
+ lchan = l1sched_find_lchan_by_chan_nr(sched, chdr->chan_nr, chdr->link_id);
+ if (OSMO_UNLIKELY(lchan == NULL || !lchan->active)) {
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "No [active] lchan for primitive " L1SCHED_PRIM_STR_FMT " "
+ "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->frame_nr, chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ return -ENODEV;
+ }
+
+ LOGP_LCHAND(lchan, LOGL_DEBUG,
+ "Enqueue primitive " L1SCHED_PRIM_STR_FMT " "
+ "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n",
+ L1SCHED_PRIM_STR_ARGS(prim),
+ chdr->frame_nr, chdr->chan_nr, chdr->link_id,
+ msgb_l2len(msg), msgb_hexdump_l2(msg));
+
+ msgb_enqueue(&lchan->tx_prims, msg);
+ return 0;
+}
+
+int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ LOGP_SCHEDD(sched, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->data_req);
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST):
+ return prim_enqeue(sched, msg, &prim->rach_req.chdr);
+ default:
+ LOGP_SCHEDD(sched, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ msgb_free(msg);
+ return -ENOTSUP;
+ }
+}
diff --git a/src/host/trxcon/src/sched_trx.c b/src/host/trxcon/src/sched_trx.c
new file mode 100644
index 00000000..f4124003
--- /dev/null
+++ b/src/host/trxcon/src/sched_trx.c
@@ -0,0 +1,894 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * TDMA scheduler: GSM PHY routines
+ *
+ * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <error.h>
+#include <errno.h>
+#include <string.h>
+#include <talloc.h>
+#include <stdbool.h>
+
+#include <osmocom/gsm/a5.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+
+/* Logging categories to be used for common/data messages */
+int l1sched_log_cat_common = DLGLOBAL;
+int l1sched_log_cat_data = DLGLOBAL;
+
+/* "Dummy" Measurement Report */
+static const uint8_t meas_rep_dummy[] = {
+ /* L1 SACCH pseudo-header */
+ 0x0f, 0x00,
+
+ /* LAPDm header */
+ 0x01, 0x03, 0x49,
+
+ /* RR Management messages, Measurement Report */
+ 0x06, 0x15,
+
+ /* Measurement results (see 3GPP TS 44.018, section 10.5.2.20):
+ * 0... .... = BA-USED: 0
+ * .0.. .... = DTX-USED: DTX was not used
+ * ..11 0110 = RXLEV-FULL-SERVING-CELL: -57 <= x < -56 dBm (54)
+ * 0... .... = 3G-BA-USED: 0
+ * .1.. .... = MEAS-VALID: The measurement results are not valid
+ * ..11 0110 = RXLEV-SUB-SERVING-CELL: -57 <= x < -56 dBm (54)
+ * 0... .... = SI23_BA_USED: 0
+ * .000 .... = RXQUAL-FULL-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
+ * .... 000. = RXQUAL-SUB-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0)
+ * .... ...1 11.. .... = NO-NCELL-M: Neighbour cell information not available */
+ 0x36, 0x76, 0x01, 0xc0,
+
+ /* 0** -- Padding with zeroes */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int l1sched_cfg_pchan_comb_ind(struct l1sched_state *sched,
+ uint8_t tn, enum gsm_phys_chan_config pchan)
+{
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->pchan_comb_ind.tn = tn;
+ prim->pchan_comb_ind.pchan = pchan;
+
+ return l1sched_prim_to_user(sched, msg);
+}
+
+static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br);
+
+/* Pull an Uplink burst from the scheduler and store it to br->burst[].
+ * The TDMA Fn advance must be applied by the caller (if needed).
+ * The given *br must be initialized by the caller. */
+void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br)
+{
+ struct l1sched_ts *ts = sched->ts[br->tn];
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_lchan_state *lchan;
+ l1sched_lchan_tx_func *handler;
+ enum l1sched_lchan_type chan;
+ unsigned int offset;
+
+ /* Check if the given timeslot is configured */
+ if (ts == NULL || ts->mf_layout == NULL)
+ return;
+
+ /* Get frame from multiframe */
+ offset = br->fn % ts->mf_layout->period;
+ frame = &ts->mf_layout->frames[offset];
+
+ /* Get required info from frame */
+ br->bid = frame->ul_bid;
+ chan = frame->ul_chan;
+ handler = l1sched_lchan_desc[chan].tx_fn;
+
+ /* Omit lchans without handler */
+ if (handler == NULL)
+ return;
+
+ /* Make sure that lchan is allocated and active */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL || !lchan->active)
+ return;
+
+ /* Handover RACH needs to be handled regardless of the
+ * current channel type and the associated handler. */
+ struct msgb *msg = llist_first_entry_or_null(&lchan->tx_prims, struct msgb, list);
+ if (msg && l1sched_prim_type_from_msgb(msg) == L1SCHED_PRIM_T_RACH)
+ handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn;
+
+ /* Poke lchan handler */
+ handler(lchan, br);
+
+ /* Perform A5/X burst encryption if required */
+ if (lchan->a5.algo)
+ l1sched_a5_burst_enc(lchan, br);
+}
+
+void l1sched_logging_init(int log_cat_common, int log_cat_data)
+{
+ l1sched_log_cat_common = log_cat_common;
+ l1sched_log_cat_data = log_cat_data;
+}
+
+struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv)
+{
+ struct l1sched_state *sched;
+
+ sched = talloc(ctx, struct l1sched_state);
+ if (!sched)
+ return NULL;
+
+ *sched = (struct l1sched_state) {
+ .priv = priv,
+ };
+
+ memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy));
+
+ if (cfg->log_prefix == NULL)
+ sched->log_prefix = talloc_asprintf(sched, "l1sched[0x%p]: ", sched);
+ else
+ sched->log_prefix = talloc_strdup(sched, cfg->log_prefix);
+
+ return sched;
+}
+
+void l1sched_free(struct l1sched_state *sched)
+{
+ unsigned int tn;
+
+ if (sched == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Shutdown scheduler\n");
+
+ /* Free all potentially allocated timeslots */
+ for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++)
+ l1sched_del_ts(sched, tn);
+
+ talloc_free(sched);
+}
+
+void l1sched_reset(struct l1sched_state *sched, bool reset_clock)
+{
+ unsigned int tn;
+
+ if (sched == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Reset scheduler %s\n",
+ reset_clock ? "and clock counter" : "");
+
+ /* Free all potentially allocated timeslots */
+ for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++)
+ l1sched_del_ts(sched, tn);
+
+ memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy));
+}
+
+struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn)
+{
+ /* Make sure that ts isn't allocated yet */
+ if (sched->ts[tn] != NULL) {
+ LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot #%u already allocated\n", tn);
+ return NULL;
+ }
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn);
+
+ sched->ts[tn] = talloc_zero(sched, struct l1sched_ts);
+ sched->ts[tn]->sched = sched;
+ sched->ts[tn]->index = tn;
+
+ return sched->ts[tn];
+}
+
+void l1sched_del_ts(struct l1sched_state *sched, int tn)
+{
+ struct l1sched_lchan_state *lchan, *lchan_next;
+ struct l1sched_ts *ts;
+
+ /* Find ts in list */
+ ts = sched->ts[tn];
+ if (ts == NULL)
+ return;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn);
+
+ /* Deactivate all logical channels */
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Free channel states */
+ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
+ llist_del(&lchan->list);
+ talloc_free(lchan);
+ }
+
+ /* Remove ts from list and free memory */
+ sched->ts[tn] = NULL;
+ talloc_free(ts);
+
+ /* Notify transceiver about that */
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
+}
+
+#define LAYOUT_HAS_LCHAN(layout, lchan) \
+ (layout->lchan_mask & ((uint64_t) 0x01 << lchan))
+
+int l1sched_configure_ts(struct l1sched_state *sched, int tn,
+ enum gsm_phys_chan_config config)
+{
+ struct l1sched_lchan_state *lchan;
+ enum l1sched_lchan_type type;
+ struct l1sched_ts *ts;
+
+ /* Try to find specified ts */
+ ts = sched->ts[tn];
+ if (ts != NULL) {
+ /* Reconfiguration of existing one */
+ l1sched_reset_ts(sched, tn);
+ } else {
+ /* Allocate a new one if doesn't exist */
+ ts = l1sched_add_ts(sched, tn);
+ if (ts == NULL)
+ return -ENOMEM;
+ }
+
+ /* Choose proper multiframe layout */
+ ts->mf_layout = l1sched_mframe_layout(config, tn);
+ if (!ts->mf_layout)
+ return -EINVAL;
+ if (ts->mf_layout->chan_config != config)
+ return -EINVAL;
+
+ LOGP_SCHEDC(sched, LOGL_NOTICE,
+ "(Re)configure TDMA timeslot #%u as %s\n",
+ tn, ts->mf_layout->name);
+
+ /* Init logical channels list */
+ INIT_LLIST_HEAD(&ts->lchans);
+
+ /* Allocate channel states */
+ for (type = 0; type < _L1SCHED_CHAN_MAX; type++) {
+ if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type))
+ continue;
+
+ /* Allocate a channel state */
+ lchan = talloc_zero(ts, struct l1sched_lchan_state);
+ if (!lchan)
+ return -ENOMEM;
+
+ /* set backpointer */
+ lchan->ts = ts;
+
+ /* Set channel type */
+ lchan->type = type;
+
+ /* Init the Tx queue */
+ INIT_LLIST_HEAD(&lchan->tx_prims);
+
+ /* Add to the list of channel states */
+ llist_add_tail(&lchan->list, &ts->lchans);
+
+ /* Enable channel automatically if required */
+ if (l1sched_lchan_desc[type].flags & L1SCHED_CH_FLAG_AUTO)
+ l1sched_activate_lchan(ts, type);
+ }
+
+ /* Notify transceiver about TS activation */
+ l1sched_cfg_pchan_comb_ind(sched, tn, config);
+
+ return 0;
+}
+
+int l1sched_reset_ts(struct l1sched_state *sched, int tn)
+{
+ struct l1sched_lchan_state *lchan, *lchan_next;
+ struct l1sched_ts *ts;
+
+ /* Try to find specified ts */
+ ts = sched->ts[tn];
+ if (ts == NULL)
+ return -EINVAL;
+
+ /* Undefine multiframe layout */
+ ts->mf_layout = NULL;
+
+ /* Deactivate all logical channels */
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Free channel states */
+ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) {
+ llist_del(&lchan->list);
+ talloc_free(lchan);
+ }
+
+ /* Notify transceiver about that */
+ l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE);
+
+ return 0;
+}
+
+int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo,
+ const uint8_t *key, uint8_t key_len)
+{
+ struct l1sched_lchan_state *lchan;
+
+ /* Prevent NULL-pointer deference */
+ if (!ts)
+ return -EINVAL;
+
+ /* Make sure we can store this key */
+ if (key_len > MAX_A5_KEY_LEN)
+ return -ERANGE;
+
+ /* Iterate over all allocated logical channels */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+
+ /* Set key length and algorithm */
+ lchan->a5.key_len = key_len;
+ lchan->a5.algo = algo;
+
+ /* Copy requested key */
+ if (key_len)
+ memcpy(lchan->a5.key, key, key_len);
+ }
+
+ return 0;
+}
+
+struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts,
+ enum l1sched_lchan_type type)
+{
+ struct l1sched_lchan_state *lchan;
+
+ llist_for_each_entry(lchan, &ts->lchans, list)
+ if (lchan->type == type)
+ return lchan;
+
+ return NULL;
+}
+
+struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched,
+ uint8_t chan_nr, uint8_t link_id)
+{
+ const struct l1sched_ts *ts = sched->ts[chan_nr & 0x07];
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_lchan_state *lchan;
+
+ if (ts == NULL)
+ return NULL;
+
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+ if (lchan_desc->chan_nr != (chan_nr & RSL_CHAN_NR_MASK))
+ continue;
+ if (lchan_desc->link_id != link_id)
+ continue;
+ return lchan;
+ }
+
+ return NULL;
+}
+
+int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr,
+ int active, uint8_t tch_mode, uint8_t tsc)
+{
+ const struct l1sched_lchan_desc *lchan_desc;
+ struct l1sched_lchan_state *lchan;
+ int rc = 0;
+
+ /* Prevent NULL-pointer deference */
+ OSMO_ASSERT(ts != NULL);
+
+ /* Iterate over all allocated lchans */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ lchan_desc = &l1sched_lchan_desc[lchan->type];
+
+ if (lchan_desc->chan_nr == (chan_nr & RSL_CHAN_NR_MASK)) {
+ if (active) {
+ rc |= l1sched_activate_lchan(ts, lchan->type);
+ lchan->tch_mode = tch_mode;
+ lchan->tsc = tsc;
+ } else
+ rc |= l1sched_deactivate_lchan(ts, lchan->type);
+ }
+ }
+
+ return rc;
+}
+
+int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan,
+ uint8_t codecs_bitmask, uint8_t start_codec)
+{
+ int n = 0;
+ int acum = 0;
+ int pos;
+
+ while ((pos = ffs(codecs_bitmask)) != 0) {
+ acum += pos;
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "AMR codec[%u] = %u\n", n, acum - 1);
+ lchan->amr.codec[n++] = acum - 1;
+ codecs_bitmask >>= pos;
+ }
+ if (n == 0) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "Empty AMR codec mode bitmask!\n");
+ return -EINVAL;
+ }
+
+ lchan->amr.codecs = n;
+ lchan->amr.dl_ft = start_codec;
+ lchan->amr.dl_cmr = start_codec;
+ lchan->amr.ul_ft = start_codec;
+ lchan->amr.ul_cmr = start_codec;
+
+ return 0;
+}
+
+int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan)
+{
+ const struct l1sched_lchan_desc *lchan_desc = &l1sched_lchan_desc[chan];
+ struct l1sched_lchan_state *lchan;
+
+ /* Try to find requested logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ if (lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "is already activated\n");
+ return -EINVAL;
+ }
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE, "activating\n");
+
+ /* Conditionally allocate memory for bursts */
+ if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) {
+ lchan->rx_bursts = talloc_zero_size(lchan,
+ lchan_desc->burst_buf_size);
+ if (lchan->rx_bursts == NULL)
+ return -ENOMEM;
+ }
+
+ if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) {
+ lchan->tx_bursts = talloc_zero_size(lchan,
+ lchan_desc->burst_buf_size);
+ if (lchan->tx_bursts == NULL)
+ return -ENOMEM;
+ }
+
+ /* Finally, update channel status */
+ lchan->active = 1;
+
+ return 0;
+}
+
+static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan)
+{
+ struct msgb *msg;
+
+ /* Prevent NULL-pointer deference */
+ OSMO_ASSERT(lchan != NULL);
+
+ /* Print some TDMA statistics for Downlink */
+ if (l1sched_lchan_desc[lchan->type].rx_fn && lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "TDMA statistics: "
+ "%lu DL frames have been processed, "
+ "%lu lost (compensated), last fn=%u\n",
+ lchan->tdma.num_proc,
+ lchan->tdma.num_lost,
+ lchan->tdma.last_proc);
+ }
+
+ /* Reset internal state variables */
+ lchan->rx_burst_mask = 0x00;
+ lchan->tx_burst_mask = 0x00;
+
+ /* Free burst memory */
+ talloc_free(lchan->rx_bursts);
+ talloc_free(lchan->tx_bursts);
+
+ lchan->rx_bursts = NULL;
+ lchan->tx_bursts = NULL;
+
+ /* Flush the queue of pending Tx prims */
+ while ((msg = msgb_dequeue(&lchan->tx_prims)) != NULL) {
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE, "%s(): dropping Tx prim (fn=%u): %s\n",
+ __func__, prim->data_req.frame_nr, msgb_hexdump_l2(msg));
+ msgb_free(msg);
+ }
+
+ /* Channel specific stuff */
+ if (L1SCHED_CHAN_IS_TCH(lchan->type)) {
+ lchan->dl_ongoing_facch = 0;
+ lchan->ul_facch_blocks = 0;
+
+ lchan->tch_mode = GSM48_CMODE_SIGN;
+
+ /* Reset AMR state */
+ memset(&lchan->amr, 0x00, sizeof(lchan->amr));
+ } else if (L1SCHED_CHAN_IS_SACCH(lchan->type)) {
+ /* Reset SACCH state */
+ memset(&lchan->sacch, 0x00, sizeof(lchan->sacch));
+ }
+
+ /* Reset ciphering state */
+ memset(&lchan->a5, 0x00, sizeof(lchan->a5));
+
+ /* Reset TDMA frame statistics */
+ memset(&lchan->tdma, 0x00, sizeof(lchan->tdma));
+}
+
+int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan)
+{
+ struct l1sched_lchan_state *lchan;
+
+ /* Try to find requested logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -EINVAL;
+
+ if (!lchan->active) {
+ LOGP_LCHANC(lchan, LOGL_ERROR, "is already deactivated\n");
+ return -EINVAL;
+ }
+
+ LOGP_LCHANC(lchan, LOGL_DEBUG, "deactivating\n");
+
+ /* Reset internal state, free memory */
+ l1sched_reset_lchan(lchan);
+
+ /* Update activation flag */
+ lchan->active = 0;
+
+ return 0;
+}
+
+void l1sched_deactivate_all_lchans(struct l1sched_ts *ts)
+{
+ struct l1sched_lchan_state *lchan;
+
+ LOGP_SCHEDC(ts->sched, LOGL_DEBUG,
+ "Deactivating all logical channels on ts=%d\n",
+ ts->index);
+
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+
+ /* Reset internal state, free memory */
+ l1sched_reset_lchan(lchan);
+
+ /* Update activation flag */
+ lchan->active = 0;
+ }
+}
+
+enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr)
+{
+ uint8_t cbits = chan_nr >> 3;
+
+ if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs)
+ return GSM_PCHAN_TCH_F;
+ else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0))
+ return GSM_PCHAN_TCH_H;
+ else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0))
+ return GSM_PCHAN_CCCH_SDCCH4;
+ else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0))
+ return GSM_PCHAN_SDCCH8_SACCH8C;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4)
+ return GSM_PCHAN_CCCH_SDCCH4_CBCH;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8)
+ return GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
+ else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH)
+ return GSM_PCHAN_PDCH;
+
+ return GSM_PCHAN_NONE;
+}
+
+static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_ind *bi)
+{
+ ubit_t ks[114];
+ int i;
+
+ /* Generate keystream for a DL burst */
+ osmo_a5(lchan->a5.algo, lchan->a5.key, bi->fn, ks, NULL);
+
+ /* Apply keystream over ciphertext */
+ for (i = 0; i < 57; i++) {
+ if (ks[i])
+ bi->burst[i + 3] *= -1;
+ if (ks[i + 57])
+ bi->burst[i + 88] *= -1;
+ }
+}
+
+static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan,
+ struct l1sched_burst_req *br)
+{
+ ubit_t ks[114];
+ int i;
+
+ /* Generate keystream for an UL burst */
+ osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks);
+
+ /* Apply keystream over plaintext */
+ for (i = 0; i < 57; i++) {
+ br->burst[i + 3] ^= ks[i];
+ br->burst[i + 88] ^= ks[i + 57];
+ }
+}
+
+static int subst_frame_loss(struct l1sched_lchan_state *lchan,
+ l1sched_lchan_rx_func *handler,
+ uint32_t fn)
+{
+ const struct l1sched_tdma_multiframe *mf;
+ const struct l1sched_tdma_frame *fp;
+ int elapsed, i;
+
+ /* Wait until at least one TDMA frame is processed */
+ if (lchan->tdma.num_proc == 0)
+ return -EAGAIN;
+
+ /* Short alias for the current multiframe */
+ mf = lchan->ts->mf_layout;
+
+ /* Calculate how many frames elapsed since the last received one.
+ * The algorithm is based on GSM::FNDelta() from osmo-trx. */
+ elapsed = fn - lchan->tdma.last_proc;
+ if (elapsed >= GSM_TDMA_HYPERFRAME / 2)
+ elapsed -= GSM_TDMA_HYPERFRAME;
+ else if (elapsed < -GSM_TDMA_HYPERFRAME / 2)
+ elapsed += GSM_TDMA_HYPERFRAME;
+
+ /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */
+ if (elapsed < 0) {
+ /* This burst has already been substituted by a dummy burst (all bits set to zero),
+ * so better drop it. Otherwise we risk to get undefined behavior in handler(). */
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Rx burst with fn=%u older than the last "
+ "processed fn=%u (see OS#4658) => dropping\n",
+ fn, lchan->tdma.last_proc);
+ return -EALREADY;
+ }
+
+ /* Check how many frames we (potentially) need to compensate */
+ if (elapsed > mf->period) {
+ LOGP_LCHANC(lchan, LOGL_NOTICE,
+ "Too many (>%u) contiguous TDMA frames elapsed (%d) "
+ "since the last processed fn=%u (current %u)\n",
+ mf->period, elapsed, lchan->tdma.last_proc, fn);
+ return -EIO;
+ } else if (elapsed == 0) {
+ LOGP_LCHANC(lchan, LOGL_ERROR,
+ "No TDMA frames elapsed since the last processed "
+ "fn=%u, must be a bug?\n", lchan->tdma.last_proc);
+ return -EIO;
+ }
+
+ struct l1sched_burst_ind bi = {
+ .fn = lchan->tdma.last_proc,
+ .tn = lchan->ts->index,
+ .toa256 = 0,
+ .rssi = -120,
+ .burst = { 0 },
+ .burst_len = GSM_NBITS_NB_GMSK_BURST,
+ };
+
+ /* Traverse from fp till the current frame */
+ for (i = 0; i < elapsed - 1; i++) {
+ fp = &mf->frames[GSM_TDMA_FN_INC(bi.fn) % mf->period];
+ if (fp->dl_chan != lchan->type)
+ continue;
+
+ LOGP_LCHANC(lchan, LOGL_NOTICE,
+ "Substituting lost TDMA frame fn=%u\n", bi.fn);
+
+ bi.bid = fp->dl_bid;
+ handler(lchan, &bi);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = bi.fn;
+ lchan->tdma.num_proc++;
+ lchan->tdma.num_lost++;
+ }
+
+ return 0;
+}
+
+int l1sched_handle_rx_burst(struct l1sched_state *sched,
+ struct l1sched_burst_ind *bi)
+{
+ struct l1sched_lchan_state *lchan;
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_ts *ts = sched->ts[bi->tn];
+
+ l1sched_lchan_rx_func *handler;
+ enum l1sched_lchan_type chan;
+ uint8_t offset;
+ int rc;
+
+ /* Check whether required timeslot is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGP_SCHEDD(sched, LOGL_DEBUG,
+ "Timeslot #%u isn't configured, ignoring burst...\n", bi->tn);
+ return -EINVAL;
+ }
+
+ /* Get frame from multiframe */
+ offset = bi->fn % ts->mf_layout->period;
+ frame = ts->mf_layout->frames + offset;
+
+ /* Get required info from frame */
+ bi->bid = frame->dl_bid;
+ chan = frame->dl_chan;
+ handler = l1sched_lchan_desc[chan].rx_fn;
+
+ /* Omit bursts which have no handler, like IDLE bursts.
+ * TODO: handle noise indications during IDLE frames. */
+ if (!handler)
+ return -ENODEV;
+
+ /* Find required channel state */
+ lchan = l1sched_find_lchan_by_type(ts, chan);
+ if (lchan == NULL)
+ return -ENODEV;
+
+ /* Ensure that channel is active */
+ if (!lchan->active)
+ return 0;
+
+ /* Compensate lost TDMA frames (if any) */
+ rc = subst_frame_loss(lchan, handler, bi->fn);
+ if (rc == -EALREADY)
+ return rc;
+
+ /* Perform A5/X decryption if required */
+ if (lchan->a5.algo)
+ l1sched_a5_burst_dec(lchan, bi);
+
+ /* Put burst to handler */
+ handler(lchan, bi);
+
+ /* Update TDMA frame statistics */
+ lchan->tdma.last_proc = bi->fn;
+
+ if (++lchan->tdma.num_proc == 0) {
+ /* Theoretically, we may have an integer overflow of num_proc counter.
+ * As a consequence, subst_frame_loss() will be unable to compensate
+ * one (potentionally lost) Downlink burst. On practice, it would
+ * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */
+ LOGP_LCHAND(lchan, LOGL_NOTICE,
+ "Too many TDMA frames have been processed. "
+ "Are you running trxcon for more than 6 years?!?\n");
+ lchan->tdma.num_proc = 1;
+ }
+
+ return 0;
+}
+
+int l1sched_handle_rx_probe(struct l1sched_state *sched,
+ struct l1sched_probe *probe)
+{
+ struct l1sched_ts *ts = sched->ts[probe->tn];
+ const struct l1sched_tdma_frame *frame;
+ struct l1sched_lchan_state *lchan;
+ unsigned int offset;
+
+ /* Check whether required timeslot is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL)
+ return -EINVAL;
+
+ /* Get frame from multiframe */
+ offset = probe->fn % ts->mf_layout->period;
+ frame = &ts->mf_layout->frames[offset];
+
+ if (l1sched_lchan_desc[frame->dl_chan].rx_fn == NULL)
+ return -ENODEV;
+
+ /* Find the appropriate logical channel */
+ lchan = l1sched_find_lchan_by_type(ts, frame->dl_chan);
+ if (lchan == NULL)
+ return -ENODEV;
+
+ if (lchan->active)
+ probe->flags |= L1SCHED_PROBE_F_ACTIVE;
+
+ return 0;
+}
+
+#define MEAS_HIST_FIRST(hist) \
+ (&hist->buf[0])
+#define MEAS_HIST_LAST(hist) \
+ (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1)
+
+/* Add a new set of measurements to the history */
+void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan,
+ const struct l1sched_burst_ind *bi)
+{
+ struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist;
+
+ /* Find a new position where to store the measurements */
+ if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL)
+ hist->head = MEAS_HIST_FIRST(hist);
+ else
+ hist->head++;
+
+ *hist->head = (struct l1sched_meas_set) {
+ .fn = bi->fn,
+ .toa256 = bi->toa256,
+ .rssi = bi->rssi,
+ };
+}
+
+/* Calculate the AVG of n measurements from the history */
+void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n)
+{
+ struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist;
+ struct l1sched_meas_set *meas = hist->head;
+ int toa256_sum = 0;
+ int rssi_sum = 0;
+ int i;
+
+ OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf));
+ OSMO_ASSERT(meas != NULL);
+
+ /* Traverse backwards up to n entries, calculate the sum */
+ for (i = 0; i < n; i++) {
+ toa256_sum += meas->toa256;
+ rssi_sum += meas->rssi;
+
+ /* Do not go below the first burst */
+ if (i + 1 == n)
+ break;
+
+ if (meas == MEAS_HIST_FIRST(hist))
+ meas = MEAS_HIST_LAST(hist);
+ else
+ meas--;
+ }
+
+ /* Calculate the AVG */
+ lchan->meas_avg.toa256 = toa256_sum / n;
+ lchan->meas_avg.rssi = rssi_sum / n;
+
+ /* As a bonus, store TDMA frame number of the first burst */
+ lchan->meas_avg.fn = meas->fn;
+}
diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/src/trx_if.c
index 55d70341..fad10264 100644
--- a/src/host/trxcon/trx_if.c
+++ b/src/host/trxcon/src/trx_if.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -38,12 +39,17 @@
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>
-#include "l1ctl.h"
-#include "trxcon.h"
-#include "trx_if.h"
-#include "logging.h"
-#include "scheduler.h"
+#include <osmocom/bb/trxcon/trx_if.h>
+#include <osmocom/bb/trxcon/logging.h>
+
+#define TRXDv0_HDR_LEN 8
+
+#define S(x) (1 << (x))
+
+static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause);
static struct value_string trx_evt_names[] = {
{ 0, NULL } /* no events? */
@@ -52,8 +58,8 @@ static struct value_string trx_evt_names[] = {
static struct osmo_fsm_state trx_fsm_states[] = {
[TRX_STATE_OFFLINE] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_RSP_WAIT)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_RSP_WAIT)),
.name = "OFFLINE",
},
[TRX_STATE_IDLE] = {
@@ -62,25 +68,26 @@ static struct osmo_fsm_state trx_fsm_states[] = {
},
[TRX_STATE_ACTIVE] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_RSP_WAIT)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_RSP_WAIT)),
.name = "ACTIVE",
},
[TRX_STATE_RSP_WAIT] = {
.out_state_mask = (
- GEN_MASK(TRX_STATE_IDLE) |
- GEN_MASK(TRX_STATE_ACTIVE) |
- GEN_MASK(TRX_STATE_OFFLINE)),
+ S(TRX_STATE_IDLE) |
+ S(TRX_STATE_ACTIVE) |
+ S(TRX_STATE_OFFLINE)),
.name = "RSP_WAIT",
},
};
static struct osmo_fsm trx_fsm = {
- .name = "trx_interface_fsm",
+ .name = "trx_interface",
.states = trx_fsm_states,
.num_states = ARRAY_SIZE(trx_fsm_states),
- .log_subsys = DTRX,
+ .log_subsys = DTRXC,
.event_names = trx_evt_names,
+ .cleanup = &trx_fsm_cleanup_cb,
};
static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local,
@@ -143,13 +150,13 @@ static void trx_ctrl_send(struct trx_instance *trx)
tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
/* Send command */
- LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd);
+ LOGPFSML(trx->fi, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd);
send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0);
/* Trigger state machine */
- if (trx->fsm->state != TRX_STATE_RSP_WAIT) {
- trx->prev_state = trx->fsm->state;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_RSP_WAIT, 0, 0);
+ if (trx->fi->state != TRX_STATE_RSP_WAIT) {
+ trx->prev_state = trx->fi->state;
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_RSP_WAIT, 0, 0);
}
/* Start expire timer */
@@ -167,13 +174,13 @@ static void trx_ctrl_timer_cb(void *data)
if (llist_empty(&trx->trx_ctrl_list))
return;
- LOGP(DTRX, LOGL_NOTICE, "No response from transceiver...\n");
+ LOGPFSML(trx->fi, LOGL_NOTICE, "No response from transceiver...\n");
tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list);
if (++tcm->retry_cnt > 3) {
- LOGP(DTRX, LOGL_NOTICE, "Transceiver offline\n");
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_OFFLINE, 0, 0);
- osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_OFFLINE, trx);
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Transceiver offline\n");
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_OFFLINE, 0, 0);
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_TIMEOUT, NULL);
return;
}
@@ -212,7 +219,7 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical,
tcm->cmd_len = strlen(cmd);
tcm->critical = critical;
llist_add_tail(&tcm->list, &trx->trx_ctrl_list);
- LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
+ LOGPFSML(trx->fi, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
/* Send message, if no pending messages */
if (!pending)
@@ -242,23 +249,22 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical,
* RSP POWERON <status>
*/
-int trx_if_cmd_echo(struct trx_instance *trx)
+static int trx_if_cmd_echo(struct trx_instance *trx)
{
return trx_ctrl_cmd(trx, 1, "ECHO", "");
}
-int trx_if_cmd_poweroff(struct trx_instance *trx)
+static int trx_if_cmd_poweroff(struct trx_instance *trx)
{
return trx_ctrl_cmd(trx, 1, "POWEROFF", "");
}
-int trx_if_cmd_poweron(struct trx_instance *trx)
+static int trx_if_cmd_poweron(struct trx_instance *trx)
{
- if (trx->powered_up) {
- /* FIXME: this should be handled by the FSM, not here! */
- LOGP(DTRX, LOGL_ERROR, "Suppressing POWERON as we're already powered up\n");
+#if 0
+ if (trx->powered_up)
return -EAGAIN;
- }
+#endif
return trx_ctrl_cmd(trx, 1, "POWERON", "");
}
@@ -273,9 +279,25 @@ int trx_if_cmd_poweron(struct trx_instance *trx)
* RSP SETSLOT <status> <timeslot> <chantype>
*/
-int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type)
+static int trx_if_cmd_setslot(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setslot *cmdp)
{
- return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", tn, type);
+ /* Values correspond to 'enum ChannelCombination' in osmo-trx.git */
+ static const uint8_t chan_types[_GSM_PCHAN_MAX] = {
+ [GSM_PCHAN_UNKNOWN] = 0,
+ [GSM_PCHAN_NONE] = 0,
+ [GSM_PCHAN_CCCH] = 4,
+ [GSM_PCHAN_CCCH_SDCCH4] = 5,
+ [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 5,
+ [GSM_PCHAN_TCH_F] = 1,
+ [GSM_PCHAN_TCH_H] = 3,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = 7,
+ [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 7,
+ [GSM_PCHAN_PDCH] = 13,
+ };
+
+ return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u",
+ cmdp->tn, chan_types[cmdp->pchan]);
}
/*
@@ -290,28 +312,30 @@ int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type)
* RSP (RX/TX)TUNE <status> <kHz>
*/
-int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn)
+static int trx_if_cmd_rxtune(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp)
{
uint16_t freq10;
/* RX is downlink on MS side */
- freq10 = gsm_arfcn2freq10(band_arfcn, 0);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
+ LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
return trx_ctrl_cmd(trx, 1, "RXTUNE", "%u", freq10 * 100);
}
-int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn)
+static int trx_if_cmd_txtune(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp)
{
uint16_t freq10;
/* TX is uplink on MS side */
- freq10 = gsm_arfcn2freq10(band_arfcn, 1);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 1);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn);
+ LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
@@ -330,19 +354,16 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn)
* RSP MEASURE <status> <kHz> <dB>
*/
-int trx_if_cmd_measure(struct trx_instance *trx,
- uint16_t band_arfcn_start, uint16_t band_arfcn_stop)
+static int trx_if_cmd_measure(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_measure *cmdp)
{
uint16_t freq10;
- /* Update ARFCN range for measurement */
- trx->pm_band_arfcn_start = band_arfcn_start;
- trx->pm_band_arfcn_stop = band_arfcn_stop;
-
/* Calculate a frequency for current ARFCN (DL) */
- freq10 = gsm_arfcn2freq10(band_arfcn_start, 0);
+ freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0);
if (freq10 == 0xffff) {
- LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn_start);
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "ARFCN %d not defined\n", cmdp->band_arfcn);
return -ENOTSUP;
}
@@ -359,23 +380,22 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
sscanf(resp, "%u %d", &freq10, &dbm);
freq10 /= 100;
- /* Check received ARFCN against expected */
band_arfcn = gsm_freq102arfcn((uint16_t) freq10, 0);
- if (band_arfcn != trx->pm_band_arfcn_start) {
- LOGP(DTRX, LOGL_ERROR, "Power measurement error: "
- "response ARFCN=%u doesn't match expected ARFCN=%u\n",
- band_arfcn &~ ARFCN_FLAG_MASK,
- trx->pm_band_arfcn_start &~ ARFCN_FLAG_MASK);
+ if (band_arfcn == 0xffff) {
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "Failed to parse ARFCN from RSP MEASURE: %s\n", resp);
return;
}
- /* Send L1CTL_PM_CONF */
- l1ctl_tx_pm_conf(trx->l1l, band_arfcn, dbm,
- band_arfcn == trx->pm_band_arfcn_stop);
+ const struct trxcon_phyif_rsp rsp = {
+ .type = TRXCON_PHYIF_CMDT_MEASURE,
+ .param.measure = {
+ .band_arfcn = band_arfcn,
+ .dbm = dbm,
+ },
+ };
- /* Schedule a next measurement */
- if (band_arfcn != trx->pm_band_arfcn_stop)
- trx_if_cmd_measure(trx, ++band_arfcn, trx->pm_band_arfcn_stop);
+ trxcon_phyif_handle_rsp(trx->priv, &rsp);
}
/*
@@ -391,53 +411,70 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp)
* RSP SETTA <status> <TA>
*/
-int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta)
+static int trx_if_cmd_setta(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setta *cmdp)
{
- return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta);
+ return trx_ctrl_cmd(trx, 0, "SETTA", "%d", cmdp->ta);
}
/*
- * Frequency Hopping parameters indication
+ * Frequency Hopping parameters indication.
+ *
+ * SETFH instructs transceiver to enable frequency hopping mode
+ * using the given HSN, MAIO, and Mobile Allocation parameters.
*
- * SETFH instructs transceiver to enable frequency
- * hopping mode using the given parameters.
- * CMD SETFH <HSN> <MAIO> <CH1> <CH2> [... <CHN>]
+ * CMD SETFH <HSN> <MAIO> <RXF1> <TXF1> [... <RXFN> <TXFN>]
+ *
+ * where <RXFN> and <TXFN> is a pair of Rx/Tx frequencies (in kHz)
+ * corresponding to one ARFCN the Mobile Allocation. Note that the
+ * channel list is expected to be sorted in ascending order.
*/
-int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
- uint8_t maio, uint16_t *ma, size_t ma_len)
+static int trx_if_cmd_setfh(struct trx_instance *trx,
+ const struct trxcon_phyif_cmdp_setfreq_h1 *cmdp)
{
- char ma_buf[100];
+ /* Reserve some room for CMD SETFH <HSN> <MAIO> */
+ char ma_buf[TRXC_BUF_SIZE - 24];
+ size_t ma_buf_len = sizeof(ma_buf) - 1;
+ uint16_t rx_freq, tx_freq;
char *ptr;
int i, rc;
- /* No channels, WTF?!? */
- if (!ma_len)
+ /* Make sure that Mobile Allocation has at least one ARFCN */
+ if (!cmdp->ma_len || cmdp->ma == NULL) {
+ LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n");
return -EINVAL;
+ }
- /**
- * Compose a sequence of channels (mobile allocation)
- * FIXME: the length of a CTRL command is limited to 128 symbols,
- * so we may have some problems if there are many channels...
- */
- for (i = 0, ptr = ma_buf; i < ma_len; i++) {
- /* Append a channel */
- rc = snprintf(ptr, ma_buf + sizeof(ma_buf) - ptr, "%u ", ma[i]);
- if (rc < 0)
- return rc;
+ /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */
+ for (i = 0, ptr = ma_buf; i < cmdp->ma_len; i++) {
+ /* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */
+ rx_freq = gsm_arfcn2freq10(cmdp->ma[i], 0); /* Rx: Downlink */
+ tx_freq = gsm_arfcn2freq10(cmdp->ma[i], 1); /* Tx: Uplink */
+ if (rx_freq == 0xffff || tx_freq == 0xffff) {
+ LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u "
+ "to a pair of Rx/Tx frequencies\n",
+ cmdp->ma[i] & ~ARFCN_FLAG_MASK);
+ return -EINVAL;
+ }
+
+ /* Append a pair of Rx/Tx frequencies (in kHz) to the buffer */
+ rc = snprintf(ptr, ma_buf_len, "%u %u ", rx_freq * 100, tx_freq * 100);
+ if (rc < 0 || rc > ma_buf_len) { /* Prevent buffer overflow */
+ LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode "
+ "Mobile Allocation (N=%u)\n", cmdp->ma_len);
+ return -ENOSPC;
+ }
/* Move pointer */
+ ma_buf_len -= rc;
ptr += rc;
-
- /* Prevent buffer overflow */
- if (ptr >= (ma_buf + 100))
- return -EIO;
}
/* Overwrite the last space */
*(ptr - 1) = '\0';
- return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf);
+ return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", cmdp->hsn, cmdp->maio, ma_buf);
}
/* Get response from CTRL socket */
@@ -446,18 +483,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
struct trx_instance *trx = ofd->data;
struct trx_ctrl_msg *tcm;
int resp, rsp_len;
- char buf[1500], *p;
+ char buf[TRXC_BUF_SIZE], *p;
ssize_t read_len;
read_len = read(ofd->fd, buf, sizeof(buf) - 1);
if (read_len <= 0) {
- LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
+ LOGPFSML(trx->fi, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
return read_len;
}
buf[read_len] = '\0';
if (!!strncmp(buf, "RSP ", 4)) {
- LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf);
return 0;
}
@@ -465,15 +502,14 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
p = strchr(buf + 4, ' ');
rsp_len = p ? p - buf - 4 : strlen(buf) - 4;
- LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf);
+ LOGPFSML(trx->fi, LOGL_INFO, "Response message: '%s'\n", buf);
/* Abort expire timer */
- if (osmo_timer_pending(&trx->trx_ctrl_timer))
- osmo_timer_del(&trx->trx_ctrl_timer);
+ osmo_timer_del(&trx->trx_ctrl_timer);
/* Get command for response message */
if (llist_empty(&trx->trx_ctrl_list)) {
- LOGP(DTRX, LOGL_NOTICE, "Response message without command\n");
+ LOGPFSML(trx->fi, LOGL_NOTICE, "Response message without command\n");
return -EINVAL;
}
@@ -482,7 +518,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Check if response matches command */
if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) {
- LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
+ LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
"Response message '%s' does not match command "
"message '%s'\n", buf, tcm->cmd);
goto rsp_error;
@@ -491,7 +527,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Check for response code */
sscanf(p + 1, "%d", &resp);
if (resp) {
- LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
+ LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR,
"Transceiver rejected TRX command with "
"response: '%s'\n", buf);
@@ -502,18 +538,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* Trigger state machine */
if (!strncmp(tcm->cmd + 4, "POWERON", 7)) {
trx->powered_up = true;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_ACTIVE, 0, 0);
}
else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) {
trx->powered_up = false;
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
}
else if (!strncmp(tcm->cmd + 4, "MEASURE", 7))
trx_if_measure_rsp_cb(trx, buf + 14);
else if (!strncmp(tcm->cmd + 4, "ECHO", 4))
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
else
- osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, trx->prev_state, 0, 0);
/* Remove command from list */
llist_del(&tcm->list);
@@ -525,11 +561,53 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
return 0;
rsp_error:
- /* Notify higher layers about the problem */
- osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_RSP_ERROR, trx);
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_ERROR, NULL);
return -EIO;
}
+int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd)
+{
+ int rc;
+
+ switch (cmd->type) {
+ case TRXCON_PHYIF_CMDT_RESET:
+ if ((rc = trx_if_cmd_poweroff(trx)) != 0)
+ return rc;
+ rc = trx_if_cmd_echo(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_POWERON:
+ rc = trx_if_cmd_poweron(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_POWEROFF:
+ rc = trx_if_cmd_poweroff(trx);
+ break;
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ rc = trx_if_cmd_measure(trx, &cmd->param.measure);
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H0:
+ if ((rc = trx_if_cmd_rxtune(trx, &cmd->param.setfreq_h0)) != 0)
+ return rc;
+ if ((rc = trx_if_cmd_txtune(trx, &cmd->param.setfreq_h0)) != 0)
+ return rc;
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H1:
+ rc = trx_if_cmd_setfh(trx, &cmd->param.setfreq_h1);
+ break;
+ case TRXCON_PHYIF_CMDT_SETSLOT:
+ rc = trx_if_cmd_setslot(trx, &cmd->param.setslot);
+ break;
+ case TRXCON_PHYIF_CMDT_SETTA:
+ rc = trx_if_cmd_setta(trx, &cmd->param.setta);
+ break;
+ default:
+ LOGPFSML(trx->fi, LOGL_ERROR,
+ "Unhandled PHYIF command type=0x%02x\n", cmd->type);
+ rc = -ENODEV;
+ }
+
+ return rc;
+}
+
/* ------------------------------------------------------------------------ */
/* Data interface handlers */
/* ------------------------------------------------------------------------ */
@@ -555,60 +633,93 @@ rsp_error:
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
{
struct trx_instance *trx = ofd->data;
- uint8_t buf[256];
- sbit_t bits[148];
- int8_t rssi, tn;
- int16_t toa256;
- uint32_t fn;
+ struct trxcon_phyif_burst_ind bi;
+ uint8_t buf[TRXD_BUF_SIZE];
ssize_t read_len;
+ sbit_t *burst;
read_len = read(ofd->fd, buf, sizeof(buf));
if (read_len <= 0) {
- LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
+ strerror_r(errno, (char *)buf, sizeof(buf));
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "read() failed on TRXD with rc=%zd (%s)\n",
+ read_len, (const char *)buf);
return read_len;
}
- if (read_len != 158) {
- LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid "
- "length '%zd'\n", read_len);
+ if (read_len < TRXDv0_HDR_LEN) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got malformed TRXD PDU (short length=%zd)\n", read_len);
return -EINVAL;
}
- tn = buf[0];
- fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
- rssi = -(int8_t) buf[5];
- toa256 = ((int16_t) (buf[6] << 8) | buf[7]);
-
- /* Copy and convert bits {254..0} to sbits {-127..127} */
- osmo_ubit2sbit(bits, buf + 8, 148);
+ if ((buf[0] >> 4) != 0) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got TRXD PDU with unexpected version\n");
+ return -ENOTSUP;
+ }
- if (tn >= 8) {
- LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn);
+ read_len -= TRXDv0_HDR_LEN;
+ switch (read_len) {
+ /* TRXDv0 PDUs may have 2 dummy bytes at the end */
+ case GSM_NBITS_NB_GMSK_BURST + 2:
+ case GSM_NBITS_NB_8PSK_BURST + 2:
+ read_len -= 2;
+ break;
+ case GSM_NBITS_NB_GMSK_BURST:
+ case GSM_NBITS_NB_8PSK_BURST:
+ break;
+ default:
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Got TRXD PDU unexpected burst length=%zd\n", read_len);
return -EINVAL;
}
- if (fn >= 2715648) {
- LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn);
+ burst = (sbit_t *)&buf[8];
+
+ bi = (struct trxcon_phyif_burst_ind) {
+ .tn = buf[0] & 0x07,
+ .fn = osmo_load32be(buf + 1),
+ .rssi = -(int8_t) buf[5],
+ .toa256 = (int16_t) (buf[6] << 8) | buf[7],
+ .burst = burst, /* at least GSM_NBITS_NB_GMSK_BURST */
+ .burst_len = read_len,
+ };
+
+ /* Convert ubits {254..0} to sbits {-127..127} in-place */
+ for (unsigned int i = 0; i < bi.burst_len; i++) {
+ if (buf[8 + i] == 255)
+ burst[i] = -127;
+ else
+ burst[i] = 127 - buf[8 + i];
+ }
+
+ if (bi.fn >= GSM_TDMA_HYPERFRAME) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal FN %u\n", bi.fn);
return -EINVAL;
}
- LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
- tn, fn, rssi, toa256);
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
+ "RX burst tn=%u fn=%u rssi=%d toa=%d\n",
+ bi.tn, bi.fn, bi.rssi, bi.toa256);
- /* Poke scheduler */
- sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, rssi, toa256);
+ trxcon_phyif_handle_burst_ind(trx->priv, &bi);
- /* Correct local clock counter */
- if (fn % 51 == 0)
- sched_clck_handle(&trx->sched, fn);
+ struct trxcon_phyif_rts_ind rts = {
+ .fn = GSM_TDMA_FN_SUM(bi.fn, trx->fn_advance),
+ .tn = bi.tn,
+ };
+
+ trxcon_phyif_handle_rts_ind(trx->priv, &rts);
return 0;
}
-int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
- uint8_t pwr, const ubit_t *bits)
+int trx_if_handle_phyif_burst_req(struct trx_instance *trx,
+ const struct trxcon_phyif_burst_req *br)
{
- uint8_t buf[256];
+ uint8_t buf[TRXD_BUF_SIZE];
+ size_t length;
/**
* We must be sure that we have clock,
@@ -617,55 +728,62 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
* TODO: introduce proper state machines for both
* transceiver and its TRXC interface.
*/
- if (trx->fsm->state != TRX_STATE_ACTIVE) {
- LOGP(DTRXD, LOGL_ERROR, "Ignoring TX data, "
- "transceiver isn't ready\n");
+#if 0
+ if (trx->fi->state != TRX_STATE_ACTIVE) {
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR,
+ "Ignoring TX data, transceiver isn't ready\n");
return -EAGAIN;
}
+#endif
- LOGP(DTRXD, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr);
+ LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG,
+ "TX burst tn=%u fn=%u pwr=%u\n",
+ br->tn, br->fn, br->pwr);
- buf[0] = tn;
- buf[1] = (fn >> 24) & 0xff;
- buf[2] = (fn >> 16) & 0xff;
- buf[3] = (fn >> 8) & 0xff;
- buf[4] = (fn >> 0) & 0xff;
- buf[5] = pwr;
+ buf[0] = br->tn;
+ osmo_store32be(br->fn, buf + 1);
+ buf[5] = br->pwr;
+ length = 6;
/* Copy ubits {0,1} */
- memcpy(buf + 6, bits, 148);
+ if (br->burst_len != 0) {
+ memcpy(buf + 6, br->burst, br->burst_len);
+ length += br->burst_len;
+ }
/* Send data to transceiver */
- send(trx->trx_ofd_data.fd, buf, 154, 0);
+ send(trx->trx_ofd_data.fd, buf, length, 0);
return 0;
}
/* Init TRX interface (TRXC, TRXD sockets and FSM) */
-struct trx_instance *trx_if_open(void *tall_ctx,
- const char *local_host, const char *remote_host,
- uint16_t base_port)
+struct trx_instance *trx_if_open(const struct trx_if_params *params)
{
+ const unsigned int offset = params->instance * 2;
struct trx_instance *trx;
+ struct osmo_fsm_inst *fi;
int rc;
- LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface "
- "(%s:%u)\n", remote_host, base_port);
+ LOGPFSML(params->parent_fi, LOGL_NOTICE,
+ "Init transceiver interface (%s:%u/%u)\n",
+ params->remote_host, params->base_port,
+ params->instance);
- /* Try to allocate memory */
- trx = talloc_zero(tall_ctx, struct trx_instance);
- if (!trx) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n");
+ /* Allocate a new dedicated state machine */
+ fi = osmo_fsm_inst_alloc_child(&trx_fsm, params->parent_fi,
+ params->parent_term_event);
+ if (fi == NULL) {
+ LOGPFSML(params->parent_fi, LOGL_ERROR,
+ "Failed to allocate an instance of FSM '%s'\n",
+ trx_fsm.name);
return NULL;
}
- /* Allocate a new dedicated state machine */
- trx->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx,
- NULL, LOGL_DEBUG, "trx_interface");
- if (trx->fsm == NULL) {
- LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance "
- "of FSM '%s'\n", trx_fsm.name);
- talloc_free(trx);
+ trx = talloc_zero(fi, struct trx_instance);
+ if (!trx) {
+ LOGPFSML(params->parent_fi, LOGL_ERROR, "Failed to allocate memory\n");
+ osmo_fsm_inst_free(fi);
return NULL;
}
@@ -673,32 +791,40 @@ struct trx_instance *trx_if_open(void *tall_ctx,
INIT_LLIST_HEAD(&trx->trx_ctrl_list);
/* Open sockets */
- rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, local_host,
- base_port + 101, remote_host, base_port + 1, trx_ctrl_read_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, /* TRXC */
+ params->local_host, params->base_port + 101 + offset,
+ params->remote_host, params->base_port + 1 + offset,
+ trx_ctrl_read_cb);
if (rc < 0)
goto udp_error;
- rc = trx_udp_open(trx, &trx->trx_ofd_data, local_host,
- base_port + 102, remote_host, base_port + 2, trx_data_rx_cb);
+ rc = trx_udp_open(trx, &trx->trx_ofd_data, /* TRXD */
+ params->local_host, params->base_port + 102 + offset,
+ params->remote_host, params->base_port + 2 + offset,
+ trx_data_rx_cb);
if (rc < 0)
goto udp_error;
+ trx->fn_advance = params->fn_advance;
+ trx->priv = params->priv;
+ fi->priv = trx;
+ trx->fi = fi;
+
return trx;
udp_error:
- LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n");
- osmo_fsm_inst_free(trx->fsm);
- talloc_free(trx);
+ LOGPFSML(params->parent_fi, LOGL_ERROR, "Couldn't establish UDP connection\n");
+ osmo_fsm_inst_free(trx->fi);
return NULL;
}
/* Flush pending control messages */
-void trx_if_flush_ctrl(struct trx_instance *trx)
+static void trx_if_flush_ctrl(struct trx_instance *trx)
{
struct trx_ctrl_msg *tcm;
/* Reset state machine */
- osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0);
+ osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0);
/* Clear command queue */
while (!llist_empty(&trx->trx_ctrl_list)) {
@@ -711,21 +837,39 @@ void trx_if_flush_ctrl(struct trx_instance *trx)
void trx_if_close(struct trx_instance *trx)
{
+ if (trx == NULL || trx->fi == NULL)
+ return;
+ osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_REQUEST, NULL);
+}
+
+static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ static const char cmd_poweroff[] = "CMD POWEROFF";
+ struct trx_instance *trx = fi->priv;
+
/* May be unallocated due to init error */
if (!trx)
return;
- LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n");
+ LOGPFSML(fi, LOGL_NOTICE, "Shutdown transceiver interface\n");
+
+ /* Abort TRXC response timer (if pending) */
+ osmo_timer_del(&trx->trx_ctrl_timer);
/* Flush CTRL message list */
trx_if_flush_ctrl(trx);
+ /* Power off if the transceiver is up */
+ if (trx->powered_up && trx->trx_ofd_ctrl.fd >= 0)
+ send(trx->trx_ofd_ctrl.fd, &cmd_poweroff[0], sizeof(cmd_poweroff), 0);
+
/* Close sockets */
trx_udp_close(&trx->trx_ofd_ctrl);
trx_udp_close(&trx->trx_ofd_data);
/* Free memory */
- osmo_fsm_inst_free(trx->fsm);
+ trx->fi->priv = NULL;
talloc_free(trx);
}
diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c
new file mode 100644
index 00000000..95458838
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_fsm.c
@@ -0,0 +1,822 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * The trxcon state machine (see 3GPP TS 44.004, section 5.1)
+ *
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/gsm/protocol/gsm_08_58.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/trxcon/l1ctl.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1gprs.h>
+
+#define S(x) (1 << (x))
+
+static void trxcon_allstate_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ struct trxcon_phyif_cmd phycmd = { };
+
+ switch (event) {
+ case TRXCON_EV_PHYIF_FAILURE:
+ trxcon->phyif = NULL;
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ break;
+ case TRXCON_EV_L2IF_FAILURE:
+ trxcon->l2if = NULL;
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ break;
+ case TRXCON_EV_RESET_FULL_REQ:
+ TALLOC_FREE(trxcon->fi_data);
+ if (fi->state != TRXCON_ST_RESET)
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
+ l1sched_reset(trxcon->sched, true);
+
+ /* Reset the L1 parameters */
+ trxcon->l1p.band_arfcn = 0xffff;
+ trxcon->l1p.tx_power = 0;
+ trxcon->l1p.ta = 0;
+
+ phycmd.type = TRXCON_PHYIF_CMDT_RESET;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ case TRXCON_EV_RESET_SCHED_REQ:
+ l1sched_reset(trxcon->sched, false);
+ break;
+ case TRXCON_EV_SET_PHY_CONFIG_REQ:
+ {
+ const struct trxcon_param_set_phy_config_req *req = data;
+
+ switch (req->type) {
+ case TRXCON_PHY_CFGT_PCHAN_COMB:
+ phycmd.type = TRXCON_PHYIF_CMDT_SETSLOT;
+ phycmd.param.setslot.tn = req->pchan_comb.tn;
+ phycmd.param.setslot.pchan = req->pchan_comb.pchan;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ case TRXCON_PHY_CFGT_TX_PARAMS:
+ if (trxcon->l1p.ta != req->tx_params.timing_advance) {
+ phycmd.type = TRXCON_PHYIF_CMDT_SETTA;
+ phycmd.param.setta.ta = req->tx_params.timing_advance;
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ }
+ trxcon->l1p.tx_power = req->tx_params.tx_power;
+ trxcon->l1p.ta = req->tx_params.timing_advance;
+ break;
+ }
+ break;
+ }
+ case TRXCON_EV_UPDATE_SACCH_CACHE_REQ:
+ {
+ const struct trxcon_param_tx_data_req *req = data;
+
+ if (req->link_id != L1SCHED_CH_LID_SACCH) {
+ LOGPFSML(fi, LOGL_ERROR, "Unexpected link_id=0x%02x\n", req->link_id);
+ break;
+ }
+ if (req->data_len != GSM_MACBLOCK_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "Unexpected data length=%zu\n", req->data_len);
+ break;
+ }
+
+ memcpy(&trxcon->sched->sacch_cache[0], req->data, req->data_len);
+ break;
+ }
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int trxcon_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (fi->state) {
+ case TRXCON_ST_FBSB_SEARCH:
+ l1ctl_tx_fbsb_fail(trxcon, trxcon->l1p.band_arfcn);
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0);
+ return 0;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_full_power_scan_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_full_power_scan_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ enum gsm_band band_start, band_stop;
+
+ if (trxcon->fi_data != NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "Full power scan is already in progress\n");
+ return;
+ }
+
+ /* The start and stop ARFCNs must be in the same band */
+ if (gsm_arfcn2band_rc(req->band_arfcn_start, &band_start) != 0 ||
+ gsm_arfcn2band_rc(req->band_arfcn_stop, &band_stop) != 0 ||
+ band_start != band_stop) {
+ LOGPFSML(fi, LOGL_ERROR, "Full power scan request has invalid params\n");
+ return;
+ }
+
+ trxcon->fi_data = talloc_memdup(fi, req, sizeof(*req));
+ OSMO_ASSERT(trxcon->fi_data != NULL);
+
+ /* trxcon_st_full_power_scan_onenter() sends the initial TRXCON_PHYIF_CMDT_MEASURE */
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
+}
+
+static void trxcon_st_reset_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FBSB_SEARCH_REQ:
+ {
+ const struct trxcon_param_fbsb_search_req *req = data;
+ unsigned long timeout_fns, timeout_ms;
+
+ /* Some PHYs need additional time to tune (in TDMA FNs) */
+ timeout_fns = req->timeout_fns + trxcon->phy_quirks.fbsb_extend_fns;
+ timeout_ms = timeout_fns * GSM_TDMA_FN_DURATION_uS / 1000;
+ osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, timeout_ms, 0);
+
+ l1sched_configure_ts(trxcon->sched, 0, req->pchan_config);
+
+ /* Only if current ARFCN differs */
+ if (trxcon->l1p.band_arfcn != req->band_arfcn) {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ .param.setfreq_h0 = {
+ .band_arfcn = req->band_arfcn,
+ },
+ };
+
+ /* Update current ARFCN */
+ trxcon->l1p.band_arfcn = req->band_arfcn;
+
+ /* Tune transceiver to required ARFCN */
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ }
+
+ const struct trxcon_phyif_cmd phycmd = { .type = TRXCON_PHYIF_CMDT_POWERON };
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+ break;
+ }
+ case TRXCON_EV_FULL_POWER_SCAN_REQ:
+ handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_full_power_scan_onenter(struct osmo_fsm_inst *fi,
+ uint32_t prev_state)
+{
+ const struct trxcon_inst *trxcon = fi->priv;
+ const struct trxcon_param_full_power_scan_req *req = trxcon->fi_data;
+
+ /* req->band_arfcn_start holds the current ARFCN */
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_MEASURE,
+ .param.measure = {
+ .band_arfcn = req->band_arfcn_start,
+ },
+ };
+
+ trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd);
+}
+
+static void trxcon_st_full_power_scan_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FULL_POWER_SCAN_RES:
+ {
+ struct trxcon_param_full_power_scan_req *req = trxcon->fi_data;
+ const struct trxcon_param_full_power_scan_res *res = data;
+
+ if (req == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx unexpected power scan result\n");
+ break;
+ }
+
+ /* req->band_arfcn_start holds the expected ARFCN */
+ if (res->band_arfcn != req->band_arfcn_start) {
+ LOGPFSML(fi, LOGL_ERROR, "Rx power scan result "
+ "with unexpected ARFCN %u (expected %u)\n",
+ res->band_arfcn & ~ARFCN_FLAG_MASK,
+ req->band_arfcn_start & ~ARFCN_FLAG_MASK);
+ break;
+ }
+
+ if (res->band_arfcn < req->band_arfcn_stop) {
+ l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, false);
+ /* trxcon_st_full_power_scan_onenter() sends the next TRXCON_PHYIF_CMDT_MEASURE */
+ req->band_arfcn_start = res->band_arfcn + 1;
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */
+ } else {
+ l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, true);
+ LOGPFSML(fi, LOGL_INFO, "Full power scan completed\n");
+ TALLOC_FREE(trxcon->fi_data);
+ }
+ break;
+ }
+ case TRXCON_EV_FULL_POWER_SCAN_REQ:
+ handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_fbsb_search_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_FBSB_SEARCH_RES:
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0);
+ l1ctl_tx_fbsb_conf(trxcon, trxcon->l1p.band_arfcn, trxcon->sched->bsic);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_tx_access_burst_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->rach_req = (struct l1sched_prim_rach) {
+ .chdr = {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ },
+ .synch_seq = req->synch_seq,
+ .offset = req->offset,
+ .is_11bit = req->is_11bit,
+ .ra = req->ra,
+ };
+
+ l1sched_prim_from_user(trxcon->sched, msg);
+}
+
+static void handle_dch_est_req(struct osmo_fsm_inst *fi,
+ const struct trxcon_param_dch_est_req *req)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+ enum gsm_phys_chan_config config;
+ struct l1sched_ts *ts;
+ int rc;
+
+ config = l1sched_chan_nr2pchan_config(req->chan_nr);
+ if (config == GSM_PCHAN_NONE) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to determine channel config\n");
+ return;
+ }
+
+ if (req->hopping) {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H1,
+ .param.setfreq_h1 = {
+ .hsn = req->h1.hsn,
+ .maio = req->h1.maio,
+ .ma = &req->h1.ma[0],
+ .ma_len = req->h1.n,
+ },
+ };
+
+ /* Apply the freq. hopping parameters */
+ if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0)
+ return;
+
+ /* Set current ARFCN to an invalid value */
+ trxcon->l1p.band_arfcn = 0xffff;
+ } else {
+ const struct trxcon_phyif_cmd phycmd = {
+ .type = TRXCON_PHYIF_CMDT_SETFREQ_H0,
+ .param.setfreq_h0 = {
+ .band_arfcn = req->h0.band_arfcn,
+ },
+ };
+
+ /* Tune transceiver to required ARFCN */
+ if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0)
+ return;
+
+ /* Update current ARFCN */
+ trxcon->l1p.band_arfcn = req->h0.band_arfcn;
+ }
+
+ /* Remove all active timeslots */
+ l1sched_reset(trxcon->sched, false);
+
+ rc = l1sched_configure_ts(trxcon->sched, req->chan_nr & 0x07, config);
+ if (rc)
+ return;
+ ts = trxcon->sched->ts[req->chan_nr & 0x07];
+ OSMO_ASSERT(ts != NULL);
+
+ l1sched_deactivate_all_lchans(ts);
+
+ /* Activate only requested lchans */
+ rc = l1sched_set_lchans(ts, req->chan_nr, 1, req->tch_mode, req->tsc);
+ if (rc) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to activate requested lchans\n");
+ return;
+ }
+
+ /* Store TSC for subsequent PDCH timeslot activation(s) */
+ trxcon->l1p.tsc = req->tsc;
+
+ if (config == GSM_PCHAN_PDCH)
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_PACKET_DATA, 0, 0);
+ else
+ osmo_fsm_inst_state_chg(fi, TRXCON_ST_DEDICATED, 0, 0);
+}
+
+static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_SET_CCCH_MODE_REQ:
+ {
+ struct trxcon_param_set_ccch_tch_mode_req *req = data;
+ enum gsm_phys_chan_config chan_config = req->mode;
+ struct l1sched_ts *ts = trxcon->sched->ts[0];
+
+ /* Make sure that TS0 is allocated and configured */
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "TS0 is not configured\n");
+ return;
+ }
+
+ /* Do nothing if the current mode matches required */
+ if (ts->mf_layout->chan_config != chan_config)
+ l1sched_configure_ts(trxcon->sched, 0, chan_config);
+ req->applied = true;
+ break;
+ }
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_RX_DATA_IND:
+ l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_DCH_REL_REQ:
+ l1sched_reset(trxcon->sched, false);
+ /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */
+ break;
+ case TRXCON_EV_SET_TCH_MODE_REQ:
+ {
+ struct trxcon_param_set_ccch_tch_mode_req *req = data;
+ unsigned int tn;
+
+ /* Iterate over timeslot list */
+ for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) {
+ struct l1sched_ts *ts = trxcon->sched->ts[tn];
+ struct l1sched_lchan_state *lchan;
+
+ /* Timeslot is not allocated */
+ if (ts == NULL || ts->mf_layout == NULL)
+ continue;
+
+ /* Iterate over all allocated lchans */
+ llist_for_each_entry(lchan, &ts->lchans, list) {
+ /* Omit inactive channels */
+ if (!lchan->active)
+ continue;
+ if (req->mode == GSM48_CMODE_SPEECH_AMR) {
+ int rc = l1sched_lchan_set_amr_cfg(lchan,
+ req->amr.codecs_bitmask,
+ req->amr.start_codec);
+ if (rc)
+ continue;
+ }
+ lchan->tch_mode = req->mode;
+ req->applied = true;
+ }
+ }
+ break;
+ }
+ case TRXCON_EV_CRYPTO_REQ:
+ {
+ const struct trxcon_param_crypto_req *req = data;
+ unsigned int tn = req->chan_nr & 0x07;
+ struct l1sched_ts *ts;
+
+ /* Make sure that required TS is allocated and configured */
+ ts = trxcon->sched->ts[tn];
+ if (ts == NULL || ts->mf_layout == NULL) {
+ LOGPFSML(fi, LOGL_ERROR, "TS%u is not configured\n", tn);
+ return;
+ }
+
+ if (l1sched_start_ciphering(ts, req->a5_algo, req->key, req->key_len) != 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to configure ciphering\n");
+ return;
+ }
+ break;
+ }
+ case TRXCON_EV_TX_DATA_REQ:
+ {
+ const struct trxcon_param_tx_data_req *req = data;
+ struct l1sched_prim *prim;
+ struct msgb *msg;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .chan_nr = req->chan_nr,
+ .link_id = req->link_id,
+ .traffic = req->traffic,
+ };
+
+ memcpy(msgb_put(msg, req->data_len), req->data, req->data_len);
+ l1sched_prim_from_user(trxcon->sched, msg);
+ break;
+ }
+ case TRXCON_EV_TX_DATA_CNF:
+ l1ctl_tx_dt_conf(trxcon, (const struct trxcon_param_tx_data_cnf *)data);
+ break;
+ case TRXCON_EV_RX_DATA_IND:
+ l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_tbf_cfg_req(struct trxcon_inst *trxcon, uint8_t tn, bool active)
+{
+ struct l1sched_state *sched = trxcon->sched;
+
+ if (active) {
+ struct l1sched_lchan_state *lchan;
+ struct l1sched_ts *ts;
+
+ if (sched->ts[tn] != NULL) /* already enabled */
+ return;
+ if (l1sched_configure_ts(sched, tn, GSM_PCHAN_PDCH) != 0)
+ return;
+ OSMO_ASSERT(sched->ts[tn] != NULL);
+ ts = sched->ts[tn];
+
+ l1sched_activate_lchan(ts, L1SCHED_PDTCH);
+ l1sched_activate_lchan(ts, L1SCHED_PTCCH);
+ llist_for_each_entry(lchan, &ts->lchans, list)
+ lchan->tsc = trxcon->l1p.tsc;
+ } else {
+ l1sched_del_ts(sched, tn);
+ }
+}
+
+static void trxcon_l1gprs_state_changed_cb(struct l1gprs_pdch *pdch, bool active)
+{
+ handle_tbf_cfg_req(pdch->gprs->priv, pdch->tn, active);
+}
+
+static void trxcon_st_packet_data_onenter(struct osmo_fsm_inst *fi,
+ uint32_t prev_state)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ OSMO_ASSERT(trxcon->gprs == NULL);
+ trxcon->gprs = l1gprs_state_alloc(trxcon, trxcon->log_prefix, trxcon);
+ l1gprs_state_set_pdch_changed_cb(trxcon->gprs, trxcon_l1gprs_state_changed_cb);
+ OSMO_ASSERT(trxcon->gprs != NULL);
+}
+
+static void trxcon_st_packet_data_onleave(struct osmo_fsm_inst *fi,
+ uint32_t next_state)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ l1gprs_state_free(trxcon->gprs);
+ trxcon->gprs = NULL;
+}
+
+static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi,
+ uint32_t event, void *data)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ switch (event) {
+ case TRXCON_EV_TX_ACCESS_BURST_REQ:
+ handle_tx_access_burst_req(fi, data);
+ break;
+ case TRXCON_EV_TX_ACCESS_BURST_CNF:
+ l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data);
+ break;
+ case TRXCON_EV_GPRS_UL_TBF_CFG_REQ:
+ l1gprs_handle_ul_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
+ break;
+ case TRXCON_EV_GPRS_DL_TBF_CFG_REQ:
+ l1gprs_handle_dl_tbf_cfg_req(trxcon->gprs, (struct msgb *)data);
+ break;
+ case TRXCON_EV_GPRS_UL_BLOCK_REQ:
+ {
+ struct l1gprs_prim_ul_block_req block_req;
+ struct l1sched_prim *prim;
+ struct msgb *msg = data;
+
+ if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0)
+ return;
+
+ msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST);
+ OSMO_ASSERT(msg != NULL);
+
+ prim = l1sched_prim_from_msgb(msg);
+ prim->data_req = (struct l1sched_prim_chdr) {
+ .frame_nr = block_req.hdr.fn,
+ .chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn,
+ .link_id = 0x00,
+ };
+
+ memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len);
+ l1sched_prim_from_user(trxcon->sched, msg);
+ break;
+ }
+ case TRXCON_EV_TX_DATA_CNF:
+ {
+ const struct trxcon_param_tx_data_cnf *cnf = data;
+ struct msgb *msg;
+
+ msg = l1gprs_handle_ul_block_cnf(trxcon->gprs,
+ cnf->frame_nr, cnf->chan_nr & 0x07,
+ cnf->data, cnf->data_len);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ break;
+ }
+ case TRXCON_EV_RX_DATA_IND:
+ {
+ const struct trxcon_param_rx_data_ind *ind = data;
+ struct l1gprs_prim_dl_block_ind block_ind;
+ struct msgb *msg;
+ uint8_t usf = 0xff;
+
+ block_ind = (struct l1gprs_prim_dl_block_ind) {
+ .hdr = {
+ .fn = ind->frame_nr,
+ .tn = ind->chan_nr & 0x07,
+ },
+ .meas = {
+ /* .ber10k is set below */
+ .ci_cb = 180, /* 18 dB */
+ .rx_lev = dbm2rxlev(ind->rssi),
+ },
+ .data_len = ind->data_len,
+ .data = ind->data,
+ };
+
+ if (ind->n_bits_total == 0)
+ block_ind.meas.ber10k = 10000;
+ else
+ block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total;
+
+ msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind, &usf);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ /* Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus
+ * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. The
+ * resulting value is first fn of the next block. */
+ const uint32_t rts_fn = GSM_TDMA_FN_SUM(ind->frame_nr, (ind->frame_nr % 13 == 8) ? 5 : 4);
+ msg = l1gprs_handle_rts_ind(trxcon->gprs, rts_fn, ind->chan_nr & 0x07, usf);
+ if (msg != NULL)
+ trxcon_l1ctl_send(trxcon, msg);
+ break;
+ }
+ case TRXCON_EV_DCH_EST_REQ:
+ handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data);
+ break;
+ case TRXCON_EV_DCH_REL_REQ:
+ l1sched_reset(trxcon->sched, false);
+ /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi,
+ enum osmo_fsm_term_cause cause)
+{
+ struct trxcon_inst *trxcon = fi->priv;
+
+ if (trxcon == NULL)
+ return;
+
+ /* Shutdown the scheduler */
+ if (trxcon->sched != NULL)
+ l1sched_free(trxcon->sched);
+ /* Clean up GPRS L1 state */
+ l1gprs_state_free(trxcon->gprs);
+
+ /* Close active connections */
+ if (trxcon->l2if != NULL)
+ trxcon_l1ctl_close(trxcon);
+ if (trxcon->phyif != NULL)
+ trxcon_phyif_close(trxcon->phyif);
+
+ talloc_free(trxcon);
+ fi->priv = NULL;
+}
+
+static const struct osmo_fsm_state trxcon_fsm_states[] = {
+ [TRXCON_ST_RESET] = {
+ .name = "RESET",
+ .out_state_mask = S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_FULL_POWER_SCAN),
+ .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_REQ)
+ | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ .action = &trxcon_st_reset_action,
+ },
+ [TRXCON_ST_FULL_POWER_SCAN] = {
+ .name = "FULL_POWER_SCAN",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FULL_POWER_SCAN),
+ .in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES)
+ | S(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ .onenter = &trxcon_st_full_power_scan_onenter,
+ .action = &trxcon_st_full_power_scan_action,
+ },
+ [TRXCON_ST_FBSB_SEARCH] = {
+ .name = "FBSB_SEARCH",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_BCCH_CCCH),
+ .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_RES),
+ .action = &trxcon_st_fbsb_search_action,
+ },
+ [TRXCON_ST_BCCH_CCCH] = {
+ .name = "BCCH_CCCH",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_SET_CCCH_MODE_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_DCH_EST_REQ),
+ .action = &trxcon_st_bcch_ccch_action,
+ },
+ [TRXCON_ST_DEDICATED] = {
+ .name = "DEDICATED",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_DCH_REL_REQ)
+ | S(TRXCON_EV_DCH_EST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_SET_TCH_MODE_REQ)
+ | S(TRXCON_EV_TX_DATA_REQ)
+ | S(TRXCON_EV_TX_DATA_CNF)
+ | S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_CRYPTO_REQ),
+ .action = &trxcon_st_dedicated_action,
+ },
+ [TRXCON_ST_PACKET_DATA] = {
+ .name = "PACKET_DATA",
+ .out_state_mask = S(TRXCON_ST_RESET)
+ | S(TRXCON_ST_FBSB_SEARCH)
+ | S(TRXCON_ST_DEDICATED)
+ | S(TRXCON_ST_PACKET_DATA),
+ .in_event_mask = S(TRXCON_EV_DCH_REL_REQ)
+ | S(TRXCON_EV_DCH_EST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_REQ)
+ | S(TRXCON_EV_TX_ACCESS_BURST_CNF)
+ | S(TRXCON_EV_GPRS_UL_TBF_CFG_REQ)
+ | S(TRXCON_EV_GPRS_DL_TBF_CFG_REQ)
+ | S(TRXCON_EV_GPRS_UL_BLOCK_REQ)
+ | S(TRXCON_EV_RX_DATA_IND)
+ | S(TRXCON_EV_TX_DATA_CNF),
+ .onenter = &trxcon_st_packet_data_onenter,
+ .onleave = &trxcon_st_packet_data_onleave,
+ .action = &trxcon_st_packet_data_action,
+ },
+};
+
+static const struct value_string trxcon_fsm_event_names[] = {
+ OSMO_VALUE_STRING(TRXCON_EV_PHYIF_FAILURE),
+ OSMO_VALUE_STRING(TRXCON_EV_L2IF_FAILURE),
+ OSMO_VALUE_STRING(TRXCON_EV_RESET_FULL_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_RESET_SCHED_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_RES),
+ OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_RES),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_CCCH_MODE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_TCH_MODE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_SET_PHY_CONFIG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_CNF),
+ OSMO_VALUE_STRING(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_DCH_EST_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_DCH_REL_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF),
+ OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND),
+ OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_DL_TBF_CFG_REQ),
+ OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_BLOCK_REQ),
+ { 0, NULL }
+};
+
+struct osmo_fsm trxcon_fsm_def = {
+ .name = "trxcon",
+ .states = trxcon_fsm_states,
+ .num_states = ARRAY_SIZE(trxcon_fsm_states),
+ .log_subsys = DLGLOBAL,
+ .event_names = trxcon_fsm_event_names,
+ .allstate_event_mask = S(TRXCON_EV_PHYIF_FAILURE)
+ | S(TRXCON_EV_L2IF_FAILURE)
+ | S(TRXCON_EV_RESET_FULL_REQ)
+ | S(TRXCON_EV_RESET_SCHED_REQ)
+ | S(TRXCON_EV_SET_PHY_CONFIG_REQ)
+ | S(TRXCON_EV_UPDATE_SACCH_CACHE_REQ),
+ .allstate_action = &trxcon_allstate_action,
+ .timer_cb = &trxcon_timer_cb,
+ .pre_term = &trxcon_fsm_pre_term_cb,
+};
+
+static __attribute__((constructor)) void on_dso_load(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0);
+}
diff --git a/src/host/trxcon/src/trxcon_inst.c b/src/host/trxcon/src/trxcon_inst.c
new file mode 100644
index 00000000..b7ff5810
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_inst.c
@@ -0,0 +1,107 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/talloc.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
+#include <osmocom/bb/l1gprs.h>
+
+extern int g_logc_l1c;
+extern int g_logc_l1d;
+
+void trxcon_set_log_cfg(const int *logc, unsigned int logc_num)
+{
+ int schc = DLGLOBAL;
+ int schd = DLGLOBAL;
+
+ for (unsigned int i = 0; i < logc_num; i++) {
+ switch ((enum trxcon_log_cat)i) {
+ case TRXCON_LOGC_FSM:
+ trxcon_fsm_def.log_subsys = logc[i];
+ break;
+ case TRXCON_LOGC_L1C:
+ g_logc_l1c = logc[i];
+ break;
+ case TRXCON_LOGC_L1D:
+ g_logc_l1d = logc[i];
+ break;
+ case TRXCON_LOGC_SCHC:
+ schc = logc[i];
+ break;
+ case TRXCON_LOGC_SCHD:
+ schd = logc[i];
+ break;
+ case TRXCON_LOGC_GPRS:
+ l1gprs_logging_init(logc[i]);
+ break;
+ }
+ }
+
+ l1sched_logging_init(schc, schd);
+}
+
+struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id)
+{
+ struct trxcon_inst *trxcon;
+ struct osmo_fsm_inst *fi;
+
+ fi = osmo_fsm_inst_alloc(&trxcon_fsm_def, ctx, NULL, LOGL_DEBUG, NULL);
+ OSMO_ASSERT(fi != NULL);
+
+ trxcon = talloc_zero(fi, struct trxcon_inst);
+ OSMO_ASSERT(trxcon != NULL);
+
+ fi->priv = trxcon;
+ trxcon->fi = fi;
+
+ osmo_fsm_inst_update_id_f(fi, "%u", id);
+ trxcon->id = id;
+
+ /* Logging context to be used by both l1ctl and l1sched modules */
+ trxcon->log_prefix = talloc_asprintf(trxcon, "%s: ", osmo_fsm_inst_name(fi));
+
+ /* Init scheduler */
+ const struct l1sched_cfg sched_cfg = {
+ .log_prefix = trxcon->log_prefix,
+ };
+
+ trxcon->sched = l1sched_alloc(trxcon, &sched_cfg, trxcon);
+ if (trxcon->sched == NULL) {
+ trxcon_inst_free(trxcon);
+ return NULL;
+ }
+
+ trxcon->phy_quirks.fbsb_extend_fns = 0;
+
+ return trxcon;
+}
+
+void trxcon_inst_free(struct trxcon_inst *trxcon)
+{
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
+ osmo_fsm_inst_term(trxcon->fi, OSMO_FSM_TERM_REQUEST, NULL);
+}
diff --git a/src/host/trxcon/src/trxcon_main.c b/src/host/trxcon/src/trxcon_main.c
new file mode 100644
index 00000000..3901e336
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_main.c
@@ -0,0 +1,423 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
+ * Contributions by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/signal.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/trxcon/trx_if.h>
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+
+#define COPYRIGHT \
+ "Copyright (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
+ "Contributions by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n" \
+ "License GPLv2+: GNU GPL version 2 or later " \
+ "<http://gnu.org/licenses/gpl.html>\n" \
+ "This is free software: you are free to change and redistribute it.\n" \
+ "There is NO WARRANTY, to the extent permitted by law.\n\n"
+
+static struct {
+ const char *debug_mask;
+ int daemonize;
+ int quit;
+
+ /* L1CTL specific */
+ unsigned int max_clients;
+ const char *bind_socket;
+
+ /* TRX specific */
+ const char *trx_bind_ip;
+ const char *trx_remote_ip;
+ uint16_t trx_base_port;
+ uint32_t trx_fn_advance;
+
+ /* PHY quirk: FBSB timeout extension (in TDMA FNs) */
+ unsigned int phyq_fbsb_extend_fns;
+
+ /* GSMTAP specific */
+ struct gsmtap_inst *gsmtap;
+ const char *gsmtap_ip;
+} app_data = {
+ .max_clients = 1, /* only one L1CTL client by default */
+ .bind_socket = "/tmp/osmocom_l2",
+ .trx_remote_ip = "127.0.0.1",
+ .trx_bind_ip = "0.0.0.0",
+ .trx_base_port = 6700,
+ .trx_fn_advance = 0,
+ .phyq_fbsb_extend_fns = 0,
+};
+
+static void *tall_trxcon_ctx = NULL;
+
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
+{
+ return trx_if_handle_phyif_burst_req(phyif, br);
+}
+
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
+{
+ return trx_if_handle_phyif_cmd(phyif, cmd);
+}
+
+void trxcon_phyif_close(void *phyif)
+{
+ trx_if_close(phyif);
+}
+
+void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
+{
+ /* Avoid use-after-free: both *fi and *trxcon are children of
+ * the L2IF (L1CTL connection), so we need to re-parent *fi
+ * to NULL before calling l1ctl_client_conn_close(). */
+ talloc_steal(NULL, trxcon->fi);
+ l1ctl_client_conn_close(trxcon->l2if);
+}
+
+int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct l1ctl_client *l1c = trxcon->l2if;
+
+ return l1ctl_client_send(l1c, msg);
+}
+
+static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
+{
+ struct trxcon_inst *trxcon = l1c->priv;
+
+ return trxcon_l1ctl_receive(trxcon, msg);
+}
+
+static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon;
+
+ trxcon = trxcon_inst_alloc(l1c, l1c->id);
+ if (trxcon == NULL) {
+ l1ctl_client_conn_close(l1c);
+ return;
+ }
+
+ l1c->log_prefix = talloc_strdup(l1c, trxcon->log_prefix);
+ l1c->priv = trxcon;
+ trxcon->l2if = l1c;
+
+ const struct trx_if_params trxcon_phyif_params = {
+ .local_host = app_data.trx_bind_ip,
+ .remote_host = app_data.trx_remote_ip,
+ .base_port = app_data.trx_base_port,
+ .fn_advance = app_data.trx_fn_advance,
+ .instance = trxcon->id,
+
+ .parent_fi = trxcon->fi,
+ .parent_term_event = TRXCON_EV_PHYIF_FAILURE,
+ .priv = trxcon,
+ };
+
+ /* Init transceiver interface */
+ trxcon->phyif = trx_if_open(&trxcon_phyif_params);
+ if (trxcon->phyif == NULL) {
+ /* TRXCON_EV_PHYIF_FAILURE triggers l1ctl_client_conn_close() */
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_PHYIF_FAILURE, NULL);
+ return;
+ }
+
+ trxcon->gsmtap = app_data.gsmtap;
+ trxcon->phy_quirks.fbsb_extend_fns = app_data.phyq_fbsb_extend_fns;
+}
+
+static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon = l1c->priv;
+
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
+
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
+}
+
+static void print_usage(const char *app)
+{
+ printf("Usage: %s\n", app);
+}
+
+static void print_help(void)
+{
+ printf(" Some help...\n");
+ printf(" -h --help this text\n");
+ printf(" -d --debug Change debug flags (e.g. DL1C:DSCH)\n");
+ printf(" -b --trx-bind TRX bind IP address (default 0.0.0.0)\n");
+ printf(" -i --trx-remote TRX remote IP address (default 127.0.0.1)\n");
+ printf(" -p --trx-port Base port of TRX instance (default 6700)\n");
+ printf(" -f --trx-advance Uplink burst scheduling advance (default 0)\n");
+ printf(" -F --fbsb-extend FBSB timeout extension (in TDMA FNs, default 0)\n");
+ printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n");
+ printf(" -g --gsmtap-ip The destination IP used for GSMTAP (disabled by default)\n");
+ printf(" -C --max-clients Maximum number of L1CTL connections (default 1)\n");
+ printf(" -D --daemonize Run as daemon\n");
+}
+
+static void handle_options(int argc, char **argv)
+{
+ while (1) {
+ char *endptr = NULL;
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"debug", 1, 0, 'd'},
+ {"socket", 1, 0, 's'},
+ {"trx-bind", 1, 0, 'b'},
+ /* NOTE: 'trx-ip' is now an alias for 'trx-remote'
+ * due to backward compatibility reasons! */
+ {"trx-ip", 1, 0, 'i'},
+ {"trx-remote", 1, 0, 'i'},
+ {"trx-port", 1, 0, 'p'},
+ {"trx-advance", 1, 0, 'f'},
+ {"fbsb-extend", 1, 0, 'F'},
+ {"gsmtap-ip", 1, 0, 'g'},
+ {"max-clients", 1, 0, 'C'},
+ {"daemonize", 0, 0, 'D'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "d:b:i:p:f:F:s:g:C:Dh",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ errno = 0;
+
+ switch (c) {
+ case 'h':
+ print_usage(argv[0]);
+ print_help();
+ exit(0);
+ break;
+ case 'd':
+ app_data.debug_mask = optarg;
+ break;
+ case 'b':
+ app_data.trx_bind_ip = optarg;
+ break;
+ case 'i':
+ app_data.trx_remote_ip = optarg;
+ break;
+ case 'p':
+ app_data.trx_base_port = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -p/--trx-port=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'f':
+ app_data.trx_fn_advance = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -f/--trx-advance=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'F':
+ app_data.phyq_fbsb_extend_fns = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -F/--fbsb-extend=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 's':
+ app_data.bind_socket = optarg;
+ break;
+ case 'g':
+ app_data.gsmtap_ip = optarg;
+ break;
+ case 'C':
+ app_data.max_clients = strtoul(optarg, &endptr, 10);
+ if (errno || *endptr != '\0') {
+ fprintf(stderr, "Failed to parse -C/--max-clients=%s\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'D':
+ app_data.daemonize = 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void signal_handler(int signum)
+{
+ fprintf(stderr, "signal %u received\n", signum);
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ app_data.quit++;
+ break;
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
+ case SIGUSR1:
+ case SIGUSR2:
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct l1ctl_server_cfg server_cfg;
+ struct l1ctl_server *server = NULL;
+ int rc = 0;
+
+ printf("%s", COPYRIGHT);
+ handle_options(argc, argv);
+
+ /* Track the use of talloc NULL memory contexts */
+ talloc_enable_null_tracking();
+
+ /* Init talloc memory management system */
+ tall_trxcon_ctx = talloc_init("trxcon context");
+ msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
+
+ /* Setup signal handlers */
+ signal(SIGINT, &signal_handler);
+ signal(SIGTERM, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
+ osmo_init_ignore_signals();
+
+ /* Init logging system */
+ trxcon_logging_init(tall_trxcon_ctx, app_data.debug_mask);
+
+ /* Configure pretty logging */
+ log_set_print_extended_timestamp(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_level(osmo_stderr_target, 1);
+
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
+
+ osmo_fsm_log_timeouts(true);
+
+ /* Optional GSMTAP */
+ if (app_data.gsmtap_ip != NULL) {
+ struct log_target *lt;
+
+ app_data.gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1);
+ if (!app_data.gsmtap) {
+ LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP Um logging\n");
+ goto exit;
+ }
+
+ lt = log_target_create_gsmtap(app_data.gsmtap_ip, GSMTAP_UDP_PORT,
+ "trxcon", false, false);
+ if (lt == NULL) {
+ LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP logging target\n");
+ goto exit;
+ } else {
+ log_add_target(lt);
+ }
+
+ /* Suppress ICMP "destination unreachable" errors */
+ gsmtap_source_add_sink(app_data.gsmtap);
+ }
+
+ /* Start the L1CTL server */
+ server_cfg = (struct l1ctl_server_cfg) {
+ .sock_path = app_data.bind_socket,
+ .num_clients_max = app_data.max_clients,
+ .conn_read_cb = &l1ctl_rx_cb,
+ .conn_accept_cb = &l1ctl_conn_accept_cb,
+ .conn_close_cb = &l1ctl_conn_close_cb,
+ };
+
+ server = l1ctl_server_alloc(tall_trxcon_ctx, &server_cfg);
+ if (server == NULL) {
+ rc = EXIT_FAILURE;
+ goto exit;
+ }
+
+ LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
+
+ if (app_data.daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ goto exit;
+ }
+ }
+
+ /* Initialize pseudo-random generator */
+ srand(time(NULL));
+
+ while (!app_data.quit)
+ osmo_select_main(0);
+
+exit:
+ if (server != NULL)
+ l1ctl_server_free(server);
+
+ /* Deinitialize logging */
+ log_fini();
+
+ /**
+ * Print report for the root talloc context in order
+ * to be able to find and fix potential memory leaks.
+ */
+ talloc_report_full(tall_trxcon_ctx, stderr);
+ talloc_free(tall_trxcon_ctx);
+
+ /* Make both Valgrind and ASAN happy */
+ talloc_report_full(NULL, stderr);
+ talloc_disable_null_tracking();
+
+ return rc;
+}
diff --git a/src/host/trxcon/src/trxcon_shim.c b/src/host/trxcon/src/trxcon_shim.c
new file mode 100644
index 00000000..ed2d402e
--- /dev/null
+++ b/src/host/trxcon/src/trxcon_shim.c
@@ -0,0 +1,262 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/gsmtap.h>
+#include <osmocom/gsm/rsl.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/phyif.h>
+#include <osmocom/bb/l1sched/l1sched.h>
+
+static void trxcon_gsmtap_send(struct trxcon_inst *trxcon,
+ const struct l1sched_prim_chdr *chdr,
+ const uint8_t *data, size_t data_len,
+ int8_t signal_dbm, uint8_t snr, bool uplink)
+{
+ uint16_t band_arfcn = trxcon->l1p.band_arfcn;
+ uint8_t chan_type, ss, tn;
+
+ if (uplink)
+ band_arfcn |= ARFCN_UPLINK;
+ if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0)
+ return;
+ chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic);
+
+ gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss,
+ chdr->frame_nr, signal_dbm, snr,
+ data, data_len);
+}
+
+/* External L1 API for the scheduler */
+int l1sched_handle_burst_req(struct l1sched_state *sched,
+ const struct l1sched_burst_req *br)
+{
+ struct trxcon_inst *trxcon = sched->priv;
+ const struct trxcon_phyif_burst_req phybr = {
+ .fn = br->fn,
+ .tn = br->tn,
+ .pwr = br->pwr,
+ .burst = &br->burst[0],
+ .burst_len = br->burst_len,
+ };
+
+ return trxcon_phyif_handle_burst_req(trxcon->phyif, &phybr);
+}
+
+/* External L2 API for the scheduler */
+static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_rx_data_ind ind = {
+ .traffic = prim->data_ind.chdr.traffic,
+ .chan_nr = prim->data_ind.chdr.chan_nr,
+ .link_id = prim->data_ind.chdr.link_id,
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->data_ind.chdr.frame_nr,
+ .toa256 = prim->data_ind.toa256,
+ .rssi = prim->data_ind.rssi,
+ .n_errors = prim->data_ind.n_errors,
+ .n_bits_total = prim->data_ind.n_bits_total,
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
+ };
+
+ if (trxcon->gsmtap != NULL && ind.data_len > 0) {
+ trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr,
+ ind.data, ind.data_len,
+ ind.rssi, 0, false);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind);
+}
+
+static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_data_cnf cnf = {
+ .traffic = prim->data_cnf.traffic,
+ .chan_nr = prim->data_cnf.chan_nr,
+ .link_id = prim->data_cnf.link_id,
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->data_cnf.frame_nr,
+ .data_len = msgb_l2len(msg),
+ .data = msgb_l2(msg),
+ };
+
+ if (trxcon->gsmtap != NULL) {
+ trxcon_gsmtap_send(trxcon, &prim->data_cnf,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf);
+}
+
+static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_param_tx_access_burst_cnf cnf = {
+ .band_arfcn = trxcon->l1p.band_arfcn,
+ .frame_nr = prim->rach_cnf.chdr.frame_nr,
+ };
+
+ if (trxcon->gsmtap != NULL) {
+ if (prim->rach_cnf.is_11bit) {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3));
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07));
+ } else {
+ msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra));
+ }
+
+ trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr,
+ msgb_l2(msg), msgb_l2len(msg),
+ 0, 0, true);
+ }
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf);
+}
+
+int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg)
+{
+ const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg);
+ struct trxcon_inst *trxcon = sched->priv;
+ int rc = 0;
+
+ LOGPFSML(trxcon->fi, LOGL_DEBUG,
+ "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+
+ switch (OSMO_PRIM_HDR(&prim->oph)) {
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION):
+ rc = handle_prim_data_ind(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM):
+ rc = handle_prim_data_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM):
+ rc = handle_prim_rach_cnf(trxcon, msg);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION):
+ if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH)
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL);
+ break;
+ case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION):
+ {
+ struct trxcon_param_set_phy_config_req req = {
+ .type = TRXCON_PHY_CFGT_PCHAN_COMB,
+ .pchan_comb = {
+ .tn = prim->pchan_comb_ind.tn,
+ .pchan = prim->pchan_comb_ind.pchan,
+ },
+ };
+
+ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req);
+ break;
+ }
+ default:
+ LOGPFSML(trxcon->fi, LOGL_ERROR,
+ "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n",
+ __func__, L1SCHED_PRIM_STR_ARGS(prim));
+ rc = -ENOTSUP;
+ }
+
+ msgb_free(msg);
+ return rc;
+}
+
+/* External L1 API for the PHYIF */
+int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_burst_req br = {
+ .fn = rts->fn,
+ .tn = rts->tn,
+ .burst_len = 0, /* NOPE.ind */
+ };
+
+ l1sched_pull_burst(trxcon->sched, &br);
+ return l1sched_handle_burst_req(trxcon->sched, &br);
+}
+
+int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind,
+ struct trxcon_phyif_rtr_rsp *rsp)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_probe probe = {
+ .fn = ind->fn,
+ .tn = ind->tn,
+ };
+
+ l1sched_handle_rx_probe(trxcon->sched, &probe);
+
+ memset(rsp, 0x00, sizeof(*rsp));
+
+ if (probe.flags & L1SCHED_PROBE_F_ACTIVE)
+ rsp->flags |= TRXCON_PHYIF_RTR_F_ACTIVE;
+
+ return 0;
+}
+
+int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *phybi)
+{
+ struct trxcon_inst *trxcon = priv;
+ struct l1sched_burst_ind bi = {
+ .fn = phybi->fn,
+ .tn = phybi->tn,
+ .toa256 = phybi->toa256,
+ .rssi = phybi->rssi,
+ /* .burst[] is populated below */
+ .burst_len = phybi->burst_len,
+ };
+
+ OSMO_ASSERT(phybi->burst_len <= sizeof(bi.burst));
+ memcpy(&bi.burst[0], phybi->burst, phybi->burst_len);
+
+ /* Poke scheduler */
+ return l1sched_handle_rx_burst(trxcon->sched, &bi);
+}
+
+int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp)
+{
+ struct trxcon_inst *trxcon = priv;
+
+ switch (rsp->type) {
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ {
+ const struct trxcon_phyif_rspp_measure *meas = &rsp->param.measure;
+ struct trxcon_param_full_power_scan_res res = {
+ .band_arfcn = meas->band_arfcn,
+ .dbm = meas->dbm,
+ };
+
+ return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res);
+ }
+ default:
+ LOGPFSML(trxcon->fi, LOGL_ERROR,
+ "Unhandled PHYIF response (type 0x%02x)\n", rsp->type);
+ return -ENODEV;
+ }
+}
diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h
deleted file mode 100644
index a44600d9..00000000
--- a/src/host/trxcon/trx_if.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#pragma once
-
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/fsm.h>
-
-#include "scheduler.h"
-#include "sched_trx.h"
-
-/* Forward declaration to avoid mutual include */
-struct l1ctl_link;
-
-enum trx_fsm_states {
- TRX_STATE_OFFLINE = 0,
- TRX_STATE_IDLE,
- TRX_STATE_ACTIVE,
- TRX_STATE_RSP_WAIT,
-};
-
-struct trx_instance {
- struct osmo_fd trx_ofd_ctrl;
- struct osmo_fd trx_ofd_data;
-
- struct osmo_timer_list trx_ctrl_timer;
- struct llist_head trx_ctrl_list;
- struct osmo_fsm_inst *fsm;
-
- /* HACK: we need proper state machines */
- uint32_t prev_state;
- bool powered_up;
-
- /* GSM L1 specific */
- uint16_t pm_band_arfcn_start;
- uint16_t pm_band_arfcn_stop;
- uint16_t band_arfcn;
- uint8_t tx_power;
- uint8_t bsic;
- uint8_t tsc;
- int8_t ta;
-
- /* Scheduler stuff */
- struct trx_sched sched;
- struct trx_ts *ts_list[TRX_TS_COUNT];
-
- /* Bind L1CTL link */
- struct l1ctl_link *l1l;
-};
-
-struct trx_ctrl_msg {
- struct llist_head list;
- char cmd[128];
- int retry_cnt;
- int critical;
- int cmd_len;
-};
-
-struct trx_instance *trx_if_open(void *tall_ctx,
- const char *local_host, const char *remote_host, uint16_t port);
-void trx_if_flush_ctrl(struct trx_instance *trx);
-void trx_if_close(struct trx_instance *trx);
-
-int trx_if_cmd_poweron(struct trx_instance *trx);
-int trx_if_cmd_poweroff(struct trx_instance *trx);
-int trx_if_cmd_echo(struct trx_instance *trx);
-
-int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta);
-
-int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn);
-int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn);
-
-int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type);
-int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn,
- uint8_t maio, uint16_t *ma, size_t ma_len);
-
-int trx_if_cmd_measure(struct trx_instance *trx,
- uint16_t band_arfcn_start, uint16_t band_arfcn_stop);
-
-int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
- uint8_t pwr, const ubit_t *bits);
diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c
deleted file mode 100644
index 8e371df1..00000000
--- a/src/host/trxcon/trxcon.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * OsmocomBB <-> SDR connection bridge
- *
- * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <signal.h>
-#include <time.h>
-
-#include <arpa/inet.h>
-
-#include <osmocom/core/fsm.h>
-#include <osmocom/core/msgb.h>
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/signal.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/application.h>
-
-#include <osmocom/gsm/gsm_utils.h>
-
-#include "trxcon.h"
-#include "trx_if.h"
-#include "logging.h"
-#include "l1ctl.h"
-#include "l1ctl_link.h"
-#include "l1ctl_proto.h"
-#include "scheduler.h"
-#include "sched_trx.h"
-
-#define COPYRIGHT \
- "Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>\n" \
- "License GPLv2+: GNU GPL version 2 or later " \
- "<http://gnu.org/licenses/gpl.html>\n" \
- "This is free software: you are free to change and redistribute it.\n" \
- "There is NO WARRANTY, to the extent permitted by law.\n\n"
-
-static struct {
- const char *debug_mask;
- int daemonize;
- int quit;
-
- /* L1CTL specific */
- struct l1ctl_link *l1l;
- const char *bind_socket;
-
- /* TRX specific */
- struct trx_instance *trx;
- const char *trx_bind_ip;
- const char *trx_remote_ip;
- uint16_t trx_base_port;
- uint32_t trx_fn_advance;
-} app_data;
-
-static void *tall_trxcon_ctx = NULL;
-struct osmo_fsm_inst *trxcon_fsm;
-
-static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- if (event == L1CTL_EVENT_CONNECT)
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0);
-}
-
-static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi,
- uint32_t event, void *data)
-{
- switch (event) {
- case L1CTL_EVENT_DISCONNECT:
- osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0);
-
- if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) {
- /* Reset scheduler and clock counter */
- sched_trx_reset(app_data.trx, true);
-
- /* TODO: implement trx_if_reset() */
- trx_if_cmd_poweroff(app_data.trx);
- trx_if_cmd_echo(app_data.trx);
- }
- break;
- case TRX_EVENT_RSP_ERROR:
- case TRX_EVENT_OFFLINE:
- /* TODO: notify L2 & L3 about that */
- break;
- default:
- LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event);
- }
-}
-
-static struct osmo_fsm_state trxcon_fsm_states[] = {
- [TRXCON_STATE_IDLE] = {
- .in_event_mask = GEN_MASK(L1CTL_EVENT_CONNECT),
- .out_state_mask = GEN_MASK(TRXCON_STATE_MANAGED),
- .name = "IDLE",
- .action = trxcon_fsm_idle_action,
- },
- [TRXCON_STATE_MANAGED] = {
- .in_event_mask = (
- GEN_MASK(L1CTL_EVENT_DISCONNECT) |
- GEN_MASK(TRX_EVENT_RSP_ERROR) |
- GEN_MASK(TRX_EVENT_OFFLINE)),
- .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE),
- .name = "MANAGED",
- .action = trxcon_fsm_managed_action,
- },
-};
-
-static const struct value_string app_evt_names[] = {
- OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT),
- OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT),
- OSMO_VALUE_STRING(TRX_EVENT_OFFLINE),
- OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR),
- { 0, NULL }
-};
-
-static struct osmo_fsm trxcon_fsm_def = {
- .name = "trxcon_app_fsm",
- .states = trxcon_fsm_states,
- .num_states = ARRAY_SIZE(trxcon_fsm_states),
- .log_subsys = DAPP,
- .event_names = app_evt_names,
-};
-
-static void print_usage(const char *app)
-{
- printf("Usage: %s\n", app);
-}
-
-static void print_help(void)
-{
- printf(" Some help...\n");
- printf(" -h --help this text\n");
- printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT);
- printf(" -b --trx-bind TRX bind IP address (default 0.0.0.0)\n");
- printf(" -i --trx-remote TRX remote IP address (default 127.0.0.1)\n");
- printf(" -p --trx-port Base port of TRX instance (default 6700)\n");
- printf(" -f --trx-advance Scheduler clock advance (default 20)\n");
- printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n");
- printf(" -D --daemonize Run as daemon\n");
-}
-
-static void handle_options(int argc, char **argv)
-{
- while (1) {
- int option_index = 0, c;
- static struct option long_options[] = {
- {"help", 0, 0, 'h'},
- {"debug", 1, 0, 'd'},
- {"socket", 1, 0, 's'},
- {"trx-bind", 1, 0, 'b'},
- /* NOTE: 'trx-ip' is now an alias for 'trx-remote'
- * due to backward compatibility reasons! */
- {"trx-ip", 1, 0, 'i'},
- {"trx-remote", 1, 0, 'i'},
- {"trx-port", 1, 0, 'p'},
- {"trx-advance", 1, 0, 'f'},
- {"daemonize", 0, 0, 'D'},
- {0, 0, 0, 0}
- };
-
- c = getopt_long(argc, argv, "d:b:i:p:f:s:Dh",
- long_options, &option_index);
- if (c == -1)
- break;
-
- switch (c) {
- case 'h':
- print_usage(argv[0]);
- print_help();
- exit(0);
- break;
- case 'd':
- app_data.debug_mask = optarg;
- break;
- case 'b':
- app_data.trx_bind_ip = optarg;
- break;
- case 'i':
- app_data.trx_remote_ip = optarg;
- break;
- case 'p':
- app_data.trx_base_port = atoi(optarg);
- break;
- case 'f':
- app_data.trx_fn_advance = atoi(optarg);
- break;
- case 's':
- app_data.bind_socket = optarg;
- break;
- case 'D':
- app_data.daemonize = 1;
- break;
- default:
- break;
- }
- }
-}
-
-static void init_defaults(void)
-{
- app_data.bind_socket = "/tmp/osmocom_l2";
- app_data.trx_remote_ip = "127.0.0.1";
- app_data.trx_bind_ip = "0.0.0.0";
- app_data.trx_base_port = 6700;
- app_data.trx_fn_advance = 20;
-
- app_data.debug_mask = NULL;
- app_data.daemonize = 0;
- app_data.quit = 0;
-}
-
-static void signal_handler(int signal)
-{
- fprintf(stderr, "signal %u received\n", signal);
-
- switch (signal) {
- case SIGINT:
- app_data.quit++;
- break;
- case SIGABRT:
- case SIGUSR1:
- case SIGUSR2:
- talloc_report_full(tall_trxcon_ctx, stderr);
- break;
- default:
- break;
- }
-}
-
-int main(int argc, char **argv)
-{
- int rc = 0;
-
- printf("%s", COPYRIGHT);
- init_defaults();
- handle_options(argc, argv);
-
- /* Track the use of talloc NULL memory contexts */
- talloc_enable_null_tracking();
-
- /* Init talloc memory management system */
- tall_trxcon_ctx = talloc_init("trxcon context");
- msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
-
- /* Setup signal handlers */
- signal(SIGINT, &signal_handler);
- signal(SIGUSR1, &signal_handler);
- signal(SIGUSR2, &signal_handler);
- osmo_init_ignore_signals();
-
- /* Init logging system */
- trx_log_init(tall_trxcon_ctx, app_data.debug_mask);
-
- /* Allocate the application state machine */
- osmo_fsm_register(&trxcon_fsm_def);
- trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trxcon_ctx,
- NULL, LOGL_DEBUG, "main");
-
- /* Init L1CTL server */
- app_data.l1l = l1ctl_link_init(tall_trxcon_ctx,
- app_data.bind_socket);
- if (app_data.l1l == NULL)
- goto exit;
-
- /* Init transceiver interface */
- app_data.trx = trx_if_open(tall_trxcon_ctx,
- app_data.trx_bind_ip, app_data.trx_remote_ip,
- app_data.trx_base_port);
- if (!app_data.trx)
- goto exit;
-
- /* Bind L1CTL with TRX and vice versa */
- app_data.l1l->trx = app_data.trx;
- app_data.trx->l1l = app_data.l1l;
-
- /* Init scheduler */
- rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance);
- if (rc)
- goto exit;
-
- LOGP(DAPP, LOGL_NOTICE, "Init complete\n");
-
- if (app_data.daemonize) {
- rc = osmo_daemonize();
- if (rc < 0) {
- perror("Error during daemonize");
- goto exit;
- }
- }
-
- /* Initialize pseudo-random generator */
- srand(time(NULL));
-
- while (!app_data.quit)
- osmo_select_main(0);
-
-exit:
- /* Close active connections */
- l1ctl_link_shutdown(app_data.l1l);
- sched_trx_shutdown(app_data.trx);
- trx_if_close(app_data.trx);
-
- /* Shutdown main state machine */
- osmo_fsm_inst_free(trxcon_fsm);
-
- /* Deinitialize logging */
- log_fini();
-
- /**
- * Print report for the root talloc context in order
- * to be able to find and fix potential memory leaks.
- */
- talloc_report_full(tall_trxcon_ctx, stderr);
- talloc_free(tall_trxcon_ctx);
-
- /* Make both Valgrind and ASAN happy */
- talloc_report_full(NULL, stderr);
- talloc_disable_null_tracking();
-
- return rc;
-}
diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h
deleted file mode 100644
index f66a6285..00000000
--- a/src/host/trxcon/trxcon.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#define GEN_MASK(state) (0x01 << state)
-
-extern struct osmo_fsm_inst *trxcon_fsm;
-
-enum trxcon_fsm_states {
- TRXCON_STATE_IDLE = 0,
- TRXCON_STATE_MANAGED,
-};
-
-enum trxcon_fsm_events {
- /* L1CTL specific events */
- L1CTL_EVENT_CONNECT,
- L1CTL_EVENT_DISCONNECT,
-
- /* TRX specific events */
- TRX_EVENT_RSP_ERROR,
- TRX_EVENT_OFFLINE,
-};