aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bts-trx/scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bts-trx/scheduler.c')
-rw-r--r--src/osmo-bts-trx/scheduler.c3044
1 files changed, 3044 insertions, 0 deletions
diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c
new file mode 100644
index 00000000..8d0c8343
--- /dev/null
+++ b/src/osmo-bts-trx/scheduler.c
@@ -0,0 +1,3044 @@
+/* Scheduler for OsmoBTS-TRX */
+
+/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/a5.h>
+
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/rsl.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/amr.h>
+
+#include "l1_if.h"
+#include "scheduler.h"
+#include "gsm0503_coding.h"
+#include "trx_if.h"
+#include "loops.h"
+#include "amr.h"
+#include "loops.h"
+
+/* Enable this to multiply TOA of RACH by 10.
+ * This is usefull to check tenth of timing advances with RSSI test tool.
+ * Note that regular phones will not work when using this test! */
+//#define TA_TEST
+
+void *tall_bts_ctx;
+
+static struct gsm_bts *bts;
+
+/* clock states */
+static uint32_t transceiver_lost;
+uint32_t transceiver_last_fn;
+static struct timeval transceiver_clock_tv;
+static struct osmo_timer_list transceiver_clock_timer;
+
+/* clock advance for the transceiver */
+uint32_t trx_clock_advance = 20;
+
+/* advance RTS to give some time for data processing. (especially PCU) */
+uint32_t trx_rts_advance = 5; /* about 20ms */
+
+typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan);
+typedef ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn,
+ uint32_t fn, enum trx_chan_type chan, uint8_t bid);
+typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+
+static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan);
+static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan);
+static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan);
+static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid);
+static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa);
+
+static ubit_t dummy_burst[148] = {
+ 0,0,0,
+ 1,1,1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,1,0,
+ 0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,
+ 0,1,0,1,1,1,0,0,0,1,0,1,1,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,0,
+ 0,0,1,1,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,0,1,0,0,1,1,1,1,1,0,0,0,1,
+ 0,0,1,0,1,1,1,1,1,0,1,0,1,0,
+ 0,0,0,
+};
+
+static ubit_t fcch_burst[148] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+
+static const ubit_t tsc[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, },
+};
+
+static const ubit_t sch_train[64] = {
+ 1,0,1,1,1,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,
+ 0,0,1,0,1,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,1,0,0,0,0,1,1,0,1,1,
+};
+
+/*
+ * subchannel description structure
+ */
+
+struct trx_chan_desc {
+ int pdch;
+ enum trx_chan_type chan;
+ uint8_t chan_nr;
+ uint8_t link_id;
+ const char *name;
+ trx_sched_rts_func *rts_fn;
+ trx_sched_dl_func *dl_fn;
+ trx_sched_ul_func *ul_fn;
+ int auto_active;
+};
+struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
+ { 0, TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 },
+ { 0, TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 },
+ { 0, TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 },
+ { 0, TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 },
+ { 0, TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 },
+ { 0, TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 },
+ { 0, TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tchf_fn, tx_tchf_fn, rx_tchf_fn, 0 },
+ { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
+ { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 },
+ { 0, TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 0, TRXC_SACCH8_7, 0x78, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+ { 1, TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 },
+ { 1, TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_data_fn, rx_data_fn, 0 },
+};
+
+
+/*
+ * init / exit
+ */
+
+int trx_sched_init(struct trx_l1h *l1h)
+{
+ uint8_t tn;
+ int i;
+ struct trx_chan_state *chan_state;
+
+ LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1h->trx->nr);
+
+ /* hack to get bts */
+ bts = l1h->trx->bts;
+
+ for (tn = 0; tn < 8; tn++) {
+ l1h->mf_index[tn] = 0;
+ l1h->mf_last_fn[tn] = 0;
+ INIT_LLIST_HEAD(&l1h->dl_prims[tn]);
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ chan_state = &l1h->chan_states[tn][i];
+ chan_state->active = 0;
+ }
+ }
+
+ return 0;
+}
+
+void trx_sched_exit(struct trx_l1h *l1h)
+{
+ uint8_t tn;
+ int i;
+ struct trx_chan_state *chan_state;
+
+ LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1h->trx->nr);
+
+ for (tn = 0; tn < 8; tn++) {
+ msgb_queue_flush(&l1h->dl_prims[tn]);
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ chan_state = &l1h->chan_states[tn][i];
+ if (chan_state->dl_bursts) {
+ talloc_free(chan_state->dl_bursts);
+ chan_state->dl_bursts = NULL;
+ }
+ if (chan_state->ul_bursts) {
+ talloc_free(chan_state->ul_bursts);
+ chan_state->ul_bursts = NULL;
+ }
+ }
+ /* clear lchan channel states */
+ for (i = 0; i < 8; i++)
+ l1h->trx->ts[tn].lchan[i].state = LCHAN_S_NONE;
+ }
+}
+
+/* close all logical channels and reset timeslots */
+void trx_sched_reset(struct trx_l1h *l1h)
+{
+ trx_sched_exit(l1h);
+ trx_sched_init(l1h);
+}
+
+
+/*
+ * data request (from upper layer)
+ */
+
+int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
+{
+ uint8_t tn = l1sap->u.data.chan_nr & 7;
+
+ LOGP(DL1C, LOGL_INFO, "PH-DATA.req: chan_nr=0x%02x link_id=0x%02x "
+ "fn=%u ts=%u trx=%u\n", l1sap->u.data.chan_nr,
+ l1sap->u.data.link_id, l1sap->u.data.fn, tn, l1h->trx->nr);
+
+ if (!l1sap->oph.msg)
+ abort();
+
+ /* ignore empty frame */
+ if (!msgb_l2len(l1sap->oph.msg)) {
+ msgb_free(l1sap->oph.msg);
+ return 0;
+ }
+
+ msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
+
+ return 0;
+}
+
+int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
+{
+ uint8_t tn = l1sap->u.tch.chan_nr & 7;
+
+ LOGP(DL1C, LOGL_INFO, "TCH.req: chan_nr=0x%02x "
+ "fn=%u ts=%u trx=%u\n", l1sap->u.tch.chan_nr,
+ l1sap->u.tch.fn, tn, l1h->trx->nr);
+
+ if (!l1sap->oph.msg)
+ abort();
+
+ /* ignore empty frame */
+ if (!msgb_l2len(l1sap->oph.msg)) {
+ msgb_free(l1sap->oph.msg);
+ return 0;
+ }
+
+ msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
+
+ return 0;
+}
+
+
+/*
+ * ready-to-send indication (to upper layer)
+ */
+
+/* RTS for data frame */
+static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan)
+{
+ uint8_t chan_nr, link_id;
+ struct msgb *msg;
+ struct osmo_phsap_prim *l1sap;
+
+ /* get data for RTS indication */
+ chan_nr = trx_chan_desc[chan].chan_nr | tn;
+ link_id = trx_chan_desc[chan].link_id;
+
+ if (!chan_nr) {
+ LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing "
+ "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr);
+ return -ENODEV;
+ }
+
+ LOGP(DL1C, LOGL_INFO, "PH-RTS.ind: chan=%s chan_nr=0x%02x "
+ "link_id=0x%02x fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
+ chan_nr, link_id, fn, tn, l1h->trx->nr);
+
+ /* send clock information to loops process */
+ if (L1SAP_IS_LINK_SACCH(link_id))
+ trx_loop_sacch_clock(l1h, chan_nr, &l1h->chan_states[tn][chan]);
+
+ /* generate prim */
+ msg = l1sap_msgb_alloc(200);
+ if (!msg)
+ return -ENOMEM;
+ l1sap = msgb_l1sap_prim(msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
+ PRIM_OP_INDICATION, msg);
+ l1sap->u.data.chan_nr = chan_nr;
+ l1sap->u.data.link_id = link_id;
+ l1sap->u.data.fn = fn;
+
+ return l1sap_up(l1h->trx, l1sap);
+}
+
+static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, int facch)
+{
+ uint8_t chan_nr, link_id;
+ struct msgb *msg;
+ struct osmo_phsap_prim *l1sap;
+ int rc = 0;
+
+ /* get data for RTS indication */
+ chan_nr = trx_chan_desc[chan].chan_nr | tn;
+ link_id = trx_chan_desc[chan].link_id;
+
+ if (!chan_nr) {
+ LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing "
+ "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr);
+ return -ENODEV;
+ }
+
+ LOGP(DL1C, LOGL_INFO, "TCH RTS.ind: chan=%s chan_nr=0x%02x "
+ "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
+ chan_nr, fn, tn, l1h->trx->nr);
+
+ /* only send, if FACCH is selected */
+ if (facch) {
+ /* generate prim */
+ msg = l1sap_msgb_alloc(200);
+ if (!msg)
+ return -ENOMEM;
+ l1sap = msgb_l1sap_prim(msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
+ PRIM_OP_INDICATION, msg);
+ l1sap->u.data.chan_nr = chan_nr;
+ l1sap->u.data.link_id = link_id;
+ l1sap->u.data.fn = fn;
+
+ rc = l1sap_up(l1h->trx, l1sap);
+ }
+
+ /* dont send, if TCH is in signalling only mode */
+ if (l1h->chan_states[tn][chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
+ /* generate prim */
+ msg = l1sap_msgb_alloc(200);
+ if (!msg)
+ return -ENOMEM;
+ l1sap = msgb_l1sap_prim(msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS,
+ PRIM_OP_INDICATION, msg);
+ l1sap->u.tch.chan_nr = chan_nr;
+ l1sap->u.tch.fn = fn;
+
+ return l1sap_up(l1h->trx, l1sap);
+ }
+
+ return rc;
+}
+
+/* RTS for full rate traffic frame */
+static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan)
+{
+ /* TCH/F may include FACCH on every 4th burst */
+ return rts_tch_common(l1h, tn, fn, chan, 1);
+}
+
+
+/* RTS for half rate traffic frame */
+static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan)
+{
+ /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */
+ return rts_tch_common(l1h, tn, fn, chan, ((fn % 26) >> 2) & 1);
+}
+
+
+/*
+ * TX on downlink
+ */
+
+/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
+static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+
+ return NULL;
+}
+
+static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+
+ return fcch_burst;
+}
+
+static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ static ubit_t bits[148], burst[78];
+ uint8_t sb_info[4];
+ struct gsm_time t;
+ uint8_t t3p, bsic;
+
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+
+ /* create SB info from GSM time and BSIC */
+ gsm_fn2gsmtime(&t, fn);
+ t3p = t.t3 / 10;
+ bsic = l1h->trx->bts->bsic;
+ sb_info[0] =
+ ((bsic & 0x3f) << 2) |
+ ((t.t1 & 0x600) >> 9);
+ sb_info[1] =
+ ((t.t1 & 0x1fe) >> 1);
+ sb_info[2] =
+ ((t.t1 & 0x001) << 7) |
+ ((t.t2 & 0x1f) << 2) |
+ ((t3p & 0x6) >> 1);
+ sb_info[3] =
+ (t3p & 0x1);
+
+ /* encode bursts */
+ sch_encode(burst, sb_info);
+
+ /* compose burst */
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 39);
+ memcpy(bits + 42, sch_train, 64);
+ memcpy(bits + 106, burst + 39, 39);
+ memset(bits + 145, 0, 3);
+
+ return bits;
+}
+
+static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn,
+ enum trx_chan_type chan)
+{
+ struct msgb *msg, *msg2;
+ struct osmo_phsap_prim *l1sap;
+ uint32_t prim_fn;
+ uint8_t chan_nr, link_id;
+
+ /* get prim of current fn from queue */
+ llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) {
+ l1sap = msgb_l1sap_prim(msg);
+ if (l1sap->oph.operation != PRIM_OP_REQUEST) {
+wrong_type:
+ LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has "
+ "wrong type.\n", tn, fn);
+free_msg:
+ /* unlink and free message */
+ llist_del(&msg->list);
+ msgb_free(msg);
+ return NULL;
+ }
+ switch (l1sap->oph.primitive) {
+ case PRIM_PH_DATA:
+ chan_nr = l1sap->u.data.chan_nr;
+ link_id = l1sap->u.data.link_id;
+ prim_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648);
+ break;
+ case PRIM_TCH:
+ chan_nr = l1sap->u.tch.chan_nr;
+ link_id = 0;
+ prim_fn = ((l1sap->u.tch.fn + 2715648 - fn) % 2715648);
+ break;
+ default:
+ goto wrong_type;
+ }
+ if (prim_fn > 100) {
+ LOGP(DL1C, LOGL_NOTICE, "Prim for trx=%u ts=%u at fn=%u "
+ "is out of range, or channel already disabled. "
+ "If this happens in conjunction with PCU, "
+ "increase 'rts-advance' by 5. (current fn=%u)\n",
+ l1h->trx->nr, tn, l1sap->u.data.fn, fn);
+ /* unlink and free message */
+ llist_del(&msg->list);
+ msgb_free(msg);
+ continue;
+ }
+ if (prim_fn > 0)
+ continue;
+
+ goto found_msg;
+ }
+
+ return NULL;
+
+found_msg:
+ if ((chan_nr ^ (trx_chan_desc[chan].chan_nr | tn))
+ || ((link_id & 0xc0) ^ trx_chan_desc[chan].link_id)) {
+ LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has wrong "
+ "chan_nr=%02x link_id=%02x, expecting chan_nr=%02x "
+ "link_id=%02x.\n", tn, fn, chan_nr, link_id,
+ trx_chan_desc[chan].chan_nr | tn,
+ trx_chan_desc[chan].link_id);
+ goto free_msg;
+ }
+
+ /* unlink and return message */
+ llist_del(&msg->list);
+ return msg;
+}
+
+static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi)
+{
+ struct msgb *msg;
+ struct osmo_phsap_prim *l1sap;
+ uint8_t chan_nr = trx_chan_desc[chan].chan_nr | tn;
+
+ /* compose primitive */
+ msg = l1sap_msgb_alloc(l2_len);
+ l1sap = msgb_l1sap_prim(msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA,
+ PRIM_OP_INDICATION, msg);
+ l1sap->u.data.chan_nr = chan_nr;
+ l1sap->u.data.link_id = trx_chan_desc[chan].link_id;
+ l1sap->u.data.fn = fn;
+ l1sap->u.data.rssi = (int8_t) (rssi);
+ msg->l2h = msgb_put(msg, l2_len);
+ if (l2_len)
+ memcpy(msg->l2h, l2, l2_len);
+
+ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id))
+ l1h->chan_states[tn][chan].lost = 0;
+
+ /* forward primitive */
+ l1sap_up(l1h->trx, l1sap);
+
+ return 0;
+}
+
+static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len)
+{
+ struct msgb *msg;
+ struct osmo_phsap_prim *l1sap;
+
+ /* compose primitive */
+ msg = l1sap_msgb_alloc(tch_len);
+ l1sap = msgb_l1sap_prim(msg);
+ osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH,
+ PRIM_OP_INDICATION, msg);
+ l1sap->u.tch.chan_nr = trx_chan_desc[chan].chan_nr | tn;
+ l1sap->u.tch.fn = fn;
+ msg->l2h = msgb_put(msg, tch_len);
+ if (tch_len)
+ memcpy(msg->l2h, tch, tch_len);
+
+ if (l1h->chan_states[tn][chan].lost)
+ l1h->chan_states[tn][chan].lost--;
+
+ /* forward primitive */
+ l1sap_up(l1h->trx, l1sap);
+
+ return 0;
+}
+
+static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ struct msgb *msg = NULL; /* make GCC happy */
+ ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
+ static ubit_t bits[148];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get burst from queue */
+ msg = dequeue_prim(l1h, tn, fn, chan);
+ if (msg)
+ goto got_msg;
+
+ LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return NULL;
+
+got_msg:
+ /* check validity of message */
+ if (msgb_l2len(msg) != 23) {
+ LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
+ "(len=%d)\n", msgb_l2len(msg));
+ /* free message */
+ msgb_free(msg);
+ goto no_msg;
+ }
+
+ /* handle loss detection of sacch */
+ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
+ /* count and send BFI */
+ if (++(l1h->chan_states[tn][chan].lost) > 1) {
+ /* TODO: Should we pass old TOA here? Otherwise we risk
+ * unnecessary decreasing TA */
+
+ /* Send uplnk measurement information to L2 */
+ l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+ 456, 456, -110, 0);
+
+ compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -110);
+ }
+ }
+
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return NULL;
+ }
+
+ /* encode bursts */
+ xcch_encode(*bursts_p, msg->l2h);
+
+ /* free message */
+ msgb_free(msg);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ return bits;
+}
+
+static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ struct msgb *msg = NULL; /* make GCC happy */
+ ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
+ static ubit_t bits[148];
+ int rc;
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get burst from queue */
+ msg = dequeue_prim(l1h, tn, fn, chan);
+ if (msg)
+ goto got_msg;
+
+ LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return NULL;
+
+got_msg:
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return NULL;
+ }
+
+ /* encode bursts */
+ rc = pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
+
+ /* check validity of message */
+ if (rc) {
+ LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! "
+ "(len=%d)\n", rc);
+ /* free message */
+ msgb_free(msg);
+ goto no_msg;
+ }
+
+ /* free message */
+ msgb_free(msg);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ return bits;
+}
+
+static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
+ struct msgb **_msg_facch, int codec_mode_request)
+{
+ struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ struct osmo_phsap_prim *l1sap;
+
+ /* handle loss detection of received TCH frames */
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH
+ && ++(l1h->chan_states[tn][chan].lost) > 5) {
+ uint8_t tch_data[33];
+ int len;
+
+ LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending "
+ "BFI for %s\n", trx_chan_desc[chan].name);
+
+ /* indicate bad frame */
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ if (chan != TRXC_TCHF) { /* HR */
+ tch_data[0] = 0x70; /* F = 0, FT = 111 */
+ memset(tch_data + 1, 0, 14);
+ len = 15;
+ break;
+ }
+ memset(tch_data, 0, 33);
+ len = 33;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ if (chan != TRXC_TCHF)
+ goto inval_mode1;
+ memset(tch_data, 0, 31);
+ len = 31;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ len = amr_compose_payload(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft], 1);
+ if (len < 2)
+ break;
+ memset(tch_data + 2, 0, len - 2);
+ compose_tch_ind(l1h, tn, 0, chan, tch_data, len);
+ break;
+ default:
+inval_mode1:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please "
+ "fix!\n");
+ len = 0;
+ }
+ if (len)
+ compose_tch_ind(l1h, tn, 0, chan, tch_data, len);
+ }
+
+ /* get frame and unlink from queue */
+ msg1 = dequeue_prim(l1h, tn, fn, chan);
+ msg2 = dequeue_prim(l1h, tn, fn, chan);
+ if (msg1) {
+ l1sap = msgb_l1sap_prim(msg1);
+ if (l1sap->oph.primitive == PRIM_TCH) {
+ msg_tch = msg1;
+ if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive == PRIM_TCH) {
+ LOGP(DL1C, LOGL_FATAL, "TCH twice, "
+ "please FIX! ");
+ msgb_free(msg2);
+ } else
+ msg_facch = msg2;
+ }
+ } else {
+ msg_facch = msg1;
+ if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive != PRIM_TCH) {
+ LOGP(DL1C, LOGL_FATAL, "FACCH twice, "
+ "please FIX! ");
+ msgb_free(msg2);
+ } else
+ msg_tch = msg2;
+ }
+ }
+ } else if (msg2) {
+ l1sap = msgb_l1sap_prim(msg2);
+ if (l1sap->oph.primitive == PRIM_TCH)
+ msg_tch = msg2;
+ else
+ msg_facch = msg2;
+ }
+
+ /* check validity of message */
+ if (msg_facch && msgb_l2len(msg_facch) != 23) {
+ LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! "
+ "(len=%d)\n", msgb_l2len(msg_facch));
+ /* free message */
+ msgb_free(msg_facch);
+ msg_facch = NULL;
+ }
+
+ /* check validity of message, get AMR ft and cmr */
+ if (!msg_facch && msg_tch) {
+ int len;
+ uint8_t bfi, cmr_codec, ft_codec;
+ int cmr, ft, i;
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
+ LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, "
+ "because we are not in speech mode trx=%u "
+ "ts=%u at fn=%u.\n", trx_chan_desc[chan].name,
+ l1h->trx->nr, tn, fn);
+ goto free_bad_msg;
+ }
+
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR / HR */
+ if (chan != TRXC_TCHF) { /* HR */
+ len = 15;
+ if (msgb_l2len(msg_tch) >= 1
+ && (msg_tch->l2h[0] & 0xf0) != 0x00) {
+ LOGP(DL1C, LOGL_NOTICE, "%s "
+ "Transmitting 'bad "
+ "HR frame' trx=%u ts=%u at "
+ "fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1h->trx->nr, tn, fn);
+ goto free_bad_msg;
+ }
+ break;
+ }
+ len = 33;
+ if (msgb_l2len(msg_tch) >= 1
+ && (msg_tch->l2h[0] >> 4) != 0xd) {
+ LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
+ "FR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1h->trx->nr, tn, fn);
+ goto free_bad_msg;
+ }
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ if (chan != TRXC_TCHF)
+ goto inval_mode2;
+ len = 31;
+ if (msgb_l2len(msg_tch) >= 1
+ && (msg_tch->l2h[0] >> 4) != 0xc) {
+ LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
+ "EFR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1h->trx->nr, tn, fn);
+ goto free_bad_msg;
+ }
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ len = amr_decompose_payload(msg_tch->l2h,
+ msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
+ &bfi);
+ cmr = -1;
+ ft = -1;
+ for (i = 0; i < chan_state->codecs; i++) {
+ if (chan_state->codec[i] == cmr_codec)
+ cmr = i;
+ if (chan_state->codec[i] == ft_codec)
+ ft = i;
+ }
+ if (cmr >= 0) { /* new request */
+ chan_state->dl_cmr = cmr;
+ /* disable AMR loop */
+ trx_loop_amr_set(chan_state, 0);
+ } else {
+ /* enable AMR loop */
+ trx_loop_amr_set(chan_state, 1);
+ }
+ if (ft < 0) {
+ LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) "
+ " of RTP frame not in list. "
+ "trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1h->trx->nr, tn);
+ goto free_bad_msg;
+ }
+ if (codec_mode_request && chan_state->dl_ft != ft) {
+ LOGP(DL1C, LOGL_NOTICE, "%s Codec (FT = %d) "
+ " of RTP cannot be changed now, but in "
+ "next frame. trx=%u ts=%u\n",
+ trx_chan_desc[chan].name, ft_codec,
+ l1h->trx->nr, tn);
+ goto free_bad_msg;
+ }
+ chan_state->dl_ft = ft;
+ if (bfi) {
+ LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
+ "AMR frame' trx=%u ts=%u at fn=%u.\n",
+ trx_chan_desc[chan].name,
+ l1h->trx->nr, tn, fn);
+ goto free_bad_msg;
+ }
+ break;
+ default:
+inval_mode2:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please "
+ "fix!\n");
+ goto free_bad_msg;
+ }
+ if (len < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR "
+ "payload\n");
+ goto free_bad_msg;
+ }
+ if (msgb_l2len(msg_tch) != len) {
+ LOGP(DL1C, LOGL_ERROR, "Cannot send payload with "
+ "invalid length! (expecing %d, received %d)\n",
+ len, msgb_l2len(msg_tch));
+free_bad_msg:
+ /* free message */
+ msgb_free(msg_tch);
+ msg_tch = NULL;
+ goto send_frame;
+ }
+ }
+
+send_frame:
+ *_msg_tch = msg_tch;
+ *_msg_facch = msg_facch;
+}
+
+static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ struct msgb *msg_tch = NULL, *msg_facch = NULL;
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ uint8_t tch_mode = chan_state->tch_mode;
+ ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
+ static ubit_t bits[148];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch,
+ (((fn + 4) % 26) >> 2) & 1);
+
+ /* alloc burst memory, if not already,
+ * otherwise shift buffer by 4 bursts for interleaving */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ if (!*bursts_p)
+ return NULL;
+ } else {
+ memcpy(*bursts_p, *bursts_p + 464, 464);
+ memset(*bursts_p + 464, 0, 464);
+ }
+
+ /* no message at all */
+ if (!msg_tch && !msg_facch) {
+ LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+ goto send_burst;
+ }
+
+ /* encode bursts (priorize FACCH) */
+ if (msg_facch)
+ tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
+ 1);
+ else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ /* the first FN 4,13,21 defines that CMI is included in frame,
+ * the first FN 0,8,17 defines that CMR is included in frame.
+ */
+ tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
+ msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1,
+ chan_state->codec, chan_state->codecs,
+ chan_state->dl_ft,
+ chan_state->dl_cmr);
+ else
+ tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1);
+
+ /* free message */
+ if (msg_tch)
+ msgb_free(msg_tch);
+ if (msg_facch)
+ msgb_free(msg_facch);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ return bits;
+}
+
+static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid)
+{
+ struct msgb *msg_tch = NULL, *msg_facch = NULL;
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ uint8_t tch_mode = chan_state->tch_mode;
+ ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
+ static ubit_t bits[148];
+
+ /* send burst, if we already got a frame */
+ if (bid > 0) {
+ if (!*bursts_p)
+ return NULL;
+ goto send_burst;
+ }
+
+ /* get TCH and/or FACCH */
+ tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch,
+ (((fn + 4) % 26) >> 2) & 1);
+
+ /* check for FACCH alignment */
+ if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) {
+ LOGP(DL1C, LOGL_ERROR, "%s Cannot transmit FACCH starting on "
+ "even frames, please fix RTS!\n",
+ trx_chan_desc[chan].name);
+ msgb_free(msg_facch);
+ msg_facch = NULL;
+ }
+
+ /* alloc burst memory, if not already,
+ * otherwise shift buffer by 2 bursts for interleaving */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ if (!*bursts_p)
+ return NULL;
+ } else {
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ if (chan_state->dl_ongoing_facch) {
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+ memset(*bursts_p + 464, 0, 232);
+ } else {
+ memset(*bursts_p + 232, 0, 232);
+ }
+ }
+
+ /* no message at all */
+ if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
+ LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
+ "trx=%u ts=%u at fn=%u to transmit.\n",
+ trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+ goto send_burst;
+ }
+
+ /* encode bursts (priorize FACCH) */
+ if (msg_facch) {
+ tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
+ chan_state->dl_ongoing_facch = 1; /* first of two tch frames */
+ } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */
+ chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
+ else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ /* the first FN 4,13,21 or 5,14,22 defines that CMI is included
+ * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
+ * included in frame. */
+ tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
+ msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1,
+ chan_state->codec, chan_state->codecs,
+ chan_state->dl_ft,
+ chan_state->dl_cmr);
+ else
+ tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch));
+
+ /* free message */
+ if (msg_tch)
+ msgb_free(msg_tch);
+ if (msg_facch)
+ msgb_free(msg_facch);
+
+send_burst:
+ /* compose burst */
+ burst = *bursts_p + bid * 116;
+ memset(bits, 0, 3);
+ memcpy(bits + 3, burst, 58);
+ memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+ memcpy(bits + 87, burst + 58, 58);
+ memset(bits + 145, 0, 3);
+
+ LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ return bits;
+}
+
+
+/*
+ * RX on uplink (indication to upper layer)
+ */
+
+static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa)
+{
+ uint8_t chan_nr;
+ struct osmo_phsap_prim l1sap;
+ uint8_t ra;
+ int rc;
+
+ chan_nr = trx_chan_desc[chan].chan_nr | tn;
+
+ LOGP(DL1C, LOGL_NOTICE, "Received Access Burst on %s fn=%u toa=%.2f\n",
+ trx_chan_desc[chan].name, fn, toa);
+
+ /* decode */
+ rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic);
+ if (rc) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u "
+ "(%u/51)\n", fn, fn % 51);
+ return 0;
+ }
+
+ /* compose primitive */
+ /* generate prim */
+ memset(&l1sap, 0, sizeof(l1sap));
+ osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION,
+ NULL);
+ l1sap.u.rach_ind.chan_nr = chan_nr;
+ l1sap.u.rach_ind.ra = ra;
+#ifdef TA_TEST
+#warning TIMING ADVANCE TEST-HACK IS ENABLED!!!
+ toa *= 10;
+#endif
+ l1sap.u.rach_ind.acc_delay = (toa >= 0) ? toa : 0;
+ l1sap.u.rach_ind.fn = fn;
+
+ /* forward primitive */
+ l1sap_up(l1h->trx, &l1sap);
+
+ return 0;
+}
+
+static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa)
+{
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint32_t *first_fn = &chan_state->ul_first_fn;
+ uint8_t *mask = &chan_state->ul_mask;
+ float *rssi_sum = &chan_state->rssi_sum;
+ uint8_t *rssi_num = &chan_state->rssi_num;
+ float *toa_sum = &chan_state->toa_sum;
+ uint8_t *toa_num = &chan_state->toa_num;
+ uint8_t l2[23], l2_len;
+ int n_errors, n_bits_total;
+ int rc;
+
+ /* handle rach, if handover rach detection is turned on */
+ if (chan_state->ho_rach_detect == 1)
+ return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+
+ LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst & store frame number of first burst */
+ if (bid == 0) {
+ memset(*bursts_p, 0, 464);
+ *mask = 0x0;
+ *first_fn = fn;
+ *rssi_sum = 0;
+ *rssi_num = 0;
+ *toa_sum = 0;
+ *toa_num = 0;
+ }
+
+ /* update mask + rssi */
+ *mask |= (1 << bid);
+ *rssi_sum += rssi;
+ (*rssi_num)++;
+ *toa_sum += toa;
+ (*toa_num)++;
+
+ /* copy burst to buffer of 4 bursts */
+ burst = *bursts_p + bid * 116;
+ memcpy(burst, bits + 3, 58);
+ memcpy(burst + 58, bits + 87, 58);
+
+ /* send burst information to loops process */
+ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
+ trx_loop_sacch_input(l1h, trx_chan_desc[chan].chan_nr | tn,
+ chan_state, rssi, toa);
+ }
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP(DL1C, LOGL_NOTICE, "Received incomplete data frame at "
+ "fn=%u (%u/%u) for %s\n", *first_fn,
+ (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn],
+ trx_chan_desc[chan].name);
+
+ /* we require first burst to have correct FN */
+ if (!(*mask & 0x1)) {
+ *mask = 0x0;
+ return 0;
+ }
+ }
+ *mask = 0x0;
+
+ /* decode */
+ rc = xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total);
+ if (rc) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u "
+ "(%u/%u) for %s\n", *first_fn,
+ (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn],
+ trx_chan_desc[chan].name);
+ l2_len = 0;
+ } else
+ l2_len = 23;
+
+ /* Send uplnk measurement information to L2 */
+ l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+ n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num);
+
+ return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num);
+}
+
+static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa)
+{
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint8_t *mask = &chan_state->ul_mask;
+ float *rssi_sum = &chan_state->rssi_sum;
+ uint8_t *rssi_num = &chan_state->rssi_num;
+ float *toa_sum = &chan_state->toa_sum;
+ uint8_t *toa_num = &chan_state->toa_num;
+ uint8_t l2[54+1];
+ int n_errors, n_bits_total;
+ int rc;
+
+ LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p, 0, 464);
+ *mask = 0x0;
+ *rssi_sum = 0;
+ *rssi_num = 0;
+ *toa_sum = 0;
+ *toa_num = 0;
+ }
+
+ /* update mask + rssi */
+ *mask |= (1 << bid);
+ *rssi_sum += rssi;
+ (*rssi_num)++;
+ *toa_sum += toa;
+ (*toa_num)++;
+
+ /* copy burst to buffer of 4 bursts */
+ burst = *bursts_p + bid * 116;
+ memcpy(burst, bits + 3, 58);
+ memcpy(burst + 58, bits + 87, 58);
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block "
+ "ending at fn=%u (%u/%u) for %s\n", fn,
+ fn % l1h->mf_period[tn], l1h->mf_period[tn],
+ trx_chan_desc[chan].name);
+ }
+ *mask = 0x0;
+
+ /* decode */
+ rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total);
+
+ /* Send uplnk measurement information to L2 */
+ l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+ n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num);
+
+ if (rc <= 0) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block ending at "
+ "fn=%u (%u/%u) for %s\n", fn, fn % l1h->mf_period[tn],
+ l1h->mf_period[tn], trx_chan_desc[chan].name);
+ return 0;
+ }
+
+ l2[0] = 7; /* valid frame */
+
+ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan,
+ l2, rc + 1, *rssi_sum / *rssi_num);
+}
+
+static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa)
+{
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint8_t *mask = &chan_state->ul_mask;
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ uint8_t tch_data[128]; /* just to be safe */
+ int rc, amr = 0;
+ int n_errors, n_bits_total;
+
+ /* handle rach, if handover rach detection is turned on */
+ if (chan_state->ho_rach_detect == 1)
+ return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+
+ LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p + 464, 0, 464);
+ *mask = 0x0;
+ }
+
+ /* update mask */
+ *mask |= (1 << bid);
+
+ /* copy burst to end of buffer of 8 bursts */
+ burst = *bursts_p + bid * 116 + 464;
+ memcpy(burst, bits + 3, 58);
+ memcpy(burst + 58, bits + 87, 58);
+
+ /* wait until complete set of bursts */
+ if (bid != 3)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0xf) != 0xf) {
+ LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending "
+ "at fn=%u (%u/%u) for %s\n", fn,
+ fn % l1h->mf_period[tn], l1h->mf_period[tn],
+ trx_chan_desc[chan].name);
+ }
+ *mask = 0x0;
+
+ /* decode
+ * also shift buffer by 4 bursts for interleaving */
+ switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
+ : tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ rc = tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ rc = tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* the first FN 0,8,17 defines that CMI is included in frame,
+ * the first FN 4,13,21 defines that CMR is included in frame.
+ * NOTE: A frame ends 7 FN after start.
+ */
+ rc = tch_afs_decode(tch_data + 2, *bursts_p,
+ (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
+ chan_state->codecs, &chan_state->ul_ft,
+ &chan_state->ul_cmr, &n_errors, &n_bits_total);
+ if (rc)
+ trx_loop_amr_input(l1h,
+ trx_chan_desc[chan].chan_nr | tn, chan_state,
+ (float)n_errors/(float)n_bits_total);
+ amr = 2; /* we store tch_data + 2 header bytes */
+ /* only good speech frames get rtp header */
+ if (rc != 23 && rc >= 4) {
+ rc = amr_compose_payload(tch_data,
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft], 0);
+ }
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n",
+ tch_mode);
+ return -EINVAL;
+ }
+ memcpy(*bursts_p, *bursts_p + 464, 464);
+
+ /* Send uplnk measurement information to L2 */
+ l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
+ n_errors, n_bits_total, rssi, toa);
+
+ /* Check if the frame is bad */
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
+ "fn=%u for %s\n", fn, trx_chan_desc[chan].name);
+ goto bfi;
+ }
+ if (rc < 4) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
+ "fn=%u for %s with codec mode %d (out of range)\n",
+ fn, trx_chan_desc[chan].name, rc);
+ goto bfi;
+ }
+
+ /* FACCH */
+ if (rc == 23) {
+ compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan,
+ tch_data + amr, 23, rssi);
+bfi:
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
+ /* indicate bad frame */
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ memset(tch_data, 0, 33);
+ rc = 33;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ memset(tch_data, 0, 31);
+ rc = 31;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ rc = amr_compose_payload(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft],
+ 1);
+ if (rc < 2)
+ break;
+ memset(tch_data + 2, 0, rc - 2);
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, "
+ "please fix!\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
+ return 0;
+
+ /* TCH or BFI */
+ return compose_tch_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan,
+ tch_data, rc);
+}
+
+static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
+ float toa)
+{
+ struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+ sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
+ uint8_t *mask = &chan_state->ul_mask;
+ uint8_t rsl_cmode = chan_state->rsl_cmode;
+ uint8_t tch_mode = chan_state->tch_mode;
+ uint8_t tch_data[128]; /* just to be safe */
+ int rc, amr = 0;
+ int n_errors, n_bits_total;
+
+ /* handle rach, if handover rach detection is turned on */
+ if (chan_state->ho_rach_detect == 1)
+ return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+
+ LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n",
+ trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+
+ /* alloc burst memory, if not already */
+ if (!*bursts_p) {
+ *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
+ if (!*bursts_p)
+ return -ENOMEM;
+ }
+
+ /* clear burst */
+ if (bid == 0) {
+ memset(*bursts_p + 464, 0, 232);
+ *mask = 0x0;
+ }
+
+ /* update mask */
+ *mask |= (1 << bid);
+
+ /* copy burst to end of buffer of 6 bursts */
+ burst = *bursts_p + bid * 116 + 464;
+ memcpy(burst, bits + 3, 58);
+ memcpy(burst + 58, bits + 87, 58);
+
+ /* wait until complete set of bursts */
+ if (bid != 1)
+ return 0;
+
+ /* check for complete set of bursts */
+ if ((*mask & 0x3) != 0x3) {
+ LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending "
+ "at fn=%u (%u/%u) for %s\n", fn,
+ fn % l1h->mf_period[tn], l1h->mf_period[tn],
+ trx_chan_desc[chan].name);
+ }
+ *mask = 0x0;
+
+ /* skip second of two TCH frames of FACCH was received */
+ if (chan_state->ul_ongoing_facch) {
+ chan_state->ul_ongoing_facch = 0;
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+ goto bfi;
+ }
+
+ /* decode
+ * also shift buffer by 4 bursts for interleaving */
+ switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
+ : tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* HR or signalling */
+ /* Note on FN-10: If we are at FN 10, we decoded an even aligned
+ * TCH/FACCH frame, because our burst buffer carries 6 bursts.
+ * Even FN ending at: 10,11,19,20,2,3
+ */
+ rc = tch_hr_decode(tch_data, *bursts_p,
+ (((fn + 26 - 10) % 26) >> 2) & 1,
+ &n_errors, &n_bits_total);
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
+ * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR
+ * is included in frame.
+ */
+ rc = tch_ahs_decode(tch_data + 2, *bursts_p,
+ (((fn + 26 - 10) % 26) >> 2) & 1,
+ (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec,
+ chan_state->codecs, &chan_state->ul_ft,
+ &chan_state->ul_cmr, &n_errors, &n_bits_total);
+ if (rc)
+ trx_loop_amr_input(l1h,
+ trx_chan_desc[chan].chan_nr | tn, chan_state,
+ (float)n_errors/(float)n_bits_total);
+ amr = 2; /* we store tch_data + 2 two */
+ /* only good speech frames get rtp header */
+ if (rc != 23 && rc >= 4) {
+ rc = amr_compose_payload(tch_data,
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft], 0);
+ }
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n",
+ tch_mode);
+ return -EINVAL;
+ }
+ memcpy(*bursts_p, *bursts_p + 232, 232);
+ memcpy(*bursts_p + 232, *bursts_p + 464, 232);
+
+ /* Send uplnk measurement information to L2 */
+ l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
+ n_errors, n_bits_total, rssi, toa);
+
+ /* Check if the frame is bad */
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
+ "fn=%u for %s\n", fn, trx_chan_desc[chan].name);
+ goto bfi;
+ }
+ if (rc < 4) {
+ LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
+ "fn=%u for %s with codec mode %d (out of range)\n",
+ fn, trx_chan_desc[chan].name, rc);
+ goto bfi;
+ }
+
+ /* FACCH */
+ if (rc == 23) {
+ chan_state->ul_ongoing_facch = 1;
+ compose_ph_data_ind(l1h, tn,
+ (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan,
+ tch_data + amr, 23, rssi);
+bfi:
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
+ /* indicate bad frame */
+ switch (tch_mode) {
+ case GSM48_CMODE_SPEECH_V1: /* HR */
+ tch_data[0] = 0x70; /* F = 0, FT = 111 */
+ memset(tch_data + 1, 0, 14);
+ rc = 15;
+ break;
+ case GSM48_CMODE_SPEECH_AMR: /* AMR */
+ rc = amr_compose_payload(tch_data,
+ chan_state->codec[chan_state->dl_cmr],
+ chan_state->codec[chan_state->dl_ft],
+ 1);
+ if (rc < 2)
+ break;
+ memset(tch_data + 2, 0, rc - 2);
+ break;
+ default:
+ LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, "
+ "please fix!\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (rsl_cmode != RSL_CMOD_SPD_SPEECH)
+ return 0;
+
+ /* TCH or BFI */
+ /* Note on FN 19 or 20: If we received the last burst of a frame,
+ * it actually starts at FN 8 or 9. A burst starting there, overlaps
+ * with the slot 12, so an extra FN must be substracted to get correct
+ * start of frame.
+ */
+ return compose_tch_ind(l1h, tn,
+ (fn + 2715648 - 10 - ((fn%26)==19) - ((fn%26)==20)) % 2715648,
+ chan, tch_data, rc);
+}
+
+
+/*
+ * multiframe structure
+ */
+
+/* frame structures */
+struct trx_sched_frame {
+ enum trx_chan_type dl_chan;
+ uint8_t dl_bid;
+ enum trx_chan_type ul_chan;
+ uint8_t ul_bid;
+};
+
+static struct trx_sched_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 },
+};
+
+static struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 struct trx_sched_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 },
+};
+
+/* multiframe structure */
+struct trx_sched_multiframe {
+ enum gsm_phys_chan_config pchan;
+ uint8_t slotmask;
+ uint8_t period;
+ struct trx_sched_frame *frames;
+ const char *name;
+};
+
+static struct trx_sched_multiframe trx_sched_multiframes[] = {
+ { GSM_PCHAN_NONE, 0xff, 0, NULL, "NONE"},
+ { GSM_PCHAN_CCCH, 0xff, 51, frame_bcch, "BCCH+CCCH" },
+ { GSM_PCHAN_CCCH_SDCCH4, 0xff, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" },
+ { GSM_PCHAN_SDCCH8_SACCH8C, 0xff, 102, frame_sdcch8, "SDCCH/8+SACCH/8" },
+ { GSM_PCHAN_TCH_F, 0x01, 104, frame_tchf_ts0, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x02, 104, frame_tchf_ts1, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x04, 104, frame_tchf_ts2, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x08, 104, frame_tchf_ts3, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x10, 104, frame_tchf_ts4, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x20, 104, frame_tchf_ts5, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x40, 104, frame_tchf_ts6, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_F, 0x80, 104, frame_tchf_ts7, "TCH/F+SACCH" },
+ { GSM_PCHAN_TCH_H, 0x03, 104, frame_tchh_ts01, "TCH/H+SACCH" },
+ { GSM_PCHAN_TCH_H, 0x0c, 104, frame_tchh_ts23, "TCH/H+SACCH" },
+ { GSM_PCHAN_TCH_H, 0x30, 104, frame_tchh_ts45, "TCH/H+SACCH" },
+ { GSM_PCHAN_TCH_H, 0xc0, 104, frame_tchh_ts67, "TCH/H+SACCH" },
+ { GSM_PCHAN_PDCH, 0xff, 104, frame_pdch, "PDCH" },
+};
+
+
+/*
+ * scheduler functions
+ */
+
+/* set multiframe scheduler to given pchan */
+int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
+ enum gsm_phys_chan_config pchan)
+{
+ int i;
+
+ /* ignore disabled slots */
+ if (!(l1h->config.slotmask & (1 << tn)))
+ return -ENOTSUP;
+
+ for (i = 0; i < ARRAY_SIZE(trx_sched_multiframes); i++) {
+ if (trx_sched_multiframes[i].pchan == pchan
+ && (trx_sched_multiframes[i].slotmask & (1 << tn))) {
+ l1h->mf_index[tn] = i;
+ l1h->mf_period[tn] = trx_sched_multiframes[i].period;
+ l1h->mf_frames[tn] = trx_sched_multiframes[i].frames;
+ LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with "
+ "%s trx=%d ts=%d\n",
+ trx_sched_multiframes[i].name,
+ l1h->trx->nr, tn);
+ return 0;
+ }
+ }
+
+ LOGP(DL1C, LOGL_NOTICE, "Failed to configuring multiframe "
+ "trx=%d ts=%d\n", l1h->trx->nr, tn);
+
+ return -ENOTSUP;
+}
+
+/* setting all logical channels given attributes to active/inactive */
+int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
+ int active)
+{
+ uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ uint8_t ss = l1sap_chan2ss(chan_nr);
+ int i;
+ int rc = -EINVAL;
+ struct trx_chan_state *chan_state;
+
+ /* look for all matching chan_nr/link_id */
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ /* skip if pchan type does not match pdch flag */
+ if ((trx_sched_multiframes[l1h->mf_index[tn]].pchan
+ == GSM_PCHAN_PDCH)
+ != trx_chan_desc[i].pdch)
+ continue;
+ if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
+ && trx_chan_desc[i].link_id == link_id) {
+ chan_state = &l1h->chan_states[tn][i];
+ rc = 0;
+ if (chan_state->active == active)
+ continue;
+ LOGP(DL1C, LOGL_NOTICE, "%s %s on trx=%d ts=%d\n",
+ (active) ? "Activating" : "Deactivating",
+ trx_chan_desc[i].name, l1h->trx->nr, tn);
+ if (active)
+ memset(chan_state, 0, sizeof(*chan_state));
+ chan_state->active = active;
+ /* free burst memory, to cleanly start with burst 0 */
+ if (chan_state->dl_bursts) {
+ talloc_free(chan_state->dl_bursts);
+ chan_state->dl_bursts = NULL;
+ }
+ if (chan_state->ul_bursts) {
+ talloc_free(chan_state->ul_bursts);
+ chan_state->ul_bursts = NULL;
+ }
+ }
+ }
+
+ /* disable handover detection (on deactivation) */
+ if (l1h->ho_rach_detect[tn][ss]) {
+ l1h->ho_rach_detect[tn][ss] = 0;
+ trx_if_cmd_nohandover(l1h, tn, ss);
+ }
+
+ return rc;
+}
+
+/* setting all logical channels given attributes to active/inactive */
+int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
+ uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
+ uint8_t codec2, uint8_t codec3, uint8_t initial_id, uint8_t handover)
+{
+ uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ uint8_t ss = l1sap_chan2ss(chan_nr);
+ int i;
+ int rc = -EINVAL;
+ struct trx_chan_state *chan_state;
+
+ /* no mode for PDCH */
+ if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+ return 0;
+
+ /* look for all matching chan_nr/link_id */
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
+ && trx_chan_desc[i].link_id == 0x00) {
+ chan_state = &l1h->chan_states[tn][i];
+ LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u, handover %u "
+ "on %s of trx=%d ts=%d\n", rsl_cmode, tch_mode,
+ handover, trx_chan_desc[i].name, l1h->trx->nr,
+ tn);
+ chan_state->rsl_cmode = rsl_cmode;
+ chan_state->tch_mode = tch_mode;
+ chan_state->ho_rach_detect = handover;
+ if (rsl_cmode == RSL_CMOD_SPD_SPEECH
+ && tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ chan_state->codecs = codecs;
+ chan_state->codec[0] = codec0;
+ chan_state->codec[1] = codec1;
+ chan_state->codec[2] = codec2;
+ chan_state->codec[3] = codec3;
+ chan_state->ul_ft = initial_id;
+ chan_state->dl_ft = initial_id;
+ chan_state->ul_cmr = initial_id;
+ chan_state->dl_cmr = initial_id;
+ chan_state->ber_sum = 0;
+ chan_state->ber_num = 0;
+ }
+ rc = 0;
+ }
+ }
+
+ /* command rach detection
+ * always enable handover, even if state is still set (due to loss
+ * of transceiver link).
+ * disable handover, if state is still set, since we might not know
+ * the actual state of transceiver (due to loss of link) */
+ if (handover) {
+ l1h->ho_rach_detect[tn][ss] = 1;
+ trx_if_cmd_handover(l1h, tn, ss);
+ } else if (l1h->ho_rach_detect[tn][ss]) {
+ l1h->ho_rach_detect[tn][ss] = 0;
+ trx_if_cmd_nohandover(l1h, tn, ss);
+ }
+
+ return rc;
+}
+
+/* setting cipher on logical channels */
+int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
+ int algo, uint8_t *key, int key_len)
+{
+ uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ int i;
+ int rc = -EINVAL;
+ struct trx_chan_state *chan_state;
+
+ /* no cipher for PDCH */
+ if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+ return 0;
+
+ /* no algorithm given means a5/0 */
+ if (algo <= 0)
+ algo = 0;
+ else if (key_len != 8) {
+ LOGP(DL1C, LOGL_ERROR, "Algo A5/%d not supported with given "
+ "key len=%d\n", algo, key_len);
+ return -ENOTSUP;
+ }
+
+ /* look for all matching chan_nr */
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ /* skip if pchan type */
+ if (trx_chan_desc[i].pdch)
+ continue;
+ if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)) {
+ chan_state = &l1h->chan_states[tn][i];
+ LOGP(DL1C, LOGL_NOTICE, "Set a5/%d %s for %s on trx=%d "
+ "ts=%d\n", algo,
+ (downlink) ? "downlink" : "uplink",
+ trx_chan_desc[i].name, l1h->trx->nr, tn);
+ if (downlink) {
+ chan_state->dl_encr_algo = algo;
+ memcpy(chan_state->dl_encr_key, key, key_len);
+ chan_state->dl_encr_key_len = key_len;
+ } else {
+ chan_state->ul_encr_algo = algo;
+ memcpy(chan_state->ul_encr_key, key, key_len);
+ chan_state->ul_encr_key_len = key_len;
+ }
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+/* process ready-to-send */
+static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn)
+{
+ struct trx_sched_frame *frame;
+ uint8_t offset, period, bid;
+ trx_sched_rts_func *func;
+ enum trx_chan_type chan;
+
+ /* no multiframe set */
+ if (!l1h->mf_index[tn])
+ return 0;
+
+ /* get frame from multiframe */
+ period = l1h->mf_period[tn];
+ offset = fn % period;
+ frame = l1h->mf_frames[tn] + offset;
+
+ chan = frame->dl_chan;
+ bid = frame->dl_bid;
+ func = trx_chan_desc[frame->dl_chan].rts_fn;
+
+ /* only on bid == 0 */
+ if (bid != 0)
+ return 0;
+
+ /* no RTS function */
+ if (!func)
+ return 0;
+
+ /* check if channel is active */
+ if (!trx_chan_desc[chan].auto_active
+ && !l1h->chan_states[tn][chan].active)
+ return -EINVAL;
+
+ return func(l1h, tn, fn, frame->dl_chan);
+}
+
+/* process downlink burst */
+static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn,
+ uint32_t fn)
+{
+ struct trx_sched_frame *frame;
+ uint8_t offset, period, bid;
+ trx_sched_dl_func *func;
+ enum trx_chan_type chan;
+ ubit_t *bits = NULL;
+
+ if (!l1h->mf_index[tn])
+ goto no_data;
+
+ /* get frame from multiframe */
+ period = l1h->mf_period[tn];
+ offset = fn % period;
+ frame = l1h->mf_frames[tn] + offset;
+
+ chan = frame->dl_chan;
+ bid = frame->dl_bid;
+ func = trx_chan_desc[chan].dl_fn;
+
+ /* check if channel is active */
+ if (!trx_chan_desc[chan].auto_active
+ && !l1h->chan_states[tn][chan].active)
+ goto no_data;
+
+ /* get burst from function */
+ bits = func(l1h, tn, fn, chan, bid);
+
+ /* encrypt */
+ if (bits && l1h->chan_states[tn][chan].dl_encr_algo) {
+ ubit_t ks[114];
+ int i;
+
+ osmo_a5(l1h->chan_states[tn][chan].dl_encr_algo,
+ l1h->chan_states[tn][chan].dl_encr_key, fn, ks, NULL);
+ for (i = 0; i < 57; i++) {
+ bits[i + 3] ^= ks[i];
+ bits[i + 88] ^= ks[i + 57];
+ }
+ }
+
+no_data:
+ /* in case of C0, we need a dummy burst to maintain RF power */
+ if (bits == NULL && l1h->trx == l1h->trx->bts->c0) {
+if (0) if (chan != TRXC_IDLE) // hack
+ LOGP(DL1C, LOGL_DEBUG, "No burst data for %s fn=%u ts=%u "
+ "burst=%d on C0, so filling with dummy burst\n",
+ trx_chan_desc[chan].name, fn, tn, bid);
+ bits = dummy_burst;
+ }
+
+ return bits;
+}
+
+/* process uplink burst */
+int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
+ sbit_t *bits, int8_t rssi, float toa)
+{
+ struct trx_sched_frame *frame;
+ uint8_t offset, period, bid;
+ trx_sched_ul_func *func;
+ enum trx_chan_type chan;
+ uint32_t fn, elapsed;
+
+ if (!l1h->mf_index[tn])
+ return -EINVAL;
+
+ /* calculate how many frames have been elapsed */
+ elapsed = (current_fn + 2715648 - l1h->mf_last_fn[tn]) % 2715648;
+
+ /* start counting from last fn + 1, but only if not too many fn have
+ * been elapsed */
+ if (elapsed < 10)
+ fn = (l1h->mf_last_fn[tn] + 1) % 2715648;
+ else
+ fn = current_fn;
+
+ while (42) {
+ /* get frame from multiframe */
+ period = l1h->mf_period[tn];
+ offset = fn % period;
+ frame = l1h->mf_frames[tn] + offset;
+
+ chan = frame->ul_chan;
+ bid = frame->ul_bid;
+ func = trx_chan_desc[chan].ul_fn;
+
+ /* check if channel is active */
+ if (!trx_chan_desc[chan].auto_active
+ && !l1h->chan_states[tn][chan].active)
+ goto next_frame;
+
+ /* omit bursts which have no handler, like IDLE bursts */
+ if (!func)
+ goto next_frame;
+
+ /* put burst to function */
+ if (fn == current_fn) {
+ /* decrypt */
+ if (bits && l1h->chan_states[tn][chan].ul_encr_algo) {
+ ubit_t ks[114];
+ int i;
+
+ osmo_a5(l1h->chan_states[tn][chan].ul_encr_algo,
+ l1h->chan_states[tn][chan].ul_encr_key,
+ fn, NULL, ks);
+ for (i = 0; i < 57; i++) {
+ if (ks[i])
+ bits[i + 3] = - bits[i + 3];
+ if (ks[i + 57])
+ bits[i + 88] = - bits[i + 88];
+ }
+ }
+
+ func(l1h, tn, fn, chan, bid, bits, rssi, toa);
+ } else if (chan != TRXC_RACH
+ && !l1h->chan_states[tn][chan].ho_rach_detect) {
+ sbit_t spare[148];
+
+ memset(spare, 0, 148);
+ func(l1h, tn, fn, chan, bid, spare, -128, 0);
+ }
+
+next_frame:
+ /* reached current fn */
+ if (fn == current_fn)
+ break;
+
+ fn = (fn + 1) % 2715648;
+ }
+
+ l1h->mf_last_fn[tn] = fn;
+
+ return 0;
+}
+
+/* schedule all frames of all TRX for given FN */
+static int trx_sched_fn(uint32_t fn)
+{
+ struct gsm_bts_trx *trx;
+ struct trx_l1h *l1h;
+ uint8_t tn;
+ const ubit_t *bits;
+ uint8_t gain;
+
+ /* send time indication */
+ l1if_mph_time_ind(bts, fn);
+
+ /* advance frame number, so the transceiver has more time until
+ * it must be transmitted. */
+ fn = (fn + trx_clock_advance) % 2715648;
+
+ /* process every TRX */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ l1h = trx_l1h_hdl(trx);
+
+ /* we don't schedule, if power is off */
+ if (!l1h->config.poweron)
+ continue;
+
+ /* process every TS of TRX */
+ for (tn = 0; tn < 8; tn++) {
+ /* ignore disabled slots */
+ if (!(l1h->config.slotmask & (1 << tn)))
+ continue;
+ /* ready-to-send */
+ trx_sched_rts(l1h, tn,
+ (fn + trx_rts_advance) % 2715648);
+ /* get burst for FN */
+ bits = trx_sched_dl_burst(l1h, tn, fn);
+ if (!bits) {
+#if 0
+ /* if no bits, send dummy burst with no gain */
+ bits = dummy_burst;
+ gain = 128;
+#else
+ /* if no bits, send no burst */
+ continue;
+#endif
+ } else
+ gain = 0;
+ trx_if_data(l1h, tn, fn, gain, bits);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * frame clock
+ */
+
+#define FRAME_DURATION_uS 4615
+#define MAX_FN_SKEW 50
+#define TRX_LOSS_FRAMES 400
+
+extern int quit;
+/* this timer fires for every FN to be processed */
+static void trx_ctrl_timer_cb(void *data)
+{
+ struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
+ int32_t elapsed;
+
+ /* check if transceiver is still alive */
+ if (transceiver_lost++ == TRX_LOSS_FRAMES) {
+ struct gsm_bts_trx *trx;
+
+ LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n");
+
+no_clock:
+ transceiver_available = 0;
+
+ /* flush pending messages of transceiver */
+ /* close all logical channels and reset timeslots */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ trx_if_flush(trx_l1h_hdl(trx));
+ trx_sched_reset(trx_l1h_hdl(trx));
+ if (trx->nr == 0)
+ trx_if_cmd_poweroff(trx_l1h_hdl(trx));
+ }
+
+ /* tell BSC */
+ check_transceiver_availability(bts, 0);
+
+ return;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
+
+ /* if someone played with clock, or if the process stalled */
+ if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) {
+ LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n",
+ elapsed);
+ goto no_clock;
+ }
+
+ /* schedule next FN clock */
+ while (elapsed > FRAME_DURATION_uS / 2) {
+ tv_clock->tv_usec += FRAME_DURATION_uS;
+ if (tv_clock->tv_usec >= 1000000) {
+ tv_clock->tv_sec++;
+ tv_clock->tv_usec -= 1000000;
+ }
+ transceiver_last_fn = (transceiver_last_fn + 1) % 2715648;
+ trx_sched_fn(transceiver_last_fn);
+ elapsed -= FRAME_DURATION_uS;
+ }
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS - elapsed);
+}
+
+
+/* receive clock from transceiver */
+int trx_sched_clock(uint32_t fn)
+{
+ struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
+ int32_t elapsed;
+ int32_t elapsed_fn;
+
+ if (quit)
+ return 0;
+
+ /* reset lost counter */
+ transceiver_lost = 0;
+
+ gettimeofday(&tv_now, NULL);
+
+ /* clock becomes valid */
+ if (!transceiver_available) {
+ LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n",
+ fn);
+
+ transceiver_available = 1;
+
+ /* start provisioning transceiver */
+ l1if_provision_transceiver(bts);
+
+ /* tell BSC */
+ check_transceiver_availability(bts, 1);
+
+new_clock:
+ transceiver_last_fn = fn;
+ trx_sched_fn(transceiver_last_fn);
+
+ /* schedule first FN clock */
+ memcpy(tv_clock, &tv_now, sizeof(struct timeval));
+ memset(&transceiver_clock_timer, 0,
+ sizeof(transceiver_clock_timer));
+ transceiver_clock_timer.cb = trx_ctrl_timer_cb;
+ transceiver_clock_timer.data = bts;
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS);
+
+ return 0;
+ }
+
+ osmo_timer_del(&transceiver_clock_timer);
+
+ /* calculate elapsed time since last_fn */
+ elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
+
+ /* how much frames have been elapsed since last fn processed */
+ elapsed_fn = (fn + 2715648 - transceiver_last_fn) % 2715648;
+ if (elapsed_fn >= 135774)
+ elapsed_fn -= 2715648;
+
+ /* check for max clock skew */
+ if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
+ LOGP(DL1C, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
+ "new fn=%u\n", transceiver_last_fn, fn);
+ goto new_clock;
+ }
+
+ LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n",
+ elapsed_fn * FRAME_DURATION_uS - elapsed);
+
+ /* too many frames have been processed already */
+ if (elapsed_fn < 0) {
+ /* set clock to the time or last FN should have been
+ * transmitted. */
+ tv_clock->tv_sec = tv_now.tv_sec;
+ tv_clock->tv_usec = tv_now.tv_usec +
+ (0 - elapsed_fn) * FRAME_DURATION_uS;
+ if (tv_clock->tv_usec >= 1000000) {
+ tv_clock->tv_sec++;
+ tv_clock->tv_usec -= 1000000;
+ }
+ /* set time to the time our next FN has to be transmitted */
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS * (1 - elapsed_fn));
+
+ return 0;
+ }
+
+ /* transmit what we still need to transmit */
+ while (fn != transceiver_last_fn) {
+ transceiver_last_fn = (transceiver_last_fn + 1) % 2715648;
+ trx_sched_fn(transceiver_last_fn);
+ }
+
+ /* schedule next FN to be transmitted */
+ memcpy(tv_clock, &tv_now, sizeof(struct timeval));
+ osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS);
+
+ return 0;
+}
+