From acc71ffb4b61b3354bbb2fa14981e4e6a46946e6 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 11:45:28 +0100 Subject: TRX: Introduce osmobts-trx, a layer 1 implementation for OpenBTS tranceivers The code is quite complete, TCH and PDCH channels are not yet tested. --- src/osmo-bts-trx/scheduler.c | 1987 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1987 insertions(+) create mode 100644 src/osmo-bts-trx/scheduler.c (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c new file mode 100644 index 00000000..a0caeeca --- /dev/null +++ b/src/osmo-bts-trx/scheduler.c @@ -0,0 +1,1987 @@ +/* Scheduler for OsmoBTS-TRX */ + +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "l1_if.h" +#include "scheduler.h" +#include "xcch.h" +#include "tch_fr.h" +#include "rach.h" +#include "sch.h" +#include "pxxch.h" +#include "trx_if.h" + +void *tall_bts_ctx; + +static struct gsm_bts *bts; + +/* clock states */ +static uint32_t tranceiver_lost; +uint32_t tranceiver_last_fn; +static struct timeval tranceiver_clock_tv; +static struct osmo_timer_list tranceiver_clock_timer; + +/* clock advance for the tranceiver */ +uint32_t trx_clock_advance = 10; + +/* 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 const 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, int16_t toa); + +static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const 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, int16_t 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, int16_t 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, int16_t 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, int16_t 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, int16_t toa); + +static const 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 const 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 { + 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] = { + { TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, + { TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, + { TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, + { TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, + { TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, + { TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, + { TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_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; + for (i = 0; i < _TRX_CHAN_MAX; i++) { + INIT_LLIST_HEAD(&l1h->dl_prims[tn]); + chan_state = &l1h->chan_states[tn][i]; + chan_state->ul_mask = 0x0; + } + } + + 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++) { + for (i = 0; i < _TRX_CHAN_MAX; i++) { + msgb_queue_flush(&l1h->dl_prims[tn]); + 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; + } + } + } +} + + +/* + * 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(); + + 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(); + + 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); + + /* 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); +} + +/* RTS for traffic frame */ +static int rts_tch_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, "TCH.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); + + /* 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 | tn; + l1sap->u.tch.fn = fn; + + l1sap_up(l1h->trx, l1sap); + + /* 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.chan_nr = link_id; + l1sap->u.data.fn = fn; + + return l1sap_up(l1h->trx, l1sap); +} + + +/* + * TX on donlink + */ + +/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ +static const 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 const 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 const 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[2] = + (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 *found = NULL, *msg, *msg2; /* make GCC happy */ + struct osmo_phsap_prim *l1sap = NULL; /* make GCC happy */ + uint32_t check_fn; + + /* get burst from queue */ + llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) { + l1sap = msgb_l1sap_prim(msg); + check_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648); + if (check_fn > 20) { + LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " + "is out of range. (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 (check_fn > 0) + continue; + + /* found second message, check if we have TCH+FACCH */ + if (found) { + /* we found a message earlier */ + l1sap = msgb_l1sap_prim(found); + /* if we have TCH+something */ + if (l1sap->oph.primitive == PRIM_TCH) { + /* unlink and free message */ + llist_del(&found->list); + msgb_free(found); + found = msg; + } else { + l1sap = msgb_l1sap_prim(msg); + /* if we have TCH+something */ + if (l1sap->oph.primitive == PRIM_TCH) { + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + } + } + break; + } + found = msg; + } + + if (!found) { + LOGP(DL1C, LOGL_NOTICE, "%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); + return NULL; + } + + l1sap = msgb_l1sap_prim(found); + + if ((l1sap->oph.primitive != PRIM_PH_DATA + && l1sap->oph.primitive != PRIM_TCH) + || l1sap->oph.operation != PRIM_OP_REQUEST) { + 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(&found->list); + msgb_free(found); + return NULL; + } + if ((l1sap->u.data.chan_nr ^ (trx_chan_desc[chan].chan_nr | tn)) + || ((l1sap->u.data.link_id ^ trx_chan_desc[chan].link_id) & 0x40)) { + 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, l1sap->u.data.chan_nr, + l1sap->u.data.link_id, trx_chan_desc[chan].chan_nr | tn, + trx_chan_desc[chan].link_id); + goto free_msg; + } + + return found; +} + +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); + +static const 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; + +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)); + /* unlink and free message */ + llist_del(&msg->list); + 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].sacch_lost) > 1) + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); + } + + /* 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); + + /* unlink and free message */ + llist_del(&msg->list); + 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 const 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; + +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); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + + /* unlink and free message */ + llist_del(&msg->list); + 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 const 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 = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; + static ubit_t bits[148]; + struct osmo_phsap_prim *l1sap; + + /* 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; + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + l1sap = msgb_l1sap_prim(msg); + if (l1sap->oph.primitive == PRIM_TCH) { + /* check validity of message */ + if (msgb_l2len(msg) != 33) { + LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg)); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + } else { + /* 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)); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + } + + /* 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); + + /* encode bursts */ + tch_fr_encode(*bursts_p, msg->l2h, msgb_l2len(msg)); + + /* unlink and free message */ + llist_del(&msg->list); + 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 const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + // FIXME + return NULL; +} + + + +/* + * 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, int16_t toa) +{ + struct osmo_phsap_prim l1sap; + uint8_t ra; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "Received %s fn=%u\n", + trx_chan_desc[chan].name, fn); + + /* decode */ + rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad rach frame at fn=%u " + "ra=%u\n", fn, ra); + 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.ra = ra; + l1sap.u.rach_ind.acc_delay = 0; //FIXME: TOA + l1sap.u.rach_ind.fn = fn; + + /* forward primitive */ + l1sap_up(l1h->trx, &l1sap); + + return 0; +} + +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) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* 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 = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.data.link_id = trx_chan_desc[chan].link_id; + l1sap->u.data.fn = fn; + 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].sacch_lost = 0; + + /* 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, int16_t 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; + uint8_t l2[23], l2_len; + int rc; + + 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; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if ((*mask & 0xf) != 0xf) + return 0; + *mask = 0x0; + + /* decode */ + rc = xcch_decode(l2, *bursts_p); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u for " + "%s\n", *first_fn, trx_chan_desc[chan].name); + l2_len = 0; + } else + l2_len = 23; + + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len); +} + +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, int16_t 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; + uint8_t l2[54+1]; + 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; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if ((*mask & 0xf) != 0xf) + return 0; + *mask = 0x0; + + /* decode */ + rc = pdch_decode(l2 + 1, *bursts_p, NULL); + if (rc <= 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH frame at fn=%u for " + "%s\n", *first_fn, trx_chan_desc[chan].name); + l2[0] = 0; /* bad frame */ + rc = 0; + } else + l2[0] = 7; /* valid frame */ + + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); +} + +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); + + /* forward primitive */ + l1sap_up(l1h->trx, l1sap); + + return 0; +} + +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, int16_t 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; + uint8_t tch_data[33]; + int rc; + + 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; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* 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); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if (*mask != 0xff) + return 0; + *mask = 0xf0; + + /* decode + * also shift buffer by 4 bursts for interleaving */ + rc = tch_fr_decode(tch_data, *bursts_p); + memcpy(*bursts_p, *bursts_p + 464, 464); + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad tch frame at fn=%u " + "for %s\n", *first_fn, trx_chan_desc[chan].name); + rc = 0; + } + + /* FACCH */ + if (rc == 23) + return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, + 23); + + /* TCH or BFI */ + return compose_tch_ind(l1h, tn, *first_fn, 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, int16_t 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); + + // FIXME + return 0; +} + + +/* + * 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[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_tchh[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_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 period; + struct trx_sched_frame *frames; + const char *name; +}; + +static struct trx_sched_multiframe trx_sched_multiframes[] = { + { GSM_PCHAN_NONE, 0, NULL, "NONE"}, + { GSM_PCHAN_CCCH, 51, frame_bcch, "BCCH+CCCH" }, + { GSM_PCHAN_CCCH_SDCCH4, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" }, + { GSM_PCHAN_SDCCH8_SACCH8C, 102, frame_sdcch8, "SDCCH/8+SACCH/8" }, + { GSM_PCHAN_TCH_F, 104, frame_tchf, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_H, 104, frame_tchh, "TCH/H+SACCH" }, + { GSM_PCHAN_PDCH, 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; ARRAY_SIZE(trx_sched_multiframes); i++) { + if (trx_sched_multiframes[i].pchan == pchan) { + l1h->mf_index[tn] = i; + LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with " + "%s trx=%d ts=%d\n", + trx_sched_multiframes[i].name, + l1h->trx->nr, tn); + return 0; + } + } + + return -EINVAL; +} + +/* 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 downlink, int active) +{ + uint8_t tn = L1SAP_CHAN2TS(chan_nr); + int i; + int rc = -EINVAL; + + /* 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 == link_id) { + LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", + (active) ? "Activating" : "Deactivating", + (downlink) ? "downlink" : "uplink", + trx_chan_desc[i].name, l1h->trx->nr, tn); + if (downlink) { + l1h->chan_states[tn][i].dl_active = active; + l1h->chan_states[tn][i].dl_active = active; + } else { + l1h->chan_states[tn][i].ul_active = active; + l1h->chan_states[tn][i].ul_active = active; + } + l1h->chan_states[tn][i].sacch_lost = 0; + 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 = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + 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].dl_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; + const ubit_t *bits = NULL; + + if (!l1h->mf_index[tn]) + goto no_data; + + /* get frame from multiframe */ + period = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + 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].dl_active) + goto no_data; + + /* get burst from function */ + bits = func(l1h, tn, fn, chan, bid); + +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 fn, + sbit_t *bits, int8_t rssi, int16_t toa) +{ + struct trx_sched_frame *frame; + uint8_t offset, period, bid; + trx_sched_ul_func *func; + enum trx_chan_type chan; + int rc; + + if (!l1h->mf_index) + return -EINVAL; + + /* get frame from multiframe */ + period = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + 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].ul_active) + return -EINVAL; + + /* put burst to function */ + rc = func(l1h, tn, fn, chan, bid, bits, toa); + + return rc; +} + +/* 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 tranceiver 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 no bits, send dummy burst with no gain */ + bits = dummy_burst; + gain = 128; + } 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 = &tranceiver_clock_tv; + int32_t elapsed; + + /* check if tranceiver is still alive */ + if (tranceiver_lost++ == TRX_LOSS_FRAMES) { + struct gsm_bts_trx *trx; + + LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); + + tranceiver_available = 0; + + /* flush pending messages of transceiver */ + llist_for_each_entry(trx, &bts->trx_list, list) + trx_if_flush(trx_l1h_hdl(trx)); + + /* start over provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + 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); + tranceiver_available = 0; + return; + } + + 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; + } + tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; + trx_sched_fn(tranceiver_last_fn); + elapsed -= FRAME_DURATION_uS; + } + osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); +} + + +/* receive clock from tranceiver */ +int trx_sched_clock(uint32_t fn) +{ + struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + int32_t elapsed; + int32_t elapsed_fn; + + /* reset lost counter */ + tranceiver_lost = 0; + + gettimeofday(&tv_now, NULL); + + /* clock becomes valid */ + if (!tranceiver_available) { + LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", + fn); +new_clock: + tranceiver_last_fn = fn; + trx_sched_fn(tranceiver_last_fn); + + /* schedule first FN to be transmitted */ + memcpy(tv_clock, &tv_now, sizeof(struct timeval)); + tranceiver_available = 1; + tranceiver_clock_timer.cb = trx_ctrl_timer_cb; + tranceiver_clock_timer.data = bts; + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS); + + return 0; + } + + osmo_timer_del(&tranceiver_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 - tranceiver_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", tranceiver_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 hast to be transmitted */ + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS * (1 - elapsed_fn)); + + return 0; + } + + /* transmit what we still need to transmit */ + while (fn != tranceiver_last_fn) { + tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; + trx_sched_fn(tranceiver_last_fn); + } + + /* schedule next FN to be transmitted */ + memcpy(tv_clock, &tv_now, sizeof(struct timeval)); + osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + + return 0; +} + -- cgit v1.2.3 From 414faaca19812016d5fb756838988f341c23ba51 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 16:53:04 +0100 Subject: TRX: Power down tranceiver and reset scheduler, if abis link is lost If BTS is gone, TRX is powered down, due to loss of abis link. If link is esablished again, tranceiver and scheduler are provisioned again by BTS. --- src/osmo-bts-trx/scheduler.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a0caeeca..d51c1eb4 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -201,10 +201,12 @@ int trx_sched_init(struct trx_l1h *l1h) for (tn = 0; tn < 8; tn++) { l1h->mf_index[tn] = 0; + INIT_LLIST_HEAD(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { - INIT_LLIST_HEAD(&l1h->dl_prims[tn]); chan_state = &l1h->chan_states[tn][i]; - chan_state->ul_mask = 0x0; + chan_state->dl_active = 0; + chan_state->ul_active = 0; + chan_state->ul_mask = 0x00; } } @@ -220,8 +222,8 @@ void trx_sched_exit(struct trx_l1h *l1h) 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++) { - msgb_queue_flush(&l1h->dl_prims[tn]); chan_state = &l1h->chan_states[tn][i]; if (chan_state->dl_bursts) { talloc_free(chan_state->dl_bursts); @@ -235,6 +237,13 @@ void trx_sched_exit(struct trx_l1h *l1h) } } +/* 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) -- cgit v1.2.3 From 2ea68e2b7be7af78fb87c42e39133a812b709d48 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 13:16:28 +0100 Subject: TRX: Fixes and improvements of scheduler --- src/osmo-bts-trx/scheduler.c | 122 +++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 28 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index d51c1eb4..3224296f 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -260,6 +260,12 @@ int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) 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; @@ -276,6 +282,12 @@ int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) 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; @@ -825,7 +837,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; int rc; - LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", + LOGP(DL1C, LOGL_NOTICE, "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 */ @@ -835,12 +847,15 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -850,15 +865,30 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if ((*mask & 0xf) != 0xf) + 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); if (rc) { - LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u for " - "%s\n", *first_fn, trx_chan_desc[chan].name); + 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; @@ -886,12 +916,15 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -901,14 +934,27 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if ((*mask & 0xf) != 0xf) + if (bid != 3) return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block 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 = pdch_decode(l2 + 1, *bursts_p, NULL); if (rc <= 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH frame at fn=%u for " + LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block at fn=%u for " "%s\n", *first_fn, trx_chan_desc[chan].name); l2[0] = 0; /* bad frame */ rc = 0; @@ -961,12 +1007,15 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to end of buffer of 8 bursts */ burst = *bursts_p + bid * 116 + 464; @@ -976,16 +1025,29 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if (*mask != 0xff) + if (bid != 3) return 0; - *mask = 0xf0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH 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 * also shift buffer by 4 bursts for interleaving */ rc = tch_fr_decode(tch_data, *bursts_p); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad tch frame at fn=%u " + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); rc = 0; } @@ -1649,6 +1711,8 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, for (i = 0; ARRAY_SIZE(trx_sched_multiframes); i++) { if (trx_sched_multiframes[i].pchan == pchan) { 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, @@ -1704,9 +1768,9 @@ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) return 0; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->dl_chan; bid = frame->dl_bid; @@ -1742,9 +1806,9 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, goto no_data; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->dl_chan; bid = frame->dl_bid; @@ -1781,13 +1845,13 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan; int rc; - if (!l1h->mf_index) + if (!l1h->mf_index[tn]) return -EINVAL; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->ul_chan; bid = frame->ul_bid; @@ -1935,6 +1999,8 @@ new_clock: /* schedule first FN to be transmitted */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); tranceiver_available = 1; + memset(&tranceiver_clock_timer, 0, + sizeof(tranceiver_clock_timer)); tranceiver_clock_timer.cb = trx_ctrl_timer_cb; tranceiver_clock_timer.data = bts; osmo_timer_schedule(&tranceiver_clock_timer, 0, -- cgit v1.2.3 From e0959e7929cf9c8659919f2849c54360eda95032 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 14:09:06 +0100 Subject: TRX: Use received TRX clocks to determine availablility of tranceiver Only if transceiver becomes available, control commands are sent. If tranceiver is gone, reset scheduler. The current availability state is sent to BSC via OML state change commands. --- src/osmo-bts-trx/scheduler.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 3224296f..917cbdaf 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1937,14 +1937,18 @@ static void trx_ctrl_timer_cb(void *data) LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); +no_clock: tranceiver_available = 0; /* flush pending messages of transceiver */ - llist_for_each_entry(trx, &bts->trx_list, list) + /* 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)); + } - /* start over provisioning tranceiver */ - l1if_provision_tranceiver(bts); + /* tell BSC */ + check_tranceiver_availability(bts, 0); return; } @@ -1958,10 +1962,10 @@ static void trx_ctrl_timer_cb(void *data) if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) { LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n", elapsed); - tranceiver_available = 0; - return; + 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) { @@ -1996,7 +2000,7 @@ new_clock: tranceiver_last_fn = fn; trx_sched_fn(tranceiver_last_fn); - /* schedule first FN to be transmitted */ + /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); tranceiver_available = 1; memset(&tranceiver_clock_timer, 0, @@ -2006,6 +2010,12 @@ new_clock: osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + /* start provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + /* tell BSC */ + check_tranceiver_availability(bts, 1); + return 0; } -- cgit v1.2.3 From cd463dd72acbe2286dc33fa266ec081d75b57751 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 11 Feb 2013 12:52:56 +0100 Subject: TRX: Minor fixes, especially handle TOA of RACH correctly --- src/osmo-bts-trx/scheduler.c | 54 ++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 19 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 917cbdaf..9f369909 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -42,6 +42,11 @@ #include "pxxch.h" #include "trx_if.h" +/* Enable this to multiply TOA of RACH by 10. + * This 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; @@ -63,7 +68,7 @@ typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, typedef const 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); @@ -84,15 +89,15 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static const 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, 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, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static const ubit_t dummy_burst[148] = { 0,0,0, @@ -768,20 +773,20 @@ static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ 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, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct osmo_phsap_prim l1sap; uint8_t ra; int rc; - LOGP(DL1C, LOGL_DEBUG, "Received %s fn=%u\n", - trx_chan_desc[chan].name, fn); + LOGP(DL1C, LOGL_NOTICE, "Received %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 rach frame at fn=%u " - "ra=%u\n", fn, ra); + "(%u/51)\n", fn, fn % 51); return 0; } @@ -791,7 +796,11 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL); l1sap.u.rach_ind.ra = ra; - l1sap.u.rach_ind.acc_delay = 0; //FIXME: TOA +#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 */ @@ -828,7 +837,7 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -837,7 +846,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; int rc; - LOGP(DL1C, LOGL_NOTICE, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", + 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 */ @@ -897,7 +906,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -988,7 +997,7 @@ static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -1062,7 +1071,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float 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); @@ -1192,6 +1201,7 @@ static struct trx_sched_frame frame_bcch_sdcch4[102] = { { 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 }, @@ -1837,7 +1847,7 @@ if (0) if (chan != TRXC_IDLE) // hack /* process uplink burst */ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - sbit_t *bits, int8_t rssi, int16_t toa) + sbit_t *bits, int8_t rssi, float toa) { struct trx_sched_frame *frame; uint8_t offset, period, bid; @@ -1862,6 +1872,10 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, && !l1h->chan_states[tn][chan].ul_active) return -EINVAL; + /* omit bursts which have no handler, like IDLE bursts */ + if (!func) + return 0; + /* put burst to function */ rc = func(l1h, tn, fn, chan, bid, bits, toa); @@ -1976,7 +1990,8 @@ no_clock: trx_sched_fn(tranceiver_last_fn); elapsed -= FRAME_DURATION_uS; } - osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS - elapsed); } @@ -2037,7 +2052,8 @@ new_clock: goto new_clock; } - LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n", elapsed_fn * FRAME_DURATION_uS - elapsed); + 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) { -- cgit v1.2.3 From d10eaee4cce04aee4907b87e5bd73b9d866061b7 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Feb 2013 11:21:13 +0100 Subject: TRX: Completed TCH/F full rate support Full rate is now tested and working. --- src/osmo-bts-trx/scheduler.c | 1087 ++++++++++++++++++++++++------------------ 1 file changed, 635 insertions(+), 452 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9f369909..d5c7bb54 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -357,7 +357,7 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENODEV; } - LOGP(DL1C, LOGL_INFO, "TCH.ind: chan=%s chan_nr=0x%02x " + 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); @@ -366,11 +366,15 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (!msg) return -ENOMEM; l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, PRIM_OP_INDICATION, msg); - l1sap->u.tch.chan_nr = chan_nr | tn; - l1sap->u.tch.fn = fn; + l1sap->u.data.chan_nr = chan_nr; + l1sap->u.data.link_id = link_id; + l1sap->u.data.fn = fn; + /* stop here, if TCH is in signalling only mode */ + if (l1h->chan_states[tn][chan].rsl_cmode == RSL_CMOD_SPD_SIGN) + return l1sap_up(l1h->trx, l1sap); l1sap_up(l1h->trx, l1sap); /* generate prim */ @@ -378,11 +382,10 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (!msg) return -ENOMEM; l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = chan_nr; - l1sap->u.data.chan_nr = link_id; - l1sap->u.data.fn = fn; + l1sap->u.tch.chan_nr = chan_nr; + l1sap->u.tch.fn = fn; return l1sap_up(l1h->trx, l1sap); } @@ -454,81 +457,69 @@ static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn, enum trx_chan_type chan) { - struct msgb *found = NULL, *msg, *msg2; /* make GCC happy */ - struct osmo_phsap_prim *l1sap = NULL; /* make GCC happy */ - uint32_t check_fn; + struct msgb *msg, *msg2; + struct osmo_phsap_prim *l1sap; + uint32_t prim_fn; + uint8_t chan_nr, link_id; - /* get burst from queue */ + /* get prim of current fn from queue */ llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) { l1sap = msgb_l1sap_prim(msg); - check_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648); - if (check_fn > 20) { + 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 > 20) { LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " "is out of range. (current fn=%u)\n", - l1h->trx->nr, tn, l1sap->u.data.fn, fn); + l1h->trx->nr, tn, prim_fn, fn); /* unlink and free message */ llist_del(&msg->list); msgb_free(msg); continue; } - if (check_fn > 0) + if (prim_fn > 0) continue; - /* found second message, check if we have TCH+FACCH */ - if (found) { - /* we found a message earlier */ - l1sap = msgb_l1sap_prim(found); - /* if we have TCH+something */ - if (l1sap->oph.primitive == PRIM_TCH) { - /* unlink and free message */ - llist_del(&found->list); - msgb_free(found); - found = msg; - } else { - l1sap = msgb_l1sap_prim(msg); - /* if we have TCH+something */ - if (l1sap->oph.primitive == PRIM_TCH) { - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - } - } - break; - } - found = msg; - } - - if (!found) { - LOGP(DL1C, LOGL_NOTICE, "%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); - return NULL; + goto found_msg; } - l1sap = msgb_l1sap_prim(found); + return NULL; - if ((l1sap->oph.primitive != PRIM_PH_DATA - && l1sap->oph.primitive != PRIM_TCH) - || l1sap->oph.operation != PRIM_OP_REQUEST) { - 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(&found->list); - msgb_free(found); - return NULL; - } - if ((l1sap->u.data.chan_nr ^ (trx_chan_desc[chan].chan_nr | tn)) - || ((l1sap->u.data.link_id ^ trx_chan_desc[chan].link_id) & 0x40)) { +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, l1sap->u.data.chan_nr, - l1sap->u.data.link_id, trx_chan_desc[chan].chan_nr | tn, + "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; } - return found; + /* 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, @@ -553,6 +544,10 @@ static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; + LOGP(DL1C, LOGL_NOTICE, "%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) { @@ -566,8 +561,7 @@ got_msg: if (msgb_l2len(msg) != 23) { LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " "(len=%d)\n", msgb_l2len(msg)); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); goto no_msg; } @@ -589,8 +583,7 @@ got_msg: /* encode bursts */ xcch_encode(*bursts_p, msg->l2h); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); send_burst: @@ -628,6 +621,10 @@ static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; + LOGP(DL1C, LOGL_NOTICE, "%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) { @@ -651,14 +648,12 @@ got_msg: if (rc) { LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! " "(len=%d)\n", rc); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); goto no_msg; } - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); send_burst: @@ -679,7 +674,7 @@ send_burst: static const 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 = NULL; /* make GCC happy */ + struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; static ubit_t bits[148]; struct osmo_phsap_prim *l1sap; @@ -691,41 +686,56 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - /* get burst from queue */ - msg = dequeue_prim(l1h, tn, fn, chan); - if (msg) - goto got_msg; - -no_msg: - /* free burst memory */ - if (*bursts_p) { - talloc_free(*bursts_p); - *bursts_p = NULL; + /* 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; } - return NULL; -got_msg: - l1sap = msgb_l1sap_prim(msg); - if (l1sap->oph.primitive == PRIM_TCH) { - /* check validity of message */ - if (msgb_l2len(msg) != 33) { - LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg)); - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - goto no_msg; - } - } else { - /* 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)); - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - goto no_msg; - } + /* check validity of message */ + if (msg_tch && msgb_l2len(msg_tch) != 33) { + LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg_tch)); + /* free message */ + msgb_free(msg_tch); + msg_tch = NULL; + } + 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; } /* alloc burst memory, if not already, @@ -734,15 +744,30 @@ got_msg: *bursts_p = talloc_zero_size(tall_bts_ctx, 928); if (!*bursts_p) return NULL; - } else + } else { memcpy(*bursts_p, *bursts_p + 464, 464); + memset(*bursts_p + 464, 0, 464); + } - /* encode bursts */ - tch_fr_encode(*bursts_p, msg->l2h, msgb_l2len(msg)); + /* mo message at all */ + if (!msg_tch && !msg_facch) { + LOGP(DL1C, LOGL_NOTICE, "%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)); + else + tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); send_burst: /* compose burst */ @@ -1018,7 +1043,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* store frame number of first burst */ if (bid == 0) { - memset(*bursts_p, 0, 464); + memset(*bursts_p + 464, 0, 464); *mask = 0x0; *first_fn = fn; } @@ -1046,7 +1071,8 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* we require first burst to have correct FN */ if (!(*mask & 0x1)) { *mask = 0x0; - return 0; + rc = 0; + goto bfi; } } *mask = 0x0; @@ -1058,7 +1084,9 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); - rc = 0; + memset(tch_data, 0, sizeof(tch_data)); + // FIXME length depends on codec + rc = 33; } /* FACCH */ @@ -1066,6 +1094,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); +bfi: /* TCH or BFI */ return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); } @@ -1097,54 +1126,24 @@ 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_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_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_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_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_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 }, }; @@ -1308,6 +1307,7 @@ static struct trx_sched_frame frame_sdcch8[102] = { { 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 }, @@ -1361,346 +1361,501 @@ static struct trx_sched_frame frame_sdcch8[102] = { { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 }, }; -static struct trx_sched_frame frame_tchf[104] = { +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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_tchh[104] = { +static struct trx_sched_frame frame_tchf_ts1[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_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_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_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_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_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_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_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_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_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_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, 0, NULL, "NONE"}, - { GSM_PCHAN_CCCH, 51, frame_bcch, "BCCH+CCCH" }, - { GSM_PCHAN_CCCH_SDCCH4, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" }, - { GSM_PCHAN_SDCCH8_SACCH8C, 102, frame_sdcch8, "SDCCH/8+SACCH/8" }, - { GSM_PCHAN_TCH_F, 104, frame_tchf, "TCH/F+SACCH" }, - { GSM_PCHAN_TCH_H, 104, frame_tchh, "TCH/H+SACCH" }, - { GSM_PCHAN_PDCH, 104, frame_pdch, "PDCH" }, + { 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" }, }; @@ -1718,8 +1873,9 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, if (!(l1h->config.slotmask & (1 << tn))) return -ENOTSUP; - for (i = 0; ARRAY_SIZE(trx_sched_multiframes); i++) { - if (trx_sched_multiframes[i].pchan == pchan) { + 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; @@ -1731,7 +1887,10 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, } } - return -EINVAL; + 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 */ @@ -1765,6 +1924,30 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, 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) +{ + uint8_t tn = L1SAP_CHAN2TS(chan_nr); + int i; + int rc = -EINVAL; + + /* 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) { + LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u on " + "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, + trx_chan_desc[i].name, l1h->trx->nr, tn); + l1h->chan_states[tn][i].rsl_cmode = rsl_cmode; + l1h->chan_states[tn][i].tch_mode = tch_mode; + rc = 0; + } + } + + return rc; +} + /* process ready-to-send */ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) { -- cgit v1.2.3 From b9880bc81289fa13b12801abcfc77fe6e83b8a94 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Feb 2013 12:22:42 +0100 Subject: TRX: Allow transcoding of TCH FR with MSB first (RTP) or LSB first (E1) --- src/osmo-bts-trx/scheduler.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index d5c7bb54..f65a7025 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -759,9 +759,10 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* encode bursts (priorize FACCH) */ if (msg_facch) - tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), + 1); else - tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); + tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); /* unlink and free message */ if (msg_tch) @@ -1079,7 +1080,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* decode * also shift buffer by 4 bursts for interleaving */ - rc = tch_fr_decode(tch_data, *bursts_p); + rc = tch_fr_decode(tch_data, *bursts_p, 1); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " -- cgit v1.2.3 From 9de67ca9621deea3283b9f9c2bab99287a110e45 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 07:51:01 +0100 Subject: TRX: Lost TCH frame detection of omitted bursts from tranceiver --- src/osmo-bts-trx/scheduler.c | 143 +++++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 60 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f65a7025..629f8788 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -523,7 +523,57 @@ found_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); + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* 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 = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.data.link_id = trx_chan_desc[chan].link_id; + l1sap->u.data.fn = fn; + 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 const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) @@ -569,7 +619,7 @@ got_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].sacch_lost) > 1) + if (++(l1h->chan_states[tn][chan].lost) > 1) compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); } @@ -686,6 +736,19 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } + /* handle loss detection of received TCH frames */ + if (++(l1h->chan_states[tn][chan].lost) > 5) { + uint8_t tch_data[33]; + + LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending " + "BFI for %s\n", trx_chan_desc[chan].name); + + /* indicate bad frame */ + memset(tch_data, 0, sizeof(tch_data)); + // FIXME length depends on codec + compose_tch_ind(l1h, tn, 0, chan, tch_data, 33); + } + /* get frame and unlink from queue */ msg1 = dequeue_prim(l1h, tn, fn, chan); msg2 = dequeue_prim(l1h, tn, fn, chan); @@ -757,6 +820,14 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } + /* bad frame */ + if (msg_tch && !msg_facch && (msg_tch->l2h[0] >> 4) != 0xd) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad frame' 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), @@ -835,33 +906,6 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return 0; } -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) -{ - struct msgb *msg; - struct osmo_phsap_prim *l1sap; - - /* 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 = trx_chan_desc[chan].chan_nr | tn; - l1sap->u.data.link_id = trx_chan_desc[chan].link_id; - l1sap->u.data.fn = fn; - 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].sacch_lost = 0; - - /* 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, float toa) { @@ -999,29 +1043,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); } -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); - - /* forward primitive */ - l1sap_up(l1h->trx, l1sap); - - return 0; -} - 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, float toa) { @@ -1085,17 +1106,19 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); - memset(tch_data, 0, sizeof(tch_data)); - // FIXME length depends on codec - rc = 33; + goto bfi; } /* FACCH */ - if (rc == 23) - return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, - 23); - + if (rc == 23) { + compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); bfi: + // FIXME length depends on codec + rc = 33; + /* indicate bad tch frame */ + memset(tch_data, 0, sizeof(tch_data)); + } + /* TCH or BFI */ return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); } @@ -1917,7 +1940,7 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, l1h->chan_states[tn][i].ul_active = active; l1h->chan_states[tn][i].ul_active = active; } - l1h->chan_states[tn][i].sacch_lost = 0; + l1h->chan_states[tn][i].lost = 0; rc = 0; } } -- cgit v1.2.3 From 78b2080027d398c7242ae6e180b462e32f57a786 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 13:29:29 +0100 Subject: TRX: PDTCH (GPRS) works now Detection and transcoding of all four coding schemes are supported. --- src/osmo-bts-trx/scheduler.c | 117 ++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 58 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 629f8788..f44df519 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -138,6 +138,7 @@ static const ubit_t sch_train[64] = { */ struct trx_chan_desc { + int pdch; enum trx_chan_type chan; uint8_t chan_nr; uint8_t link_id; @@ -148,44 +149,44 @@ struct trx_chan_desc { int auto_active; }; struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { - { TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, - { TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, - { TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, - { TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, - { TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, - { TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, - { TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, - { TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, - { TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, + { 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_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, + { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_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, 0x68, 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 }, }; @@ -926,7 +927,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst & store frame number of first burst */ if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; @@ -980,7 +981,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { 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; uint8_t l2[54+1]; int rc; @@ -995,11 +995,10 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst */ if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; - *first_fn = fn; } /* update mask */ @@ -1018,29 +1017,26 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + 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); - /* we require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - return 0; - } } *mask = 0x0; /* decode */ - rc = pdch_decode(l2 + 1, *bursts_p, NULL); + rc = pdtch_decode(l2 + 1, *bursts_p, NULL); if (rc <= 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block at fn=%u for " - "%s\n", *first_fn, trx_chan_desc[chan].name); - l2[0] = 0; /* bad frame */ - rc = 0; - } else - l2[0] = 7; /* valid frame */ + 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; + } - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); + l2[0] = 7; /* valid frame */ + + return compose_ph_data_ind(l1h, tn, (fn + 2715648 -3) % 2715648, chan, + l2, rc + 1); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1927,6 +1923,11 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* 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) { LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", -- cgit v1.2.3 From 7451ce29a79a8da6da0ac1dbf155ed4af016dcdc Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 16:17:43 +0100 Subject: TRX: Detect missing received bursts and fill them with zero-sbits --- src/osmo-bts-trx/scheduler.c | 95 +++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 36 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f44df519..31a0fcd4 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -207,6 +207,7 @@ int trx_sched_init(struct trx_l1h *l1h) 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]; @@ -1035,7 +1036,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ - return compose_ph_data_ind(l1h, tn, (fn + 2715648 -3) % 2715648, chan, + return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, l2, rc + 1); } @@ -1044,7 +1045,6 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { 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; uint8_t tch_data[33]; int rc; @@ -1059,11 +1059,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst */ if (bid == 0) { memset(*bursts_p + 464, 0, 464); *mask = 0x0; - *first_fn = fn; } /* update mask */ @@ -1082,16 +1081,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + 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); - /* we require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - rc = 0; - goto bfi; - } } *mask = 0x0; @@ -1100,14 +1093,15 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, rc = tch_fr_decode(tch_data, *bursts_p, 1); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " - "for %s\n", *first_fn, trx_chan_desc[chan].name); + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); goto bfi; } /* FACCH */ if (rc == 23) { - compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); + compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, + tch_data, 23); bfi: // FIXME length depends on codec rc = 33; @@ -1116,7 +1110,8 @@ bfi: } /* TCH or BFI */ - return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); + 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, @@ -2054,40 +2049,68 @@ if (0) if (chan != TRXC_IDLE) // hack } /* process uplink burst */ -int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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; - int rc; + uint32_t fn, elapsed; if (!l1h->mf_index[tn]) return -EINVAL; - /* get frame from multiframe */ - period = l1h->mf_period[tn]; - offset = fn % period; - frame = l1h->mf_frames[tn] + offset; + /* calculate how many frames have been elapsed */ + elapsed = (current_fn + 2715648 - l1h->mf_last_fn[tn]) % 2715648; - chan = frame->ul_chan; - bid = frame->ul_bid; - func = trx_chan_desc[chan].ul_fn; + /* 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].ul_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) + func(l1h, tn, fn, chan, bid, bits, toa); + else { + sbit_t spare[148]; + + memset(spare, 0, 148); + func(l1h, tn, fn, chan, bid, spare, toa); + } - /* check if channel is active */ - if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].ul_active) - return -EINVAL; +next_frame: + /* reached current fn */ + if (fn == current_fn) + break; - /* omit bursts which have no handler, like IDLE bursts */ - if (!func) - return 0; + fn = (fn + 1) % 2715648; + } - /* put burst to function */ - rc = func(l1h, tn, fn, chan, bid, bits, toa); + l1h->mf_last_fn[tn] = fn; - return rc; + return 0; } /* schedule all frames of all TRX for given FN */ -- cgit v1.2.3 From 801c182c02a38c9e3fb89c431caabc4c5abddf6f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 16:20:19 +0100 Subject: TRX: By default, send 20 frames in advance to tranceiver --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 31a0fcd4..afe4aba3 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -58,7 +58,7 @@ static struct timeval tranceiver_clock_tv; static struct osmo_timer_list tranceiver_clock_timer; /* clock advance for the tranceiver */ -uint32_t trx_clock_advance = 10; +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 */ -- cgit v1.2.3 From 89e36c0e646a9e73728ee65cc6ac32f01d388885 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 24 Feb 2013 10:12:09 +0100 Subject: TRX: Cleanup of channel transcoding --- src/osmo-bts-trx/scheduler.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index afe4aba3..6f5dddb0 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -35,11 +35,7 @@ #include "l1_if.h" #include "scheduler.h" -#include "xcch.h" -#include "tch_fr.h" -#include "rach.h" -#include "sch.h" -#include "pxxch.h" +#include "gsm0503_coding.h" #include "trx_if.h" /* Enable this to multiply TOA of RACH by 10. -- cgit v1.2.3 From 7bd6e8b89bbb38b292caa0df3c52902c46911c1a Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 21 Feb 2013 09:27:52 +0100 Subject: TRX: Ciphering --- src/osmo-bts-trx/scheduler.c | 118 +++++++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 26 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6f5dddb0..f2e7dad5 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,7 @@ 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 const ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, +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, float toa); @@ -70,19 +71,19 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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, float toa); @@ -95,7 +96,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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, float toa); -static const ubit_t dummy_burst[148] = { +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, @@ -105,7 +106,7 @@ static const ubit_t dummy_burst[148] = { 0,0,0, }; -static const ubit_t fcch_burst[148] = { +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, @@ -394,7 +395,7 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ /* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ -static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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", @@ -403,7 +404,7 @@ static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return NULL; } -static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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", @@ -412,7 +413,7 @@ static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return fcch_burst; } -static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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]; @@ -573,7 +574,7 @@ static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return 0; } -static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 */ @@ -649,7 +650,7 @@ send_burst: return bits; } -static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 */ @@ -719,7 +720,7 @@ send_burst: return bits; } -static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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 *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; @@ -854,7 +855,7 @@ send_burst: return bits; } -static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { // FIXME @@ -939,8 +940,6 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1006,8 +1005,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1069,8 +1066,6 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1964,6 +1959,48 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, 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; + + if (algo < 0 || key_len > 8 || (algo && 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) { @@ -2009,7 +2046,7 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, uint8_t offset, period, bid; trx_sched_dl_func *func; enum trx_chan_type chan; - const ubit_t *bits = NULL; + ubit_t *bits = NULL; if (!l1h->mf_index[tn]) goto no_data; @@ -2031,6 +2068,19 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, /* 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) { @@ -2087,9 +2137,25 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, goto next_frame; /* put burst to function */ - if (fn == current_fn) + 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, toa); - else { + } else { sbit_t spare[148]; memset(spare, 0, 148); -- cgit v1.2.3 From ce0f20b597343f472c6df44c1adf596a388f9728 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 21 Feb 2013 15:39:59 +0100 Subject: TRX: Fix of SCH burst data --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f2e7dad5..b5fe08f2 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -437,7 +437,7 @@ static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, ((t.t1 & 0x001) << 7) | ((t.t2 & 0x1f) << 2) | ((t3p & 0x6) >> 1); - sb_info[2] = + sb_info[3] = (t3p & 0x1); /* encode bursts */ -- cgit v1.2.3 From 889890da4312916bd617b3c95326a89237078a3b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 22 Feb 2013 07:52:51 +0100 Subject: TRX: Improved handling of clock indications. If no clock is received, a POWEROFF is sent until clock is detected. --- src/osmo-bts-trx/scheduler.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index b5fe08f2..057d5c2e 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2252,6 +2252,8 @@ no_clock: 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 */ @@ -2295,6 +2297,9 @@ int trx_sched_clock(uint32_t fn) int32_t elapsed; int32_t elapsed_fn; + if (quit) + return 0; + /* reset lost counter */ tranceiver_lost = 0; @@ -2304,13 +2309,21 @@ int trx_sched_clock(uint32_t fn) if (!tranceiver_available) { LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", fn); + + tranceiver_available = 1; + + /* start provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + /* tell BSC */ + check_tranceiver_availability(bts, 1); + new_clock: tranceiver_last_fn = fn; trx_sched_fn(tranceiver_last_fn); /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); - tranceiver_available = 1; memset(&tranceiver_clock_timer, 0, sizeof(tranceiver_clock_timer)); tranceiver_clock_timer.cb = trx_ctrl_timer_cb; @@ -2318,12 +2331,6 @@ new_clock: osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); - /* start provisioning tranceiver */ - l1if_provision_tranceiver(bts); - - /* tell BSC */ - check_tranceiver_availability(bts, 1); - return 0; } -- cgit v1.2.3 From 219ece83a3ffe7fa2bd58943cddb47a3eacc2fab Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 24 Feb 2013 11:09:19 +0100 Subject: TRX: Implementation of MS power and timing advance loops --- src/osmo-bts-trx/scheduler.c | 67 +++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 19 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 057d5c2e..a0ffe163 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -38,6 +38,7 @@ #include "scheduler.h" #include "gsm0503_coding.h" #include "trx_if.h" +#include "loops.h" /* Enable this to multiply TOA of RACH by 10. * This usefull to check tenth of timing advances with RSSI test tool. @@ -65,7 +66,8 @@ typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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, float toa); + 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); @@ -86,15 +88,20 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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, float toa); + 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, float toa); + 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, float toa); + 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, float toa); + 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, float toa); + 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, @@ -324,6 +331,10 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "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) @@ -869,7 +880,8 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ 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, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { struct osmo_phsap_prim l1sap; uint8_t ra; @@ -906,7 +918,8 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, float toa) + 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; @@ -940,6 +953,12 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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_input(l1h, trx_chan_desc[chan].chan_nr | tn, + chan_state, rssi, toa); + } + /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -950,6 +969,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "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; @@ -973,7 +993,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, float toa) + 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; @@ -1034,7 +1055,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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, float toa) + 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; @@ -1106,7 +1128,8 @@ bfi: } 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, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float 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); @@ -1906,6 +1929,7 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, uint8_t tn = L1SAP_CHAN2TS(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++) { @@ -1916,18 +1940,23 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, 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]; LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", (active) ? "Activating" : "Deactivating", (downlink) ? "downlink" : "uplink", trx_chan_desc[i].name, l1h->trx->nr, tn); if (downlink) { - l1h->chan_states[tn][i].dl_active = active; - l1h->chan_states[tn][i].dl_active = active; + chan_state->dl_active = active; + chan_state->dl_active = active; } else { - l1h->chan_states[tn][i].ul_active = active; - l1h->chan_states[tn][i].ul_active = active; + chan_state->ul_active = active; + chan_state->ul_active = active; + } + chan_state->lost = 0; + if (L1SAP_IS_LINK_SACCH(link_id)) { + memset(&chan_state->meas, 0, + sizeof(chan_state->meas)); } - l1h->chan_states[tn][i].lost = 0; rc = 0; } } @@ -2154,12 +2183,12 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, } } - func(l1h, tn, fn, chan, bid, bits, toa); - } else { + func(l1h, tn, fn, chan, bid, bits, rssi, toa); + } else if (chan != TRXC_RACH) { sbit_t spare[148]; memset(spare, 0, 148); - func(l1h, tn, fn, chan, bid, spare, toa); + func(l1h, tn, fn, chan, bid, spare, -128, 0); } next_frame: -- cgit v1.2.3 From 9855e8bd4854f6afb740ad940f2999be354b0af1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 1 Mar 2013 10:05:59 +0100 Subject: TRX: Out of range primitives found in downlink queue are not an error --- src/osmo-bts-trx/scheduler.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a0ffe163..fde14fa8 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -500,9 +500,10 @@ free_msg: goto wrong_type; } if (prim_fn > 20) { - LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " - "is out of range. (current fn=%u)\n", - l1h->trx->nr, tn, prim_fn, fn); + LOGP(DL1C, LOGL_NOTICE, "Prim for trx=%u ts=%u at fn=%u " + "is out of range, or channel already disabled. " + "(current fn=%u)\n", l1h->trx->nr, tn, prim_fn, + fn); /* unlink and free message */ llist_del(&msg->list); msgb_free(msg); -- cgit v1.2.3 From 84b9a445351937703334629f3fec02689153f986 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Mar 2013 07:56:05 +0100 Subject: TRX: Code cleanup, prepare for other codecs than GSM full rate --- src/osmo-bts-trx/scheduler.c | 277 +++++++++++++++++++++++++++++-------------- 1 file changed, 187 insertions(+), 90 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index fde14fa8..023be372 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -71,7 +71,9 @@ typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +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); @@ -159,9 +161,9 @@ struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { { 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_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, - { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 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 }, @@ -215,9 +217,7 @@ int trx_sched_init(struct trx_l1h *l1h) INIT_LLIST_HEAD(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { chan_state = &l1h->chan_states[tn][i]; - chan_state->dl_active = 0; - chan_state->ul_active = 0; - chan_state->ul_mask = 0x00; + chan_state->active = 0; } } @@ -349,13 +349,13 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return l1sap_up(l1h->trx, l1sap); } -/* RTS for traffic frame */ -static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan) +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; @@ -371,33 +371,55 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, chan_nr, fn, tn, l1h->trx->nr); - /* 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; + /* 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; - /* stop here, if TCH is in signalling only mode */ - if (l1h->chan_states[tn][chan].rsl_cmode == RSL_CMOD_SPD_SIGN) return l1sap_up(l1h->trx, l1sap); - l1sap_up(l1h->trx, l1sap); + } - /* 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 rc; +} - return l1sap_up(l1h->trx, l1sap); +/* 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) +{ + // FIXME + return 0; } @@ -732,32 +754,38 @@ send_burst: return bits; } -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 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) { struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; - ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; - static ubit_t bits[148]; + 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; - /* send burst, if we already got a frame */ - if (bid > 0) { - if (!*bursts_p) - return NULL; - goto send_burst; - } - /* handle loss detection of received TCH frames */ - if (++(l1h->chan_states[tn][chan].lost) > 5) { + 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 */ - memset(tch_data, 0, sizeof(tch_data)); - // FIXME length depends on codec - compose_tch_ind(l1h, tn, 0, chan, tch_data, 33); + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + memset(tch_data, 0, 33); + len = 33; + break; + default: + 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 */ @@ -797,13 +825,6 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } /* check validity of message */ - if (msg_tch && msgb_l2len(msg_tch) != 33) { - LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg_tch)); - /* free message */ - msgb_free(msg_tch); - msg_tch = NULL; - } 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)); @@ -812,6 +833,69 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, msg_facch = NULL; } + /* check validity of message */ + if (!msg_facch && msg_tch) { + int len; + + 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 */ + 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; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " + "fix!\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]; + 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); + /* alloc burst memory, if not already, * otherwise shift buffer by 4 bursts for interleaving */ if (!*bursts_p) { @@ -831,14 +915,6 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - /* bad frame */ - if (msg_tch && !msg_facch && (msg_tch->l2h[0] >> 4) != 0xd) { - LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad frame' 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), @@ -846,7 +922,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, else tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); - /* unlink and free message */ + /* free message */ if (msg_tch) msgb_free(msg_tch); if (msg_facch) @@ -956,7 +1032,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* send burst information to loops process */ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { - trx_loop_input(l1h, trx_chan_desc[chan].chan_nr | tn, + trx_loop_sacch_input(l1h, trx_chan_desc[chan].chan_nr | tn, chan_state, rssi, toa); } @@ -1062,7 +1138,9 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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 tch_data[33]; + 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; LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", @@ -1104,25 +1182,52 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* decode * also shift buffer by 4 bursts for interleaving */ - rc = tch_fr_decode(tch_data, *bursts_p, 1); + 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); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } memcpy(*bursts_p, *bursts_p + 464, 464); 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, 23); bfi: - // FIXME length depends on codec - rc = 33; - /* indicate bad tch frame */ - memset(tch_data, 0, sizeof(tch_data)); + 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; + 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); @@ -1925,7 +2030,7 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, /* 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 downlink, int active) + int active) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); int i; @@ -1942,23 +2047,15 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, 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]; - LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", + rc = 0; + if (chan_state->active == active) + continue; + LOGP(DL1C, LOGL_NOTICE, "%s %s on trx=%d ts=%d\n", (active) ? "Activating" : "Deactivating", - (downlink) ? "downlink" : "uplink", trx_chan_desc[i].name, l1h->trx->nr, tn); - if (downlink) { - chan_state->dl_active = active; - chan_state->dl_active = active; - } else { - chan_state->ul_active = active; - chan_state->ul_active = active; - } - chan_state->lost = 0; - if (L1SAP_IS_LINK_SACCH(link_id)) { - memset(&chan_state->meas, 0, - sizeof(chan_state->meas)); - } - rc = 0; + if (active) + memset(chan_state, 0, sizeof(*chan_state)); + chan_state->active = active; } } @@ -2062,7 +2159,7 @@ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].dl_active) + && !l1h->chan_states[tn][chan].active) return -EINVAL; return func(l1h, tn, fn, frame->dl_chan); @@ -2092,7 +2189,7 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].dl_active) + && !l1h->chan_states[tn][chan].active) goto no_data; /* get burst from function */ @@ -2159,7 +2256,7 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].ul_active) + && !l1h->chan_states[tn][chan].active) goto next_frame; /* omit bursts which have no handler, like IDLE bursts */ -- cgit v1.2.3 From 917cf7018b6a9ec778add719fcec1692f0e35931 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Mar 2013 07:57:07 +0100 Subject: TRX: Add support for EFR transcoding --- src/osmo-bts-trx/scheduler.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 023be372..f1a0b629 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -779,7 +779,14 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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; default: +inval_mode1: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); len = 0; @@ -857,7 +864,21 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t 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; default: +inval_mode2: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); goto free_bad_msg; @@ -1185,7 +1206,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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); + rc = tch_fr_decode(tch_data, *bursts_p, 1, 0); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", @@ -1217,6 +1241,10 @@ bfi: memset(tch_data, 0, 33); rc = 33; break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + memset(tch_data, 0, 31); + rc = 31; + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " "please fix!\n"); -- cgit v1.2.3 From 5e2341411f25f4faa66ff9b4506e77d897b97daf Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 16 Mar 2013 16:46:13 +0100 Subject: Get RSSI from received uplink data and send to PCU --- src/osmo-bts-trx/scheduler.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f1a0b629..6a40f97d 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -556,7 +556,7 @@ found_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) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -569,6 +569,7 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; 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); @@ -653,7 +654,7 @@ got_msg: if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ if (++(l1h->chan_states[tn][chan].lost) > 1) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -128); } /* alloc burst memory, if not already */ @@ -1023,6 +1024,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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; uint8_t l2[23], l2_len; int rc; @@ -1041,10 +1044,14 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(*bursts_p, 0, 464); *mask = 0x0; *first_fn = fn; + *rssi_sum = 0; + *rssi_num = 0; } - /* update mask */ + /* update mask + rssi */ *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1087,7 +1094,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } else l2_len = 23; - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len); + 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, @@ -1097,6 +1105,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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; uint8_t l2[54+1]; int rc; @@ -1114,10 +1124,14 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; + *rssi_sum = 0; + *rssi_num = 0; } - /* update mask */ + /* update mask + rssi */ *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1149,7 +1163,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1); + l2, rc + 1, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1232,7 +1246,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data, 23); + tch_data, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From a7f5e077121f857e373b6e68c5fdf803933a12dc Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 09:05:14 +0100 Subject: TRX: Support for AMR full speech --- src/osmo-bts-trx/scheduler.c | 139 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 8 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6a40f97d..eb6130eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -33,12 +33,15 @@ #include #include #include +#include #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 usefull to check tenth of timing advances with RSSI test tool. @@ -757,7 +760,7 @@ send_burst: 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) + 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]; @@ -786,6 +789,15 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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 " @@ -841,9 +853,11 @@ inval_mode1: msg_facch = NULL; } - /* check validity of message */ + /* 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, " @@ -878,12 +892,62 @@ inval_mode1: 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", @@ -906,6 +970,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { 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]; @@ -916,7 +981,8 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch); + 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 */ @@ -941,6 +1007,15 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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); @@ -1176,7 +1251,8 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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; + int rc, amr = 0; + float ber; 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); @@ -1225,6 +1301,27 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, case GSM48_CMODE_SPEECH_EFR: /* EFR */ rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); 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, &ber); + if (rc) + trx_loop_amr_input(l1h, + trx_chan_desc[chan].chan_nr | tn, chan_state, + ber); + 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); @@ -1246,7 +1343,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data, 23, rssi); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1259,6 +1356,15 @@ bfi: 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"); @@ -2106,21 +2212,38 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* 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) + uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, + uint8_t codec2, uint8_t codec3, uint8_t initial_id) { uint8_t tn = L1SAP_CHAN2TS(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++) { 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 on " "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, trx_chan_desc[i].name, l1h->trx->nr, tn); - l1h->chan_states[tn][i].rsl_cmode = rsl_cmode; - l1h->chan_states[tn][i].tch_mode = tch_mode; + chan_state->rsl_cmode = rsl_cmode; + chan_state->tch_mode = tch_mode; + 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; } } -- cgit v1.2.3 From c910a332b261d48fa594f1a3133987c8f650dcb1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:17:02 +0200 Subject: TRX: Support for TCH/H and GSM half rate transcoding --- src/osmo-bts-trx/scheduler.c | 224 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 216 insertions(+), 8 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index eb6130eb..65da30eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -421,8 +421,8 @@ static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan) { - // FIXME - return 0; + /* 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); } @@ -780,6 +780,12 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* 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; @@ -869,6 +875,20 @@ inval_mode1: 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) { @@ -1043,10 +1063,84 @@ send_burst: static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { - // FIXME - return NULL; -} + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + 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); + } + } + + /* mo message at all */ + if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { + LOGP(DL1C, LOGL_NOTICE, "%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 + 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; +} /* @@ -1385,11 +1479,125 @@ 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) { - LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", + 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; + + 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); - // FIXME - return 0; + /* 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); + 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); + 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; + 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); } -- cgit v1.2.3 From c5241c3aa42d7ff89a369f2a816a98bdca932217 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:19:26 +0200 Subject: TRX: Support for AMR half speech --- src/osmo-bts-trx/scheduler.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 65da30eb..9126e342 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1065,6 +1065,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { 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]; @@ -1118,6 +1119,15 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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)); @@ -1486,6 +1496,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; + float ber; 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); @@ -1544,6 +1555,28 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, rc = tch_hr_decode(tch_data, *bursts_p, (((fn + 26 - 10) % 26) >> 2) & 1); 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, &ber); + if (rc) + trx_loop_amr_input(l1h, + trx_chan_desc[chan].chan_nr | tn, chan_state, + ber); + 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); @@ -1578,6 +1611,15 @@ bfi: 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"); -- cgit v1.2.3 From 05597a7ddbf9e838caaeb36980cefaa728ab8eec Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 9 Apr 2013 10:55:37 +0200 Subject: TRX: Fixed typos tranceiver -> transceiver --- src/osmo-bts-trx/scheduler.c | 74 ++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 37 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9126e342..9d6a8a97 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -53,12 +53,12 @@ void *tall_bts_ctx; static struct gsm_bts *bts; /* clock states */ -static uint32_t tranceiver_lost; -uint32_t tranceiver_last_fn; -static struct timeval tranceiver_clock_tv; -static struct osmo_timer_list tranceiver_clock_timer; +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 tranceiver */ +/* clock advance for the transceiver */ uint32_t trx_clock_advance = 20; /* advance RTS to give some time for data processing. (especially PCU) */ @@ -2729,7 +2729,7 @@ static int trx_sched_fn(uint32_t fn) /* send time indication */ l1if_mph_time_ind(bts, fn); - /* advance frame number, so the tranceiver has more time until + /* advance frame number, so the transceiver has more time until * it must be transmitted. */ fn = (fn + trx_clock_advance) % 2715648; @@ -2777,17 +2777,17 @@ 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 = &tranceiver_clock_tv; + struct timeval tv_now, *tv_clock = &transceiver_clock_tv; int32_t elapsed; - /* check if tranceiver is still alive */ - if (tranceiver_lost++ == TRX_LOSS_FRAMES) { + /* 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 traneiver\n"); no_clock: - tranceiver_available = 0; + transceiver_available = 0; /* flush pending messages of transceiver */ /* close all logical channels and reset timeslots */ @@ -2799,7 +2799,7 @@ no_clock: } /* tell BSC */ - check_tranceiver_availability(bts, 0); + check_transceiver_availability(bts, 0); return; } @@ -2823,19 +2823,19 @@ no_clock: tv_clock->tv_sec++; tv_clock->tv_usec -= 1000000; } - tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; - trx_sched_fn(tranceiver_last_fn); + transceiver_last_fn = (transceiver_last_fn + 1) % 2715648; + trx_sched_fn(transceiver_last_fn); elapsed -= FRAME_DURATION_uS; } - osmo_timer_schedule(&tranceiver_clock_timer, 0, + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); } -/* receive clock from tranceiver */ +/* receive clock from transceiver */ int trx_sched_clock(uint32_t fn) { - struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + struct timeval tv_now, *tv_clock = &transceiver_clock_tv; int32_t elapsed; int32_t elapsed_fn; @@ -2843,54 +2843,54 @@ int trx_sched_clock(uint32_t fn) return 0; /* reset lost counter */ - tranceiver_lost = 0; + transceiver_lost = 0; gettimeofday(&tv_now, NULL); /* clock becomes valid */ - if (!tranceiver_available) { + if (!transceiver_available) { LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", fn); - tranceiver_available = 1; + transceiver_available = 1; - /* start provisioning tranceiver */ - l1if_provision_tranceiver(bts); + /* start provisioning transceiver */ + l1if_provision_transceiver(bts); /* tell BSC */ - check_tranceiver_availability(bts, 1); + check_transceiver_availability(bts, 1); new_clock: - tranceiver_last_fn = fn; - trx_sched_fn(tranceiver_last_fn); + transceiver_last_fn = fn; + trx_sched_fn(transceiver_last_fn); /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); - memset(&tranceiver_clock_timer, 0, - sizeof(tranceiver_clock_timer)); - tranceiver_clock_timer.cb = trx_ctrl_timer_cb; - tranceiver_clock_timer.data = bts; - osmo_timer_schedule(&tranceiver_clock_timer, 0, + 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(&tranceiver_clock_timer); + 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 - tranceiver_last_fn) % 2715648; + 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", tranceiver_last_fn, fn); + "new fn=%u\n", transceiver_last_fn, fn); goto new_clock; } @@ -2909,21 +2909,21 @@ new_clock: tv_clock->tv_usec -= 1000000; } /* set time to the time our next FN hast to be transmitted */ - osmo_timer_schedule(&tranceiver_clock_timer, 0, + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS * (1 - elapsed_fn)); return 0; } /* transmit what we still need to transmit */ - while (fn != tranceiver_last_fn) { - tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; - trx_sched_fn(tranceiver_last_fn); + 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(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS); return 0; } -- cgit v1.2.3 From fb04746bce94a5cdd3e95500c59ad019e552f9c0 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 6 Jun 2013 13:24:40 +0200 Subject: TRX: Report measurements --- src/osmo-bts-trx/scheduler.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9d6a8a97..20cafade 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -559,17 +559,19 @@ found_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) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float toa, + float ber, 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 = trx_chan_desc[chan].chan_nr | tn; + 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); @@ -583,6 +585,12 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* forward primitive */ l1sap_up(l1h->trx, l1sap); + /* process measurement */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) + l1if_process_meas_res(l1h->trx, chan_nr, + (l1h->trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)].rqd_ta + toa) * 4, + ber, rssi); + return 0; } @@ -657,7 +665,8 @@ got_msg: if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ if (++(l1h->chan_states[tn][chan].lost) > 1) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -128); + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, 0, 0, + -110); } /* alloc burst memory, if not already */ @@ -1205,6 +1214,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t 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 rc; @@ -1225,12 +1236,16 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *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; @@ -1274,7 +1289,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2_len = 23; return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, - *rssi_sum / *rssi_num); + *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1286,6 +1301,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t 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[54+1]; int rc; @@ -1305,12 +1322,16 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *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; @@ -1342,7 +1363,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1, *rssi_sum / *rssi_num); + l2, rc + 1, *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1447,7 +1468,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data + amr, 23, rssi); + tch_data + amr, 23, 0, 0, 0); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1601,7 +1622,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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); + tch_data + amr, 23, 0, 0, 0); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From 6527dffc94686672add10fe6b66f898c436ea34c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 09:08:44 +0200 Subject: TRX: Clear lchan state when resetting TRX --- src/osmo-bts-trx/scheduler.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 20cafade..bcde7f30 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -248,6 +248,9 @@ void trx_sched_exit(struct trx_l1h *l1h) 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; } } -- cgit v1.2.3 From b9a917a13880f9d6274409b9d3c9b56de484125f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 09:12:04 +0200 Subject: TRX: Handover access burst support --- src/osmo-bts-trx/scheduler.c | 47 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index bcde7f30..13e14e8b 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1173,17 +1173,20 @@ 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; - LOGP(DL1C, LOGL_NOTICE, "Received %s fn=%u toa=%.2f\n", + 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 rach frame at fn=%u " + LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u " "(%u/51)\n", fn, fn % 51); return 0; } @@ -1193,6 +1196,7 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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!!! @@ -1222,6 +1226,10 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; 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); @@ -1382,6 +1390,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, int rc, amr = 0; float ber; + /* 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); @@ -1522,6 +1534,10 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, int rc, amr = 0; float ber; + /* 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); @@ -2487,9 +2503,10 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* 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 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; @@ -2499,11 +2516,13 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, 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 on " - "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, - trx_chan_desc[i].name, l1h->trx->nr, tn); + 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; @@ -2522,6 +2541,19 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, } } + /* 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; } @@ -2721,7 +2753,8 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, } func(l1h, tn, fn, chan, bid, bits, rssi, toa); - } else if (chan != TRXC_RACH) { + } else if (chan != TRXC_RACH + && !l1h->chan_states[tn][chan].ho_rach_detect) { sbit_t spare[148]; memset(spare, 0, 148); -- cgit v1.2.3 From db0b93ac3967f12b519ca4e2528e30aacd59ce72 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 16:53:55 +0200 Subject: TRX: Disable handover burst detection when closing channel during detection --- src/osmo-bts-trx/scheduler.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 13e14e8b..9aa05390 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2471,6 +2471,7 @@ 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; @@ -2497,6 +2498,12 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, } } + /* 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; } -- cgit v1.2.3 From f5aaf523c58dd7bc2f57f840b717da359d08bb5c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 20 Jun 2013 20:10:46 +0200 Subject: TRX: If no cipher algorithm is given, or if it is a5/0, reset cipher state --- src/osmo-bts-trx/scheduler.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9aa05390..5a22c5c3 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2573,7 +2573,10 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, int rc = -EINVAL; struct trx_chan_state *chan_state; - if (algo < 0 || key_len > 8 || (algo && key_len != 8)) { + /* 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; -- cgit v1.2.3 From ee479133891317be37b7fc850e6cb945ef0c9f38 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 29 Jun 2013 21:42:58 +0200 Subject: TRX: No need to set mode and cipher for PDCH --- src/osmo-bts-trx/scheduler.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 5a22c5c3..bf5d7d96 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2518,6 +2518,10 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, 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) @@ -2573,6 +2577,10 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, 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; -- cgit v1.2.3 From f0072a8de82b48bb8927994a07efec7218f91640 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:42:48 +0200 Subject: TRX: Do not send burst on IDLE channels at TRX != C0 This is required, so the transceiver transmits no power. --- src/osmo-bts-trx/scheduler.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index bf5d7d96..3598d55c 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2827,9 +2827,14 @@ static int trx_sched_fn(uint32_t fn) /* 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); -- cgit v1.2.3 From ec6225e3e067221a3e2c19f6eda03453a2fe60e7 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 12 Aug 2013 09:29:40 +0200 Subject: TRX: Fixed chan_nr for SACCH/8(7) at scheduler --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 3598d55c..ef000144 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -193,7 +193,7 @@ struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { { 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, 0x68, 0x40, "SACCH/8(7)", 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 }, }; -- cgit v1.2.3 From 812fdd92c70679f4d5a46f9078a74e2a94cd74e4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 12 Aug 2013 09:31:27 +0200 Subject: TRX: Changed logging of unserved primitives from LOGL_NOTICE to LOGL_INFO --- src/osmo-bts-trx/scheduler.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index ef000144..6652909f 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -642,7 +642,7 @@ static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + 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); @@ -720,7 +720,7 @@ static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + 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); @@ -1029,7 +1029,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* mo message at all */ if (!msg_tch && !msg_facch) { - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + 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; @@ -1119,7 +1119,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* mo message at all */ if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + 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; -- cgit v1.2.3 From 178d618d5a7728c876ae66777351aea37f88cc5d Mon Sep 17 00:00:00 2001 From: Martin Hauke Date: Fri, 23 Aug 2013 23:10:16 +0200 Subject: TRX: fix some typos in comments --- src/osmo-bts-trx/scheduler.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6652909f..a02a2de1 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -44,7 +44,7 @@ #include "loops.h" /* Enable this to multiply TOA of RACH by 10. - * This usefull to check tenth of timing advances with RSSI test tool. + * 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 @@ -430,7 +430,7 @@ static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* - * TX on donlink + * TX on downlink */ /* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ @@ -1027,7 +1027,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(*bursts_p + 464, 0, 464); } - /* mo message at all */ + /* 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", @@ -1117,7 +1117,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } } - /* mo message at all */ + /* 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", @@ -2988,7 +2988,7 @@ new_clock: tv_clock->tv_sec++; tv_clock->tv_usec -= 1000000; } - /* set time to the time our next FN hast to be transmitted */ + /* 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)); -- cgit v1.2.3 From c241afa87c2fa8713a1fae89e6b599e011d45c28 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 29 Aug 2013 16:00:31 +0200 Subject: TRX: Add VTY option to allow setting RTS advance in frames RTS (ready-to-send) must be issued in advance, so BTS core and especially osmo-pcu can provide downlink data frames early enough. In some cases PCU might provide frames too late, so they must be dropped. If PCU provides frames too late, due to high system load, this "RTS advance" setting must be increased. --- src/osmo-bts-trx/scheduler.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a02a2de1..4cf6a683 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -527,11 +527,12 @@ free_msg: default: goto wrong_type; } - if (prim_fn > 20) { + 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. " - "(current fn=%u)\n", l1h->trx->nr, tn, prim_fn, - fn); + "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); -- cgit v1.2.3 From f66f5b3ddc5c894ae7ad4909cbd261c6920260b0 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 9 Sep 2013 16:17:37 +0200 Subject: TRX: Free bust buffer memory to when changing lchan type --- src/osmo-bts-trx/scheduler.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 4cf6a683..f4b0d9cc 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2496,6 +2496,15 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, 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; + } } } -- cgit v1.2.3 From 6fceaca584aa84214ccf747257344f1fe95caeee Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 6 Apr 2015 00:12:02 +0300 Subject: trx: Implement BER calculations. A known issue with this code is that BER is not updated for lost TCH frames, because osmo-trx doesn't send any indication for them and we don't have a callback to handle this. Otherwise the code seem to work fine. --- src/osmo-bts-trx/scheduler.c | 78 +++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 27 deletions(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f4b0d9cc..23bfebc0 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1,6 +1,7 @@ /* Scheduler for OsmoBTS-TRX */ /* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris * * All Rights Reserved * @@ -563,8 +564,7 @@ found_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 toa, - float ber, float rssi) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -589,12 +589,6 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* forward primitive */ l1sap_up(l1h->trx, l1sap); - /* process measurement */ - if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) - l1if_process_meas_res(l1h->trx, chan_nr, - (l1h->trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)].rqd_ta + toa) * 4, - ber, rssi); - return 0; } @@ -668,9 +662,16 @@ got_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) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, 0, 0, - -110); + 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 */ @@ -1225,6 +1226,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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 */ @@ -1290,7 +1292,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *mask = 0x0; /* decode */ - rc = xcch_decode(l2, *bursts_p); + 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, @@ -1300,8 +1302,11 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } else l2_len = 23; - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, - *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); + /* 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, @@ -1316,6 +1321,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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", @@ -1364,7 +1370,12 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *mask = 0x0; /* decode */ - rc = pdtch_decode(l2 + 1, *bursts_p, NULL); + 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], @@ -1375,7 +1386,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1, *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); + l2, rc + 1, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1389,7 +1400,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; - float ber; + int n_errors, n_bits_total; /* handle rach, if handover rach detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1437,10 +1448,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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); + 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); + 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, @@ -1450,11 +1461,11 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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, &ber); + &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, - ber); + (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) { @@ -1469,6 +1480,12 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, 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); @@ -1484,7 +1501,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data + amr, 23, 0, 0, 0); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1533,7 +1550,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; - float ber; + int n_errors, n_bits_total; /* handle rach, if handover rach detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1594,7 +1611,8 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, * Even FN ending at: 10,11,19,20,2,3 */ rc = tch_hr_decode(tch_data, *bursts_p, - (((fn + 26 - 10) % 26) >> 2) & 1); + (((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 @@ -1605,11 +1623,11 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, (((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, &ber); + &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, - ber); + (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) { @@ -1625,6 +1643,12 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } 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); @@ -1642,7 +1666,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, chan_state->ul_ongoing_facch = 1; compose_ph_data_ind(l1h, tn, (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan, - tch_data + amr, 23, 0, 0, 0); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From 68e8b2b1d5ea6e6236c277641cf565e7f99d7527 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Fri, 5 Jun 2015 00:54:03 -0400 Subject: trx: Fix typo in a log message. --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/osmo-bts-trx/scheduler.c') diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 23bfebc0..8d0c8343 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2898,7 +2898,7 @@ static void trx_ctrl_timer_cb(void *data) if (transceiver_lost++ == TRX_LOSS_FRAMES) { struct gsm_bts_trx *trx; - LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); + LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n"); no_clock: transceiver_available = 0; -- cgit v1.2.3