diff --git a/trxcon/.gitignore b/trxcon/.gitignore deleted file mode 100644 index fe90e43c..00000000 --- a/trxcon/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# autoreconf by-products -*.in - -aclocal.m4 -autom4te.cache/ -configure -depcomp -install-sh -missing -compile - -# configure by-products -.deps/ -Makefile - -config.status -version.h - -# build by-products -*.o -*.a - -trxcon - -# various -.version -.tarball-version diff --git a/trxcon/Makefile.am b/trxcon/Makefile.am index 76165a80..5b1002c8 100644 --- a/trxcon/Makefile.am +++ b/trxcon/Makefile.am @@ -1,58 +1,21 @@ -#AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 -include $(top_srcdir)/Makefile.common +SUBDIRS = \ + include \ + src \ + $(NULL) -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -AM_CXXFLAGS = -Wall -O3 -g -ldl -lpthread +ACLOCAL_AMFLAGS = -I m4 + +BUILT_SOURCES = \ + $(top_srcdir)/.version \ + $(NULL) +EXTRA_DIST = \ + .version \ + $(NULL) # 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) \ - $(NULL) - -noinst_LTLIBRARIES = libtrxcon.la - -libtrxcon_la_SOURCES = \ - l1ctl_link.c \ - l1ctl.c \ - trx_if.c \ - logging.c \ - trxcon.c \ - $(NULL) - -# Scheduler -libtrxcon_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_clck.c \ - sched_prim.c \ - sched_trx.c \ - $(NULL) - -libtrxcon_la_LIBADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOCODING_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(NULL) - diff --git a/trxcon/configure.ac b/trxcon/configure.ac new file mode 100644 index 00000000..46991a28 --- /dev/null +++ b/trxcon/configure.ac @@ -0,0 +1,45 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([trxcon], [0.0.0]) +AM_INIT_AUTOMAKE +LT_INIT + +CFLAGS="$CFLAGS -std=gnu11" + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) + +dnl checks for header files +AC_HEADER_STDC + +AC_ARG_ENABLE(sanitize, + [AS_HELP_STRING( + [--enable-sanitize], + [Compile with address sanitizer enabled], + )], [sanitize=$enableval], [sanitize="no"]) +if test x"$sanitize" = x"yes" +then + CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" + CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" +fi + +dnl Checks for typedefs, structures and compiler characteristics + +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/trxcon/include/Makefile.am b/trxcon/include/Makefile.am new file mode 100644 index 00000000..9d963a02 --- /dev/null +++ b/trxcon/include/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + osmocom \ + $(NULL) diff --git a/trxcon/include/osmocom/Makefile.am b/trxcon/include/osmocom/Makefile.am new file mode 100644 index 00000000..83c6385c --- /dev/null +++ b/trxcon/include/osmocom/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + bb \ + $(NULL) diff --git a/trxcon/include/osmocom/bb/Makefile.am b/trxcon/include/osmocom/bb/Makefile.am new file mode 100644 index 00000000..c26db00d --- /dev/null +++ b/trxcon/include/osmocom/bb/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = \ + l1sched \ + trxcon \ + $(NULL) diff --git a/trxcon/include/osmocom/bb/l1sched/Makefile.am b/trxcon/include/osmocom/bb/l1sched/Makefile.am new file mode 100644 index 00000000..76f54a7e --- /dev/null +++ b/trxcon/include/osmocom/bb/l1sched/Makefile.am @@ -0,0 +1,4 @@ +noinst_HEADERS = \ + l1sched.h \ + logging.h \ + $(NULL) diff --git a/trxcon/include/osmocom/bb/l1sched/l1sched.h b/trxcon/include/osmocom/bb/l1sched/l1sched.h new file mode 100644 index 00000000..9686562f --- /dev/null +++ b/trxcon/include/osmocom/bb/l1sched/l1sched.h @@ -0,0 +1,508 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 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) +/* Is continuous burst transmission assumed */ +#define L1SCHED_CH_FLAG_CBTX (1 << 2) + +#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_clck_state { + L1SCHED_CLCK_ST_WAIT, + L1SCHED_CLCK_ST_OK, +}; + +enum l1sched_burst_type { + L1SCHED_BURST_GMSK, + L1SCHED_BURST_8PSK, +}; + +enum l1sched_ts_prim_type { + L1SCHED_PRIM_DATA, + L1SCHED_PRIM_RACH8, + L1SCHED_PRIM_RACH11, +}; + +/** + * 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 +}; + +enum l1sched_data_type { + L1SCHED_DT_PACKET_DATA, + L1SCHED_DT_SIGNALING, + L1SCHED_DT_TRAFFIC, + L1SCHED_DT_OTHER, /* SCH and RACH */ +}; + +enum l1sched_config_type { + /*! Channel combination for a timeslot */ + L1SCHED_CFG_PCHAN_COMB, +}; + +/* Represents a (re)configuration request */ +struct l1sched_config_req { + enum l1sched_config_type type; + union { + struct { + uint8_t tn; + enum gsm_phys_chan_config pchan; + } pchan_comb; + }; +}; + +/* 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[EDGE_BURST_LEN]; + size_t burst_len; +}; + +typedef int l1sched_lchan_rx_func(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); + +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; + /*! Sub-slot number (for SDCCH and TCH/H) */ + uint8_t ss_nr; + /*! GSMTAP channel type (see GSMTAP_CHANNEL_*) */ + uint8_t gsmtap_chan_type; + + /*! 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 8 unique measurements) */ +struct l1sched_lchan_meas_hist { + struct l1sched_meas_set buf[8]; + 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 */ + uint8_t rx_burst_mask; + /*! Mask of transmitted bursts */ + uint8_t tx_burst_mask; + /*! Burst buffer for RX */ + sbit_t *rx_bursts; + /*! Burst buffer for TX */ + ubit_t *tx_bursts; + + /*! A primitive being sent */ + struct l1sched_ts_prim *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; + /*! Queue primitives for TX */ + struct llist_head tx_prims; + /*! Backpointer to the scheduler */ + struct l1sched_state *sched; +}; + +/* Represents one TX primitive in the queue of l1sched_ts */ +struct l1sched_ts_prim { + /*! Link to queue of TS */ + struct llist_head list; + /*! Type of primitive */ + enum l1sched_ts_prim_type type; + /*! Logical channel type */ + enum l1sched_lchan_type chan; + /*! Payload length */ + size_t payload_len; + /*! Payload */ + uint8_t payload[0]; +}; + +/*! Represents a RACH (8-bit or 11-bit) primitive */ +struct l1sched_ts_prim_rach { + /*! RA value */ + uint16_t ra; + /*! Training Sequence (only for 11-bit RA) */ + uint8_t synch_seq; + /*! Transmission offset (how many frames to skip) */ + uint8_t offset; +}; + +/*! Scheduler configuration */ +struct l1sched_cfg { + /*! Logging context (used as prefix for messages) */ + const char *log_prefix; + /*! TDMA frame-number advance */ + uint32_t fn_advance; +}; + +/*! One scheduler instance */ +struct l1sched_state { + /*! Clock state */ + enum l1sched_clck_state clck_state; + /*! Local clock source */ + struct timespec clock; + /*! Count of processed frames */ + uint32_t fn_counter_proc; + /*! Local frame counter advance */ + uint32_t fn_counter_advance; + /*! Count of lost frames */ + uint32_t fn_counter_lost; + /*! Frame callback timer */ + struct osmo_timer_list clock_timer; + /*! Frame callback */ + void (*clock_cb)(struct l1sched_state *sched); + /*! 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, int tn); + +/* Scheduler management functions */ +void l1sched_logging_init(int log_cat_common, int log_cat_data); +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); +enum l1sched_lchan_type l1sched_chan_nr2lchan_type(uint8_t chan_nr, + uint8_t link_id); + +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_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(struct l1sched_ts *ts, + enum l1sched_lchan_type chan); + +/* Primitive management functions */ +struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched, + enum l1sched_ts_prim_type type, + uint8_t chan_nr, uint8_t link_id, + const uint8_t *pl, size_t pl_len); + +#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) + +#define L1SCHED_PRIM_IS_RACH11(prim) \ + (prim->type == L1SCHED_PRIM_RACH11) + +#define L1SCHED_PRIM_IS_RACH8(prim) \ + (prim->type == L1SCHED_PRIM_RACH8) + +#define L1SCHED_PRIM_IS_RACH(prim) \ + (L1SCHED_PRIM_IS_RACH8(prim) || L1SCHED_PRIM_IS_RACH11(prim)) + +#define L1SCHED_PRIM_IS_TCH(prim) \ + (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN) + +#define L1SCHED_PRIM_IS_FACCH(prim) \ + (L1SCHED_CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN) + +struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue, + uint32_t fn, struct l1sched_lchan_state *lchan); +int l1sched_prim_dummy(struct l1sched_lchan_state *lchan); +void l1sched_prim_drop(struct l1sched_lchan_state *lchan); +void l1sched_prim_flush_queue(struct llist_head *list); + +int l1sched_handle_rx_burst(struct l1sched_state *sched, uint8_t tn, + uint32_t fn, sbit_t *bits, uint16_t nbits, + const struct l1sched_meas_set *meas); + +/* Shared declarations for lchan handlers */ +extern const uint8_t l1sched_nb_training_bits[8][26]; + +const char *l1sched_burst_mask2str(const uint8_t *mask, int bits); +size_t l1sched_bad_frame_ind(uint8_t *l2, struct l1sched_lchan_state *lchan); + +/* 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_meas_set *meas); +void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n); + +int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn); +void l1sched_clck_reset(struct l1sched_state *sched); + +/* External L1 API, must be implemented by the API user */ +int l1sched_handle_config_req(struct l1sched_state *sched, + const struct l1sched_config_req *cr); +int l1sched_handle_burst_req(struct l1sched_state *sched, + const struct l1sched_burst_req *br); + +/* External L2 API, must be implemented by the API user */ +int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan, + const uint8_t *data, size_t data_len, + int n_errors, int n_bits_total, + enum l1sched_data_type dt); +int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan, + uint32_t fn, enum l1sched_data_type dt); diff --git a/trxcon/include/osmocom/bb/l1sched/logging.h b/trxcon/include/osmocom/bb/l1sched/logging.h new file mode 100644 index 00000000..d2b80e37 --- /dev/null +++ b/trxcon/include/osmocom/bb/l1sched/logging.h @@ -0,0 +1,35 @@ +#pragma once + +extern int l1sched_log_cat_common; +extern int l1sched_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, common, 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/trxcon/include/osmocom/bb/trxcon/Makefile.am b/trxcon/include/osmocom/bb/trxcon/Makefile.am new file mode 100644 index 00000000..8636c936 --- /dev/null +++ b/trxcon/include/osmocom/bb/trxcon/Makefile.am @@ -0,0 +1,9 @@ +noinst_HEADERS = \ + l1ctl_proto.h \ + l1ctl_server.h \ + l1ctl.h \ + sched_utils.h \ + trx_if.h \ + logging.h \ + trxcon.h \ + $(NULL) diff --git a/trxcon/include/osmocom/bb/trxcon/l1ctl.h b/trxcon/include/osmocom/bb/trxcon/l1ctl.h new file mode 100644 index 00000000..9518bb97 --- /dev/null +++ b/trxcon/include/osmocom/bb/trxcon/l1ctl.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include +#include + +/* Event handlers */ +int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg); + +int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint16_t band_arfcn, uint8_t bsic); +int l1ctl_tx_fbsb_fail(struct l1ctl_client *l1c, uint16_t band_arfcn); +int l1ctl_tx_ccch_mode_conf(struct l1ctl_client *l1c, uint8_t mode); +int l1ctl_tx_pm_conf(struct l1ctl_client *l1c, uint16_t band_arfcn, + int dbm, int last); +int l1ctl_tx_reset_conf(struct l1ctl_client *l1c, uint8_t type); +int l1ctl_tx_reset_ind(struct l1ctl_client *l1c, uint8_t type); + +int l1ctl_tx_dt_ind(struct l1ctl_client *l1c, + const struct l1ctl_info_dl *dl_info, + const uint8_t *l2, size_t l2_len, + bool traffic); +int l1ctl_tx_dt_conf(struct l1ctl_client *l1c, + struct l1ctl_info_dl *data, bool traffic); +int l1ctl_tx_rach_conf(struct l1ctl_client *l1c, + uint16_t band_arfcn, uint32_t fn); diff --git a/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h b/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h new file mode 100644 index 00000000..8a7e48ee --- /dev/null +++ b/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#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 + +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/trxcon/logging.h b/trxcon/include/osmocom/bb/trxcon/logging.h similarity index 70% rename from trxcon/logging.h rename to trxcon/include/osmocom/bb/trxcon/logging.h index 152c3467..617e8f16 100644 --- a/trxcon/logging.h +++ b/trxcon/include/osmocom/bb/trxcon/logging.h @@ -2,13 +2,11 @@ #include -#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH:DSCHD" - enum { DAPP, DL1C, DL1D, - DTRX, + DTRXC, DTRXD, DSCH, DSCHD, diff --git a/trxcon/include/osmocom/bb/trxcon/sched_utils.h b/trxcon/include/osmocom/bb/trxcon/sched_utils.h new file mode 100644 index 00000000..f1ca0936 --- /dev/null +++ b/trxcon/include/osmocom/bb/trxcon/sched_utils.h @@ -0,0 +1,62 @@ +/* Auxiliary scheduler utilities. + * + * (C) 2017 by Harald Welte + * (C) 2022 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 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 . + * + */ + +#pragma once + +#include +#include +#include + +/*! determine whether an uplink AMR block is CMI according to 3GPP TS 45.009. + * \param[in] fn_begin frame number of the beginning of the block. + * \returns true in case of CMI; false otherwise. */ +static inline bool ul_amr_fn_is_cmi(uint32_t fn_begin) +{ + switch (fn_begin % 26) { + /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */ + /* valid for AHS subslot 0 and AFS: */ + case 0: + case 8: + case 17: + /* valid for AHS subslot 1: */ + case 1: + case 9: + case 18: + return true; + break; + /* Complementary values for sanity check */ + /* valid for AHS subslot 0 and AFS: */ + case 4: + case 13: + case 21: + /* valid for AHS subslot 1: */ + case 5: + case 14: + case 22: + return false; + break; + default: + OSMO_ASSERT(false); + return false; + break; + } +} diff --git a/trxcon/trx_if.h b/trxcon/include/osmocom/bb/trxcon/trx_if.h similarity index 66% rename from trxcon/trx_if.h rename to trxcon/include/osmocom/bb/trxcon/trx_if.h index abbde973..1faa6105 100644 --- a/trxcon/trx_if.h +++ b/trxcon/include/osmocom/bb/trxcon/trx_if.h @@ -5,14 +5,14 @@ #include #include -#include "scheduler.h" -#include "sched_trx.h" +#include #define TRXC_BUF_SIZE 1024 #define TRXD_BUF_SIZE 512 /* Forward declaration to avoid mutual include */ -struct l1ctl_link; +struct l1sched_burst_req; +struct trxcon_inst; enum trx_fsm_states { TRX_STATE_OFFLINE = 0, @@ -22,14 +22,15 @@ enum trx_fsm_states { }; struct trx_instance { -#ifndef IPCIF + /* trxcon instance we belong to */ + struct trxcon_inst *trxcon; + struct osmo_fd trx_ofd_ctrl; struct osmo_fd trx_ofd_data; -#endif struct osmo_timer_list trx_ctrl_timer; struct llist_head trx_ctrl_list; - struct osmo_fsm_inst *fsm; + struct osmo_fsm_inst *fi; /* HACK: we need proper state machines */ uint32_t prev_state; @@ -38,18 +39,6 @@ struct trx_instance { /* 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 { @@ -60,7 +49,7 @@ struct trx_ctrl_msg { int cmd_len; }; -struct trx_instance *trx_if_open(void *tall_ctx, +struct trx_instance *trx_if_open(struct trxcon_inst *trxcon, 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); @@ -68,19 +57,19 @@ 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_sync(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_setslot(struct trx_instance *trx, uint8_t tn, + enum gsm_phys_chan_config pchan); +int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, uint8_t maio, + const 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); +int trx_if_tx_burst(struct trx_instance *trx, + const struct l1sched_burst_req *br); diff --git a/trxcon/include/osmocom/bb/trxcon/trxcon.h b/trxcon/include/osmocom/bb/trxcon/trxcon.h new file mode 100644 index 00000000..9aa5e554 --- /dev/null +++ b/trxcon/include/osmocom/bb/trxcon/trxcon.h @@ -0,0 +1,169 @@ +#pragma once + +struct l1sched_state; + +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_UPDATE_SACCH_CACHE_REQ, + TRXCON_EV_DEDICATED_ESTABLISH_REQ, + TRXCON_EV_DEDICATED_RELEASE_REQ, + TRXCON_EV_TX_TRAFFIC_REQ, + TRXCON_EV_RX_TRAFFIC_IND, + TRXCON_EV_TX_DATA_REQ, + TRXCON_EV_RX_DATA_IND, + TRXCON_EV_CRYPTO_REQ, +}; + +/* 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 { + bool last_result; + 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_ms; + 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_{TRAFFIC,DATA}_REQ */ +struct trxcon_param_tx_traffic_data_req { + uint8_t chan_nr; + uint8_t link_id; + size_t data_len; + const uint8_t *data; +}; + +/* param of TRXCON_EV_RX_{TRAFFIC,DATA}_IND */ +struct trxcon_param_rx_traffic_data_ind { + uint8_t chan_nr; + uint8_t link_id; + 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_DEDICATED_ESTABLISH_REQ */ +struct trxcon_param_dedicated_establish_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; +}; + +struct trxcon_inst { + struct osmo_fsm_inst *fi; + unsigned int id; + + /* Logging context for sched and l1c */ + const char *log_prefix; + + /* The L1 scheduler */ + struct l1sched_state *sched; + /* PHY interface (e.g. TRXC/TRXD) */ + void *phyif; + /* L2 interface (e.g. L1CTL) */ + void *l2if; + + /* L1 parameters */ + struct { + uint16_t band_arfcn; + uint8_t tx_power; + int8_t ta; + } l1p; +}; + +struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id); +void trxcon_inst_free(struct trxcon_inst *trxcon); diff --git a/trxcon/l1ctl.c b/trxcon/l1ctl.c deleted file mode 100644 index 8853c632..00000000 --- a/trxcon/l1ctl.c +++ /dev/null @@ -1,917 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * GSM L1 control interface handlers - * - * (C) 2014 by Sylvain Munaut - * (C) 2016-2017 by Vadim Yanitskiy - * - * 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 -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#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); - - trx_if_cmd_sync(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 * GSM_TDMA_FN_DURATION_uS / 1000); - osmo_timer_schedule(&l1l->fbsb_timer, 2, timeout * GSM_TDMA_FN_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) -{ - uint16_t ma[64]; - int i, 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; - } else if (h->n > ARRAY_SIZE(ma)) { - LOGP(DL1C, 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++) - ma[i] = ntohs(h->ma[i]); - - /* Forward hopping parameters to TRX */ - rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, 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? */ - - /* Re-use the original message as confirmation */ - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; - l1h->msg_type = L1CTL_TCH_MODE_CONF; - - return l1ctl_link_send(l1l, msg); -} - -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/trxcon/l1ctl.h b/trxcon/l1ctl.h deleted file mode 100644 index 48bbe097..00000000 --- a/trxcon/l1ctl.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -#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/trxcon/l1ctl_link.c b/trxcon/l1ctl_link.c deleted file mode 100644 index 4c406d6c..00000000 --- a/trxcon/l1ctl_link.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * GSM L1 control socket (/tmp/osmocom_l2) handlers - * - * (C) 2013 by Sylvain Munaut - * (C) 2016-2017 by Vadim Yanitskiy - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#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; - osmo_fd_setup(conn_bfd, cfd, OSMO_FD_READ, osmo_wqueue_bfd_cb, l1l, 0); - - 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) -{ - uint8_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 = msgb_push(msg, L1CTL_MSG_LEN_FIELD); - osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len); - - 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; - - /* Bind connection handler */ - osmo_fd_setup(bfd, -1, OSMO_FD_READ, l1ctl_link_accept, l1l, 0); - - 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; - - /** - * 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/trxcon/l1ctl_link.h b/trxcon/l1ctl_link.h deleted file mode 100644 index a333e407..00000000 --- a/trxcon/l1ctl_link.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include - -#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/trxcon/l1ctl_proto.h b/trxcon/l1ctl_proto.h deleted file mode 100644 index cf41ac74..00000000 --- a/trxcon/l1ctl_proto.h +++ /dev/null @@ -1,387 +0,0 @@ -/* Messages to be sent between the different layers */ - -/* (C) 2010 by Harald Welte - * (C) 2010 by Holger Hans Peter Freyther - * - * 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. - * - */ - -#ifndef __L1CTL_PROTO_H__ -#define __L1CTL_PROTO_H__ - -enum { - _L1CTL_NONE = 0, - L1CTL_FBSB_REQ, - L1CTL_FBSB_CONF, - L1CTL_DATA_IND, - L1CTL_RACH_REQ, - L1CTL_DM_EST_REQ, - L1CTL_DATA_REQ, - L1CTL_RESET_IND, - L1CTL_PM_REQ, /* power measurement */ - L1CTL_PM_CONF, /* power measurement */ - L1CTL_ECHO_REQ, - L1CTL_ECHO_CONF, - L1CTL_RACH_CONF, - L1CTL_RESET_REQ, - L1CTL_RESET_CONF, - L1CTL_DATA_CONF, - L1CTL_CCCH_MODE_REQ, - L1CTL_CCCH_MODE_CONF, - L1CTL_DM_REL_REQ, - L1CTL_PARAM_REQ, - L1CTL_DM_FREQ_REQ, - L1CTL_CRYPTO_REQ, - L1CTL_SIM_REQ, - L1CTL_SIM_CONF, - L1CTL_TCH_MODE_REQ, - L1CTL_TCH_MODE_CONF, - L1CTL_NEIGH_PM_REQ, - L1CTL_NEIGH_PM_IND, - L1CTL_TRAFFIC_REQ, - L1CTL_TRAFFIC_CONF, - L1CTL_TRAFFIC_IND, - L1CTL_BURST_IND, - - /* configure TBF for uplink/downlink */ - L1CTL_TBF_CFG_REQ, - L1CTL_TBF_CFG_CONF, - - L1CTL_DATA_TBF_REQ, - L1CTL_DATA_TBF_CONF, - - /* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */ - L1CTL_EXT_RACH_REQ, -}; - -enum ccch_mode { - CCCH_MODE_NONE = 0, - CCCH_MODE_NON_COMBINED, - CCCH_MODE_COMBINED, - CCCH_MODE_COMBINED_CBCH, -}; - -enum neigh_mode { - NEIGH_MODE_NONE = 0, - NEIGH_MODE_PM, - NEIGH_MODE_SB, -}; - -enum l1ctl_coding_scheme { - L1CTL_CS_NONE, - L1CTL_CS1, - L1CTL_CS2, - L1CTL_CS3, - L1CTL_CS4, - L1CTL_MCS1, - L1CTL_MCS2, - L1CTL_MCS3, - L1CTL_MCS4, - L1CTL_MCS5, - L1CTL_MCS6, - L1CTL_MCS7, - L1CTL_MCS8, - L1CTL_MCS9, -}; - -/* - * NOTE: struct size. We do add manual padding out of the believe - * that it will avoid some unaligned access. - */ - -/* there are no more messages in a sequence */ -#define L1CTL_F_DONE 0x01 - -struct l1ctl_hdr { - uint8_t msg_type; - uint8_t flags; - uint8_t padding[2]; - uint8_t data[0]; -} __attribute__((packed)); - -/* - * downlink info ... down from the BTS.. - */ -struct l1ctl_info_dl { - /* GSM 08.58 channel number (9.3.1) */ - uint8_t chan_nr; - /* GSM 08.58 link identifier (9.3.2) */ - uint8_t link_id; - /* the ARFCN and the band. FIXME: what about MAIO? */ - uint16_t band_arfcn; - - uint32_t frame_nr; - - uint8_t rx_level; /* 0 .. 63 in typical GSM notation (dBm+110) */ - uint8_t snr; /* Signal/Noise Ration (dB) */ - uint8_t num_biterr; - uint8_t fire_crc; - - uint8_t payload[0]; -} __attribute__((packed)); - -/* new CCCH was found. This is following the header */ -struct l1ctl_fbsb_conf { - int16_t initial_freq_err; - uint8_t result; - uint8_t bsic; - /* FIXME: contents of cell_info ? */ -} __attribute__((packed)); - -/* CCCH mode was changed */ -struct l1ctl_ccch_mode_conf { - uint8_t ccch_mode; /* enum ccch_mode */ - uint8_t padding[3]; -} __attribute__((packed)); - -/* 3GPP TS 44.014, section 5.1 (Calypso specific numbers) */ -enum l1ctl_tch_loop_mode { - L1CTL_TCH_LOOP_OPEN = 0x00, - L1CTL_TCH_LOOP_A = 0x01, - L1CTL_TCH_LOOP_B = 0x02, - L1CTL_TCH_LOOP_C = 0x03, - L1CTL_TCH_LOOP_D = 0x04, - L1CTL_TCH_LOOP_E = 0x05, - L1CTL_TCH_LOOP_F = 0x06, - L1CTL_TCH_LOOP_I = 0x07, -}; - -/* TCH mode was changed */ -struct l1ctl_tch_mode_conf { - uint8_t tch_mode; /* enum tch_mode */ - uint8_t audio_mode; - uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */ - uint8_t padding[1]; -} __attribute__((packed)); - -/* data on the CCCH was found. This is following the header */ -struct l1ctl_data_ind { - uint8_t data[23]; -} __attribute__((packed)); - -/* traffic from the network */ -struct l1ctl_traffic_ind { - uint8_t data[0]; -} __attribute__((packed)); - -/* - * uplink info - */ -struct l1ctl_info_ul { - /* GSM 08.58 channel number (9.3.1) */ - uint8_t chan_nr; - /* GSM 08.58 link identifier (9.3.2) */ - uint8_t link_id; - uint8_t padding[2]; - - uint8_t payload[0]; -} __attribute__((packed)); - -struct l1ctl_info_ul_tbf { - /* references l1ctl_tbf_cfg_req.tbf_nr */ - uint8_t tbf_nr; - uint8_t coding_scheme; - uint8_t padding[2]; - /* RLC/MAC block, size determines CS */ - uint8_t payload[0]; -} __attribute__((packed)); - -/* - * msg for FBSB_REQ - * the l1_info_ul header is in front - */ -struct l1ctl_fbsb_req { - uint16_t band_arfcn; - uint16_t timeout; /* in TDMA frames */ - - uint16_t freq_err_thresh1; - uint16_t freq_err_thresh2; - - uint8_t num_freqerr_avg; - uint8_t flags; /* L1CTL_FBSB_F_* */ - uint8_t sync_info_idx; - uint8_t ccch_mode; /* enum ccch_mode */ - uint8_t rxlev_exp; /* expected signal level */ -} __attribute__((packed)); - -#define L1CTL_FBSB_F_FB0 (1 << 0) -#define L1CTL_FBSB_F_FB1 (1 << 1) -#define L1CTL_FBSB_F_SB (1 << 2) -#define L1CTL_FBSB_F_FB01SB (L1CTL_FBSB_F_FB0|L1CTL_FBSB_F_FB1|L1CTL_FBSB_F_SB) - -/* - * msg for CCCH_MODE_REQ - * the l1_info_ul header is in front - */ -struct l1ctl_ccch_mode_req { - uint8_t ccch_mode; /* enum ccch_mode */ - uint8_t padding[3]; -} __attribute__((packed)); - -/* - * msg for TCH_MODE_REQ - * the l1_info_ul header is in front - */ -struct l1ctl_tch_mode_req { - uint8_t tch_mode; /* enum gsm48_chan_mode */ -#define AUDIO_TX_MICROPHONE (1<<0) -#define AUDIO_TX_TRAFFIC_REQ (1<<1) -#define AUDIO_RX_SPEAKER (1<<2) -#define AUDIO_RX_TRAFFIC_IND (1<<3) - uint8_t audio_mode; - uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */ - uint8_t padding[1]; -} __attribute__((packed)); - -/* the l1_info_ul header is in front */ -struct l1ctl_rach_req { - uint8_t ra; - uint8_t combined; - uint16_t offset; -} __attribute__((packed)); - - -/* the l1_info_ul header is in front */ -struct l1ctl_ext_rach_req { - uint16_t ra11; - uint8_t synch_seq; - uint8_t combined; - uint16_t offset; -} __attribute__((packed)); - -/* the l1_info_ul header is in front */ -struct l1ctl_par_req { - int8_t ta; - uint8_t tx_power; - uint8_t padding[2]; -} __attribute__((packed)); - -struct l1ctl_h0 { - uint16_t band_arfcn; -} __attribute__((packed)); - -struct l1ctl_h1 { - uint8_t hsn; - uint8_t maio; - uint8_t n; - uint8_t _padding[1]; - uint16_t ma[64]; -} __attribute__((packed)); - -struct l1ctl_dm_est_req { - uint8_t tsc; - uint8_t h; - union { - struct l1ctl_h0 h0; - struct l1ctl_h1 h1; - }; - uint8_t tch_mode; - uint8_t audio_mode; -} __attribute__((packed)); - -struct l1ctl_dm_freq_req { - uint16_t fn; - uint8_t tsc; - uint8_t h; - union { - struct l1ctl_h0 h0; - struct l1ctl_h1 h1; - }; -} __attribute__((packed)); - -struct l1ctl_crypto_req { - uint8_t algo; - uint8_t key_len; - uint8_t key[0]; -} __attribute__((packed)); - -struct l1ctl_pm_req { - uint8_t type; - uint8_t padding[3]; - - union { - struct { - uint16_t band_arfcn_from; - uint16_t band_arfcn_to; - } range; - }; -} __attribute__((packed)); - -#define BI_FLG_DUMMY (1 << 4) -#define BI_FLG_SACCH (1 << 5) - -struct l1ctl_burst_ind { - uint32_t frame_nr; - uint16_t band_arfcn; /* ARFCN + band + ul indicator */ - uint8_t chan_nr; /* GSM 08.58 channel number (9.3.1) */ - uint8_t flags; /* BI_FLG_xxx + burst_id = 2LSBs */ - uint8_t rx_level; /* 0 .. 63 in typical GSM notation (dBm+110) */ - uint8_t snr; /* Reported SNR >> 8 (0-255) */ - uint8_t bits[15]; /* 114 bits + 2 steal bits. Filled MSB first */ -} __attribute__((packed)); - -/* a single L1CTL_PM response */ -struct l1ctl_pm_conf { - uint16_t band_arfcn; - uint8_t pm[2]; -} __attribute__((packed)); - -enum l1ctl_reset_type { - L1CTL_RES_T_BOOT, /* only _IND */ - L1CTL_RES_T_FULL, - L1CTL_RES_T_SCHED, -}; - -/* argument to L1CTL_RESET_REQ and L1CTL_RESET_IND */ -struct l1ctl_reset { - uint8_t type; - uint8_t pad[3]; -} __attribute__((packed)); - -struct l1ctl_neigh_pm_req { - uint8_t n; - uint8_t padding[1]; - uint16_t band_arfcn[64]; - uint8_t tn[64]; -} __attribute__((packed)); - -/* neighbour cell measurement results */ -struct l1ctl_neigh_pm_ind { - uint16_t band_arfcn; - uint8_t pm[2]; - uint8_t tn; - uint8_t padding; -} __attribute__((packed)); - -/* traffic data to network */ -struct l1ctl_traffic_req { - uint8_t data[0]; -} __attribute__((packed)); - -struct l1ctl_tbf_cfg_req { - /* future support for multiple concurrent TBFs. 0 for now */ - uint8_t tbf_nr; - /* is this about an UL TBF (1) or DL (0) */ - uint8_t is_uplink; - uint8_t padding[2]; - - /* one USF for each TN, or 255 for invalid/unused */ - uint8_t usf[8]; -} __attribute__((packed)); - -#endif /* __L1CTL_PROTO_H__ */ diff --git a/trxcon/sched_lchan_common.c b/trxcon/sched_lchan_common.c deleted file mode 100644 index ae43ca94..00000000 --- a/trxcon/sched_lchan_common.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: common routines for lchan handlers - * - * (C) 2017-2020 by Vadim Yanitskiy - * - * 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 -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trxcon.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, - }, -}; - -/* 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 *burst_mask2str(const uint8_t *mask, int bits) -{ - /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be extended */ - static char buf[8 + 1]; - char *ptr = buf; - - OSMO_ASSERT(bits <= 8 && bits > 0); - - while (--bits >= 0) - *(ptr++) = (*mask & (1 << bits)) ? '*' : '.'; - *ptr = '\0'; - - return buf; -} - -int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn, - uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr, - const uint8_t *data, size_t data_len) -{ - const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[lchan_type]; - - /* GSMTAP logging may not be enabled */ - if (gsmtap == NULL) - return 0; - - /* Omit frames with unknown channel type */ - if (lchan_desc->gsmtap_chan_type == GSMTAP_CHANNEL_UNKNOWN) - return 0; - - /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */ - return gsmtap_send(gsmtap, band_arfcn, tn, lchan_desc->gsmtap_chan_type, - lchan_desc->ss_nr, fn, signal_dbm, snr, data, data_len); -} - -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_meas_set *meas = &lchan->meas_avg; - const struct trx_lchan_desc *lchan_desc; - struct l1ctl_info_dl dl_hdr; - - /* 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.num_biterr = bit_error_count; - - /* sched_trx_meas_avg() gives us TDMA frame number of the first burst */ - dl_hdr.frame_nr = htonl(meas->fn); - - /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */ - dl_hdr.rx_level = dbm2rxlev(meas->rssi); - - /* 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); - - /* Optional GSMTAP logging */ - if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) { - sched_gsmtap_send(lchan->type, meas->fn, ts->index, - trx->band_arfcn, meas->rssi, 0, l2, l2_len); - } - - 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); - - /* Optional GSMTAP logging */ - if (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH) { - sched_gsmtap_send(lchan->type, fn, ts->index, - trx->band_arfcn | ARFCN_UPLINK, - 0, 0, lchan->prim->payload, - lchan->prim->payload_len); - } - - 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/trxcon/sched_lchan_tchf.c b/trxcon/sched_lchan_tchf.c deleted file mode 100644 index c5362f0b..00000000 --- a/trxcon/sched_lchan_tchf.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2017-2020 by Vadim Yanitskiy - * - * 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 -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#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, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - 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); - - /* Align to the first burst of a block */ - if (*mask == 0x00 && bid != 0) - return 0; - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* 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; - - /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 8); - - /* Check for complete set of bursts */ - if ((*mask & 0xff) != 0xff) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 8), lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */ - - } - - /* Keep the mask updated */ - *mask = *mask << 4; - - 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 substituting a stolen TCH frame */ - n_errors = -1; /* ensure fake measurements */ - 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, fake measurements */ - if (n_errors < 0) { - lchan->meas_avg = (struct trx_meas_set) { - .fn = lchan->meas_avg.fn, - .toa256 = 0, - .rssi = -110, - }; - - /* No bursts => no errors */ - n_errors = 0; - } - - /* 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/trxcon/sched_lchan_tchh.c b/trxcon/sched_lchan_tchh.c deleted file mode 100644 index b6f07082..00000000 --- a/trxcon/sched_lchan_tchh.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2018-2020 by Vadim Yanitskiy - * (C) 2018 by Harald Welte - * - * 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 -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#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 = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \ - return GSM_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, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - 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); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* 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) { - /* Calculate AVG of the measurements (assuming 4 bursts) */ - sched_trx_meas_avg(lchan, 4); - - LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame (%s) " - "at fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6), - lchan->meas_avg.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 AVG of the measurements (FACCH/H takes 6 bursts) */ - sched_trx_meas_avg(lchan, 6); - - /* FACCH/H received, forward to the higher layers */ - sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, - n_errors, false, false); - - /* Send BFI substituting 1/2 stolen TCH frames */ - n_errors = -1; /* ensure fake measurements */ - goto bfi; - } else { - /* A good TCH frame received */ - l2_len = rc; - - /* Calculate AVG of the measurements (traffic takes 4 bursts) */ - sched_trx_meas_avg(lchan, 4); - } - - /* 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, fake measurements */ - if (n_errors < 0) { - lchan->meas_avg = (struct trx_meas_set) { - .fn = sched_tchh_block_dl_first_fn(lchan->type, fn, false), - .toa256 = 0, - .rssi = -110, - }; - - /* No bursts => no errors */ - n_errors = 0; - } - - /* 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/trxcon/sched_lchan_xcch.c b/trxcon/sched_lchan_xcch.c deleted file mode 100644 index da7f0375..00000000 --- a/trxcon/sched_lchan_xcch.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2017-2020 by Vadim Yanitskiy - * - * 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 -#include -#include - -#include -#include - -#include -#include -#include - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -__attribute__((xray_always_instrument)) __attribute__((noinline)) -static int gsm0503_xcch_decode_xray(uint8_t *l2_data, const sbit_t *bursts, - int *n_errors, int *n_bits_total) { - return gsm0503_xcch_decode(l2_data, bursts, n_errors, n_bits_total); -} - -__attribute__((xray_always_instrument)) __attribute__((noinline)) int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - 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; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - 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); - - /* Align to the first burst of a block */ - if (*mask == 0x00 && bid != 0) - return 0; - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* 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; - - /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 4); - - /* Check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 4), lchan->meas_avg.fn, - lchan->meas_avg.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. */ - } - - /* Keep the mask updated */ - *mask = *mask << 4; - - /* Attempt to decode */ - rc = gsm0503_xcch_decode_xray(l2, buffer, &n_errors, &n_bits_total); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Received bad data frame with %d errors at fn=%u " - "(%u/%u) for %s\n", n_errors, lchan->meas_avg.fn, - lchan->meas_avg.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) { - /* Confirm data sending */ - sched_send_dt_conf(trx, ts, lchan, fn, false); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - - /* Reset mask */ - *mask = 0x00; - } - - return 0; -} diff --git a/trxcon/sched_mframe.c b/trxcon/sched_mframe.c deleted file mode 100644 index 9b759af3..00000000 --- a/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 - * (C) 2015 by Alexander Chemeris - * (C) 2015 by Harald Welte - * - * 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 . - * - */ - -#include - -#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/trxcon/sched_trx.h b/trxcon/sched_trx.h deleted file mode 100644 index fb7ecd49..00000000 --- a/trxcon/sched_trx.h +++ /dev/null @@ -1,405 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#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 - -/* 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 TRX_CH_LID_PTCCH 0x80 - -/* 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_meas_set; -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, const sbit_t *bits, - const struct trx_meas_set *meas); - -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 Sub-slot number (for SDCCH and TCH/H) */ - uint8_t ss_nr; - /*! \brief GSMTAP channel type (see GSMTAP_CHANNEL_*) */ - uint8_t gsmtap_chan_type; - - /*! \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; -}; - -struct trx_meas_set { - /*! \brief TDMA frame number of the first burst this set belongs to */ - uint32_t fn; - /*! \brief ToA256 (Timing of Arrival, 1/256 of a symbol) */ - int16_t toa256; - /*! \brief RSSI (Received Signal Strength Indication) */ - int8_t rssi; -}; - -/* Simple ring buffer (up to 8 unique measurements) */ -struct trx_lchan_meas_hist { - struct trx_meas_set buf[8]; - struct trx_meas_set *head; -}; - -/* 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 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; - - /*! \brief Downlink measurements history */ - struct trx_lchan_meas_hist meas_hist; - /*! \brief AVG measurements of the last received block */ - struct trx_meas_set meas_avg; - - /*! \brief TDMA loss detection state */ - struct { - /*! \brief Last processed TDMA frame number */ - uint32_t last_proc; - /*! \brief Number of processed TDMA frames */ - unsigned long num_proc; - /*! \brief Number of lost TDMA frames */ - unsigned long num_lost; - } tdma; - - /*! \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; - - /* TS that this lchan belongs to */ - struct trx_ts *ts; -}; - -struct trx_ts { - /*! \brief Timeslot index within a frame (0..7) */ - uint8_t index; - - /*! \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; - /* backpointer to its TRX */ - struct trx_instance *trx; -}; - -/* 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 fn, sbit_t *bits, uint16_t nbits, - const struct trx_meas_set *meas); -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]; - -const char *burst_mask2str(const uint8_t *mask, int bits); -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); -int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn, - uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr, - const uint8_t *data, size_t data_len); - -/* 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) - -/* Measurement history */ -void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas); -void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n); diff --git a/trxcon/scheduler.h b/trxcon/scheduler.h deleted file mode 100644 index 43127cc1..00000000 --- a/trxcon/scheduler.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -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 */ - enum tdma_sched_clck_state 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 Count of lost frames */ - 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/trxcon/src/Makefile.am b/trxcon/src/Makefile.am new file mode 100644 index 00000000..3af004d6 --- /dev/null +++ b/trxcon/src/Makefile.am @@ -0,0 +1,53 @@ +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_clck.c \ + sched_prim.c \ + sched_trx.c \ + $(NULL) + +libl1sched_la_LIBADD = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODING_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) + + +bin_PROGRAMS = trxcon + +trxcon_SOURCES = \ + l1ctl_server.c \ + l1ctl.c \ + trx_if.c \ + logging.c \ + trxcon_fsm.c \ + trxcon.c \ + $(NULL) + +trxcon_LDADD = \ + libl1sched.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) diff --git a/trxcon/src/l1ctl.c b/trxcon/src/l1ctl.c new file mode 100644 index 00000000..5f1e6107 --- /dev/null +++ b/trxcon/src/l1ctl.c @@ -0,0 +1,807 @@ +/* + * OsmocomBB <-> SDR connection bridge + * GSM L1 control interface handlers + * + * (C) 2014 by Sylvain Munaut + * (C) 2016-2022 by Vadim Yanitskiy + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +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_client *l1c, uint16_t band_arfcn, + int dbm, int last) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_pm_conf *pmc; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_PM_CONF); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, 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_client_send(l1c, msg); +} + +int l1ctl_tx_reset_ind(struct l1ctl_client *l1c, uint8_t type) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct msgb *msg; + struct l1ctl_reset *res; + + msg = l1ctl_alloc_msg(L1CTL_RESET_IND); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, DL1C, LOGL_DEBUG, "Send Reset Ind (%u)\n", type); + + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return l1ctl_client_send(l1c, msg); +} + +int l1ctl_tx_reset_conf(struct l1ctl_client *l1c, uint8_t type) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct msgb *msg; + struct l1ctl_reset *res; + + msg = l1ctl_alloc_msg(L1CTL_RESET_CONF); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, DL1C, LOGL_DEBUG, "Send Reset Conf (%u)\n", type); + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return l1ctl_client_send(l1c, 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 l1ctl_client *l1c, uint16_t band_arfcn) +{ + struct osmo_fsm_inst *fi = l1c->priv; + 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, DL1C, LOGL_DEBUG, "Send FBSB Conf (timeout)\n"); + + return l1ctl_client_send(l1c, msg); +} + +int l1ctl_tx_fbsb_conf(struct l1ctl_client *l1c, uint16_t band_arfcn, uint8_t bsic) +{ + struct osmo_fsm_inst *fi = l1c->priv; + 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, DL1C, LOGL_DEBUG, + "Send FBSB Conf (result=%u, bsic=%u)\n", + conf->result, conf->bsic); + + return l1ctl_client_send(l1c, msg); +} + +int l1ctl_tx_ccch_mode_conf(struct l1ctl_client *l1c, 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_client_send(l1c, msg); +} + +/** + * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND. + */ +int l1ctl_tx_dt_ind(struct l1ctl_client *l1c, + const struct l1ctl_info_dl *dl_info, + const 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, dl_info); + + /* 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_client_send(l1c, msg); +} + +int l1ctl_tx_rach_conf(struct l1ctl_client *l1c, + 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_client_send(l1c, msg); +} + + +/** + * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF. + */ +int l1ctl_tx_dt_conf(struct l1ctl_client *l1c, + 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_client_send(l1c, 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; + } +} + +static int l1ctl_rx_fbsb_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_fbsb_req *fbsb; + int rc = 0; + + fbsb = (struct l1ctl_fbsb_req *) msg->l1h; + if (msgb_l1len(msg) < sizeof(*fbsb)) { + LOGPFSMSL(fi, DL1C, 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_ms = ntohs(fbsb->timeout) * GSM_TDMA_FN_DURATION_uS / 1000, + .band_arfcn = ntohs(fbsb->band_arfcn), + }; + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, + "Received FBSB request (%s %d, timeout %u ms)\n", + arfcn2band_name(req.band_arfcn), + req.band_arfcn & ~ARFCN_FLAG_MASK, + req.timeout_ms); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_FBSB_SEARCH_REQ, &req); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_pm_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_pm_req *pmr; + int rc = 0; + + pmr = (struct l1ctl_pm_req *) msg->l1h; + if (msgb_l1len(msg) < sizeof(*pmr)) { + LOGPFSMSL(fi, DL1C, 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, DL1C, 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 l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_reset *res; + int rc = 0; + + res = (struct l1ctl_reset *) msg->l1h; + if (msgb_l1len(msg) < sizeof(*res)) { + LOGPFSMSL(fi, DL1C, LOGL_ERROR, + "MSG too short Reset Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, + "Received reset request (%u)\n", 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, DL1C, LOGL_ERROR, + "Unknown L1CTL_RESET_REQ type\n"); + goto exit; + } + + /* Confirm */ + rc = l1ctl_tx_reset_conf(l1c, res->type); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_echo_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_hdr *l1h; + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, "Recv Echo Req\n"); + LOGPFSMSL(fi, 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_client_send(l1c, msg); +} + +static int l1ctl_rx_ccch_mode_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_ccch_mode_req *mode_req; + int rc; + + mode_req = (struct l1ctl_ccch_mode_req *)msg->l1h; + if (msgb_l1len(msg) < sizeof(*mode_req)) { + LOGPFSMSL(fi, DL1C, LOGL_ERROR, + "MSG too short Reset Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n", + mode_req->ccch_mode); /* TODO: add value-string for 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(l1c, mode_req->ccch_mode); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_rach_req(struct l1ctl_client *l1c, struct msgb *msg, bool is_11bit) +{ + struct trxcon_param_tx_access_burst_req req; + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_info_ul *ul; + + ul = (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, DL1C, 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, DL1C, 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, DL1C, 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_dedicated_establish_req *req, + const struct l1ctl_h0 *h) +{ + req->h0.band_arfcn = ntohs(h->band_arfcn); + + LOGPFSMSL(fi, DL1C, 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_dedicated_establish_req *req, + const struct l1ctl_h1 *h) +{ + unsigned int i; + + LOGPFSMSL(fi, 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) { + LOGPFSMSL(fi, DL1C, LOGL_ERROR, + "No channels in mobile allocation?!?\n"); + return -EINVAL; + } else if (h->n > ARRAY_SIZE(h->ma)) { + LOGPFSMSL(fi, DL1C, 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 l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_dm_est_req *est_req; + struct l1ctl_info_ul *ul; + int rc; + + ul = (struct l1ctl_info_ul *) msg->l1h; + est_req = (struct l1ctl_dm_est_req *) ul->payload; + + struct trxcon_param_dedicated_establish_req req = { + .chan_nr = ul->chan_nr, + .tch_mode = est_req->tch_mode, + .tsc = est_req->tsc, + .hopping = est_req->h, + }; + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, + "Received L1CTL_DM_EST_REQ " + "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n", + req.chan_nr & 0x07, req.chan_nr, req.tsc, 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_DEDICATED_ESTABLISH_REQ, &req); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_dm_rel_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ\n"); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_DEDICATED_RELEASE_REQ, NULL); + + msgb_free(msg); + return 0; +} + +/** + * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ. + */ +static int l1ctl_rx_dt_req(struct l1ctl_client *l1c, + struct msgb *msg, bool traffic) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_info_ul *ul; + + /* Extract UL frame header */ + ul = (struct l1ctl_info_ul *) msg->l1h; + msg->l2h = ul->payload; + + struct trxcon_param_tx_traffic_data_req req = { + .chan_nr = ul->chan_nr, + .link_id = ul->link_id & 0x40, + .data_len = msgb_l2len(msg), + .data = ul->payload, + }; + + LOGPFSMSL(fi, DL1D, 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: + if (traffic) + osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_TRAFFIC_REQ, &req); + else + 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 l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + 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; + + LOGPFSMSL(fi, DL1C, 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 l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_tch_mode_req *mode_req; + int rc; + + mode_req = (struct l1ctl_tch_mode_req *)msg->l1h; + + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, + "Received L1CTL_TCH_MODE_REQ (tch_mode=%u, audio_mode=%u)\n", + 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 l1ctl_client_send(l1c, msg); +} + +static int l1ctl_rx_crypto_req(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + struct l1ctl_crypto_req *cr; + struct l1ctl_info_ul *ul; + + ul = (struct l1ctl_info_ul *) msg->l1h; + cr = (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, DL1C, 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 l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = l1c->priv; + 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(l1c, msg); + case L1CTL_PM_REQ: + return l1ctl_rx_pm_req(l1c, msg); + case L1CTL_RESET_REQ: + return l1ctl_rx_reset_req(l1c, msg); + case L1CTL_ECHO_REQ: + return l1ctl_rx_echo_req(l1c, msg); + case L1CTL_CCCH_MODE_REQ: + return l1ctl_rx_ccch_mode_req(l1c, msg); + case L1CTL_RACH_REQ: + return l1ctl_rx_rach_req(l1c, msg, false); + case L1CTL_EXT_RACH_REQ: + return l1ctl_rx_rach_req(l1c, msg, true); + case L1CTL_DM_EST_REQ: + return l1ctl_rx_dm_est_req(l1c, msg); + case L1CTL_DM_REL_REQ: + return l1ctl_rx_dm_rel_req(l1c, msg); + case L1CTL_DATA_REQ: + return l1ctl_rx_dt_req(l1c, msg, false); + case L1CTL_TRAFFIC_REQ: + return l1ctl_rx_dt_req(l1c, msg, true); + case L1CTL_PARAM_REQ: + return l1ctl_rx_param_req(l1c, msg); + case L1CTL_TCH_MODE_REQ: + return l1ctl_rx_tch_mode_req(l1c, msg); + case L1CTL_CRYPTO_REQ: + return l1ctl_rx_crypto_req(l1c, 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: + LOGPFSMSL(fi, DL1C, LOGL_NOTICE, + "Ignoring unsupported message (type=%u)\n", + l1h->msg_type); + msgb_free(msg); + return -ENOTSUP; + default: + LOGPFSMSL(fi, 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; + } +} diff --git a/trxcon/src/l1ctl_server.c b/trxcon/src/l1ctl_server.c new file mode 100644 index 00000000..bfbd9976 --- /dev/null +++ b/trxcon/src/l1ctl_server.c @@ -0,0 +1,282 @@ +/* + * OsmocomBB <-> SDR connection bridge + * UNIX socket server for L1CTL + * + * (C) 2013 by Sylvain Munaut + * (C) 2016-2017 by Vadim Yanitskiy + * (C) 2022 by 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 rc; + } + + /* 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/trxcon/logging.c b/trxcon/src/logging.c similarity index 86% rename from trxcon/logging.c rename to trxcon/src/logging.c index 0a4c08aa..5c16ac5e 100644 --- a/trxcon/logging.c +++ b/trxcon/src/logging.c @@ -15,17 +15,13 @@ * 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 #include #include -#include "logging.h" +#include static struct log_info_cat trx_log_info_cat[] = { [DAPP] = { @@ -46,8 +42,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, @@ -62,7 +58,7 @@ static struct log_info_cat trx_log_info_cat[] = { .name = "DSCH", .description = "Scheduler management", .color = "\033[1;36m", - .enabled = 0, .loglevel = LOGL_NOTICE, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSCHD] = { .name = "DSCHD", diff --git a/trxcon/sched_clck.c b/trxcon/src/sched_clck.c similarity index 77% rename from trxcon/sched_clck.c rename to trxcon/src/sched_clck.c index 9476ccd7..a3d75fce 100644 --- a/trxcon/sched_clck.c +++ b/trxcon/src/sched_clck.c @@ -30,32 +30,29 @@ #include #include +#include #include #include -#include -#include #include #include -#include -#include "scheduler.h" -#include "logging.h" -#include "trx_if.h" +#include +#include #define MAX_FN_SKEW 50 #define TRX_LOSS_FRAMES 400 -static void sched_clck_tick(void *data) +static void l1sched_clck_tick(void *data) { - struct trx_sched *sched = (struct trx_sched *) data; + struct l1sched_state *sched = (struct l1sched_state *) data; struct timespec tv_now, *tv_clock, elapsed; int64_t elapsed_us; const struct timespec frame_duration = { .tv_sec = 0, .tv_nsec = GSM_TDMA_FN_DURATION_nS }; /* 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; + LOGP_SCHEDC(sched, LOGL_DEBUG, "No more clock from transceiver\n"); + sched->clck_state = L1SCHED_CLCK_ST_WAIT; return; } @@ -69,10 +66,10 @@ static void sched_clck_tick(void *data) /* If someone played with clock, or if the process stalled */ if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) { - LOGP(DSCH, LOGL_NOTICE, "PC clock skew: " - "elapsed uS %" PRId64 "\n", elapsed_us); + LOGP_SCHEDC(sched, LOGL_NOTICE, "PC clock skew: " + "elapsed uS %" PRId64 "\n", elapsed_us); - sched->state = SCH_CLCK_STATE_WAIT; + sched->clck_state = L1SCHED_CLCK_ST_WAIT; return; } @@ -93,7 +90,7 @@ static void sched_clck_tick(void *data) GSM_TDMA_FN_DURATION_uS - elapsed_us); } -static void sched_clck_correct(struct trx_sched *sched, +static void l1sched_clck_correct(struct l1sched_state *sched, struct timespec *tv_now, uint32_t fn) { sched->fn_counter_proc = fn; @@ -106,12 +103,12 @@ static void sched_clck_correct(struct trx_sched *sched, sched->clock = *tv_now; memset(&sched->clock_timer, 0, sizeof(sched->clock_timer)); - sched->clock_timer.cb = sched_clck_tick; + sched->clock_timer.cb = l1sched_clck_tick; sched->clock_timer.data = sched; osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS); } -int sched_clck_handle(struct trx_sched *sched, uint32_t fn) +int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn) { struct timespec tv_now, *tv_clock, elapsed; int64_t elapsed_us, elapsed_fn; @@ -124,16 +121,16 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) 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); + if (sched->clck_state == L1SCHED_CLCK_ST_WAIT) { + l1sched_clck_correct(sched, &tv_now, fn); - LOGP(DSCH, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn); - sched->state = SCH_CLCK_STATE_OK; + LOGP_SCHEDC(sched, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn); + sched->clck_state = L1SCHED_CLCK_ST_OK; return 0; } - LOGP(DSCH, LOGL_NOTICE, "Clock indication: fn=%u\n", fn); + LOGP_SCHEDC(sched, LOGL_NOTICE, "Clock indication: fn=%u\n", fn); osmo_timer_del(&sched->clock_timer); @@ -147,15 +144,15 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) /* 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); + LOGP_SCHEDC(sched, LOGL_NOTICE, "GSM clock skew: old fn=%u, " + "new fn=%u\n", sched->fn_counter_proc, fn); - sched_clck_correct(sched, &tv_now, fn); + l1sched_clck_correct(sched, &tv_now, fn); return 0; } - LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n", - elapsed_fn * GSM_TDMA_FN_DURATION_uS - elapsed_us); + LOGP_SCHEDC(sched, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n", + elapsed_fn * GSM_TDMA_FN_DURATION_uS - elapsed_us); /* Too many frames have been processed already */ if (elapsed_fn < 0) { @@ -192,10 +189,10 @@ int sched_clck_handle(struct trx_sched *sched, uint32_t fn) return 0; } -void sched_clck_reset(struct trx_sched *sched) +void l1sched_clck_reset(struct l1sched_state *sched) { /* Reset internal state */ - sched->state = SCH_CLCK_STATE_WAIT; + sched->clck_state = L1SCHED_CLCK_ST_WAIT; /* Stop clock timer */ osmo_timer_del(&sched->clock_timer); diff --git a/trxcon/src/sched_lchan_common.c b/trxcon/src/sched_lchan_common.c new file mode 100644 index 00000000..cddc9e5d --- /dev/null +++ b/trxcon/src/sched_lchan_common.c @@ -0,0 +1,143 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: common routines for lchan handlers + * + * (C) 2017-2022 by Vadim Yanitskiy + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +/* 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 uint8_t *mask, int bits) +{ + /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be extended */ + static char buf[8 + 1]; + char *ptr = buf; + + OSMO_ASSERT(bits <= 8 && bits > 0); + + while (--bits >= 0) + *(ptr++) = (*mask & (1 << bits)) ? '*' : '.'; + *ptr = '\0'; + + return buf; +} + +/** + * 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 l1sched_bad_frame_ind(uint8_t *l2, struct l1sched_lchan_state *lchan) +{ + int rc; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + if (lchan->type == L1SCHED_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 */ + rc = osmo_amr_rtp_enc(l2, + lchan->amr.codec[lchan->amr.dl_cmr], + lchan->amr.codec[lchan->amr.dl_ft], + AMR_BAD); + if (rc < 2) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Failed to encode AMR_BAD frame (rc=%d), " + "not sending BFI\n", rc); + return 0; + } + memset(l2 + 2, 0, rc - 2); + return rc; + case GSM48_CMODE_SIGN: + LOGP_LCHAND(lchan, LOGL_ERROR, "BFI is not allowed in signalling mode\n"); + return 0; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); + return 0; + } +} diff --git a/trxcon/sched_lchan_desc.c b/trxcon/src/sched_lchan_desc.c similarity index 70% rename from trxcon/sched_lchan_desc.c rename to trxcon/src/sched_lchan_desc.c index 2c3c3b2e..54635ba1 100644 --- a/trxcon/sched_lchan_desc.c +++ b/trxcon/src/sched_lchan_desc.c @@ -5,6 +5,7 @@ * (C) 2013 by Andreas Eversberg * (C) 2015 by Alexander Chemeris * (C) 2015 by Harald Welte + * Contributions by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * @@ -26,66 +27,66 @@ #include #include -#include "sched_trx.h" +#include /* 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, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_data_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); -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, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_sch_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); -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, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_tchf_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); -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, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_tchh_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); -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, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_pdtch_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas); -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", .gsmtap_chan_type = GSMTAP_CHANNEL_BCCH, @@ -95,20 +96,20 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * 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, + .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", .gsmtap_chan_type = GSMTAP_CHANNEL_RACH, .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", .gsmtap_chan_type = GSMTAP_CHANNEL_CCCH, @@ -118,15 +119,15 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * 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, + .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", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F, .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): @@ -140,16 +141,16 @@ 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, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H, .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 0, /* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03, @@ -169,402 +170,402 @@ 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, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H, .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 1, - /* Same as for TRXC_TCHH_0, see above. */ + /* Same as for L1SCHED_TCHH_0, see above. */ .burst_buf_size = 6 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 0, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 1, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 2, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 3, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 0, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 1, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 2, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 3, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 4, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 5, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 6, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3), - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, .ss_nr = 7, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F | GSMTAP_CHANNEL_ACCH, .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. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 0, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 1, - /* Same as for TRXC_BCCH (xCCH), see above. */ + /* Same as for L1SCHED_BCCH (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 0, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 1, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 2, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 3, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 0, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 1, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 2, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 3, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 4, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 5, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 6, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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)", .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3), - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, .ss_nr = 7, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .flags = L1SCHED_CH_FLAG_CBTX, .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", .gsmtap_chan_type = GSMTAP_CHANNEL_PDTCH, @@ -575,16 +576,16 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * 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, + .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", .gsmtap_chan_type = GSMTAP_CHANNEL_PTCCH, .chan_nr = RSL_CHAN_OSMO_PDCH, - .link_id = TRX_CH_LID_PTCCH, + .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 @@ -592,30 +593,30 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * 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_BURST_PL_LEN, - .flags = TRX_CH_FLAG_PDCH, + .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", .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH51, .chan_nr = RSL_CHAN_OSMO_CBCH4, .ss_nr = 2, - /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */ + /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_AUTO, + .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", .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH52, .chan_nr = RSL_CHAN_OSMO_CBCH8, .ss_nr = 2, - /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */ + /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */ .burst_buf_size = 4 * GSM_BURST_PL_LEN, .rx_fn = rx_data_fn, }, diff --git a/trxcon/sched_lchan_pdtch.c b/trxcon/src/sched_lchan_pdtch.c similarity index 51% rename from trxcon/sched_lchan_pdtch.c rename to trxcon/src/sched_lchan_pdtch.c index 6a684897..0ef4573e 100644 --- a/trxcon/sched_lchan_pdtch.c +++ b/trxcon/src/sched_lchan_pdtch.c @@ -2,7 +2,8 @@ * OsmocomBB <-> SDR connection bridge * TDMA scheduler: handlers for DL / UL bursts on logical channels * - * (C) 2018-2020 by Vadim Yanitskiy + * (C) 2018-2022 by Vadim Yanitskiy + * 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 @@ -33,30 +30,23 @@ #include #include -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" +#include +#include -int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) +int rx_pdtch_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas) { - 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; 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, "Packet data received on %s: " - "fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid); + LOGP_LCHAND(lchan, LOGL_DEBUG, "Packet data received: fn=%u bid=%u\n", fn, bid); /* Align to the first burst of a block */ if (*mask == 0x00 && bid != 0) @@ -66,7 +56,7 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, *mask |= (1 << bid); /* Store the measurements */ - sched_trx_meas_push(lchan, meas); + l1sched_lchan_meas_push(lchan, meas); /* Copy burst to buffer of 4 bursts */ offset = buffer + bid * 116; @@ -78,16 +68,15 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, return 0; /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 4); + l1sched_lchan_meas_avg(lchan, 4); /* Check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 4), lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); + 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 ;) */ } @@ -98,40 +87,34 @@ int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, 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", lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); + 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 */ - sched_send_dt_ind(trx, ts, lchan, - l2, l2_len, n_errors, rc < 0, true); + l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_PACKET_DATA); 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) +int tx_pdtch_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) { - 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 (br->bid > 0) { /* If we have encoded bursts */ if (*mask) goto send_burst; @@ -143,55 +126,43 @@ int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, 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)); + LOGP_LCHAND(lchan, 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); + l1sched_prim_drop(lchan); return -EINVAL; } send_burst: /* Determine which burst should be sent */ - offset = buffer + bid * 116; + offset = buffer + br->bid * 116; /* Update mask */ - *mask |= (1 << bid); + *mask |= (1 << br->bid); /* Choose proper TSC */ - tsc = sched_nb_training_bits[trx->tsc]; + tsc = l1sched_nb_training_bits[lchan->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 */ + memset(br->burst, 0, 3); /* TB */ + memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_BURST_LEN; - 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; - } + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled at fn=%u burst=%u\n", br->fn, br->bid); /* 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); + l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_PACKET_DATA); /* Forget processed primitive */ - sched_prim_drop(lchan); + l1sched_prim_drop(lchan); /* Reset mask */ *mask = 0x00; diff --git a/trxcon/sched_lchan_rach.c b/trxcon/src/sched_lchan_rach.c similarity index 50% rename from trxcon/sched_lchan_rach.c rename to trxcon/src/sched_lchan_rach.c index fe5821ba..8fd7fcc2 100644 --- a/trxcon/sched_lchan_rach.c +++ b/trxcon/src/sched_lchan_rach.c @@ -2,7 +2,8 @@ * OsmocomBB <-> SDR connection bridge * TDMA scheduler: handlers for DL / UL bursts on logical channels * - * (C) 2017-2019 by Vadim Yanitskiy + * (C) 2017-2022 by Vadim Yanitskiy + * 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 @@ -33,12 +30,8 @@ #include #include -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" +#include +#include /* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */ #define RACH_EXT_TAIL_BITS_LEN 8 @@ -76,68 +69,63 @@ static struct value_string rach_synch_seq_names[] = { }; /* 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) +int tx_rach_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) { - 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; + const uint8_t bsic = lchan->ts->sched->bsic; + struct l1sched_ts_prim_rach *rach; + uint8_t *burst_ptr = br->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; + rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload; + /* Delay sending according to offset value */ + if (rach->offset-- > 0) + return 0; + + if (L1SCHED_PRIM_IS_RACH11(lchan->prim)) { /* Check requested synch. sequence */ - if (synch_seq >= RACH_SYNCH_SEQ_NUM) { - LOGP(DSCHD, LOGL_ERROR, "Unknown RACH synch. sequence=0x%02x\n", synch_seq); + if (rach->synch_seq >= RACH_SYNCH_SEQ_NUM) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Unknown RACH synch. sequence=0x%02x\n", + rach->synch_seq); /* Forget this primitive */ - sched_prim_drop(lchan); + l1sched_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); + /* Encode 11-bit payload */ + rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, true); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not encode extended RACH burst " - "(ra=%u bsic=%u)\n", ext_req->ra11, trx->bsic); + LOGP_LCHAND(lchan, LOGL_ERROR, + "Could not encode 11-bit RACH burst (ra=%u bsic=%u)\n", + rach->ra, bsic); /* Forget this primitive */ - sched_prim_drop(lchan); + l1sched_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; + } else if (L1SCHED_PRIM_IS_RACH8(lchan->prim)) { + rach->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); + /* Encode 8-bit payload */ + rc = gsm0503_rach_ext_encode(payload, rach->ra, bsic, false); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst " - "(ra=%u bsic=%u)\n", req->ra, trx->bsic); + LOGP_LCHAND(lchan, LOGL_ERROR, + "Could not encode RACH burst (ra=%u bsic=%u)\n", + rach->ra, bsic); /* Forget this primitive */ - sched_prim_drop(lchan); + l1sched_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); + LOGP_LCHAND(lchan, LOGL_ERROR, + "Primitive has unexpected type=0x%02x\n", + lchan->prim->type); + l1sched_prim_drop(lchan); return -EINVAL; } @@ -148,40 +136,25 @@ int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, /* 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'; + *(burst_ptr++) = rach_synch_seq_bits[rach->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); + memset(burst_ptr, 0, br->burst + GSM_BURST_LEN - burst_ptr); + br->burst_len = GSM_BURST_LEN; - 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; - } + LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n", + L1SCHED_PRIM_IS_RACH11(lchan->prim) ? "11" : "8", + get_value_string(rach_synch_seq_names, rach->synch_seq), br->fn); /* Confirm RACH request */ - l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, fn); - - /* Optional GSMTAP logging */ - sched_gsmtap_send(lchan->type, fn, ts->index, - trx->band_arfcn | ARFCN_UPLINK, 0, 0, - PRIM_IS_RACH11(lchan->prim) ? (uint8_t *) &ext_req->ra11 : &req->ra, - PRIM_IS_RACH11(lchan->prim) ? 2 : 1); + l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_OTHER); /* Forget processed primitive */ - sched_prim_drop(lchan); + l1sched_prim_drop(lchan); return 0; } diff --git a/trxcon/sched_lchan_sch.c b/trxcon/src/sched_lchan_sch.c similarity index 51% rename from trxcon/sched_lchan_sch.c rename to trxcon/src/sched_lchan_sch.c index 4fe07ea7..c61e1340 100644 --- a/trxcon/sched_lchan_sch.c +++ b/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 + * (C) 2017-2022 by Vadim Yanitskiy + * 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 @@ -35,18 +32,8 @@ #include #include -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -__attribute__((xray_always_instrument)) __attribute__((noinline)) -static int gsm0503_sch_decode_xray(uint8_t *sb_info, const sbit_t *burst) -{ - return gsm0503_sch_decode(sb_info, burst); -} +#include +#include static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) { @@ -74,9 +61,9 @@ 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, - const sbit_t *bits, const struct trx_meas_set *meas) +int rx_sch_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas) { sbit_t payload[2 * 39]; struct gsm_time time; @@ -89,51 +76,31 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, memcpy(payload + 39, bits + 3 + 39 + 64, 39); /* Attempt to decode */ - rc = gsm0503_sch_decode_xray(sb_info, payload); + 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", 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, lchan->ts->sched->fn_counter_proc); /* 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); + LOGP_LCHAND(lchan, LOGL_ERROR, + "Decoded fn=%u does not match fn=%u provided by scheduler\n", + time.fn, fn); return -EINVAL; } - /* We don't need to send L1CTL_FBSB_CONF */ - if (trx->l1l->fbsb_conf_sent) - return 0; + /* Update BSIC value in the scheduler state */ + lchan->ts->sched->bsic = bsic; - /* 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 = -(meas->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; + l1sched_handle_data_ind(lchan, (const uint8_t *)&time, sizeof(time), + 0, 39 * 2, L1SCHED_DT_OTHER); return 0; } diff --git a/trxcon/src/sched_lchan_tchf.c b/trxcon/src/sched_lchan_tchf.c new file mode 100644 index 00000000..5e741c8c --- /dev/null +++ b/trxcon/src/sched_lchan_tchf.c @@ -0,0 +1,366 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017-2022 by Vadim Yanitskiy + * 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 +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* 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) */ +}; + +int rx_tchf_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas) +{ + int n_errors = -1, n_bits_total = 0, rc; + sbit_t *buffer, *offset; + uint8_t l2[128], *mask; + size_t l2_len; + int amr = 0; + uint8_t ft; + bool amr_is_cmr; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Traffic received: fn=%u bid=%u\n", fn, bid); + + /* Align to the first burst of a block */ + if (*mask == 0x00 && bid != 0) + return 0; + + /* Update mask */ + *mask |= (1 << bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, meas); + + /* 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; + + /* Calculate AVG of the measurements */ + l1sched_lchan_meas_avg(lchan, 8); + + /* 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 ;) */ + + } + + /* Keep the mask updated */ + *mask = *mask << 4; + + 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 */ + /* the first FN 4,13,21 defines that CMI is included in frame, + * the first FN 0,8,17 defines that CMR/CMC is included in frame. + * NOTE: A frame ends 7 FN after start. + */ + amr_is_cmr = !sched_tchf_dl_amr_cmi_map[fn % 26]; + + /* 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(l2 + amr, buffer, + amr_is_cmr, 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(l2, + 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; + default: + LOGP_LCHAND(lchan, 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_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 */ + goto bfi; + } else if (rc == GSM_MACBLOCK_LEN) { + /* FACCH received, forward it to the higher layers */ + l1sched_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN, + n_errors, n_bits_total, + L1SCHED_DT_SIGNALING); + + /* Send BFI substituting a stolen TCH frame */ + n_errors = -1; /* ensure fake measurements */ + goto bfi; + } else { + /* A good TCH frame received */ + l2_len = rc; + } + + /* Send a traffic frame to the higher layers */ + return l1sched_handle_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, L1SCHED_DT_TRAFFIC); + +bfi: + /* Didn't try to decode, fake measurements */ + if (n_errors < 0) { + lchan->meas_avg = (struct l1sched_meas_set) { + .fn = lchan->meas_avg.fn, + .toa256 = 0, + .rssi = -110, + }; + + /* No bursts => no errors */ + n_errors = 0; + } + + /* BFI is not applicable in signalling mode */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + return l1sched_handle_data_ind(lchan, NULL, 0, + n_errors, n_bits_total, + L1SCHED_DT_TRAFFIC); + } + + /* Bad frame indication */ + l2_len = l1sched_bad_frame_ind(l2, lchan); + + /* Send a BFI frame to the higher layers */ + return l1sched_handle_data_ind(lchan, l2, l2_len, + n_errors, n_bits_total, + L1SCHED_DT_TRAFFIC); +} + +int tx_tchf_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t *buffer, *offset; + const uint8_t *tsc; + uint8_t *mask; + size_t l2_len; + int rc; + + /* Set up pointers */ + 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 (br->bid > 0) + return 0; + + /* Shift buffer by 4 bursts back for interleaving */ + memcpy(buffer, buffer + 464, 464); + + /* populate the buffer with bursts */ + if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) { + /* Encode payload */ + rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, GSM_MACBLOCK_LEN, 1); + } else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + int len; + uint8_t cmr_codec; + int ft, cmr, i; + enum osmo_amr_type ft_codec; + enum osmo_amr_quality bfi; + int8_t sti, cmi; + bool amr_fn_is_cmr; + /* the first FN 0,8,17 defines that CMI is included in frame, + * the first FN 4,13,21 defines that CMR is included in frame. + */ + amr_fn_is_cmr = !ul_amr_fn_is_cmi(br->fn); + + len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len, + &cmr_codec, &cmi, &ft_codec, + &bfi, &sti); + if (len < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n", + lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len)); + goto free_bad_msg; + } + ft = -1; + cmr = -1; + for (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); + goto free_bad_msg; + } + if (amr_fn_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); + goto free_bad_msg; + } + 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; + } + rc = gsm0503_tch_afs_encode(buffer, lchan->prim->payload + 2, + lchan->prim->payload_len - 2, amr_fn_is_cmr, + lchan->amr.codec, lchan->amr.codecs, + lchan->amr.ul_ft, + lchan->amr.ul_cmr); + } else { + /* Determine and check the payload length */ + 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; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, + "Invalid TCH mode: %u, dropping frame...\n", + lchan->tch_mode); + /* Forget this primitive */ + l1sched_prim_drop(lchan); + return -EINVAL; + } + if (lchan->prim->payload_len != l2_len) { + LOGP_LCHAND(lchan, 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); + + l1sched_prim_drop(lchan); + return -EINVAL; + } + rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1); + } + + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", + lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, + lchan->prim->payload_len)); +free_bad_msg: + /* Forget this primitive */ + l1sched_prim_drop(lchan); + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + 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, offset, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_BURST_LEN; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid); + + /* If we have sent the last (4/4) burst */ + if (*mask == 0x0f) { + /* Confirm data / traffic sending */ + enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ? + L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING; + l1sched_handle_data_cnf(lchan, br->fn, dt); + + /* Forget processed primitive */ + l1sched_prim_drop(lchan); + + /* Reset mask */ + *mask = 0x00; + } + + return 0; +} diff --git a/trxcon/src/sched_lchan_tchh.c b/trxcon/src/sched_lchan_tchh.c new file mode 100644 index 00000000..9125a395 --- /dev/null +++ b/trxcon/src/sched_lchan_tchh.c @@ -0,0 +1,587 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2018-2022 by Vadim Yanitskiy + * (C) 2018 by Harald Welte + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* 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 */ +}; + +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) */ +}; + +/** + * 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; +} + +/** + * 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 (L1SCHED_TCHH_0 or L1SCHED_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 + */ +static uint32_t tchh_block_dl_first_fn(const struct l1sched_lchan_state *lchan, + uint32_t last_fn, bool facch) +{ + enum l1sched_lchan_type chan = lchan->type; + uint8_t fn_mf, fn_diff; + int i = 0; + + /* Just to be sure */ + OSMO_ASSERT(chan == L1SCHED_TCHH_0 || chan == L1SCHED_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 = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \ + return GSM_TDMA_FN_SUB(last_fn, fn_diff); \ + } \ + } while (++i < ARRAY_SIZE(map)) + + /* Choose a proper block map */ + if (facch) { + if (chan == L1SCHED_TCHH_0) + BLOCK_FIRST_FN(tch_h0_dl_facch_block_map); + else + BLOCK_FIRST_FN(tch_h1_dl_facch_block_map); + } else { + if (chan == L1SCHED_TCHH_0) + BLOCK_FIRST_FN(tch_h0_traffic_block_map); + else + BLOCK_FIRST_FN(tch_h1_traffic_block_map); + } + + LOGP_LCHAND(lchan, 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 l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas) +{ + int n_errors = -1, n_bits_total = 0, rc; + sbit_t *buffer, *offset; + uint8_t l2[128], *mask; + size_t l2_len; + int amr = 0; + uint8_t ft; + bool fn_is_cmi; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Traffic received: fn=%u bid=%u\n", fn, 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 (!l1sched_tchh_facch_start(lchan->type, fn, 0)) + return 0; + } else { /* or TCH/H traffic frame */ + if (!l1sched_tchh_traffic_start(lchan->type, fn, 0)) + return 0; + } + } + + /* Update mask */ + *mask |= (1 << bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, meas); + + /* 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, + !l1sched_tchh_facch_end(lchan->type, fn, 0), + &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN FN 4,13,21 or 5,14,22 defines that CMI is + * included in frame, the first FN FN 0,8,17 or 1,9,18 defines + * that CMR/CMC is included in frame. */ + fn_is_cmi = sched_tchh_dl_amr_cmi_map[fn % 26]; + + /* See comment in function rx_tchf_fn() */ + amr = 2; + rc = gsm0503_tch_ahs_decode_dtx(l2 + amr, buffer, + !sched_tchh_dl_facch_map[fn % 26], + !fn_is_cmi, 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(l2, + 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; + default: + LOGP_LCHAND(lchan, 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) { + /* Calculate AVG of the measurements (assuming 4 bursts) */ + l1sched_lchan_meas_avg(lchan, 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 */ + goto bfi; + } 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_handle_data_ind(lchan, l2 + amr, GSM_MACBLOCK_LEN, + n_errors, n_bits_total, + L1SCHED_DT_SIGNALING); + + /* Send BFI substituting 1/2 stolen TCH frames */ + n_errors = -1; /* ensure fake measurements */ + goto bfi; + } else { + /* A good TCH frame received */ + l2_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_handle_data_ind(lchan, l2, l2_len, + n_errors, n_bits_total, + L1SCHED_DT_TRAFFIC); + +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, fake measurements */ + if (n_errors < 0) { + lchan->meas_avg = (struct l1sched_meas_set) { + .fn = tchh_block_dl_first_fn(lchan, fn, false), + .toa256 = 0, + .rssi = -110, + }; + + /* No bursts => no errors */ + n_errors = 0; + } + + /* BFI is not applicable in signalling mode */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + return l1sched_handle_data_ind(lchan, NULL, 0, + n_errors, n_bits_total, + L1SCHED_DT_SIGNALING); + } + + /* Bad frame indication */ + l2_len = l1sched_bad_frame_ind(l2, lchan); + + /* Send a BFI frame to the higher layers */ + return l1sched_handle_data_ind(lchan, l2, l2_len, + n_errors, n_bits_total, + L1SCHED_DT_TRAFFIC); +} + +int tx_tchh_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t *buffer, *offset; + const uint8_t *tsc; + uint8_t *mask; + size_t l2_len; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + buffer = lchan->tx_bursts; + + if (br->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 (!l1sched_tchh_facch_start(lchan->type, br->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; + } + + /* populate the buffer with bursts */ + if (L1SCHED_PRIM_IS_FACCH(lchan->prim)) { + rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, lchan->prim->payload_len); + lchan->ul_facch_blocks = 6; + } else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + int len; + uint8_t cmr_codec; + int ft, cmr, i; + enum osmo_amr_type ft_codec; + enum osmo_amr_quality bfi; + int8_t sti, cmi; + bool amr_fn_is_cmr; + /* the first FN 0,8,17 defines that CMI is included in frame, + * the first FN 4,13,21 defines that CMR is included in frame. + */ + amr_fn_is_cmr = !ul_amr_fn_is_cmi(br->fn); + + len = osmo_amr_rtp_dec(lchan->prim->payload, lchan->prim->payload_len, + &cmr_codec, &cmi, &ft_codec, + &bfi, &sti); + if (len < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%zu): %s\n", + lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, lchan->prim->payload_len)); + goto free_bad_msg; + } + ft = -1; + cmr = -1; + for (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); + goto free_bad_msg; + } + if (amr_fn_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); + goto free_bad_msg; + } + 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; + } + rc = gsm0503_tch_ahs_encode(buffer, lchan->prim->payload + 2, + lchan->prim->payload_len - 2, amr_fn_is_cmr, + lchan->amr.codec, lchan->amr.codecs, + lchan->amr.ul_ft, + lchan->amr.ul_cmr); + } else { + /* Determine and check the payload length */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* HR */ + l2_len = GSM_HR_BYTES + 1; + break; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, + "Invalid TCH mode: %u, dropping frame...\n", + lchan->tch_mode); + /* Forget this primitive */ + l1sched_prim_drop(lchan); + return -EINVAL; + } + if (lchan->prim->payload_len != l2_len) { + LOGP_LCHAND(lchan, 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 */ + l1sched_prim_drop(lchan); + return -EINVAL; + } + rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len); + } + + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", + lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, + lchan->prim->payload_len)); +free_bad_msg: + /* Forget this primitive */ + l1sched_prim_drop(lchan); + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + 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, offset, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_BURST_LEN; + + 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--; + + if ((*mask & 0x0f) == 0x0f) { + /** + * If no more FACCH/H blocks pending, + * confirm data / traffic sending + */ + if (!lchan->ul_facch_blocks) { + enum l1sched_data_type dt = L1SCHED_PRIM_IS_TCH(lchan->prim) ? + L1SCHED_DT_TRAFFIC : L1SCHED_DT_SIGNALING; + l1sched_handle_data_cnf(lchan, br->fn, dt); + } + + /* Forget processed primitive */ + l1sched_prim_drop(lchan); + } + + return 0; +} diff --git a/trxcon/src/sched_lchan_xcch.c b/trxcon/src/sched_lchan_xcch.c new file mode 100644 index 00000000..0d248662 --- /dev/null +++ b/trxcon/src/sched_lchan_xcch.c @@ -0,0 +1,178 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017-2022 by Vadim Yanitskiy + * 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +int rx_data_fn(struct l1sched_lchan_state *lchan, + uint32_t fn, uint8_t bid, const sbit_t *bits, + const struct l1sched_meas_set *meas) +{ + uint8_t l2[GSM_MACBLOCK_LEN], *mask; + int n_errors, n_bits_total, rc; + sbit_t *buffer, *offset; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + buffer = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Data received: fn=%u bid=%u\n", fn, bid); + + /* Align to the first burst of a block */ + if (*mask == 0x00 && bid != 0) + return 0; + + /* Update mask */ + *mask |= (1 << bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, meas); + + /* 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; + + /* 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, buffer, &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_handle_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN, + n_errors, n_bits_total, + L1SCHED_DT_SIGNALING); +} + +int tx_data_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t *buffer, *offset; + const uint8_t *tsc; + uint8_t *mask; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + buffer = lchan->tx_bursts; + + if (br->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_LCHAND(lchan, LOGL_ERROR, + "Primitive has odd length %zu (expected %u), so dropping...\n", + lchan->prim->payload_len, GSM_MACBLOCK_LEN); + + l1sched_prim_drop(lchan); + return -EINVAL; + } + + /* Encode payload */ + rc = gsm0503_xcch_encode(buffer, lchan->prim->payload); + if (rc) { + LOGP_LCHAND(lchan, 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 */ + l1sched_prim_drop(lchan); + + return -EINVAL; + } + +send_burst: + /* Determine which burst should be sent */ + offset = buffer + 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, offset, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_BURST_LEN; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid); + + /* If we have sent the last (4/4) burst */ + if ((*mask & 0x0f) == 0x0f) { + /* Confirm data sending */ + l1sched_handle_data_cnf(lchan, br->fn, L1SCHED_DT_SIGNALING); + + /* Forget processed primitive */ + l1sched_prim_drop(lchan); + + /* Reset mask */ + *mask = 0x00; + } + + return 0; +} diff --git a/trxcon/src/sched_mframe.c b/trxcon/src/sched_mframe.c new file mode 100644 index 00000000..90b7763c --- /dev/null +++ b/trxcon/src/sched_mframe.c @@ -0,0 +1,2102 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: channel combinations, burst mapping + * + * (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * (C) 2015 by Harald Welte + * + * 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 . + * + */ + +#include + +#include +#include + +/* 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, 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/trxcon/sched_prim.c b/trxcon/src/sched_prim.c similarity index 63% rename from trxcon/sched_prim.c rename to trxcon/src/sched_prim.c index 94733203..f97e5052 100644 --- a/trxcon/sched_prim.c +++ b/trxcon/src/sched_prim.c @@ -2,7 +2,8 @@ * OsmocomBB <-> SDR connection bridge * TDMA scheduler: primitive management * - * (C) 2017 by Vadim Yanitskiy + * (C) 2017-2022 by Vadim Yanitskiy + * 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 @@ -33,181 +30,130 @@ #include -#include "scheduler.h" -#include "sched_trx.h" -#include "trx_if.h" -#include "logging.h" +#include +#include /** * 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 type prim payload type * @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 + * @return allocated primitive or NULL */ -int sched_prim_init(void *ctx, struct trx_ts_prim **prim, - size_t pl_len, uint8_t chan_nr, uint8_t link_id) +static struct l1sched_ts_prim *prim_alloc(void *ctx, size_t pl_len, + enum l1sched_ts_prim_type type, + uint8_t chan_nr, uint8_t link_id) { - enum trx_lchan_type lchan_type; - struct trx_ts_prim *new_prim; - uint8_t len; + enum l1sched_lchan_type lchan_type; + struct l1sched_ts_prim *prim; /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); + lchan_type = l1sched_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; + /* TODO: use proper logging context */ + LOGP(DLGLOBAL, LOGL_ERROR, "Couldn't determine lchan type " + "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); + return NULL; } - /* 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; - } + prim = talloc_zero_size(ctx, sizeof(*prim) + pl_len); + if (prim == NULL) + return NULL; /* Init primitive header */ - new_prim->payload_len = pl_len; - new_prim->chan = lchan_type; + prim->payload_len = pl_len; + prim->chan = lchan_type; + prim->type = type; - /* Set external pointer */ - *prim = new_prim; - - return 0; + return prim; } /** * 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 sched scheduler instance * @param chan_nr RSL channel description - * @return zero in case of success, otherwise a error number + * @param link_id RSL link description + * @param pl Payload data + * @param pl_len Payload length + * @return queued primitive or NULL */ -int sched_prim_push(struct trx_instance *trx, - struct trx_ts_prim *prim, uint8_t chan_nr) +struct l1sched_ts_prim *l1sched_prim_push(struct l1sched_state *sched, + enum l1sched_ts_prim_type type, + uint8_t chan_nr, uint8_t link_id, + const uint8_t *pl, size_t pl_len) { - struct trx_ts *ts; + struct l1sched_ts_prim *prim; + struct l1sched_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]; + ts = sched->ts[tn]; if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); - return -EINVAL; + LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); + return NULL; } - /** - * Change talloc context of primitive - * from trx to the parent ts - */ - talloc_steal(ts, prim); + prim = prim_alloc(ts, pl_len, type, chan_nr, link_id); + if (prim == NULL) + return NULL; + + memcpy(&prim->payload[0], pl, pl_len); /* Add primitive to TS transmit queue */ llist_add_tail(&prim->list, &ts->tx_prims); - return 0; + return prim; } /** - * Composes a new primitive using either cached (if populated), - * or "dummy" Measurement Report message. + * Composes a new primitive from cached RR Measurement Report. * * @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) +static struct l1sched_ts_prim *prim_compose_mr(struct l1sched_lchan_state *lchan) { - struct trx_ts_prim *prim; - uint8_t *mr_src_ptr; + struct l1sched_ts_prim *prim; 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, - - /* 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, - }; /* 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); + prim = prim_alloc(lchan, GSM_MACBLOCK_LEN, L1SCHED_PRIM_DATA, + l1sched_lchan_desc[lchan->type].chan_nr, + L1SCHED_CH_LID_SACCH); + OSMO_ASSERT(prim != NULL); /* 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; + 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(prim->payload, mr_src_ptr, GSM_MACBLOCK_LEN); - - /** - * Update the L1 SACCH pseudo-header (only for cached MRs) - * - * 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] = lchan->ts->trx->tx_power; - prim->payload[1] = lchan->ts->trx->ta; - } + memcpy(&prim->payload[0], &lchan->sacch.mr_cache[0], GSM_MACBLOCK_LEN); /* 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); + 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(DSCHD, LOGL_NOTICE, "Using a %s Measurement Report " - "on lchan=%s\n", (cached ? "cached" : "dummy"), - trx_lchan_desc[lchan->type].name); + LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n"); return prim; } @@ -238,12 +184,12 @@ static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan) * @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) +static struct l1sched_ts_prim *prim_dequeue_sacch(struct llist_head *queue, + struct l1sched_lchan_state *lchan) { - struct trx_ts_prim *prim_nmr = NULL; - struct trx_ts_prim *prim_mr = NULL; - struct trx_ts_prim *prim; + struct l1sched_ts_prim *prim_nmr = NULL; + struct l1sched_ts_prim *prim_mr = NULL; + struct l1sched_ts_prim *prim; bool mr_now; /* Shall we transmit MR now? */ @@ -274,11 +220,9 @@ static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue, 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); + LOGP_LCHAND(lchan, LOGL_DEBUG, + "SACCH MR selection: mr_tx_last=%d prim_mr=%p prim_nmr=%p\n", + lchan->sacch.mr_tx_last, prim_mr, prim_nmr); /* Prioritize non-MR prim if possible */ if (mr_now && prim_mr) @@ -302,25 +246,23 @@ static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue, 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); + LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n"); } /* 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"); + LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n", + 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) +static struct l1sched_ts_prim *prim_dequeue_one(struct llist_head *queue, + enum l1sched_lchan_type lchan_type) { - struct trx_ts_prim *prim; + struct l1sched_ts_prim *prim; /** * There is no need to use the 'safe' list iteration here @@ -341,22 +283,22 @@ static struct trx_ts_prim *prim_dequeue_one(struct llist_head *queue, * 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(), + * check the prim's channel type using L1SCHED_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 + * e.g. L1SCHED_TCHF, L1SCHED_TCHH_0, or L1SCHED_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) +static struct l1sched_ts_prim *prim_dequeue_tch(struct llist_head *queue, + enum l1sched_lchan_type lchan_type, bool facch) { - struct trx_ts_prim *prim; + struct l1sched_ts_prim *prim; /** * There is no need to use the 'safe' list iteration here @@ -367,7 +309,7 @@ static struct trx_ts_prim *prim_dequeue_tch(struct llist_head *queue, continue; /* Either FACCH, or not FACCH */ - if (PRIM_IS_FACCH(prim) != facch) + if (L1SCHED_PRIM_IS_FACCH(prim) != facch) continue; llist_del(&prim->list); @@ -386,14 +328,14 @@ static struct trx_ts_prim *prim_dequeue_tch(struct llist_head *queue, * @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) +static struct l1sched_ts_prim *prim_dequeue_tch_f(struct llist_head *queue) { - struct trx_ts_prim *facch; - struct trx_ts_prim *tch; + struct l1sched_ts_prim *facch; + struct l1sched_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); + facch = prim_dequeue_tch(queue, L1SCHED_TCHF, true); + tch = prim_dequeue_tch(queue, L1SCHED_TCHF, false); /* Prioritize FACCH/F, if found */ if (facch) { @@ -432,15 +374,15 @@ static struct trx_ts_prim *prim_dequeue_tch_f(struct llist_head *queue) * @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) +static struct l1sched_ts_prim *prim_dequeue_tch_h(struct llist_head *queue, + uint32_t fn, enum l1sched_lchan_type lchan_type) { - struct trx_ts_prim *facch; - struct trx_ts_prim *tch; + struct l1sched_ts_prim *facch; + struct l1sched_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); + facch_now = l1sched_tchh_facch_start(lchan_type, fn, true); if (!facch_now) /* Just dequeue a TCH/H prim */ goto no_facch; @@ -476,11 +418,11 @@ no_facch: * @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) +struct l1sched_ts_prim *l1sched_prim_dequeue(struct llist_head *queue, + uint32_t fn, struct l1sched_lchan_state *lchan) { /* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */ - if (CHAN_IS_SACCH(lchan->type)) + if (L1SCHED_CHAN_IS_SACCH(lchan->type)) return prim_dequeue_sacch(queue, lchan); /* There is nothing to dequeue */ @@ -489,12 +431,12 @@ struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, switch (lchan->type) { /* TCH/F requires FACCH/F prioritization */ - case TRXC_TCHF: + case L1SCHED_TCHF: return prim_dequeue_tch_f(queue); /* FACCH/H prioritization is a bit more complex */ - case TRXC_TCHH_0: - case TRXC_TCHH_1: + case L1SCHED_TCHH_0: + case L1SCHED_TCHH_1: return prim_dequeue_tch_h(queue, fn, lchan->type); /* Other kinds of logical channels */ @@ -508,7 +450,7 @@ struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, * * @param lchan a logical channel to drop prim from */ -void sched_prim_drop(struct trx_lchan_state *lchan) +void l1sched_prim_drop(struct l1sched_lchan_state *lchan) { /* Forget this primitive */ talloc_free(lchan->prim); @@ -523,11 +465,11 @@ void sched_prim_drop(struct trx_lchan_state *lchan) * @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) +int l1sched_prim_dummy(struct l1sched_lchan_state *lchan) { - enum trx_lchan_type chan = lchan->type; + enum l1sched_lchan_type chan = lchan->type; uint8_t tch_mode = lchan->tch_mode; - struct trx_ts_prim *prim; + struct l1sched_ts_prim *prim; uint8_t prim_buffer[40]; size_t prim_len = 0; int i; @@ -545,7 +487,7 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) /* Make sure that there is no existing primitive */ OSMO_ASSERT(lchan->prim == NULL); /* Not applicable for SACCH! */ - OSMO_ASSERT(!CHAN_IS_SACCH(lchan->type)); + OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type)); /** * Determine what actually should be generated: @@ -553,10 +495,10 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) * TCH in other modes: silence frame; * other channels: LAPDm fill frame. */ - if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) { + if (L1SCHED_CHAN_IS_TCH(chan) && L1SCHED_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)) { + prim_len = l1sched_bad_frame_ind(prim_buffer, lchan); + } else if (L1SCHED_CHAN_IS_TCH(chan) && L1SCHED_TCH_MODE_IS_DATA(tch_mode)) { /* FIXME: should we do anything for CSD? */ return 0; } else { @@ -581,7 +523,7 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) return 0; /* Allocate a new primitive */ - prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len); + prim = talloc_zero_size(lchan, sizeof(struct l1sched_ts_prim) + prim_len); if (prim == NULL) return -ENOMEM; @@ -595,8 +537,7 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) /* 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); + LOGP_LCHAND(lchan, LOGL_DEBUG, "Transmitting a dummy / silence frame\n"); return 0; } @@ -606,9 +547,9 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) * * @param list list of prims going to be flushed */ -void sched_prim_flush_queue(struct llist_head *list) +void l1sched_prim_flush_queue(struct llist_head *list) { - struct trx_ts_prim *prim, *prim_next; + struct l1sched_ts_prim *prim, *prim_next; llist_for_each_entry_safe(prim, prim_next, list, list) { llist_del(&prim->list); diff --git a/trxcon/sched_trx.c b/trxcon/src/sched_trx.c similarity index 53% rename from trxcon/sched_trx.c rename to trxcon/src/sched_trx.c index 0025e0cc..be1f8203 100644 --- a/trxcon/sched_trx.c +++ b/trxcon/src/sched_trx.c @@ -2,7 +2,8 @@ * OsmocomBB <-> SDR connection bridge * TDMA scheduler: GSM PHY routines * - * (C) 2017-2019 by Vadim Yanitskiy + * (C) 2017-2022 by Vadim Yanitskiy + * 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 @@ -35,28 +32,86 @@ #include #include -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "trx_if.h" -#include "logging.h" +#include +#include -static void sched_frame_clck_cb(struct trx_sched *sched) +/* 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_req(struct l1sched_state *sched, + uint8_t tn, enum gsm_phys_chan_config pchan) { - 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; + const struct l1sched_config_req cr = { + .type = L1SCHED_CFG_PCHAN_COMB, + .pchan_comb = { + .tn = tn, + .pchan = pchan, + }, + }; + + return l1sched_handle_config_req(sched, &cr); +} + +static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); + +static void sched_frame_clck_cb(struct l1sched_state *sched) +{ + struct l1sched_burst_req br[TRX_TS_COUNT]; + const struct l1sched_tdma_frame *frame; + struct l1sched_lchan_state *lchan; + l1sched_lchan_tx_func *handler; + enum l1sched_lchan_type chan; + uint8_t offset; + struct l1sched_ts *ts; + unsigned int tn; + + /* Advance TDMA frame number in order to give the transceiver + * more time to handle the burst before the actual transmission. */ + const uint32_t fn = GSM_TDMA_FN_SUM(sched->fn_counter_proc, + sched->fn_counter_advance); /* Iterate over timeslot list */ - for (i = 0; i < TRX_TS_COUNT; i++) { + for (tn = 0; tn < ARRAY_SIZE(br); tn++) { + /* Initialize the buffer for this timeslot */ + br[tn] = (struct l1sched_burst_req) { + .fn = fn, + .tn = tn, + .burst_len = 0, /* NOPE.ind */ + }; + /* Timeslot is not allocated */ - ts = trx->ts_list[i]; + ts = sched->ts[tn]; if (ts == NULL) continue; @@ -64,27 +119,21 @@ static void sched_frame_clck_cb(struct trx_sched *sched) if (ts->mf_layout == NULL) continue; - /** - * Advance frame number, giving the transceiver more - * time until a burst must be transmitted... - */ - fn = GSM_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; + br[tn].bid = frame->ul_bid; chan = frame->ul_chan; - handler = trx_lchan_desc[chan].tx_fn; + handler = l1sched_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); + lchan = l1sched_find_lchan(ts, chan); if (lchan == NULL) continue; @@ -97,19 +146,19 @@ static void sched_frame_clck_cb(struct trx_sched *sched) * attempt to obtain a new one from queue */ if (lchan->prim == NULL) - lchan->prim = sched_prim_dequeue(&ts->tx_prims, fn, lchan); + lchan->prim = l1sched_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) { + if (l1sched_lchan_desc[chan].flags & L1SCHED_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); + l1sched_prim_dummy(lchan); } /* If there is no primitive, do nothing */ @@ -118,112 +167,122 @@ static void sched_frame_clck_cb(struct trx_sched *sched) /* 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; + if (L1SCHED_PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != L1SCHED_RACH) + handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn; /* Poke lchan handler */ - handler(trx, ts, lchan, fn, bid); + handler(lchan, &br[tn]); + + /* Perform A5/X burst encryption if required */ + if (lchan->a5.algo) + l1sched_a5_burst_enc(lchan, &br[tn]); } + + /* Send all bursts for this TDMA frame */ + for (tn = 0; tn < ARRAY_SIZE(br); tn++) + l1sched_handle_burst_req(sched, &br[tn]); } -int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance) +void l1sched_logging_init(int log_cat_common, int log_cat_data) { - 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; + l1sched_log_cat_common = log_cat_common; + l1sched_log_cat_data = log_cat_data; } -int sched_trx_shutdown(struct trx_instance *trx) +struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv) { - int i; + struct l1sched_state *sched; - if (!trx) - return -EINVAL; + sched = talloc(ctx, struct l1sched_state); + if (!sched) + return NULL; - LOGP(DSCH, LOGL_NOTICE, "Shutdown scheduler\n"); + *sched = (struct l1sched_state) { + /* .clock_timer is set up in l1sched_clck_correct() */ + .clock_cb = &sched_frame_clck_cb, + .fn_counter_advance = cfg->fn_advance, + .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 (i = 0; i < TRX_TS_COUNT; i++) - sched_trx_del_ts(trx, i); + for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++) + l1sched_del_ts(sched, tn); - return 0; + l1sched_clck_reset(sched); + talloc_free(sched); } -int sched_trx_reset(struct trx_instance *trx, bool reset_clock) +void l1sched_reset(struct l1sched_state *sched, bool reset_clock) { - int i; + unsigned int tn; - if (!trx) - return -EINVAL; + if (sched == NULL) + return; - LOGP(DSCH, LOGL_NOTICE, "Reset scheduler %s\n", - reset_clock ? "and clock counter" : ""); + LOGP_SCHEDC(sched, 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); + for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++) + l1sched_del_ts(sched, tn); /* Stop and reset clock counter if required */ if (reset_clock) - sched_clck_reset(&trx->sched); + l1sched_clck_reset(sched); - return 0; + memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy)); } -struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) +struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, 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); + if (sched->ts[tn] != NULL) { + LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot #%u already allocated\n", tn); return NULL; } - LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn); + LOGP_SCHEDC(sched, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn); - /* Allocate a new one */ - trx->ts_list[tn] = talloc_zero(trx, struct trx_ts); + sched->ts[tn] = talloc_zero(sched, struct l1sched_ts); + sched->ts[tn]->sched = sched; + sched->ts[tn]->index = tn; - /* Add backpointer */ - trx->ts_list[tn]->trx = trx; - - /* Assign TS index */ - trx->ts_list[tn]->index = tn; - - return trx->ts_list[tn]; + return sched->ts[tn]; } -void sched_trx_del_ts(struct trx_instance *trx, int tn) +void l1sched_del_ts(struct l1sched_state *sched, int tn) { - struct trx_lchan_state *lchan, *lchan_next; - struct trx_ts *ts; + struct l1sched_lchan_state *lchan, *lchan_next; + struct l1sched_ts *ts; /* Find ts in list */ - ts = trx->ts_list[tn]; + ts = sched->ts[tn]; if (ts == NULL) return; - LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); + LOGP_SCHEDC(sched, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); /* Deactivate all logical channels */ - sched_trx_deactivate_all_lchans(ts); + l1sched_deactivate_all_lchans(ts); /* Free channel states */ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { @@ -232,47 +291,48 @@ void sched_trx_del_ts(struct trx_instance *trx, int tn) } /* Flush queue primitives for TX */ - sched_prim_flush_queue(&ts->tx_prims); + l1sched_prim_flush_queue(&ts->tx_prims); /* Remove ts from list and free memory */ - trx->ts_list[tn] = NULL; + sched->ts[tn] = NULL; talloc_free(ts); /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, tn, 0); + l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE); } #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) +int l1sched_configure_ts(struct l1sched_state *sched, int tn, + enum gsm_phys_chan_config config) { - struct trx_lchan_state *lchan; - enum trx_lchan_type type; - struct trx_ts *ts; + struct l1sched_lchan_state *lchan; + enum l1sched_lchan_type type; + struct l1sched_ts *ts; /* Try to find specified ts */ - ts = trx->ts_list[tn]; + ts = sched->ts[tn]; if (ts != NULL) { /* Reconfiguration of existing one */ - sched_trx_reset_ts(trx, tn); + l1sched_reset_ts(sched, tn); } else { /* Allocate a new one if doesn't exist */ - ts = sched_trx_add_ts(trx, tn); + ts = l1sched_add_ts(sched, tn); if (ts == NULL) return -ENOMEM; } /* Choose proper multiframe layout */ - ts->mf_layout = sched_mframe_layout(config, tn); + ts->mf_layout = l1sched_mframe_layout(config, tn); if (!ts->mf_layout) return -EINVAL; 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); + LOGP_SCHEDC(sched, 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); @@ -280,12 +340,12 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, INIT_LLIST_HEAD(&ts->lchans); /* Allocate channel states */ - for (type = 0; type < _TRX_CHAN_MAX; type++) { + 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 trx_lchan_state); + lchan = talloc_zero(ts, struct l1sched_lchan_state); if (!lchan) return -ENOMEM; @@ -299,24 +359,23 @@ int sched_trx_configure_ts(struct trx_instance *trx, int tn, 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); + if (l1sched_lchan_desc[type].flags & L1SCHED_CH_FLAG_AUTO) + l1sched_activate_lchan(ts, type); } /* Notify transceiver about TS activation */ - /* FIXME: set proper channel type */ - trx_if_cmd_setslot(trx, tn, 1); + l1sched_cfg_pchan_comb_req(sched, tn, config); return 0; } -int sched_trx_reset_ts(struct trx_instance *trx, int tn) +int l1sched_reset_ts(struct l1sched_state *sched, int tn) { - struct trx_lchan_state *lchan, *lchan_next; - struct trx_ts *ts; + struct l1sched_lchan_state *lchan, *lchan_next; + struct l1sched_ts *ts; /* Try to find specified ts */ - ts = trx->ts_list[tn]; + ts = sched->ts[tn]; if (ts == NULL) return -EINVAL; @@ -324,10 +383,10 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) ts->mf_layout = NULL; /* Flush queue primitives for TX */ - sched_prim_flush_queue(&ts->tx_prims); + l1sched_prim_flush_queue(&ts->tx_prims); /* Deactivate all logical channels */ - sched_trx_deactivate_all_lchans(ts); + l1sched_deactivate_all_lchans(ts); /* Free channel states */ llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { @@ -336,15 +395,15 @@ int sched_trx_reset_ts(struct trx_instance *trx, int tn) } /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, tn, 0); + l1sched_cfg_pchan_comb_req(sched, tn, GSM_PCHAN_NONE); return 0; } -int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, - uint8_t *key, uint8_t key_len) +int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo, + const uint8_t *key, uint8_t key_len) { - struct trx_lchan_state *lchan; + struct l1sched_lchan_state *lchan; /* Prevent NULL-pointer deference */ if (!ts) @@ -372,10 +431,10 @@ int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, return 0; } -struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, - enum trx_lchan_type chan) +struct l1sched_lchan_state *l1sched_find_lchan(struct l1sched_ts *ts, + enum l1sched_lchan_type chan) { - struct trx_lchan_state *lchan; + struct l1sched_lchan_state *lchan; llist_for_each_entry(lchan, &ts->lchans, list) if (lchan->type == chan) @@ -384,52 +443,49 @@ struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, return NULL; } -int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode) +int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr, + int active, uint8_t tch_mode, uint8_t tsc) { - const struct trx_lchan_desc *lchan_desc; - struct trx_lchan_state *lchan; + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_lchan_state *lchan; int rc = 0; /* Prevent NULL-pointer deference */ - if (ts == NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n"); - return -EINVAL; - } + OSMO_ASSERT(ts != NULL); /* Iterate over all allocated lchans */ llist_for_each_entry(lchan, &ts->lchans, list) { - lchan_desc = &trx_lchan_desc[lchan->type]; + lchan_desc = &l1sched_lchan_desc[lchan->type]; if (lchan_desc->chan_nr == (chan_nr & 0xf8)) { if (active) { - rc |= sched_trx_activate_lchan(ts, lchan->type); + rc |= l1sched_activate_lchan(ts, lchan->type); lchan->tch_mode = tch_mode; + lchan->tsc = tsc; } else - rc |= sched_trx_deactivate_lchan(ts, lchan->type); + rc |= l1sched_deactivate_lchan(ts, lchan->type); } } return rc; } -int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) +int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan) { - const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan]; - struct trx_lchan_state *lchan; + const struct l1sched_lchan_desc *lchan_desc = &l1sched_lchan_desc[chan]; + struct l1sched_lchan_state *lchan; /* Try to find requested logical channel */ - lchan = sched_trx_find_lchan(ts, chan); + lchan = l1sched_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); + LOGP_LCHANC(lchan, LOGL_ERROR, "is already activated\n"); return -EINVAL; } - LOGP(DSCH, LOGL_NOTICE, "Activating lchan=%s " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); + LOGP_LCHANC(lchan, LOGL_NOTICE, "activating\n"); /* Conditionally allocate memory for bursts */ if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { @@ -452,19 +508,19 @@ int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return 0; } -static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) +static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan) { /* Prevent NULL-pointer deference */ OSMO_ASSERT(lchan != NULL); /* Print some TDMA statistics for Downlink */ - if (trx_lchan_desc[lchan->type].rx_fn && lchan->active) { - LOGP(DSCH, LOGL_DEBUG, "TDMA statistics for lchan=%s on ts=%u: " - "%lu DL frames have been processed, " - "%lu lost (compensated), last fn=%u\n", - trx_lchan_desc[lchan->type].name, lchan->ts->index, - lchan->tdma.num_proc, lchan->tdma.num_lost, - lchan->tdma.last_proc); + 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 */ @@ -479,10 +535,10 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) lchan->tx_bursts = NULL; /* Forget the current prim */ - sched_prim_drop(lchan); + l1sched_prim_drop(lchan); /* Channel specific stuff */ - if (CHAN_IS_TCH(lchan->type)) { + if (L1SCHED_CHAN_IS_TCH(lchan->type)) { lchan->dl_ongoing_facch = 0; lchan->ul_facch_blocks = 0; @@ -490,7 +546,7 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) /* Reset AMR state */ memset(&lchan->amr, 0x00, sizeof(lchan->amr)); - } else if (CHAN_IS_SACCH(lchan->type)) { + } else if (L1SCHED_CHAN_IS_SACCH(lchan->type)) { /* Reset SACCH state */ memset(&lchan->sacch, 0x00, sizeof(lchan->sacch)); } @@ -502,26 +558,24 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) memset(&lchan->tdma, 0x00, sizeof(lchan->tdma)); } -int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) +int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan) { - struct trx_lchan_state *lchan; + struct l1sched_lchan_state *lchan; /* Try to find requested logical channel */ - lchan = sched_trx_find_lchan(ts, chan); + lchan = l1sched_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); + LOGP_LCHANC(lchan, LOGL_ERROR, "is already deactivated\n"); return -EINVAL; } - LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); + LOGP_LCHANC(lchan, LOGL_DEBUG, "deactivating\n"); /* Reset internal state, free memory */ - sched_trx_reset_lchan(lchan); + l1sched_reset_lchan(lchan); /* Update activation flag */ lchan->active = 0; @@ -529,12 +583,13 @@ int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) return 0; } -void sched_trx_deactivate_all_lchans(struct trx_ts *ts) +void l1sched_deactivate_all_lchans(struct l1sched_ts *ts) { - struct trx_lchan_state *lchan; + struct l1sched_lchan_state *lchan; - LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels " - "on ts=%d\n", ts->index); + 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 */ @@ -542,14 +597,14 @@ void sched_trx_deactivate_all_lchans(struct trx_ts *ts) continue; /* Reset internal state, free memory */ - sched_trx_reset_lchan(lchan); + l1sched_reset_lchan(lchan); /* Update activation flag */ lchan->active = 0; } } -enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) +enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr) { uint8_t cbits = chan_nr >> 3; @@ -571,21 +626,21 @@ enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) return GSM_PCHAN_NONE; } -enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, +enum l1sched_lchan_type l1sched_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) + for (i = 0; i < _L1SCHED_CHAN_MAX; i++) + if (l1sched_lchan_desc[i].chan_nr == (chan_nr & 0xf8)) + if (l1sched_lchan_desc[i].link_id == link_id) return i; - return TRXC_IDLE; + return L1SCHED_IDLE; } -static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan, +static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan, uint32_t fn, sbit_t *burst) { ubit_t ks[114]; @@ -603,28 +658,28 @@ static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan, } } -static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan, - uint32_t fn, ubit_t *burst) +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, fn, NULL, ks); + osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks); /* Apply keystream over plaintext */ for (i = 0; i < 57; i++) { - burst[i + 3] ^= ks[i]; - burst[i + 88] ^= ks[i + 57]; + br->burst[i + 3] ^= ks[i]; + br->burst[i + 88] ^= ks[i + 57]; } } -static int subst_frame_loss(struct trx_lchan_state *lchan, - trx_lchan_rx_func *handler, +static int subst_frame_loss(struct l1sched_lchan_state *lchan, + l1sched_lchan_rx_func *handler, uint32_t fn) { - const struct trx_multiframe *mf; - const struct trx_frame *fp; + const struct l1sched_tdma_multiframe *mf; + const struct l1sched_tdma_frame *fp; int elapsed, i; /* Wait until at least one TDMA frame is processed */ @@ -646,27 +701,28 @@ static int subst_frame_loss(struct trx_lchan_state *lchan, 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(DSCHD, LOGL_ERROR, "(%s) Rx burst with fn=%u older than the last " - "processed fn=%u (see OS#4658) => dropping\n", - trx_lchan_desc[lchan->type].name, - fn, lchan->tdma.last_proc); + 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(DSCHD, 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); + 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(DSCHD, LOGL_ERROR, "No TDMA frames elapsed since the last processed " - "fn=%u, must be a bug?\n", lchan->tdma.last_proc); + 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; } static const sbit_t bits[148] = { 0 }; - struct trx_meas_set fake_meas = { + struct l1sched_meas_set fake_meas = { .fn = lchan->tdma.last_proc, .rssi = -120, .toa256 = 0, @@ -678,12 +734,11 @@ static int subst_frame_loss(struct trx_lchan_state *lchan, if (fp->dl_chan != lchan->type) continue; - LOGP(DSCHD, LOGL_NOTICE, "Substituting lost TDMA frame %u on %s\n", - fake_meas.fn, trx_lchan_desc[lchan->type].name); + LOGP_LCHANC(lchan, LOGL_NOTICE, + "Substituting lost TDMA frame fn=%u\n", + fake_meas.fn); - handler(lchan->ts->trx, lchan->ts, lchan, - fake_meas.fn, fp->dl_bid, - bits, &fake_meas); + handler(lchan, fake_meas.fn, fp->dl_bid, bits, &fake_meas); /* Update TDMA frame statistics */ lchan->tdma.last_proc = fake_meas.fn; @@ -694,24 +749,24 @@ static int subst_frame_loss(struct trx_lchan_state *lchan, return 0; } -int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, +int l1sched_handle_rx_burst(struct l1sched_state *sched, uint8_t tn, uint32_t fn, sbit_t *bits, uint16_t nbits, - const struct trx_meas_set *meas) + const struct l1sched_meas_set *meas) { - struct trx_lchan_state *lchan; - const struct trx_frame *frame; - struct trx_ts *ts; + struct l1sched_lchan_state *lchan; + const struct l1sched_tdma_frame *frame; + struct l1sched_ts *ts; - trx_lchan_rx_func *handler; - enum trx_lchan_type chan; + l1sched_lchan_rx_func *handler; + enum l1sched_lchan_type chan; uint8_t offset, bid; int rc; /* Check whether required timeslot is allocated and configured */ - ts = trx->ts_list[tn]; + ts = sched->ts[tn]; if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCHD, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " - "ignoring burst...\n", tn); + LOGP_SCHEDD(sched, LOGL_DEBUG, + "Timeslot #%u isn't configured, ignoring burst...\n", tn); return -EINVAL; } @@ -722,7 +777,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Get required info from frame */ bid = frame->dl_bid; chan = frame->dl_chan; - handler = trx_lchan_desc[chan].rx_fn; + handler = l1sched_lchan_desc[chan].rx_fn; /* Omit bursts which have no handler, like IDLE bursts. * TODO: handle noise indications during IDLE frames. */ @@ -730,7 +785,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, return -ENODEV; /* Find required channel state */ - lchan = sched_trx_find_lchan(ts, chan); + lchan = l1sched_find_lchan(ts, chan); if (lchan == NULL) return -ENODEV; @@ -745,10 +800,10 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, /* Perform A5/X decryption if required */ if (lchan->a5.algo) - sched_trx_a5_burst_dec(lchan, fn, bits); + l1sched_a5_burst_dec(lchan, fn, bits); /* Put burst to handler */ - handler(trx, ts, lchan, fn, bid, bits, meas); + handler(lchan, fn, bid, bits, meas); /* Update TDMA frame statistics */ lchan->tdma.last_proc = fn; @@ -758,43 +813,24 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, * 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(DSCHD, LOGL_NOTICE, "Too many TDMA frames have been processed. " - "Are you running trxcon for more than 6 years?!?\n"); + 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 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; -} - #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 sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas) +void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan, const struct l1sched_meas_set *meas) { - struct trx_lchan_meas_hist *hist = &lchan->meas_hist; + 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) @@ -806,10 +842,10 @@ void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_se } /* Calculate the AVG of n measurements from the history */ -void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n) +void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n) { - struct trx_lchan_meas_hist *hist = &lchan->meas_hist; - struct trx_meas_set *meas = hist->head; + 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; diff --git a/trxcon/trx_if.c b/trxcon/src/trx_if.c similarity index 72% rename from trxcon/trx_if.c rename to trxcon/src/trx_if.c index ffad6c08..6f225ee0 100644 --- a/trxcon/trx_if.c +++ b/trxcon/src/trx_if.c @@ -30,8 +30,6 @@ #include -#include - #include #include #include @@ -41,15 +39,15 @@ #include -#include "l1ctl.h" -#include "trxcon.h" -#include "trx_if.h" -#include "logging.h" -#include "scheduler.h" +#include +#include +#include +#include -#ifdef IPCIF -#include "../Transceiver52M/l1if.h" -#endif +#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? */ @@ -58,8 +56,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] = { @@ -68,25 +66,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, @@ -148,24 +147,14 @@ static void trx_ctrl_send(struct trx_instance *trx) return; tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); -#ifdef IPCIF - char* cmd = malloc(TRXC_BUF_SIZE); - memcpy(cmd, tcm->cmd, TRXC_BUF_SIZE); - /* Send command */ - LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); - trxif_to_trx_c(cmd); - -#else - /* 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); -#endif /* 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 */ @@ -183,13 +172,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; } @@ -228,7 +217,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) @@ -258,11 +247,6 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, * RSP POWERON */ -int trx_if_cmd_sync(struct trx_instance *trx) -{ - return trx_ctrl_cmd(trx, 1, "SYNC", ""); -} - int trx_if_cmd_echo(struct trx_instance *trx) { return trx_ctrl_cmd(trx, 1, "ECHO", ""); @@ -277,7 +261,7 @@ 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"); + LOGPFSML(trx->fi, LOGL_ERROR, "Suppressing POWERON as we're already powered up\n"); return -EAGAIN; } return trx_ctrl_cmd(trx, 1, "POWERON", ""); @@ -294,9 +278,24 @@ int trx_if_cmd_poweron(struct trx_instance *trx) * RSP SETSLOT */ -int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type) +int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, + enum gsm_phys_chan_config pchan) { - 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", tn, chan_types[pchan]); } /* @@ -318,7 +317,7 @@ int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn) /* RX is downlink on MS side */ freq10 = gsm_arfcn2freq10(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", band_arfcn); return -ENOTSUP; } @@ -332,7 +331,7 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn) /* TX is uplink on MS side */ freq10 = gsm_arfcn2freq10(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", band_arfcn); return -ENOTSUP; } @@ -363,7 +362,7 @@ int trx_if_cmd_measure(struct trx_instance *trx, /* Calculate a frequency for current ARFCN (DL) */ freq10 = gsm_arfcn2freq10(band_arfcn_start, 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", band_arfcn_start); return -ENOTSUP; } @@ -372,6 +371,7 @@ int trx_if_cmd_measure(struct trx_instance *trx, static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) { + struct trxcon_inst *trxcon = trx->trxcon; unsigned int freq10; uint16_t band_arfcn; int dbm; @@ -383,16 +383,20 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) /* 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: " + LOGPFSML(trx->fi, 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); + band_arfcn & ~ARFCN_FLAG_MASK, + trx->pm_band_arfcn_start & ~ARFCN_FLAG_MASK); return; } - /* Send L1CTL_PM_CONF */ - l1ctl_tx_pm_conf(trx->l1l, band_arfcn, dbm, - band_arfcn == trx->pm_band_arfcn_stop); + struct trxcon_param_full_power_scan_res res = { + .last_result = band_arfcn == trx->pm_band_arfcn_stop, + .band_arfcn = band_arfcn, + .dbm = dbm, + }; + + osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res); /* Schedule a next measurement */ if (band_arfcn != trx->pm_band_arfcn_stop) @@ -430,8 +434,8 @@ int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta) * 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) +int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, uint8_t maio, + const uint16_t *ma, size_t ma_len) { /* Reserve some room for CMD SETFH */ char ma_buf[TRXC_BUF_SIZE - 24]; @@ -442,7 +446,7 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, /* Make sure that Mobile Allocation has at least one ARFCN */ if (!ma_len || ma == NULL) { - LOGP(DTRX, LOGL_ERROR, "Mobile Allocation is empty?!?\n"); + LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n"); return -EINVAL; } @@ -452,7 +456,7 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */ tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */ if (rx_freq == 0xffff || tx_freq == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "Failed to convert ARFCN %u " + LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u " "to a pair of Rx/Tx frequencies\n", ma[i] & ~ARFCN_FLAG_MASK); return -EINVAL; @@ -461,7 +465,7 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, /* 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 */ - LOGP(DTRX, LOGL_ERROR, "Not enough room to encode " + LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode " "Mobile Allocation (N=%zu)\n", ma_len); return -ENOSPC; } @@ -486,25 +490,15 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) char buf[TRXC_BUF_SIZE], *p; ssize_t read_len; -#ifdef IPCIF - char* response = trxif_from_trx_c(); - if (!response) { - LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", response); - goto rsp_error; - } - memcpy(buf, response, TRXC_BUF_SIZE); - free(response); -#else 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'; -#endif 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; } @@ -512,15 +506,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; } @@ -529,7 +522,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; @@ -538,7 +531,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); @@ -549,18 +542,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); @@ -572,8 +565,7 @@ 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; } @@ -602,7 +594,8 @@ rsp_error: static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) { struct trx_instance *trx = ofd->data; - struct trx_meas_set meas; + struct trxcon_inst *trxcon = trx->trxcon; + struct l1sched_meas_set meas; uint8_t buf[TRXD_BUF_SIZE]; sbit_t bits[148]; int8_t rssi, tn; @@ -610,89 +603,67 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) uint32_t fn; ssize_t read_len; -#ifdef IPCIF - struct trxd_from_trx* rcvd = trxif_from_trx_d(); - if (!rcvd) { - LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", rcvd); - return rcvd; - } - - tn = rcvd->ts; - fn = rcvd->fn; - rssi = -(int8_t) rcvd->rssi; - toa256 = (int16_t) rcvd->toa; - - /* Copy and convert bits {254..0} to sbits {-127..127} */ - //osmo_ubit2sbit(bits, rcvd->symbols, 148); - memcpy(bits, rcvd->symbols, 148); - - free(rcvd); -#else read_len = read(ofd->fd, buf, sizeof(buf)); if (read_len <= 0) { - LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); return read_len; } - if (read_len != 158) { - LOGP(DTRXD, LOGL_ERROR, - "Got data message with invalid " - "length '%zd'\n", - read_len); + if (read_len < (8 + 148)) { /* TRXDv0 header + GMSK burst */ + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "Got data message with invalid length '%zd'\n", read_len); return -EINVAL; } -#endif + tn = buf[0]; fn = osmo_load32be(buf + 1); - rssi = -(int8_t)buf[5]; - toa256 = ((int16_t)(buf[6] << 8) | buf[7]); + 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); - memcpy(bits, buf + 8, 148); + for (unsigned int i = 0; i < 148; i++) { + if (buf[8 + i] == 255) + bits[i] = -127; + else + bits[i] = 127 - buf[8 + i]; + } if (tn >= 8) { - LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); return -EINVAL; } if (fn >= 2715648) { - LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn); + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal FN %u\n", 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", + tn, fn, rssi, toa256); /* Group the measurements together */ - meas = (struct trx_meas_set) { + meas = (struct l1sched_meas_set) { .toa256 = toa256, .rssi = rssi, .fn = fn, }; /* Poke scheduler */ - sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas); + l1sched_handle_rx_burst(trxcon->sched, tn, fn, bits, 148, &meas); /* Correct local clock counter */ if (fn % 51 == 0) - sched_clck_handle(&trx->sched, fn); + l1sched_clck_handle(trxcon->sched, fn); 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_tx_burst(struct trx_instance *trx, + const struct l1sched_burst_req *br) { -#ifdef IPCIF - struct trxd_to_trx* t = malloc(sizeof(struct trxd_to_trx)); - t->ts = tn; - t->fn = fn; - t->txlev = pwr; - memcpy(t->symbols, bits, 148); - trxif_to_trx_d(t); -#else uint8_t buf[TRXD_BUF_SIZE]; + size_t length; /** * We must be sure that we have clock, @@ -702,86 +673,89 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, * transceiver and its TRXC interface. */ #if 0 - if (trx->fsm->state != TRX_STATE_ACTIVE) { - LOGP(DTRXD, LOGL_ERROR, "Ignoring TX data, " - "transceiver isn't ready\n"); + 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; - osmo_store32be(fn, buf + 1); - 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); -#endif return 0; } /* Init TRX interface (TRXC, TRXD sockets and FSM) */ -struct trx_instance *trx_if_open(void *tall_ctx, +struct trx_instance *trx_if_open(struct trxcon_inst *trxcon, const char *local_host, const char *remote_host, uint16_t base_port) { + const unsigned int offset = trxcon->id * 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(trxcon->fi, LOGL_NOTICE, "Init transceiver interface " + "(%s:%u/%u)\n", remote_host, base_port, trxcon->id); - /* 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, trxcon->fi, TRXCON_EV_PHYIF_FAILURE); + if (fi == NULL) { + LOGPFSML(trxcon->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(trxcon->fi, LOGL_ERROR, "Failed to allocate memory\n"); + osmo_fsm_inst_free(fi); return NULL; } /* Initialize CTRL queue */ INIT_LLIST_HEAD(&trx->trx_ctrl_list); -#ifdef IPCIF - rc = eventfd(0, 0); - osmo_fd_setup(get_c_fd(), rc, OSMO_FD_READ, trx_ctrl_read_cb, trx, 0); - osmo_fd_register(get_c_fd()); - - rc = eventfd(0, 0); - osmo_fd_setup(get_d_fd(), rc, OSMO_FD_READ, trx_data_rx_cb, trx, 0); - osmo_fd_register(get_d_fd()); -#else /* Open sockets */ - rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, local_host, base_port + 101, remote_host, base_port + 1, + rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, /* TRXC */ + local_host, base_port + 101 + offset, + remote_host, 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, + rc = trx_udp_open(trx, &trx->trx_ofd_data, /* TRXD */ + local_host, base_port + 102 + offset, + remote_host, base_port + 2 + offset, trx_data_rx_cb); if (rc < 0) goto udp_error; -#endif + + trx->trxcon = trxcon; + 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(trx->fi, LOGL_ERROR, "Couldn't establish UDP connection\n"); + osmo_fsm_inst_free(trx->fi); return NULL; } @@ -791,7 +765,7 @@ 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)) { @@ -804,26 +778,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 */ -#ifdef IPCIF - close(get_c_fd()->fd); - close(get_d_fd()->fd); -#else trx_udp_close(&trx->trx_ofd_ctrl); trx_udp_close(&trx->trx_ofd_data); -#endif /* Free memory */ - osmo_fsm_inst_free(trx->fsm); + trx->fi->priv = NULL; talloc_free(trx); } diff --git a/trxcon/src/trxcon.c b/trxcon/src/trxcon.c new file mode 100644 index 00000000..d46cd8ec --- /dev/null +++ b/trxcon/src/trxcon.c @@ -0,0 +1,555 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2016-2022 by Vadim Yanitskiy + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COPYRIGHT \ + "Copyright (C) 2016-2022 by Vadim Yanitskiy \n" \ + "Contributions by sysmocom - s.f.m.c. GmbH \n" \ + "License GPLv2+: GNU GPL version 2 or later " \ + "\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; + + /* 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 = 3, +}; + +static void *tall_trxcon_ctx = NULL; + +static void trxcon_gsmtap_send(const struct l1sched_lchan_desc *lchan_desc, + uint32_t fn, uint8_t tn, uint16_t band_arfcn, + int8_t signal_dbm, uint8_t snr, + const uint8_t *data, size_t data_len) +{ + /* GSMTAP logging may be not enabled */ + if (app_data.gsmtap == NULL) + return; + + /* Omit frames with unknown channel type */ + if (lchan_desc->gsmtap_chan_type == GSMTAP_CHANNEL_UNKNOWN) + return; + + /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */ + gsmtap_send(app_data.gsmtap, band_arfcn, tn, lchan_desc->gsmtap_chan_type, + lchan_desc->ss_nr, fn, signal_dbm, snr, data, data_len); +} + +/* External L1 API for the scheduler */ +int l1sched_handle_config_req(struct l1sched_state *sched, + const struct l1sched_config_req *cr) +{ + struct trxcon_inst *trxcon = sched->priv; + + switch (cr->type) { + case L1SCHED_CFG_PCHAN_COMB: + { + struct trxcon_param_set_phy_config_req req = { + .type = TRXCON_PHY_CFGT_PCHAN_COMB, + .pchan_comb = { + .tn = cr->pchan_comb.tn, + .pchan = cr->pchan_comb.pchan, + }, + }; + + return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req); + } + default: + LOGPFSML(trxcon->fi, LOGL_ERROR, + "Unhandled config request (type 0x%02x)\n", cr->type); + return -ENODEV; + } +} + +int l1sched_handle_burst_req(struct l1sched_state *sched, + const struct l1sched_burst_req *br) +{ + struct trxcon_inst *trxcon = sched->priv; + + return trx_if_tx_burst(trxcon->phyif, br); +} + +/* External L2 API for the scheduler */ +int l1sched_handle_data_ind(struct l1sched_lchan_state *lchan, + const uint8_t *data, size_t data_len, + int n_errors, int n_bits_total, + enum l1sched_data_type dt) +{ + const struct l1sched_meas_set *meas = &lchan->meas_avg; + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_state *sched = lchan->ts->sched; + struct trxcon_inst *trxcon = sched->priv; + int rc; + + lchan_desc = &l1sched_lchan_desc[lchan->type]; + + struct trxcon_param_rx_traffic_data_ind ind = { + .chan_nr = lchan_desc->chan_nr | lchan->ts->index, + .link_id = lchan_desc->link_id, + .frame_nr = meas->fn, + .toa256 = meas->toa256, + .rssi = meas->rssi, + .n_errors = n_errors, + .n_bits_total = n_bits_total, + .data_len = data_len, + .data = data, + }; + + switch (dt) { + case L1SCHED_DT_TRAFFIC: + case L1SCHED_DT_PACKET_DATA: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_TRAFFIC_IND, &ind); + break; + case L1SCHED_DT_SIGNALING: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind); + break; + case L1SCHED_DT_OTHER: + if (lchan->type == L1SCHED_SCH) { + if (trxcon->fi->state != TRXCON_ST_FBSB_SEARCH) + return 0; + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL); + break; + } + /* fall through */ + default: + LOGPFSML(trxcon->fi, LOGL_ERROR, + "Unhandled L2 DATA.ind (type 0x%02x)\n", dt); + return -ENODEV; + } + + if (data != NULL && data_len > 0) { + trxcon_gsmtap_send(lchan_desc, meas->fn, lchan->ts->index, + trxcon->l1p.band_arfcn, meas->rssi, 0, + data, data_len); + } + + return rc; +} + +int l1sched_handle_data_cnf(struct l1sched_lchan_state *lchan, + uint32_t fn, enum l1sched_data_type dt) +{ + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_state *sched = lchan->ts->sched; + struct trxcon_inst *trxcon = sched->priv; + struct l1ctl_info_dl dl_hdr; + const uint8_t *data; + uint8_t ra_buf[2]; + size_t data_len; + int rc; + + lchan_desc = &l1sched_lchan_desc[lchan->type]; + + dl_hdr = (struct l1ctl_info_dl) { + .chan_nr = lchan_desc->chan_nr | lchan->ts->index, + .link_id = lchan_desc->link_id, + .frame_nr = htonl(fn), + .band_arfcn = htons(trxcon->l1p.band_arfcn), + }; + + switch (dt) { + case L1SCHED_DT_TRAFFIC: + case L1SCHED_DT_PACKET_DATA: + rc = l1ctl_tx_dt_conf(trxcon->l2if, &dl_hdr, true); + data_len = lchan->prim->payload_len; + data = lchan->prim->payload; + break; + case L1SCHED_DT_SIGNALING: + rc = l1ctl_tx_dt_conf(trxcon->l2if, &dl_hdr, false); + data_len = lchan->prim->payload_len; + data = lchan->prim->payload; + break; + case L1SCHED_DT_OTHER: + if (L1SCHED_PRIM_IS_RACH(lchan->prim)) { + const struct l1sched_ts_prim_rach *rach; + + rach = (struct l1sched_ts_prim_rach *)lchan->prim->payload; + + rc = l1ctl_tx_rach_conf(trxcon->l2if, trxcon->l1p.band_arfcn, fn); + if (lchan->prim->type == L1SCHED_PRIM_RACH11) { + ra_buf[0] = (uint8_t)(rach->ra >> 3); + ra_buf[1] = (uint8_t)(rach->ra & 0x07); + data = &ra_buf[0]; + data_len = 2; + } else { + ra_buf[0] = (uint8_t)(rach->ra); + data = &ra_buf[0]; + data_len = 1; + } + break; + } + /* fall through */ + default: + LOGPFSML(trxcon->fi, LOGL_ERROR, + "Unhandled L2 DATA.cnf (type 0x%02x)\n", dt); + return -ENODEV; + } + + trxcon_gsmtap_send(lchan_desc, fn, lchan->ts->index, + trxcon->l1p.band_arfcn | ARFCN_UPLINK, + 0, 0, data, data_len); + + return rc; +} + +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 transceiver interface */ + trxcon->phyif = trx_if_open(trxcon, + app_data.trx_bind_ip, + app_data.trx_remote_ip, + app_data.trx_base_port); + if (trxcon->phyif == NULL) { + trxcon_inst_free(trxcon); + return NULL; + } + + /* Init scheduler */ + const struct l1sched_cfg sched_cfg = { + .fn_advance = app_data.trx_fn_advance, + .log_prefix = trxcon->log_prefix, + }; + + trxcon->sched = l1sched_alloc(trxcon, &sched_cfg, trxcon); + if (trxcon->sched == NULL) { + trxcon_inst_free(trxcon); + return NULL; + } + + 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); +} + +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->fi; + trxcon->l2if = l1c; +} + +static void l1ctl_conn_close_cb(struct l1ctl_client *l1c) +{ + struct osmo_fsm_inst *fi = l1c->priv; + + if (fi == NULL) + return; + + osmo_fsm_inst_dispatch(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 3)\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) { + 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'}, + {"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:s:g:C: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 'g': + app_data.gsmtap_ip = optarg; + break; + case 'C': + app_data.max_clients = atoi(optarg); + 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 */ + trx_log_init(tall_trxcon_ctx, app_data.debug_mask); + l1sched_logging_init(DSCH, DSCHD); + + /* 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) { + 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\n"); + goto exit; + } + /* 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/trxcon/src/trxcon_fsm.c b/trxcon/src/trxcon_fsm.c new file mode 100644 index 00000000..0d03740e --- /dev/null +++ b/trxcon/src/trxcon_fsm.c @@ -0,0 +1,611 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2022 by sysmocom - s.f.m.c. GmbH + * Author: Vadim Yanitskiy + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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: + if (fi->state != TRXCON_ST_RESET) + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + l1sched_reset(trxcon->sched, true); + trx_if_cmd_poweroff(trxcon->phyif); + trx_if_cmd_echo(trxcon->phyif); + 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: + trx_if_cmd_setslot(trxcon->phyif, + req->pchan_comb.tn, + req->pchan_comb.pchan); + break; + case TRXCON_PHY_CFGT_TX_PARAMS: + if (trxcon->l1p.ta != req->tx_params.timing_advance) + trx_if_cmd_setta(trxcon->phyif, req->tx_params.timing_advance); + 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_traffic_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->l2if, trxcon->l1p.band_arfcn); + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + return 0; + default: + OSMO_ASSERT(0); + } +} + +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; + const struct trx_instance *trx = trxcon->phyif; + + osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, req->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) { + /* Update current ARFCN */ + trxcon->l1p.band_arfcn = req->band_arfcn; + + /* Tune transceiver to required ARFCN */ + trx_if_cmd_rxtune(trxcon->phyif, req->band_arfcn); + trx_if_cmd_txtune(trxcon->phyif, req->band_arfcn); + } + + /* Transceiver might have been powered on before, e.g. + * in case of sending L1CTL_FBSB_REQ due to signal loss. */ + if (!trx->powered_up) + trx_if_cmd_poweron(trxcon->phyif); + break; + } + case TRXCON_EV_FULL_POWER_SCAN_REQ: + { + const struct trxcon_param_full_power_scan_req *req = data; + + osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */ + trx_if_cmd_measure(trxcon->phyif, req->band_arfcn_start, req->band_arfcn_stop); + break; + } + default: + OSMO_ASSERT(0); + } +} + +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: + { + const struct trxcon_param_full_power_scan_res *res = data; + + l1ctl_tx_pm_conf(trxcon->l2if, res->band_arfcn, res->dbm, res->last_result); + break; + } + case TRXCON_EV_FULL_POWER_SCAN_REQ: + { + const struct trxcon_param_full_power_scan_req *req = data; + + trx_if_cmd_measure(trxcon->phyif, req->band_arfcn_start, req->band_arfcn_stop); + 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->l2if, + 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; + enum l1sched_ts_prim_type prim_type; + const struct l1sched_ts_prim *prim; + + const struct l1sched_ts_prim_rach rach = { + .synch_seq = req->synch_seq, + .offset = req->offset, + .ra = req->ra, + }; + + prim_type = req->is_11bit ? L1SCHED_PRIM_RACH11 : L1SCHED_PRIM_RACH8; + prim = l1sched_prim_push(trxcon->sched, prim_type, + req->chan_nr, req->link_id, + (const uint8_t *)&rach, sizeof(rach)); + if (prim == NULL) + LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n"); +} + +static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + struct l1sched_ts *ts; + int rc; + + switch (event) { + case TRXCON_EV_TX_ACCESS_BURST_REQ: + handle_tx_access_burst_req(fi, 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; + + /* Make sure that TS0 is allocated and configured */ + ts = trxcon->sched->ts[0]; + 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_DEDICATED_ESTABLISH_REQ: + { + const struct trxcon_param_dedicated_establish_req *req = data; + enum gsm_phys_chan_config config; + + 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) { + /* Apply the freq. hopping parameters */ + rc = trx_if_cmd_setfh(trxcon->phyif, + req->h1.hsn, req->h1.maio, + &req->h1.ma[0], req->h1.n); + if (rc) + return; + + /* Set current ARFCN to an invalid value */ + trxcon->l1p.band_arfcn = 0xffff; + } else { + /* Tune transceiver to required ARFCN */ + if (trx_if_cmd_rxtune(trxcon->phyif, req->h0.band_arfcn)) + return; + if (trx_if_cmd_txtune(trxcon->phyif, req->h0.band_arfcn)) + return; + + /* Update current ARFCN */ + trxcon->l1p.band_arfcn = req->h0.band_arfcn; + } + + 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; + } + + 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); + break; + } + case TRXCON_EV_RX_DATA_IND: + { + const struct trxcon_param_rx_traffic_data_ind *ind = data; + 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(trxcon->l1p.band_arfcn), + .fire_crc = ind->data_len > 0 ? 0 : 2, + .rx_level = dbm2rxlev(ind->rssi), + .num_biterr = ind->n_errors, + /* TODO: set proper .snr */ + }; + + l1ctl_tx_dt_ind(trxcon->l2if, &dl_hdr, ind->data, ind->data_len, false); + 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_DEDICATED_RELEASE_REQ: + l1sched_reset(trxcon->sched, false); + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + 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; + lchan->tch_mode = req->mode; + if (req->mode == GSM48_CMODE_SPEECH_AMR) { + uint8_t bmask = req->amr.codecs_bitmask; + int n = 0; + int acum = 0; + int pos; + while ((pos = ffs(bmask)) != 0) { + acum += pos; + LOGPFSML(fi, LOGL_DEBUG, + LOGP_LCHAN_NAME_FMT " AMR codec[%u] = %u\n", + LOGP_LCHAN_NAME_ARGS(lchan), n, acum - 1); + lchan->amr.codec[n++] = acum - 1; + bmask >>= pos; + } + if (n == 0) { + LOGPFSML(fi, LOGL_ERROR, + LOGP_LCHAN_NAME_FMT " Empty AMR codec mode bitmask!\n", + LOGP_LCHAN_NAME_ARGS(lchan)); + continue; + } + lchan->amr.codecs = n; + lchan->amr.dl_ft = req->amr.start_codec; + lchan->amr.dl_cmr = req->amr.start_codec; + lchan->amr.ul_ft = req->amr.start_codec; + lchan->amr.ul_cmr = req->amr.start_codec; + } + 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_TRAFFIC_REQ: + case TRXCON_EV_TX_DATA_REQ: + { + const struct trxcon_param_tx_traffic_data_req *req = data; + struct l1sched_ts_prim *prim; + + prim = l1sched_prim_push(trxcon->sched, L1SCHED_PRIM_DATA, + req->chan_nr, req->link_id, + req->data, req->data_len); + if (prim == NULL) { + LOGPFSML(fi, LOGL_ERROR, "Failed to enqueue a prim\n"); + return; + } + break; + } + case TRXCON_EV_RX_TRAFFIC_IND: + case TRXCON_EV_RX_DATA_IND: + { + const struct trxcon_param_rx_traffic_data_ind *ind = data; + 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(trxcon->l1p.band_arfcn), + .fire_crc = ind->data_len > 0 ? 0 : 2, + .rx_level = dbm2rxlev(ind->rssi), + .num_biterr = ind->n_errors, + /* TODO: set proper .snr */ + }; + + l1ctl_tx_dt_ind(trxcon->l2if, &dl_hdr, + ind->data, ind->data_len, + event == TRXCON_EV_RX_TRAFFIC_IND); + break; + } + default: + OSMO_ASSERT(0); + } +} + +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_RX_TRAFFIC_IND: + LOGPFSML(fi, LOGL_NOTICE, "Rx PDTCH/D message\n"); + break; + case TRXCON_EV_RX_DATA_IND: + LOGPFSML(fi, LOGL_NOTICE, "Rx PTCCH/D message\n"); + break; + case TRXCON_EV_DEDICATED_RELEASE_REQ: + l1sched_reset(trxcon->sched, false); + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + 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); + /* Close active connections */ + if (trxcon->l2if != NULL) { + /* 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, fi); + l1ctl_client_conn_close(trxcon->l2if); + } + if (trxcon->phyif != NULL) + trx_if_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), + .in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES) + | S(TRXCON_EV_FULL_POWER_SCAN_REQ), + .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_DEDICATED_ESTABLISH_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_BCCH_CCCH), + .in_event_mask = S(TRXCON_EV_DEDICATED_RELEASE_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_REQ) + | S(TRXCON_EV_SET_TCH_MODE_REQ) + | S(TRXCON_EV_TX_TRAFFIC_REQ) + | S(TRXCON_EV_RX_TRAFFIC_IND) + | S(TRXCON_EV_TX_DATA_REQ) + | 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_BCCH_CCCH), + .in_event_mask = S(TRXCON_EV_DEDICATED_RELEASE_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_REQ) + | S(TRXCON_EV_RX_TRAFFIC_IND) + | S(TRXCON_EV_RX_DATA_IND), + .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_UPDATE_SACCH_CACHE_REQ), + OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_ESTABLISH_REQ), + OSMO_VALUE_STRING(TRXCON_EV_DEDICATED_RELEASE_REQ), + OSMO_VALUE_STRING(TRXCON_EV_TX_TRAFFIC_REQ), + OSMO_VALUE_STRING(TRXCON_EV_RX_TRAFFIC_IND), + OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ), + OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND), + OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ), + { 0, NULL } +}; + +struct osmo_fsm trxcon_fsm_def = { + .name = "trxcon", + .states = trxcon_fsm_states, + .num_states = ARRAY_SIZE(trxcon_fsm_states), + .log_subsys = DAPP, + .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/trxcon/trx_if.c.backup.c b/trxcon/trx_if.c.backup.c deleted file mode 100644 index 6a937ed2..00000000 --- a/trxcon/trx_if.c.backup.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * Transceiver interface handlers - * - * Copyright (C) 2013 by Andreas Eversberg - * Copyright (C) 2016-2017 by Vadim Yanitskiy - * - * 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 Affero 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 . - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "l1ctl.h" -#include "trxcon.h" -#include "trx_if.h" -#include "logging.h" -#include "scheduler.h" - -#include "../Transceiver52M/l1if.h" - -static struct value_string trx_evt_names[] = { - { 0, NULL } /* no events? */ -}; - -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)), - .name = "OFFLINE", - }, - [TRX_STATE_IDLE] = { - .out_state_mask = UINT32_MAX, - .name = "IDLE", - }, - [TRX_STATE_ACTIVE] = { - .out_state_mask = ( - GEN_MASK(TRX_STATE_IDLE) | - GEN_MASK(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)), - .name = "RSP_WAIT", - }, -}; - -static struct osmo_fsm trx_fsm = { - .name = "trx_interface_fsm", - .states = trx_fsm_states, - .num_states = ARRAY_SIZE(trx_fsm_states), - .log_subsys = DTRX, - .event_names = trx_evt_names, -}; - -static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local, - uint16_t port_local, const char *host_remote, uint16_t port_remote, - int (*cb)(struct osmo_fd *fd, unsigned int what)) -{ - int rc; - - ofd->data = priv; - ofd->fd = -1; - ofd->cb = cb; - - /* Init UDP Connection */ - rc = osmo_sock_init2_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, host_local, port_local, - host_remote, port_remote, - OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); - return rc; -} - -static void trx_udp_close(struct osmo_fd *ofd) -{ - if (ofd->fd > 0) { - osmo_fd_unregister(ofd); - close(ofd->fd); - ofd->fd = -1; - } -} - -/* ------------------------------------------------------------------------ */ -/* Control (CTRL) interface handlers */ -/* ------------------------------------------------------------------------ */ -/* Commands on the Per-ARFCN Control Interface */ -/* */ -/* The per-ARFCN control interface uses a command-response protocol. */ -/* Commands are NULL-terminated ASCII strings, one per UDP socket. */ -/* Each command has a corresponding response. */ -/* Every command is of the form: */ -/* */ -/* CMD [params] */ -/* */ -/* The is the actual command. */ -/* Parameters are optional depending on the commands type. */ -/* Every response is of the form: */ -/* */ -/* RSP [result] */ -/* */ -/* The is 0 for success and a non-zero error code for failure. */ -/* Successful responses may include results, depending on the command type. */ -/* ------------------------------------------------------------------------ */ - -static void trx_ctrl_timer_cb(void *data); - -/* Send first CTRL message and start timer */ -static void trx_ctrl_send(struct trx_instance *trx) -{ - struct trx_ctrl_msg *tcm; - - if (llist_empty(&trx->trx_ctrl_list)) - return; - tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); - - char* cmd = malloc(TRXC_BUF_SIZE); - memcpy(cmd, tcm->cmd, TRXC_BUF_SIZE); - - /* Send command */ - LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); - trxif_to_trx_c(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); - } - - /* Start expire timer */ - trx->trx_ctrl_timer.data = trx; - trx->trx_ctrl_timer.cb = trx_ctrl_timer_cb; - osmo_timer_schedule(&trx->trx_ctrl_timer, 2, 0); -} - -static void trx_ctrl_timer_cb(void *data) -{ - struct trx_instance *trx = (struct trx_instance *) data; - struct trx_ctrl_msg *tcm; - - /* Queue may be cleaned at this moment */ - if (llist_empty(&trx->trx_ctrl_list)) - return; - - LOGP(DTRX, 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); - return; - } - - /* Attempt to send a command again */ - trx_ctrl_send(trx); -} - -/* Add a new CTRL command to the trx_ctrl_list */ -static int trx_ctrl_cmd(struct trx_instance *trx, int critical, - const char *cmd, const char *fmt, ...) -{ - struct trx_ctrl_msg *tcm; - int len, pending = 0; - va_list ap; - - /* TODO: make sure that transceiver online */ - - if (!llist_empty(&trx->trx_ctrl_list)) - pending = 1; - - /* Allocate a message */ - tcm = talloc_zero(trx, struct trx_ctrl_msg); - if (!tcm) - return -ENOMEM; - - /* Fill in command arguments */ - if (fmt && fmt[0]) { - len = snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s ", cmd); - va_start(ap, fmt); - vsnprintf(tcm->cmd + len, sizeof(tcm->cmd) - len - 1, fmt, ap); - va_end(ap); - } else { - snprintf(tcm->cmd, sizeof(tcm->cmd) - 1, "CMD %s", cmd); - } - - 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); - - /* Send message, if no pending messages */ - if (!pending) - trx_ctrl_send(trx); - - return 0; -} - -/* - * Power Control - * - * ECHO is used to check transceiver availability. - * CMD ECHO - * RSP ECHO - * - * POWEROFF shuts off transmitter power and stops the demodulator. - * CMD POWEROFF - * RSP POWEROFF - * - * POWERON starts the transmitter and starts the demodulator. - * Initial power level is very low. - * This command fails if the transmitter and receiver are not yet tuned. - * This command fails if the transmit or receive frequency creates a conflict - * with another ARFCN that is already running. - * If the transceiver is already on, it response with success to this command. - * CMD POWERON - * RSP POWERON - */ - -int trx_if_cmd_sync(struct trx_instance *trx) -{ - return trx_ctrl_cmd(trx, 1, "SYNC", ""); -} - -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) -{ - return trx_ctrl_cmd(trx, 1, "POWEROFF", ""); -} - -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"); - return -EAGAIN; - } - return trx_ctrl_cmd(trx, 1, "POWERON", ""); -} - -/* - * Timeslot Control - * - * SETSLOT sets the format of the uplink timeslots in the ARFCN. - * The indicates the timeslot of interest. - * The indicates the type of channel that occupies the timeslot. - * A chantype of zero indicates the timeslot is off. - * CMD SETSLOT - * RSP SETSLOT - */ - -int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type) -{ - return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", tn, type); -} - -/* - * Tuning Control - * - * (RX/TX)TUNE tunes the receiver to a given frequency in kHz. - * This command fails if the receiver is already running. - * (To re-tune you stop the radio, re-tune, and restart.) - * This command fails if the transmit or receive frequency - * creates a conflict with another ARFCN that is already running. - * CMD (RX/TX)TUNE - * RSP (RX/TX)TUNE - */ - -int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn) -{ - uint16_t freq10; - - /* RX is downlink on MS side */ - freq10 = gsm_arfcn2freq10(band_arfcn, 0); - if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", 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) -{ - uint16_t freq10; - - /* TX is uplink on MS side */ - freq10 = gsm_arfcn2freq10(band_arfcn, 1); - if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn); - return -ENOTSUP; - } - - return trx_ctrl_cmd(trx, 1, "TXTUNE", "%u", freq10 * 100); -} - -/* - * Power measurement - * - * MEASURE instructs the transceiver to perform a power - * measurement on specified frequency. After receiving this - * request, transceiver should quickly re-tune to requested - * frequency, measure power level and re-tune back to the - * previous frequency. - * CMD MEASURE - * RSP MEASURE - */ - -int trx_if_cmd_measure(struct trx_instance *trx, - uint16_t band_arfcn_start, uint16_t band_arfcn_stop) -{ - 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); - if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn_start); - return -ENOTSUP; - } - - return trx_ctrl_cmd(trx, 1, "MEASURE", "%u", freq10 * 100); -} - -static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) -{ - unsigned int freq10; - uint16_t band_arfcn; - int dbm; - - /* Parse freq. and power level */ - 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); - return; - } - - /* Send L1CTL_PM_CONF */ - l1ctl_tx_pm_conf(trx->l1l, band_arfcn, dbm, - band_arfcn == trx->pm_band_arfcn_stop); - - /* Schedule a next measurement */ - if (band_arfcn != trx->pm_band_arfcn_stop) - trx_if_cmd_measure(trx, ++band_arfcn, trx->pm_band_arfcn_stop); -} - -/* - * Timing Advance control - * - * SETTA instructs the transceiver to transmit bursts in - * advance calculated from requested TA value. This value is - * normally between 0 and 63, with each step representing - * an advance of one bit period (about 3.69 microseconds). - * Since OsmocomBB has a special feature, which allows one - * to spoof the distance from BTS, the range is extended. - * CMD SETTA <-128..127> - * RSP SETTA - */ - -int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta) -{ - return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta); -} - -/* - * Frequency Hopping parameters indication. - * - * SETFH instructs transceiver to enable frequency hopping mode - * using the given HSN, MAIO, and Mobile Allocation parameters. - * - * CMD SETFH [... ] - * - * where and 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) -{ - /* Reserve some room for CMD SETFH */ - 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; - - /* Make sure that Mobile Allocation has at least one ARFCN */ - if (!ma_len || ma == NULL) { - LOGP(DTRX, LOGL_ERROR, "Mobile Allocation is empty?!?\n"); - return -EINVAL; - } - - /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */ - for (i = 0, ptr = ma_buf; i < ma_len; i++) { - /* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */ - rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */ - tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */ - if (rx_freq == 0xffff || tx_freq == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "Failed to convert ARFCN %u " - "to a pair of Rx/Tx frequencies\n", - 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 */ - LOGP(DTRX, LOGL_ERROR, "Not enough room to encode " - "Mobile Allocation (N=%zu)\n", ma_len); - return -ENOSPC; - } - - /* Move pointer */ - ma_buf_len -= rc; - ptr += rc; - } - - /* Overwrite the last space */ - *(ptr - 1) = '\0'; - - return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf); -} - -/* Get response from CTRL socket */ -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[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); -// return read_len; -// } -// buf[read_len] = '\0'; - - LOGP(DTRX, LOGL_NOTICE, "C wat: %d\n", what); - char* response = trxif_from_trx_c(); - if (!response) { - LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", response); - return response; - } - memcpy(buf, response, TRXC_BUF_SIZE); - free(response); - - if (!!strncmp(buf, "RSP ", 4)) { - LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf); - return 0; - } - - /* Calculate the length of response item */ - p = strchr(buf + 4, ' '); - rsp_len = p ? p - buf - 4 : strlen(buf) - 4; - - LOGP(DTRX, 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); - - /* Get command for response message */ - if (llist_empty(&trx->trx_ctrl_list)) { - LOGP(DTRX, LOGL_NOTICE, "Response message without command\n"); - return -EINVAL; - } - - tcm = llist_entry(trx->trx_ctrl_list.next, - struct trx_ctrl_msg, list); - - /* Check if response matches command */ - if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) { - LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, - "Response message '%s' does not match command " - "message '%s'\n", buf, tcm->cmd); - goto rsp_error; - } - - /* Check for response code */ - sscanf(p + 1, "%d", &resp); - if (resp) { - LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, - "Transceiver rejected TRX command with " - "response: '%s'\n", buf); - - if (tcm->critical) - goto rsp_error; - } - - /* 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); - } - else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) { - trx->powered_up = false; - osmo_fsm_inst_state_chg(trx->fsm, 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); - else - osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0); - - /* Remove command from list */ - llist_del(&tcm->list); - talloc_free(tcm); - - /* Send next message, if any */ - trx_ctrl_send(trx); - - return 0; - -rsp_error: - /* Notify higher layers about the problem */ - osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_RSP_ERROR, trx); - return -EIO; -} - -/* ------------------------------------------------------------------------ */ -/* Data interface handlers */ -/* ------------------------------------------------------------------------ */ -/* DATA interface */ -/* */ -/* Messages on the data interface carry one radio burst per UDP message. */ -/* */ -/* Received Data Burst: */ -/* 1 byte timeslot index */ -/* 4 bytes GSM frame number, BE */ -/* 1 byte RSSI in -dBm */ -/* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */ -/* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */ -/* 2 bytes are not used, but being sent by OsmoTRX */ -/* */ -/* Transmit Data Burst: */ -/* 1 byte timeslot index */ -/* 4 bytes GSM frame number, BE */ -/* 1 byte transmit level wrt ARFCN max, -dB (attenuation) */ -/* 148 bytes output symbol values, 0 & 1 */ -/* ------------------------------------------------------------------------ */ - -static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct trx_instance *trx = ofd->data; - struct trx_meas_set meas; - uint8_t buf[TRXD_BUF_SIZE]; - sbit_t bits[148]; - int8_t rssi, tn; - int16_t toa256; - uint32_t fn; - ssize_t read_len; - - - -// read_len = read(ofd->fd, buf, sizeof(buf)); -// if (read_len <= 0) { -// LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); -// return read_len; -// } - -// if (read_len != 158) { -// LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid " -// "length '%zd'\n", read_len); -// return -EINVAL; -// } - -// tn = buf[0]; -// fn = osmo_load32be(buf + 1); -// 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); - - struct trxd_from_trx* rcvd = trxif_from_trx_d(); - if (!rcvd) { - LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", rcvd); - return rcvd; - } - - tn = rcvd->ts; - fn = rcvd->fn; - rssi = -(int8_t) rcvd->rssi; - toa256 = (int16_t) rcvd->toa; - - /* Copy and convert bits {254..0} to sbits {-127..127} */ - //osmo_ubit2sbit(bits, rcvd->symbols, 148); - memcpy(bits, rcvd->symbols, 148); - - free(rcvd); - - if (tn >= 8) { - LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); - return -EINVAL; - } - - if (fn >= 2715648) { - LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn); - return -EINVAL; - } - - LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n", - tn, fn, rssi, toa256); - - /* Group the measurements together */ - meas = (struct trx_meas_set) { - .toa256 = toa256, - .rssi = rssi, - .fn = fn, - }; - - /* Poke scheduler */ - sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas); - - /* Correct local clock counter */ - if (fn % 51 == 0) - sched_clck_handle(&trx->sched, fn); - - return 0; -} - -int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn, - uint8_t pwr, const ubit_t *bits) -{ -// uint8_t buf[TRXD_BUF_SIZE]; - -// /** -// * We must be sure that we have clock, -// * and we have sent all control data -// * -// * TODO: introduce proper state machines for both -// * transceiver and its TRXC interface. -// */ -//#if 0 -// if (trx->fsm->state != TRX_STATE_ACTIVE) { -// LOGP(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); - -// buf[0] = tn; -// osmo_store32be(fn, buf + 1); -// buf[5] = pwr; - -// /* Copy ubits {0,1} */ -// memcpy(buf + 6, bits, 148); - -// /* Send data to transceiver */ -// send(trx->trx_ofd_data.fd, buf, 154, 0); - - struct trxd_to_trx* t = malloc(sizeof(struct trxd_to_trx)); - t->ts = tn; - t->fn = fn; - t->txlev = pwr; - memcpy(t->symbols, bits, 148); - trxif_to_trx_d(t); - 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; - int rc; - - LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface " - "(%s:%u)\n", remote_host, base_port); - - /* Try to allocate memory */ - trx = talloc_zero(tall_ctx, struct trx_instance); - if (!trx) { - LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n"); - 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); - return NULL; - } - - /* Initialize CTRL queue */ - INIT_LLIST_HEAD(&trx->trx_ctrl_list); - - rc = eventfd(0, 0); - osmo_fd_setup(get_c_fd(), rc, OSMO_FD_READ, trx_ctrl_read_cb, trx, 0); - osmo_fd_register(get_c_fd()); - - rc = eventfd(0, 0); - osmo_fd_setup(get_d_fd(), rc, OSMO_FD_READ, trx_data_rx_cb, trx, 0); - osmo_fd_register(get_d_fd()); - -// /* 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); -// 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); -// if (rc < 0) -// goto udp_error; - - return trx; - -//udp_error: -// LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n"); -// osmo_fsm_inst_free(trx->fsm); -// talloc_free(trx); -// return NULL; -} - -/* Flush pending control messages */ -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); - - /* Clear command queue */ - while (!llist_empty(&trx->trx_ctrl_list)) { - tcm = llist_entry(trx->trx_ctrl_list.next, - struct trx_ctrl_msg, list); - llist_del(&tcm->list); - talloc_free(tcm); - } -} - -void trx_if_close(struct trx_instance *trx) -{ - /* May be unallocated due to init error */ - if (!trx) - return; - - LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n"); - - /* Flush CTRL message list */ - trx_if_flush_ctrl(trx); - - /* Close sockets */ - close(get_c_fd()->fd); - close(get_d_fd()->fd); - -// trx_udp_close(&trx->trx_ofd_ctrl); -// trx_udp_close(&trx->trx_ofd_data); - - /* Free memory */ - osmo_fsm_inst_free(trx->fsm); - talloc_free(trx); -} - -static __attribute__((constructor)) void on_dso_load(void) -{ - OSMO_ASSERT(osmo_fsm_register(&trx_fsm) == 0); -} diff --git a/trxcon/trxcon.c b/trxcon/trxcon.c deleted file mode 100644 index 4e3ac278..00000000 --- a/trxcon/trxcon.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * - * (C) 2016-2020 by Vadim Yanitskiy - * - * 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. - * - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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-2020 by Vadim Yanitskiy \n" \ - "License GPLv2+: GNU GPL version 2 or later " \ - "\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; - const char *gsmtap_ip; -} app_data; - -static void *tall_trxcon_ctx = NULL; -struct gsmtap_inst *gsmtap = 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 Uplink burst scheduling advance (default 3)\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(" -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'}, - {"gsmtap-ip", 1, 0, 'g'}, - {"daemonize", 0, 0, 'D'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "d:b:i:p:f:s:g: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 'g': - app_data.gsmtap_ip = 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 = 3; - - app_data.debug_mask = NULL; - app_data.gsmtap_ip = NULL; - app_data.daemonize = 0; - app_data.quit = 0; -} - -static void signal_handler(int signum) -{ - fprintf(stderr, "signal %u received\n", signum); - - switch (signum) { - case SIGINT: - 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; - } -} - -extern void init_external_transceiver(int argc, char **argv); -extern void stop_trx(); -extern volatile bool gshutdown; - -int main(int argc, char **argv) -{ - int rc = 0; - - cpu_set_t cpuset; - - CPU_ZERO(&cpuset); - CPU_SET(3, &cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); - - int prio = sched_get_priority_max(SCHED_RR) - 5; - struct sched_param param; - param.sched_priority = prio; - int rv = sched_setscheduler(0, SCHED_RR, ¶m); - if (rv < 0) { - LOGP(DAPP, LOGL_ERROR, "Failed to set sched!\n"); - exit(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(SIGABRT, &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); - - /* 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); - - /* Optional GSMTAP */ - if (app_data.gsmtap_ip != NULL) { - gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1); - if (!gsmtap) { - LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n"); - goto exit; - } - /* Suppress ICMP "destination unreachable" errors */ - gsmtap_source_add_sink(gsmtap); - } - - /* Allocate the application state machine */ - OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0); - 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)); - - init_external_transceiver(argc, argv); - - // while (!app_data.quit) - // osmo_select_main(0); - - gshutdown = true; - stop_trx(); - - -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/trxcon/trxcon.h b/trxcon/trxcon.h deleted file mode 100644 index 9a0792bf..00000000 --- a/trxcon/trxcon.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#define GEN_MASK(state) (0x01 << state) - -extern struct osmo_fsm_inst *trxcon_fsm; -extern struct gsmtap_inst *gsmtap; - -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, -};