From patchwork Wed Feb 3 18:24:01 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Harald Welte X-Patchwork-Id: 578281 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by ozlabs.org (Postfix) with ESMTP id A439B1402EC for ; Thu, 4 Feb 2016 05:24:52 +1100 (AEDT) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id 4AADF14176; Wed, 3 Feb 2016 18:24:51 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from ganesha.gnumonks.org (ganesha.gnumonks.org [213.95.27.120]) by lists.osmocom.org (Postfix) with ESMTP id D8CDA14105 for ; Wed, 3 Feb 2016 18:24:39 +0000 (UTC) Received: from uucp by ganesha.gnumonks.org with local-bsmtp (Exim 4.72) (envelope-from ) id 1aR26Y-0004B9-J4; Wed, 03 Feb 2016 19:24:38 +0100 Received: from laforge by localhost.localdomain with local (Exim 4.86) (envelope-from ) id 1aR268-0003Op-20; Wed, 03 Feb 2016 19:24:12 +0100 From: laforge@gnumonks.org To: openbsc@lists.osmocom.org Subject: [PATCH 2/7] TRX: split scheduler in generic part and backend part Date: Wed, 3 Feb 2016 19:24:01 +0100 Message-Id: <1454523846-13022-2-git-send-email-laforge@gnumonks.org> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1454523846-13022-1-git-send-email-laforge@gnumonks.org> References: <1454523846-13022-1-git-send-email-laforge@gnumonks.org> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" From: Harald Welte the backend is performing the actual encoding and decoding functions, while the generic part constsits of the TDMA structures and generating the RTS.ind --- src/osmo-bts-trx/Makefile.am | 4 +- src/osmo-bts-trx/scheduler.c | 1485 +++------------------------------- src/osmo-bts-trx/scheduler_backend.h | 79 ++ src/osmo-bts-trx/scheduler_trx.c | 1234 ++++++++++++++++++++++++++++ 4 files changed, 1440 insertions(+), 1362 deletions(-) create mode 100644 src/osmo-bts-trx/scheduler_backend.h create mode 100644 src/osmo-bts-trx/scheduler_trx.c diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index 26bc8e2..1644af6 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -2,10 +2,10 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(ORTP_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) $(ORTP_LIBS) -EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h +EXTRA_DIST = trx_if.h l1_if.h scheduler.h scheduler_backend.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h bin_PROGRAMS = osmobts-trx -osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c scheduler_trx.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6d0f180..c904e62 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -31,28 +31,18 @@ #include #include -#include - #include #include #include #include -#include #include "l1_if.h" #include "scheduler.h" -#include "gsm0503_coding.h" +#include "scheduler_backend.h" #include "trx_if.h" #include "loops.h" -#include "amr.h" -#include "loops.h" -/* Enable this to multiply TOA of RACH by 10. - * This is usefull to check tenth of timing advances with RSSI test tool. - * Note that regular phones will not work when using this test! */ -//#define TA_TEST - -void *tall_bts_ctx; +extern void *tall_bts_ctx; static struct gsm_bts *bts; @@ -68,50 +58,12 @@ uint32_t trx_clock_advance = 20; /* advance RTS to give some time for data processing. (especially PCU) */ uint32_t trx_rts_advance = 5; /* about 20ms */ -typedef int trx_sched_rts_func(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan); -typedef ubit_t *trx_sched_dl_func(struct l1sched_trx *l1t, uint8_t tn, - uint32_t fn, enum trx_chan_type chan, uint8_t bid); -typedef int trx_sched_ul_func(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); - static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, enum trx_chan_type chan); static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, enum trx_chan_type chan); static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -static ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid); -static int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); -static int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); -static int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); -static int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); -static int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa); - /*! \brief Dummy Burst (TS 05.02 Chapter 5.2.6) */ static const ubit_t dummy_burst[148] = { 0,0,0, @@ -124,7 +76,7 @@ static const ubit_t dummy_burst[148] = { }; /*! \brief FCCH Burst (TS 05.02 Chapter 5.2.4) */ -static const ubit_t fcch_burst[148] = { +const ubit_t _sched_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, @@ -133,7 +85,7 @@ static const ubit_t fcch_burst[148] = { }; /*! \brief Training Sequences (TS 05.02 Chapter 5.2.3) */ -static const ubit_t tsc[8][26] = { +const ubit_t _sched_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, }, @@ -145,7 +97,7 @@ static const ubit_t tsc[8][26] = { }; /*! \brief SCH trainign sequence (TS 05.02 Chapter 5.2.5) */ -static const ubit_t sch_train[64] = { +const ubit_t _sched_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, }; @@ -154,27 +106,7 @@ static const ubit_t sch_train[64] = { * subchannel description structure */ -struct trx_chan_desc { - /*! \brief Is this on a PDCH (PS) ? */ - int pdch; - /*! \brief TRX Channel Type */ - enum trx_chan_type chan; - /*! \brief Channel Number (like in RSL) */ - uint8_t chan_nr; - /*! \brief Link ID (like in RSL) */ - uint8_t link_id; - /*! \brief Human-readable name */ - const char *name; - /*! \brief function to call when we want to generate RTS.req to L2 */ - trx_sched_rts_func *rts_fn; - /*! \brief function to call when DATA.req received from L2 */ - trx_sched_dl_func *dl_fn; - /*! \brief function to call when burst received from PHY */ - trx_sched_ul_func *ul_fn; - /*! \breif is this channel automatically active at start? */ - int auto_active; -}; -static const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { +const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { { 0, TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, { 0, TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, { 0, TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, @@ -280,243 +212,8 @@ void trx_sched_reset(struct l1sched_trx *l1t) trx_sched_init(l1t); } - -/* - * data request (from upper layer) - */ - -int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap) -{ - uint8_t tn = l1sap->u.data.chan_nr & 7; - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - - 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, l1t->trx->nr); - - if (!l1sap->oph.msg) - abort(); - - /* ignore empty frame */ - if (!msgb_l2len(l1sap->oph.msg)) { - msgb_free(l1sap->oph.msg); - return 0; - } - - msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg); - - return 0; -} - -int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap) -{ - uint8_t tn = l1sap->u.tch.chan_nr & 7; - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - - 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, l1t->trx->nr); - - if (!l1sap->oph.msg) - abort(); - - /* ignore empty frame */ - if (!msgb_l2len(l1sap->oph.msg)) { - msgb_free(l1sap->oph.msg); - return 0; - } - - msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg); - - return 0; -} - - -/* - * ready-to-send indication (to upper layer) - */ - -/* RTS for data frame */ -static int rts_data_fn(struct l1sched_trx *l1t, 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; - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - - /* 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, l1t->trx->nr); - - /* send clock information to loops process */ - if (L1SAP_IS_LINK_SACCH(link_id)) - trx_loop_sacch_clock(l1t, chan_nr, &l1ts->chan_state[chan]); - - /* generate prim */ - msg = l1sap_msgb_alloc(200); - if (!msg) - return -ENOMEM; - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, - PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = chan_nr; - l1sap->u.data.link_id = link_id; - l1sap->u.data.fn = fn; - - return l1sap_up(l1t->trx, l1sap); -} - -static int rts_tch_common(struct l1sched_trx *l1t, 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; - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - int rc = 0; - - /* get data for RTS indication */ - chan_nr = trx_chan_desc[chan].chan_nr | tn; - link_id = trx_chan_desc[chan].link_id; - - if (!chan_nr) { - LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing " - "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr); - return -ENODEV; - } - - LOGP(DL1C, LOGL_INFO, "TCH RTS.ind: chan=%s chan_nr=0x%02x " - "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, - chan_nr, fn, tn, l1t->trx->nr); - - /* only send, if FACCH is selected */ - if (facch) { - /* generate prim */ - msg = l1sap_msgb_alloc(200); - if (!msg) - return -ENOMEM; - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, - PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = chan_nr; - l1sap->u.data.link_id = link_id; - l1sap->u.data.fn = fn; - - rc = l1sap_up(l1t->trx, l1sap); - } - - /* dont send, if TCH is in signalling only mode */ - if (l1ts->chan_state[chan].rsl_cmode != RSL_CMOD_SPD_SIGN) { - /* generate prim */ - msg = l1sap_msgb_alloc(200); - if (!msg) - return -ENOMEM; - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, - PRIM_OP_INDICATION, msg); - l1sap->u.tch.chan_nr = chan_nr; - l1sap->u.tch.fn = fn; - - return l1sap_up(l1t->trx, l1sap); - } - - return rc; -} - -/* RTS for full rate traffic frame */ -static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan) -{ - /* TCH/F may include FACCH on every 4th burst */ - return rts_tch_common(l1t, tn, fn, chan, 1); -} - - -/* RTS for half rate traffic frame */ -static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan) -{ - /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */ - return rts_tch_common(l1t, tn, fn, chan, ((fn % 26) >> 2) & 1); -} - - -/* - * TX on downlink - */ - -/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ -static ubit_t *tx_idle_fn(struct l1sched_trx *l1t, 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, l1t->trx->nr); - - return NULL; -} - -static ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, 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, l1t->trx->nr); - - return (ubit_t *) fcch_burst; -} - -static ubit_t *tx_sch_fn(struct l1sched_trx *l1t, 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, l1t->trx->nr); - - /* create SB info from GSM time and BSIC */ - gsm_fn2gsmtime(&t, fn); - t3p = t.t3 / 10; - bsic = l1t->trx->bts->bsic; - sb_info[0] = - ((bsic & 0x3f) << 2) | - ((t.t1 & 0x600) >> 9); - sb_info[1] = - ((t.t1 & 0x1fe) >> 1); - sb_info[2] = - ((t.t1 & 0x001) << 7) | - ((t.t2 & 0x1f) << 2) | - ((t3p & 0x6) >> 1); - sb_info[3] = - (t3p & 0x1); - - /* encode bursts */ - 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 l1sched_trx *l1t, int8_t tn, uint32_t fn, - enum trx_chan_type chan) +struct msgb *_sched_dequeue_prim(struct l1sched_trx *l1t, int8_t tn, uint32_t fn, + enum trx_chan_type chan) { struct msgb *msg, *msg2; struct osmo_phsap_prim *l1sap; @@ -586,8 +283,8 @@ found_msg: return msg; } -static int compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) +int _sched_compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -616,8 +313,8 @@ static int compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, return 0; } -static int compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len) +int _sched_compose_tch_ind(struct l1sched_trx *l1t, 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; @@ -643,1110 +340,178 @@ static int compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, return 0; } -static ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid) -{ - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; - struct msgb *msg = NULL; /* make GCC happy */ - ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts; - static ubit_t bits[148]; - - /* send burst, if we already got a frame */ - if (bid > 0) { - if (!*bursts_p) - return NULL; - goto send_burst; - } - - /* get mac block from queue */ - msg = dequeue_prim(l1t, tn, fn, chan); - if (msg) - goto got_msg; - - LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " - "trx=%u ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); - -no_msg: - /* free burst memory */ - if (*bursts_p) { - talloc_free(*bursts_p); - *bursts_p = NULL; - } - return NULL; - -got_msg: - /* check validity of message */ - if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) { - LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg)); - /* free message */ - msgb_free(msg); - goto no_msg; - } - - /* handle loss detection of sacch */ - if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { - /* count and send BFI */ - if (++(l1ts->chan_state[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(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, - 456, 456, -110, 0); - - compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0, -110); - } - } - - /* alloc burst memory, if not already */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 464); - if (!*bursts_p) - return NULL; - } - - /* encode bursts */ - xcch_encode(*bursts_p, msg->l2h); - /* free message */ - msgb_free(msg); -send_burst: - /* compose burst */ - burst = *bursts_p + bid * 116; - memset(bits, 0, 3); - memcpy(bits + 3, burst, 58); - memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); - - return bits; -} +/* + * data request (from upper layer) + */ -static ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid) +int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap) { + uint8_t tn = l1sap->u.data.chan_nr & 7; struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; - struct msgb *msg = NULL; /* make GCC happy */ - ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts; - static ubit_t bits[148]; - int rc; - - /* send burst, if we already got a frame */ - if (bid > 0) { - if (!*bursts_p) - return NULL; - goto send_burst; - } - - /* get mac block from queue */ - msg = dequeue_prim(l1t, tn, fn, chan); - if (msg) - goto got_msg; - - LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " - "trx=%u ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); - -no_msg: - /* free burst memory */ - if (*bursts_p) { - talloc_free(*bursts_p); - *bursts_p = NULL; - } - return NULL; -got_msg: - /* alloc burst memory, if not already */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 464); - if (!*bursts_p) - return NULL; - } + 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, l1t->trx->nr); - /* encode bursts */ - rc = pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h); + if (!l1sap->oph.msg) + abort(); - /* check validity of message */ - if (rc) { - LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! " - "(len=%d)\n", rc); - /* free message */ - msgb_free(msg); - goto no_msg; + /* ignore empty frame */ + if (!msgb_l2len(l1sap->oph.msg)) { + msgb_free(l1sap->oph.msg); + return 0; } - /* free message */ - msgb_free(msg); - -send_burst: - /* compose burst */ - burst = *bursts_p + bid * 116; - memset(bits, 0, 3); - memcpy(bits + 3, burst, 58); - memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); + msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg); - return bits; + return 0; } -static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch, - struct msgb **_msg_facch, int codec_mode_request) +int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap) { + uint8_t tn = l1sap->u.tch.chan_nr & 7; struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - uint8_t rsl_cmode = chan_state->rsl_cmode; - uint8_t tch_mode = chan_state->tch_mode; - struct osmo_phsap_prim *l1sap; - /* handle loss detection of received TCH frames */ - if (rsl_cmode == RSL_CMOD_SPD_SPEECH - && ++(chan_state->lost) > 5) { - uint8_t tch_data[GSM_FR_BYTES]; - int len; - - LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending " - "BFI for %s\n", trx_chan_desc[chan].name); - - /* indicate bad frame */ - switch (tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* FR / HR */ - if (chan != TRXC_TCHF) { /* HR */ - tch_data[0] = 0x70; /* F = 0, FT = 111 */ - memset(tch_data + 1, 0, 14); - len = 15; - break; - } - memset(tch_data, 0, GSM_FR_BYTES); - len = GSM_FR_BYTES; - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - if (chan != TRXC_TCHF) - goto inval_mode1; - memset(tch_data, 0, GSM_EFR_BYTES); - len = GSM_EFR_BYTES; - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - len = 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(l1t, tn, 0, chan, tch_data, len); - break; - default: -inval_mode1: - LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " - "fix!\n"); - len = 0; - } - if (len) - compose_tch_ind(l1t, tn, 0, chan, tch_data, len); - } + 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, l1t->trx->nr); - /* get frame and unlink from queue */ - msg1 = dequeue_prim(l1t, tn, fn, chan); - msg2 = dequeue_prim(l1t, tn, fn, chan); - if (msg1) { - l1sap = msgb_l1sap_prim(msg1); - if (l1sap->oph.primitive == PRIM_TCH) { - msg_tch = msg1; - if (msg2) { - l1sap = msgb_l1sap_prim(msg2); - if (l1sap->oph.primitive == PRIM_TCH) { - 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; - } + if (!l1sap->oph.msg) + abort(); - /* check validity of message */ - if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) { - 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; + /* ignore empty frame */ + if (!msgb_l2len(l1sap->oph.msg)) { + msgb_free(l1sap->oph.msg); + return 0; } - /* check validity of message, get AMR ft and cmr */ - if (!msg_facch && msg_tch) { - int len; - uint8_t bfi, cmr_codec, ft_codec; - int cmr, ft, i; - - if (rsl_cmode != RSL_CMOD_SPD_SPEECH) { - LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, " - "because we are not in speech mode trx=%u " - "ts=%u at fn=%u.\n", trx_chan_desc[chan].name, - l1t->trx->nr, tn, fn); - goto free_bad_msg; - } - - switch (tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* FR / HR */ - if (chan != TRXC_TCHF) { /* HR */ - len = 15; - if (msgb_l2len(msg_tch) >= 1 - && (msg_tch->l2h[0] & 0xf0) != 0x00) { - LOGP(DL1C, LOGL_NOTICE, "%s " - "Transmitting 'bad " - "HR frame' trx=%u ts=%u at " - "fn=%u.\n", - trx_chan_desc[chan].name, - l1t->trx->nr, tn, fn); - goto free_bad_msg; - } - break; - } - len = GSM_FR_BYTES; - 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, - l1t->trx->nr, tn, fn); - goto free_bad_msg; - } - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - if (chan != TRXC_TCHF) - goto inval_mode2; - len = GSM_EFR_BYTES; - 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, - l1t->trx->nr, tn, fn); - goto free_bad_msg; - } - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - len = amr_decompose_payload(msg_tch->l2h, - msgb_l2len(msg_tch), &cmr_codec, &ft_codec, - &bfi); - cmr = -1; - ft = -1; - for (i = 0; i < chan_state->codecs; i++) { - if (chan_state->codec[i] == cmr_codec) - cmr = i; - if (chan_state->codec[i] == ft_codec) - ft = i; - } - if (cmr >= 0) { /* new request */ - chan_state->dl_cmr = cmr; - /* disable AMR loop */ - trx_loop_amr_set(chan_state, 0); - } else { - /* enable AMR loop */ - trx_loop_amr_set(chan_state, 1); - } - if (ft < 0) { - LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) " - " of RTP frame not in list. " - "trx=%u ts=%u\n", - trx_chan_desc[chan].name, ft_codec, - l1t->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, - l1t->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, - l1t->trx->nr, tn, fn); - goto free_bad_msg; - } - break; - default: -inval_mode2: - LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " - "fix!\n"); - goto free_bad_msg; - } - if (len < 0) { - LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR " - "payload\n"); - goto free_bad_msg; - } - if (msgb_l2len(msg_tch) != len) { - LOGP(DL1C, LOGL_ERROR, "Cannot send payload with " - "invalid length! (expecing %d, received %d)\n", - len, msgb_l2len(msg_tch)); -free_bad_msg: - /* free message */ - msgb_free(msg_tch); - msg_tch = NULL; - goto send_frame; - } - } + msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg); -send_frame: - *_msg_tch = msg_tch; - *_msg_facch = msg_facch; + return 0; } -static ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid) -{ - struct msgb *msg_tch = NULL, *msg_facch = NULL; - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - uint8_t tch_mode = chan_state->tch_mode; - ubit_t *burst, **bursts_p = &chan_state->dl_bursts; - static ubit_t bits[148]; - - /* send burst, if we already got a frame */ - if (bid > 0) { - if (!*bursts_p) - return NULL; - goto send_burst; - } - tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch, - (((fn + 4) % 26) >> 2) & 1); - - /* alloc burst memory, if not already, - * otherwise shift buffer by 4 bursts for interleaving */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 928); - if (!*bursts_p) - return NULL; - } else { - memcpy(*bursts_p, *bursts_p + 464, 464); - memset(*bursts_p + 464, 0, 464); - } - - /* no message at all */ - if (!msg_tch && !msg_facch) { - LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " - "trx=%u ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); - goto send_burst; - } - - /* encode bursts (priorize FACCH) */ - if (msg_facch) - tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), - 1); - else if (tch_mode == GSM48_CMODE_SPEECH_AMR) - /* the first FN 4,13,21 defines that CMI is included in frame, - * the first FN 0,8,17 defines that CMR is included in frame. - */ - tch_afs_encode(*bursts_p, msg_tch->l2h + 2, - msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, - chan_state->codec, chan_state->codecs, - chan_state->dl_ft, - chan_state->dl_cmr); - else - tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); - - /* free message */ - if (msg_tch) - msgb_free(msg_tch); - if (msg_facch) - msgb_free(msg_facch); - -send_burst: - /* compose burst */ - burst = *bursts_p + bid * 116; - memset(bits, 0, 3); - memcpy(bits + 3, burst, 58); - memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); - - return bits; -} +/* + * ready-to-send indication (to upper layer) + */ -static ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid) +/* RTS for data frame */ +static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) { - struct msgb *msg_tch = NULL, *msg_facch = NULL; + uint8_t chan_nr, link_id; + struct msgb *msg; + struct osmo_phsap_prim *l1sap; struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - uint8_t tch_mode = chan_state->tch_mode; - ubit_t *burst, **bursts_p = &chan_state->dl_bursts; - static ubit_t bits[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(l1t, 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); - } - } + /* get data for RTS indication */ + chan_nr = trx_chan_desc[chan].chan_nr | tn; + link_id = trx_chan_desc[chan].link_id; - /* no message at all */ - if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { - LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " - "trx=%u ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); - goto send_burst; + 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; } - /* encode bursts (priorize FACCH) */ - if (msg_facch) { - tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); - chan_state->dl_ongoing_facch = 1; /* first of two tch frames */ - } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */ - chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */ - else if (tch_mode == GSM48_CMODE_SPEECH_AMR) - /* the first FN 4,13,21 or 5,14,22 defines that CMI is included - * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is - * included in frame. */ - tch_ahs_encode(*bursts_p, msg_tch->l2h + 2, - msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, - chan_state->codec, chan_state->codecs, - chan_state->dl_ft, - chan_state->dl_cmr); - else - tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); - - /* free message */ - if (msg_tch) - msgb_free(msg_tch); - if (msg_facch) - msgb_free(msg_facch); - -send_burst: - /* compose burst */ - burst = *bursts_p + bid * 116; - memset(bits, 0, 3); - memcpy(bits + 3, burst, 58); - memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); - - return bits; -} - - -/* - * RX on uplink (indication to upper layer) - */ - -static int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa) -{ - uint8_t chan_nr; - struct osmo_phsap_prim l1sap; - uint8_t ra; - int rc; - - chan_nr = trx_chan_desc[chan].chan_nr | tn; - - LOGP(DL1C, LOGL_NOTICE, "Received Access Burst on %s fn=%u toa=%.2f\n", - trx_chan_desc[chan].name, fn, toa); + 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, l1t->trx->nr); - /* decode */ - rc = rach_decode(&ra, bits + 8 + 41, l1t->trx->bts->bsic); - if (rc) { - LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u " - "(%u/51)\n", fn, fn % 51); - return 0; - } + /* send clock information to loops process */ + if (L1SAP_IS_LINK_SACCH(link_id)) + trx_loop_sacch_clock(l1t, chan_nr, &l1ts->chan_state[chan]); - /* compose primitive */ /* generate prim */ - memset(&l1sap, 0, sizeof(l1sap)); - osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, - NULL); - l1sap.u.rach_ind.chan_nr = chan_nr; - l1sap.u.rach_ind.ra = ra; -#ifdef TA_TEST -#warning TIMING ADVANCE TEST-HACK IS ENABLED!!! - toa *= 10; -#endif - l1sap.u.rach_ind.acc_delay = (toa >= 0) ? toa : 0; - l1sap.u.rach_ind.fn = fn; - - /* forward primitive */ - l1sap_up(l1t->trx, &l1sap); + 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 0; + return l1sap_up(l1t->trx, l1sap); } -/*! \brief a single burst was received by the PHY, process it */ -static int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa) +static int rts_tch_common(struct l1sched_trx *l1t, 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; struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint32_t *first_fn = &chan_state->ul_first_fn; - uint8_t *mask = &chan_state->ul_mask; - float *rssi_sum = &chan_state->rssi_sum; - uint8_t *rssi_num = &chan_state->rssi_num; - float *toa_sum = &chan_state->toa_sum; - uint8_t *toa_num = &chan_state->toa_num; - uint8_t l2[GSM_MACBLOCK_LEN], l2_len; - int n_errors, n_bits_total; - int rc; - - /* handle rach, if handover rach detection is turned on */ - if (chan_state->ho_rach_detect == 1) - return rx_rach_fn(l1t, 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, l1t->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; - } + int rc = 0; - /* clear burst & store frame number of first burst */ - if (bid == 0) { - memset(*bursts_p, 0, 464); - *mask = 0x0; - *first_fn = fn; - *rssi_sum = 0; - *rssi_num = 0; - *toa_sum = 0; - *toa_num = 0; - } + /* get data for RTS indication */ + chan_nr = trx_chan_desc[chan].chan_nr | tn; + link_id = trx_chan_desc[chan].link_id; - /* update mask + rssi */ - *mask |= (1 << bid); - *rssi_sum += rssi; - (*rssi_num)++; - *toa_sum += toa; - (*toa_num)++; - - /* copy burst to buffer of 4 bursts */ - burst = *bursts_p + bid * 116; - memcpy(burst, bits + 3, 58); - memcpy(burst + 58, bits + 87, 58); - - /* send burst information to loops process */ - if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { - trx_loop_sacch_input(l1t, trx_chan_desc[chan].chan_nr | tn, - chan_state, rssi, toa); + 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; } - /* wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete data frame at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % l1ts->mf_period, l1ts->mf_period, - trx_chan_desc[chan].name); - - /* we require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - return 0; - } - } - *mask = 0x0; - - /* decode */ - rc = xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total); - if (rc) { - LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u " - "(%u/%u) for %s\n", *first_fn, - (*first_fn) % l1ts->mf_period, l1ts->mf_period, - trx_chan_desc[chan].name); - l2_len = 0; - } else - l2_len = GSM_MACBLOCK_LEN; - - /* Send uplnk measurement information to L2 */ - l1if_process_meas_res(l1t->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(l1t, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num); -} + 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, l1t->trx->nr); -static int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa) -{ - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint8_t *mask = &chan_state->ul_mask; - float *rssi_sum = &chan_state->rssi_sum; - uint8_t *rssi_num = &chan_state->rssi_num; - float *toa_sum = &chan_state->toa_sum; - uint8_t *toa_num = &chan_state->toa_num; - uint8_t l2[54+1]; - int n_errors, n_bits_total; - int rc; - - LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n", - trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid); - - /* alloc burst memory, if not already */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 464); - if (!*bursts_p) + /* only send, if FACCH is selected */ + if (facch) { + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) return -ENOMEM; - } - - /* clear burst */ - if (bid == 0) { - memset(*bursts_p, 0, 464); - *mask = 0x0; - *rssi_sum = 0; - *rssi_num = 0; - *toa_sum = 0; - *toa_num = 0; - } - - /* update mask + rssi */ - *mask |= (1 << bid); - *rssi_sum += rssi; - (*rssi_num)++; - *toa_sum += toa; - (*toa_num)++; - - /* copy burst to buffer of 4 bursts */ - burst = *bursts_p + bid * 116; - memcpy(burst, bits + 3, 58); - memcpy(burst + 58, bits + 87, 58); - - /* wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block " - "ending at fn=%u (%u/%u) for %s\n", fn, - fn % l1ts->mf_period, l1ts->mf_period, - trx_chan_desc[chan].name); - } - *mask = 0x0; - - /* decode */ - rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total); - - /* Send uplnk measurement information to L2 */ - l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, - n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num); + 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; - if (rc <= 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block ending at " - "fn=%u (%u/%u) for %s\n", fn, fn % l1ts->mf_period, - l1ts->mf_period, trx_chan_desc[chan].name); - return 0; + rc = l1sap_up(l1t->trx, l1sap); } - l2[0] = 7; /* valid frame */ - - return compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 3) % GSM_HYPERFRAME, chan, - l2, rc + 1, *rssi_sum / *rssi_num); -} - -static int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa) -{ - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint8_t *mask = &chan_state->ul_mask; - uint8_t rsl_cmode = chan_state->rsl_cmode; - uint8_t tch_mode = chan_state->tch_mode; - uint8_t tch_data[128]; /* just to be safe */ - int rc, amr = 0; - int n_errors, n_bits_total; - - /* handle rach, if handover rach detection is turned on */ - if (chan_state->ho_rach_detect == 1) - return rx_rach_fn(l1t, 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, l1t->trx->nr, bid); - - /* alloc burst memory, if not already */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 928); - if (!*bursts_p) + /* dont send, if TCH is in signalling only mode */ + if (l1ts->chan_state[chan].rsl_cmode != RSL_CMOD_SPD_SIGN) { + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) return -ENOMEM; - } - - /* clear burst */ - if (bid == 0) { - memset(*bursts_p + 464, 0, 464); - *mask = 0x0; - } - - /* update mask */ - *mask |= (1 << bid); - - /* copy burst to end of buffer of 8 bursts */ - burst = *bursts_p + bid * 116 + 464; - memcpy(burst, bits + 3, 58); - memcpy(burst + 58, bits + 87, 58); - - /* wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " - "at fn=%u (%u/%u) for %s\n", fn, - fn % l1ts->mf_period, l1ts->mf_period, - trx_chan_desc[chan].name); - } - *mask = 0x0; - - /* decode - * also shift buffer by 4 bursts for interleaving */ - switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 - : tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* FR */ - rc = tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - rc = tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /* the first FN 0,8,17 defines that CMI is included in frame, - * the first FN 4,13,21 defines that CMR is included in frame. - * NOTE: A frame ends 7 FN after start. - */ - rc = tch_afs_decode(tch_data + 2, *bursts_p, - (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec, - chan_state->codecs, &chan_state->ul_ft, - &chan_state->ul_cmr, &n_errors, &n_bits_total); - if (rc) - trx_loop_amr_input(l1t, - trx_chan_desc[chan].chan_nr | tn, chan_state, - (float)n_errors/(float)n_bits_total); - amr = 2; /* we store tch_data + 2 header bytes */ - /* only good speech frames get rtp header */ - if (rc != GSM_MACBLOCK_LEN && rc >= 4) { - rc = amr_compose_payload(tch_data, - chan_state->codec[chan_state->ul_cmr], - chan_state->codec[chan_state->ul_ft], 0); - } - break; - default: - LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", - tch_mode); - return -EINVAL; - } - memcpy(*bursts_p, *bursts_p + 464, 464); - - /* Send uplnk measurement information to L2 */ - l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, - n_errors, n_bits_total, rssi, toa); - - /* Check if the frame is bad */ - if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " - "fn=%u for %s\n", fn, trx_chan_desc[chan].name); - goto bfi; - } - if (rc < 4) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " - "fn=%u for %s with codec mode %d (out of range)\n", - fn, trx_chan_desc[chan].name, rc); - goto bfi; - } + 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; - /* FACCH */ - if (rc == GSM_MACBLOCK_LEN) { - compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan, - tch_data + amr, GSM_MACBLOCK_LEN, rssi); -bfi: - if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { - /* indicate bad frame */ - switch (tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* FR */ - memset(tch_data, 0, GSM_FR_BYTES); - rc = GSM_FR_BYTES; - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - memset(tch_data, 0, GSM_EFR_BYTES); - rc = GSM_EFR_BYTES; - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - rc = amr_compose_payload(tch_data, - chan_state->codec[chan_state->dl_cmr], - chan_state->codec[chan_state->dl_ft], - 1); - if (rc < 2) - break; - memset(tch_data + 2, 0, rc - 2); - break; - default: - LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " - "please fix!\n"); - return -EINVAL; - } - } + return l1sap_up(l1t->trx, l1sap); } - if (rsl_cmode != RSL_CMOD_SPD_SPEECH) - return 0; - - /* TCH or BFI */ - return compose_tch_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan, - tch_data, rc); + return rc; } -static int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, - float toa) +/* RTS for full rate traffic frame */ +static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) { - struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); - struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; - sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint8_t *mask = &chan_state->ul_mask; - uint8_t rsl_cmode = chan_state->rsl_cmode; - uint8_t tch_mode = chan_state->tch_mode; - uint8_t tch_data[128]; /* just to be safe */ - int rc, amr = 0; - int n_errors, n_bits_total; - - /* handle rach, if handover rach detection is turned on */ - if (chan_state->ho_rach_detect == 1) - return rx_rach_fn(l1t, 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, l1t->trx->nr, bid); - - /* alloc burst memory, if not already */ - if (!*bursts_p) { - *bursts_p = talloc_zero_size(tall_bts_ctx, 696); - if (!*bursts_p) - return -ENOMEM; - } - - /* clear burst */ - if (bid == 0) { - memset(*bursts_p + 464, 0, 232); - *mask = 0x0; - } - - /* update mask */ - *mask |= (1 << bid); - - /* copy burst to end of buffer of 6 bursts */ - burst = *bursts_p + bid * 116 + 464; - memcpy(burst, bits + 3, 58); - memcpy(burst + 58, bits + 87, 58); - - /* wait until complete set of bursts */ - if (bid != 1) - return 0; - - /* check for complete set of bursts */ - if ((*mask & 0x3) != 0x3) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " - "at fn=%u (%u/%u) for %s\n", fn, - fn % l1ts->mf_period, l1ts->mf_period, - trx_chan_desc[chan].name); - } - *mask = 0x0; - - /* skip second of two TCH frames of FACCH was received */ - if (chan_state->ul_ongoing_facch) { - chan_state->ul_ongoing_facch = 0; - memcpy(*bursts_p, *bursts_p + 232, 232); - memcpy(*bursts_p + 232, *bursts_p + 464, 232); - goto bfi; - } - - /* decode - * also shift buffer by 4 bursts for interleaving */ - switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 - : tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* HR or signalling */ - /* Note on FN-10: If we are at FN 10, we decoded an even aligned - * TCH/FACCH frame, because our burst buffer carries 6 bursts. - * Even FN ending at: 10,11,19,20,2,3 - */ - rc = tch_hr_decode(tch_data, *bursts_p, - (((fn + 26 - 10) % 26) >> 2) & 1, - &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /* the first FN 0,8,17 or 1,9,18 defines that CMI is included - * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR - * is included in frame. - */ - rc = tch_ahs_decode(tch_data + 2, *bursts_p, - (((fn + 26 - 10) % 26) >> 2) & 1, - (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec, - chan_state->codecs, &chan_state->ul_ft, - &chan_state->ul_cmr, &n_errors, &n_bits_total); - if (rc) - trx_loop_amr_input(l1t, - trx_chan_desc[chan].chan_nr | tn, chan_state, - (float)n_errors/(float)n_bits_total); - amr = 2; /* we store tch_data + 2 two */ - /* only good speech frames get rtp header */ - if (rc != GSM_MACBLOCK_LEN && rc >= 4) { - rc = amr_compose_payload(tch_data, - chan_state->codec[chan_state->ul_cmr], - chan_state->codec[chan_state->ul_ft], 0); - } - break; - default: - LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", - tch_mode); - return -EINVAL; - } - memcpy(*bursts_p, *bursts_p + 232, 232); - memcpy(*bursts_p + 232, *bursts_p + 464, 232); - - /* Send uplnk measurement information to L2 */ - l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, - n_errors, n_bits_total, rssi, toa); - - /* Check if the frame is bad */ - if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " - "fn=%u for %s\n", fn, trx_chan_desc[chan].name); - goto bfi; - } - if (rc < 4) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " - "fn=%u for %s with codec mode %d (out of range)\n", - fn, trx_chan_desc[chan].name, rc); - goto bfi; - } - - /* FACCH */ - if (rc == GSM_MACBLOCK_LEN) { - chan_state->ul_ongoing_facch = 1; - compose_ph_data_ind(l1t, tn, - (fn + GSM_HYPERFRAME - 10 - ((fn % 26) >= 19)) % GSM_HYPERFRAME, chan, - tch_data + amr, GSM_MACBLOCK_LEN, rssi); -bfi: - if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { - /* indicate bad frame */ - switch (tch_mode) { - case GSM48_CMODE_SPEECH_V1: /* HR */ - tch_data[0] = 0x70; /* F = 0, FT = 111 */ - memset(tch_data + 1, 0, 14); - rc = 15; - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - rc = amr_compose_payload(tch_data, - chan_state->codec[chan_state->dl_cmr], - chan_state->codec[chan_state->dl_ft], - 1); - if (rc < 2) - break; - memset(tch_data + 2, 0, rc - 2); - break; - default: - LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " - "please fix!\n"); - return -EINVAL; - } - } - } + /* TCH/F may include FACCH on every 4th burst */ + return rts_tch_common(l1t, tn, fn, chan, 1); +} - 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(l1t, tn, - (fn + GSM_HYPERFRAME - 10 - ((fn%26)==19) - ((fn%26)==20)) % GSM_HYPERFRAME, - chan, tch_data, rc); +/* RTS for half rate traffic frame */ +static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) +{ + /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */ + return rts_tch_common(l1t, tn, fn, chan, ((fn % 26) >> 2) & 1); } - /* * multiframe structure */ diff --git a/src/osmo-bts-trx/scheduler_backend.h b/src/osmo-bts-trx/scheduler_backend.h new file mode 100644 index 0000000..ea3e620 --- /dev/null +++ b/src/osmo-bts-trx/scheduler_backend.h @@ -0,0 +1,79 @@ +#pragma once + +typedef int trx_sched_rts_func(struct l1sched_trx *l1t, uint8_t tn, + uint32_t fn, enum trx_chan_type chan); + +typedef ubit_t *trx_sched_dl_func(struct l1sched_trx *l1t, uint8_t tn, + uint32_t fn, enum trx_chan_type chan, + uint8_t bid); + +typedef int trx_sched_ul_func(struct l1sched_trx *l1t, uint8_t tn, + uint32_t fn, enum trx_chan_type chan, + uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); + +struct trx_chan_desc { + /*! \brief Is this on a PDCH (PS) ? */ + int pdch; + /*! \brief TRX Channel Type */ + enum trx_chan_type chan; + /*! \brief Channel Number (like in RSL) */ + uint8_t chan_nr; + /*! \brief Link ID (like in RSL) */ + uint8_t link_id; + /*! \brief Human-readable name */ + const char *name; + /*! \brief function to call when we want to generate RTS.req to L2 */ + trx_sched_rts_func *rts_fn; + /*! \brief function to call when DATA.req received from L2 */ + trx_sched_dl_func *dl_fn; + /*! \brief function to call when burst received from PHY */ + trx_sched_ul_func *ul_fn; + /*! \breif is this channel automatically active at start? */ + int auto_active; +}; +extern const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX]; + +extern const ubit_t _sched_tsc[8][26]; +const ubit_t _sched_fcch_burst[148]; +const ubit_t _sched_sch_train[64]; + +struct msgb *_sched_dequeue_prim(struct l1sched_trx *l1t, int8_t tn, uint32_t fn, + enum trx_chan_type chan); + +int _sched_compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi); + +int _sched_compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len); + +ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); +int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); +int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); +int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); +int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); + diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c new file mode 100644 index 0000000..ec53fbf --- /dev/null +++ b/src/osmo-bts-trx/scheduler_trx.c @@ -0,0 +1,1234 @@ +/* Scheduler worker functiosn for OsmoBTS-TRX */ + +/* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * (C) 2015 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "l1_if.h" +#include "scheduler.h" +#include "scheduler_backend.h" +#include "gsm0503_coding.h" +#include "trx_if.h" +#include "loops.h" +#include "amr.h" + +extern void *tall_bts_ctx; + +/* Enable this to multiply TOA of RACH by 10. + * This is usefull to check tenth of timing advances with RSSI test tool. + * Note that regular phones will not work when using this test! */ +//#define TA_TEST + + +/* + * TX on downlink + */ + +/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ +ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + 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, l1t->trx->nr); + + return NULL; +} + +ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, 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, l1t->trx->nr); + + /* BURST BYPASS */ + + return (ubit_t *) _sched_fcch_burst; +} + +ubit_t *tx_sch_fn(struct l1sched_trx *l1t, 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, l1t->trx->nr); + + /* BURST BYPASS */ + + /* create SB info from GSM time and BSIC */ + gsm_fn2gsmtime(&t, fn); + t3p = t.t3 / 10; + bsic = l1t->trx->bts->bsic; + sb_info[0] = + ((bsic & 0x3f) << 2) | + ((t.t1 & 0x600) >> 9); + sb_info[1] = + ((t.t1 & 0x1fe) >> 1); + sb_info[2] = + ((t.t1 & 0x001) << 7) | + ((t.t2 & 0x1f) << 2) | + ((t3p & 0x6) >> 1); + sb_info[3] = + (t3p & 0x1); + + /* encode bursts */ + sch_encode(burst, sb_info); + + /* compose burst */ + memset(bits, 0, 3); + memcpy(bits + 3, burst, 39); + memcpy(bits + 42, _sched_sch_train, 64); + memcpy(bits + 106, burst + 39, 39); + memset(bits + 145, 0, 3); + + return bits; +} + +ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; + struct msgb *msg = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + /* get mac block from queue */ + msg = _sched_dequeue_prim(l1t, tn, fn, chan); + if (msg) + goto got_msg; + + 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, l1t->trx->nr, tn, fn); + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + /* check validity of message */ + if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) { + LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg)); + /* free message */ + msgb_free(msg); + goto no_msg; + } + + /* BURST BYPASS */ + + /* handle loss detection of sacch */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { + /* count and send BFI */ + if (++(l1ts->chan_state[chan].lost) > 1) { + /* TODO: Should we pass old TOA here? Otherwise we risk + * unnecessary decreasing TA */ + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, + 456, 456, -110, 0); + + _sched_compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0, -110); + } + } + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return NULL; + } + + /* encode bursts */ + xcch_encode(*bursts_p, msg->l2h); + + /* free message */ + msgb_free(msg); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); + + return bits; +} + +ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; + struct msgb *msg = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts; + static ubit_t bits[148]; + int rc; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + /* get mac block from queue */ + msg = _sched_dequeue_prim(l1t, tn, fn, chan); + if (msg) + goto got_msg; + + 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, l1t->trx->nr, tn, fn); + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + /* BURST BYPASS */ + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return NULL; + } + + /* encode bursts */ + rc = pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h); + + /* check validity of message */ + if (rc) { + LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! " + "(len=%d)\n", rc); + /* free message */ + msgb_free(msg); + goto no_msg; + } + + /* free message */ + msgb_free(msg); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); + + return bits; +} + +static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch, + struct msgb **_msg_facch, int codec_mode_request) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; + struct osmo_phsap_prim *l1sap; + + /* handle loss detection of received TCH frames */ + if (rsl_cmode == RSL_CMOD_SPD_SPEECH + && ++(chan_state->lost) > 5) { + uint8_t tch_data[GSM_FR_BYTES]; + int len; + + LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending " + "BFI for %s\n", trx_chan_desc[chan].name); + + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + len = 15; + break; + } + memset(tch_data, 0, GSM_FR_BYTES); + len = GSM_FR_BYTES; + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + if (chan != TRXC_TCHF) + goto inval_mode1; + memset(tch_data, 0, GSM_EFR_BYTES); + len = GSM_EFR_BYTES; + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = 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); + _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len); + break; + default: +inval_mode1: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " + "fix!\n"); + len = 0; + } + if (len) + _sched_compose_tch_ind(l1t, tn, 0, chan, tch_data, len); + } + + /* get frame and unlink from queue */ + msg1 = _sched_dequeue_prim(l1t, tn, fn, chan); + msg2 = _sched_dequeue_prim(l1t, tn, fn, chan); + if (msg1) { + l1sap = msgb_l1sap_prim(msg1); + if (l1sap->oph.primitive == PRIM_TCH) { + msg_tch = msg1; + if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive == PRIM_TCH) { + LOGP(DL1C, LOGL_FATAL, "TCH twice, " + "please FIX! "); + msgb_free(msg2); + } else + msg_facch = msg2; + } + } else { + msg_facch = msg1; + if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive != PRIM_TCH) { + LOGP(DL1C, LOGL_FATAL, "FACCH twice, " + "please FIX! "); + msgb_free(msg2); + } else + msg_tch = msg2; + } + } + } else if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive == PRIM_TCH) + msg_tch = msg2; + else + msg_facch = msg2; + } + + /* check validity of message */ + if (msg_facch && msgb_l2len(msg_facch) != GSM_MACBLOCK_LEN) { + LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg_facch)); + /* free message */ + msgb_free(msg_facch); + msg_facch = NULL; + } + + /* check validity of message, get AMR ft and cmr */ + if (!msg_facch && msg_tch) { + int len; + uint8_t bfi, cmr_codec, ft_codec; + int cmr, ft, i; + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) { + LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, " + "because we are not in speech mode trx=%u " + "ts=%u at fn=%u.\n", trx_chan_desc[chan].name, + l1t->trx->nr, tn, fn); + goto free_bad_msg; + } + + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + len = 15; + if (msgb_l2len(msg_tch) >= 1 + && (msg_tch->l2h[0] & 0xf0) != 0x00) { + LOGP(DL1C, LOGL_NOTICE, "%s " + "Transmitting 'bad " + "HR frame' trx=%u ts=%u at " + "fn=%u.\n", + trx_chan_desc[chan].name, + l1t->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + } + len = GSM_FR_BYTES; + 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, + l1t->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + if (chan != TRXC_TCHF) + goto inval_mode2; + len = GSM_EFR_BYTES; + 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, + l1t->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = amr_decompose_payload(msg_tch->l2h, + msgb_l2len(msg_tch), &cmr_codec, &ft_codec, + &bfi); + cmr = -1; + ft = -1; + for (i = 0; i < chan_state->codecs; i++) { + if (chan_state->codec[i] == cmr_codec) + cmr = i; + if (chan_state->codec[i] == ft_codec) + ft = i; + } + if (cmr >= 0) { /* new request */ + chan_state->dl_cmr = cmr; + /* disable AMR loop */ + trx_loop_amr_set(chan_state, 0); + } else { + /* enable AMR loop */ + trx_loop_amr_set(chan_state, 1); + } + if (ft < 0) { + LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) " + " of RTP frame not in list. " + "trx=%u ts=%u\n", + trx_chan_desc[chan].name, ft_codec, + l1t->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, + l1t->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, + l1t->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + default: +inval_mode2: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " + "fix!\n"); + goto free_bad_msg; + } + if (len < 0) { + LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR " + "payload\n"); + goto free_bad_msg; + } + if (msgb_l2len(msg_tch) != len) { + LOGP(DL1C, LOGL_ERROR, "Cannot send payload with " + "invalid length! (expecing %d, received %d)\n", + len, msgb_l2len(msg_tch)); +free_bad_msg: + /* free message */ + msgb_free(msg_tch); + msg_tch = NULL; + goto send_frame; + } + } + +send_frame: + *_msg_tch = msg_tch; + *_msg_facch = msg_facch; +} + +ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + uint8_t tch_mode = chan_state->tch_mode; + ubit_t *burst, **bursts_p = &chan_state->dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch, + (((fn + 4) % 26) >> 2) & 1); + + /* BURST BYPASS */ + + /* alloc burst memory, if not already, + * otherwise shift buffer by 4 bursts for interleaving */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 928); + if (!*bursts_p) + return NULL; + } else { + memcpy(*bursts_p, *bursts_p + 464, 464); + memset(*bursts_p + 464, 0, 464); + } + + /* no message at all */ + if (!msg_tch && !msg_facch) { + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); + goto send_burst; + } + + /* encode bursts (priorize FACCH) */ + if (msg_facch) + tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), + 1); + else if (tch_mode == GSM48_CMODE_SPEECH_AMR) + /* the first FN 4,13,21 defines that CMI is included in frame, + * the first FN 0,8,17 defines that CMR is included in frame. + */ + tch_afs_encode(*bursts_p, msg_tch->l2h + 2, + msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, + chan_state->codec, chan_state->codecs, + chan_state->dl_ft, + chan_state->dl_cmr); + else + tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); + + /* free message */ + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); + + return bits; +} + +ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn]; + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + uint8_t tch_mode = chan_state->tch_mode; + ubit_t *burst, **bursts_p = &chan_state->dl_bursts; + static ubit_t bits[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(l1t, 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; + } + + /* BURST BYPASS */ + + /* alloc burst memory, if not already, + * otherwise shift buffer by 2 bursts for interleaving */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return NULL; + } else { + memcpy(*bursts_p, *bursts_p + 232, 232); + if (chan_state->dl_ongoing_facch) { + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + memset(*bursts_p + 464, 0, 232); + } else { + memset(*bursts_p + 232, 0, 232); + } + } + + /* no message at all */ + if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1t->trx->nr, tn, fn); + goto send_burst; + } + + /* encode bursts (priorize FACCH) */ + if (msg_facch) { + tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + chan_state->dl_ongoing_facch = 1; /* first of two tch frames */ + } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */ + chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */ + else if (tch_mode == GSM48_CMODE_SPEECH_AMR) + /* the first FN 4,13,21 or 5,14,22 defines that CMI is included + * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is + * included in frame. */ + tch_ahs_encode(*bursts_p, msg_tch->l2h + 2, + msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, + chan_state->codec, chan_state->codecs, + chan_state->dl_ft, + chan_state->dl_cmr); + else + tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); + + /* free message */ + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, _sched_tsc[gsm_ts_tsc(ts)], 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, l1t->trx->nr, bid); + + return bits; +} + + +/* + * RX on uplink (indication to upper layer) + */ + +int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) +{ + uint8_t chan_nr; + struct osmo_phsap_prim l1sap; + uint8_t ra; + int rc; + + chan_nr = trx_chan_desc[chan].chan_nr | tn; + + LOGP(DL1C, LOGL_NOTICE, "Received Access Burst on %s fn=%u toa=%.2f\n", + trx_chan_desc[chan].name, fn, toa); + + /* decode */ + rc = rach_decode(&ra, bits + 8 + 41, l1t->trx->bts->bsic); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u " + "(%u/51)\n", fn, fn % 51); + return 0; + } + + /* compose primitive */ + /* generate prim */ + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, + NULL); + l1sap.u.rach_ind.chan_nr = chan_nr; + l1sap.u.rach_ind.ra = ra; +#ifdef TA_TEST +#warning TIMING ADVANCE TEST-HACK IS ENABLED!!! + toa *= 10; +#endif + l1sap.u.rach_ind.acc_delay = (toa >= 0) ? toa : 0; + l1sap.u.rach_ind.fn = fn; + + /* forward primitive */ + l1sap_up(l1t->trx, &l1sap); + + return 0; +} + +/*! \brief a single burst was received by the PHY, process it */ +int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint32_t *first_fn = &chan_state->ul_first_fn; + uint8_t *mask = &chan_state->ul_mask; + float *rssi_sum = &chan_state->rssi_sum; + uint8_t *rssi_num = &chan_state->rssi_num; + float *toa_sum = &chan_state->toa_sum; + uint8_t *toa_num = &chan_state->toa_num; + uint8_t l2[GSM_MACBLOCK_LEN], l2_len; + int n_errors, n_bits_total; + int rc; + + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1t, 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, l1t->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst & store frame number of first burst */ + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; + *first_fn = fn; + *rssi_sum = 0; + *rssi_num = 0; + *toa_sum = 0; + *toa_num = 0; + } + + /* update mask + rssi */ + *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; + *toa_sum += toa; + (*toa_num)++; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* send burst information to loops process */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { + trx_loop_sacch_input(l1t, trx_chan_desc[chan].chan_nr | tn, + chan_state, rssi, toa); + } + + /* wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete data frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % l1ts->mf_period, l1ts->mf_period, + trx_chan_desc[chan].name); + + /* we require first burst to have correct FN */ + if (!(*mask & 0x1)) { + *mask = 0x0; + return 0; + } + } + *mask = 0x0; + + /* decode */ + rc = xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u " + "(%u/%u) for %s\n", *first_fn, + (*first_fn) % l1ts->mf_period, l1ts->mf_period, + trx_chan_desc[chan].name); + l2_len = 0; + } else + l2_len = GSM_MACBLOCK_LEN; + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, + n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num); + + return _sched_compose_ph_data_ind(l1t, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num); +} + +int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint8_t *mask = &chan_state->ul_mask; + float *rssi_sum = &chan_state->rssi_sum; + uint8_t *rssi_num = &chan_state->rssi_num; + float *toa_sum = &chan_state->toa_sum; + uint8_t *toa_num = &chan_state->toa_num; + uint8_t l2[54+1]; + int n_errors, n_bits_total; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n", + trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst */ + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; + *rssi_sum = 0; + *rssi_num = 0; + *toa_sum = 0; + *toa_num = 0; + } + + /* update mask + rssi */ + *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; + *toa_sum += toa; + (*toa_num)++; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block " + "ending at fn=%u (%u/%u) for %s\n", fn, + fn % l1ts->mf_period, l1ts->mf_period, + trx_chan_desc[chan].name); + } + *mask = 0x0; + + /* decode */ + rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1t->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 % l1ts->mf_period, + l1ts->mf_period, trx_chan_desc[chan].name); + return 0; + } + + l2[0] = 7; /* valid frame */ + + return _sched_compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 3) % GSM_HYPERFRAME, chan, + l2, rc + 1, *rssi_sum / *rssi_num); +} + +int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint8_t *mask = &chan_state->ul_mask; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; + uint8_t tch_data[128]; /* just to be safe */ + int rc, amr = 0; + int n_errors, n_bits_total; + + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1t, 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, l1t->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 928); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst */ + if (bid == 0) { + memset(*bursts_p + 464, 0, 464); + *mask = 0x0; + } + + /* update mask */ + *mask |= (1 << bid); + + /* copy burst to end of buffer of 8 bursts */ + burst = *bursts_p + bid * 116 + 464; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* wait until complete set of bursts */ + if (bid != 3) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " + "at fn=%u (%u/%u) for %s\n", fn, + fn % l1ts->mf_period, l1ts->mf_period, + trx_chan_desc[chan].name); + } + *mask = 0x0; + + /* decode + * also shift buffer by 4 bursts for interleaving */ + switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 + : tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR */ + rc = tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN 0,8,17 defines that CMI is included in frame, + * the first FN 4,13,21 defines that CMR is included in frame. + * NOTE: A frame ends 7 FN after start. + */ + rc = tch_afs_decode(tch_data + 2, *bursts_p, + (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec, + chan_state->codecs, &chan_state->ul_ft, + &chan_state->ul_cmr, &n_errors, &n_bits_total); + if (rc) + trx_loop_amr_input(l1t, + trx_chan_desc[chan].chan_nr | tn, chan_state, + (float)n_errors/(float)n_bits_total); + amr = 2; /* we store tch_data + 2 header bytes */ + /* only good speech frames get rtp header */ + if (rc != GSM_MACBLOCK_LEN && rc >= 4) { + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->ul_cmr], + chan_state->codec[chan_state->ul_ft], 0); + } + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } + memcpy(*bursts_p, *bursts_p + 464, 464); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, + n_errors, n_bits_total, rssi, toa); + + /* Check if the frame is bad */ + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); + goto bfi; + } + if (rc < 4) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s with codec mode %d (out of range)\n", + fn, trx_chan_desc[chan].name, rc); + goto bfi; + } + + /* FACCH */ + if (rc == GSM_MACBLOCK_LEN) { + _sched_compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan, + tch_data + amr, GSM_MACBLOCK_LEN, rssi); +bfi: + if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR */ + memset(tch_data, 0, GSM_FR_BYTES); + rc = GSM_FR_BYTES; + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + memset(tch_data, 0, GSM_EFR_BYTES); + rc = GSM_EFR_BYTES; + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], + 1); + if (rc < 2) + break; + memset(tch_data + 2, 0, rc - 2); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " + "please fix!\n"); + return -EINVAL; + } + } + } + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + /* TCH or BFI */ + return _sched_compose_tch_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan, + tch_data, rc); +} + +int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) +{ + struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn); + struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint8_t *mask = &chan_state->ul_mask; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; + uint8_t tch_data[128]; /* just to be safe */ + int rc, amr = 0; + int n_errors, n_bits_total; + + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1t, 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, l1t->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst */ + if (bid == 0) { + memset(*bursts_p + 464, 0, 232); + *mask = 0x0; + } + + /* update mask */ + *mask |= (1 << bid); + + /* copy burst to end of buffer of 6 bursts */ + burst = *bursts_p + bid * 116 + 464; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* wait until complete set of bursts */ + if (bid != 1) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0x3) != 0x3) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " + "at fn=%u (%u/%u) for %s\n", fn, + fn % l1ts->mf_period, l1ts->mf_period, + trx_chan_desc[chan].name); + } + *mask = 0x0; + + /* skip second of two TCH frames of FACCH was received */ + if (chan_state->ul_ongoing_facch) { + chan_state->ul_ongoing_facch = 0; + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + goto bfi; + } + + /* decode + * also shift buffer by 4 bursts for interleaving */ + switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 + : tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR or signalling */ + /* Note on FN-10: If we are at FN 10, we decoded an even aligned + * TCH/FACCH frame, because our burst buffer carries 6 bursts. + * Even FN ending at: 10,11,19,20,2,3 + */ + rc = tch_hr_decode(tch_data, *bursts_p, + (((fn + 26 - 10) % 26) >> 2) & 1, + &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN 0,8,17 or 1,9,18 defines that CMI is included + * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR + * is included in frame. + */ + rc = tch_ahs_decode(tch_data + 2, *bursts_p, + (((fn + 26 - 10) % 26) >> 2) & 1, + (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec, + chan_state->codecs, &chan_state->ul_ft, + &chan_state->ul_cmr, &n_errors, &n_bits_total); + if (rc) + trx_loop_amr_input(l1t, + trx_chan_desc[chan].chan_nr | tn, chan_state, + (float)n_errors/(float)n_bits_total); + amr = 2; /* we store tch_data + 2 two */ + /* only good speech frames get rtp header */ + if (rc != GSM_MACBLOCK_LEN && rc >= 4) { + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->ul_cmr], + chan_state->codec[chan_state->ul_ft], 0); + } + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, + n_errors, n_bits_total, rssi, toa); + + /* Check if the frame is bad */ + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); + goto bfi; + } + if (rc < 4) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s with codec mode %d (out of range)\n", + fn, trx_chan_desc[chan].name, rc); + goto bfi; + } + + /* FACCH */ + if (rc == GSM_MACBLOCK_LEN) { + chan_state->ul_ongoing_facch = 1; + _sched_compose_ph_data_ind(l1t, tn, + (fn + GSM_HYPERFRAME - 10 - ((fn % 26) >= 19)) % GSM_HYPERFRAME, chan, + tch_data + amr, GSM_MACBLOCK_LEN, rssi); +bfi: + if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + rc = 15; + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], + 1); + if (rc < 2) + break; + memset(tch_data + 2, 0, rc - 2); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " + "please fix!\n"); + return -EINVAL; + } + } + } + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + /* TCH or BFI */ + /* Note on FN 19 or 20: If we received the last burst of a frame, + * it actually starts at FN 8 or 9. A burst starting there, overlaps + * with the slot 12, so an extra FN must be substracted to get correct + * start of frame. + */ + return _sched_compose_tch_ind(l1t, tn, + (fn + GSM_HYPERFRAME - 10 - ((fn%26)==19) - ((fn%26)==20)) % GSM_HYPERFRAME, + chan, tch_data, rc); +}