aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-bts/Makefile.am2
-rw-r--r--include/osmo-bts/scheduler.h175
-rw-r--r--include/osmo-bts/scheduler_backend.h82
-rw-r--r--src/common/Makefile.am4
-rw-r--r--src/common/scheduler.c (renamed from src/osmo-bts-trx/scheduler.c)1910
-rw-r--r--src/osmo-bts-trx/Makefile.am6
-rw-r--r--src/osmo-bts-trx/l1_if.c37
-rw-r--r--src/osmo-bts-trx/l1_if.h121
-rw-r--r--src/osmo-bts-trx/loops.c83
-rw-r--r--src/osmo-bts-trx/loops.h14
-rw-r--r--src/osmo-bts-trx/main.c2
-rw-r--r--src/osmo-bts-trx/scheduler.h52
-rw-r--r--src/osmo-bts-trx/scheduler_trx.c1470
-rw-r--r--src/osmo-bts-trx/trx_if.c13
-rw-r--r--src/osmo-bts-trx/trx_if.h1
-rw-r--r--src/osmo-bts-trx/trx_vty.c2
16 files changed, 2061 insertions, 1913 deletions
diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index bf037ae3..af88a1ac 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -1,4 +1,4 @@
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
- power_control.h
+ power_control.h scheduler.h scheduler_backend.h
diff --git a/include/osmo-bts/scheduler.h b/include/osmo-bts/scheduler.h
new file mode 100644
index 00000000..13ef0511
--- /dev/null
+++ b/include/osmo-bts/scheduler.h
@@ -0,0 +1,175 @@
+#ifndef TRX_SCHEDULER_H
+#define TRX_SCHEDULER_H
+
+/* These types define the different channels on a multiframe.
+ * Each channel has queues and can be activated individually.
+ */
+enum trx_chan_type {
+ TRXC_IDLE = 0,
+ TRXC_FCCH,
+ TRXC_SCH,
+ TRXC_BCCH,
+ TRXC_RACH,
+ TRXC_CCCH,
+ TRXC_TCHF,
+ TRXC_TCHH_0,
+ TRXC_TCHH_1,
+ TRXC_SDCCH4_0,
+ TRXC_SDCCH4_1,
+ TRXC_SDCCH4_2,
+ TRXC_SDCCH4_3,
+ TRXC_SDCCH8_0,
+ TRXC_SDCCH8_1,
+ TRXC_SDCCH8_2,
+ TRXC_SDCCH8_3,
+ TRXC_SDCCH8_4,
+ TRXC_SDCCH8_5,
+ TRXC_SDCCH8_6,
+ TRXC_SDCCH8_7,
+ TRXC_SACCHTF,
+ TRXC_SACCHTH_0,
+ TRXC_SACCHTH_1,
+ TRXC_SACCH4_0,
+ TRXC_SACCH4_1,
+ TRXC_SACCH4_2,
+ TRXC_SACCH4_3,
+ TRXC_SACCH8_0,
+ TRXC_SACCH8_1,
+ TRXC_SACCH8_2,
+ TRXC_SACCH8_3,
+ TRXC_SACCH8_4,
+ TRXC_SACCH8_5,
+ TRXC_SACCH8_6,
+ TRXC_SACCH8_7,
+ TRXC_PDTCH,
+ TRXC_PTCCH,
+ _TRX_CHAN_MAX
+};
+
+/* States each channel on a multiframe */
+struct l1sched_chan_state {
+ /* scheduler */
+ uint8_t active; /* Channel is active */
+ ubit_t *dl_bursts; /* burst buffer for TX */
+ sbit_t *ul_bursts; /* burst buffer for RX */
+ uint32_t ul_first_fn; /* fn of first burst */
+ uint8_t ul_mask; /* mask of received bursts */
+
+ /* RSSI / TOA */
+ uint8_t rssi_num; /* number of RSSI values */
+ float rssi_sum; /* sum of RSSI values */
+ uint8_t toa_num; /* number of TOA values */
+ float toa_sum; /* sum of TOA values */
+
+ /* loss detection */
+ uint8_t lost; /* (SACCH) loss detection */
+
+ /* mode */
+ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */
+
+ /* AMR */
+ uint8_t codec[4]; /* 4 possible codecs for amr */
+ int codecs; /* number of possible codecs */
+ float ber_sum; /* sum of bit error rates */
+ int ber_num; /* number of bit error rates */
+ uint8_t ul_ft; /* current uplink FT index */
+ uint8_t dl_ft; /* current downlink FT index */
+ uint8_t ul_cmr; /* current uplink CMR index */
+ uint8_t dl_cmr; /* current downlink CMR index */
+ uint8_t amr_loop; /* if AMR loop is enabled */
+
+ /* TCH/H */
+ uint8_t dl_ongoing_facch; /* FACCH/H on downlink */
+ uint8_t ul_ongoing_facch; /* FACCH/H on uplink */
+
+ /* encryption */
+ int ul_encr_algo; /* A5/x encry algo downlink */
+ int dl_encr_algo; /* A5/x encry algo uplink */
+ int ul_encr_key_len;
+ int dl_encr_key_len;
+ uint8_t ul_encr_key[MAX_A5_KEY_LEN];
+ uint8_t dl_encr_key[MAX_A5_KEY_LEN];
+
+ /* measurements */
+ struct {
+ uint8_t clock; /* cyclic clock counter */
+ int8_t rssi[32]; /* last RSSI values */
+ int rssi_count; /* received RSSI values */
+ int rssi_valid_count; /* number of stored value */
+ int rssi_got_burst; /* any burst received so far */
+ float toa_sum; /* sum of TOA values */
+ int toa_num; /* number of TOA value */
+ } meas;
+
+ /* handover */
+ uint8_t ho_rach_detect; /* if rach detection is on */
+};
+
+struct l1sched_ts {
+ uint8_t mf_index; /* selected multiframe index */
+ uint32_t mf_last_fn; /* last received frame number */
+ uint8_t mf_period; /* period of multiframe */
+ const struct trx_sched_frame *mf_frames; /* pointer to frame layout */
+
+ struct llist_head dl_prims; /* Queue primitves for TX */
+
+ /* Channel states for all logical channels */
+ struct l1sched_chan_state chan_state[_TRX_CHAN_MAX];
+};
+
+struct l1sched_trx {
+ struct gsm_bts_trx *trx;
+ struct l1sched_ts ts[TRX_NR_TS];
+};
+
+struct l1sched_ts *l1sched_trx_get_ts(struct l1sched_trx *l1t, uint8_t tn);
+
+/*! \brief how many frame numbers in advance we should send bursts to PHY */
+extern uint32_t trx_clock_advance;
+/*! \brief advance RTS.ind to L2 by that many clocks */
+extern uint32_t trx_rts_advance;
+/*! \brief last frame number as received from PHY */
+extern uint32_t transceiver_last_fn;
+
+
+/*! \brief Initialize the scheudler data structures */
+int trx_sched_init(struct l1sched_trx *l1t);
+
+/*! \brief De-initialize the scheudler data structures */
+void trx_sched_exit(struct l1sched_trx *l1t);
+
+/*! \brief Handle a PH-DATA.req from L2 down to L1 */
+int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap);
+
+/*! \brief Handle a PH-TCH.req from L2 down to L1 */
+int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap);
+
+/*! \brief PHY informs us of new (current) GSM freme nunmber */
+int trx_sched_clock(struct gsm_bts *bts, uint32_t fn);
+
+/*! \brief handle an UL burst received by PHY */
+int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ sbit_t *bits, int8_t rssi, float toa);
+
+/*! \brief set multiframe scheduler to given physical channel config */
+int trx_sched_set_pchan(struct l1sched_trx *l1t, uint8_t tn,
+ enum gsm_phys_chan_config pchan);
+
+/*! \brief set all matching logical channels active/inactive */
+int trx_sched_set_lchan(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t link_id,
+ int active);
+
+/*! \brief set mode of all matching logical channels to given mode(s) */
+int trx_sched_set_mode(struct l1sched_trx *l1t, 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_codec,
+ uint8_t handover);
+
+/*! \brief set ciphering on given logical channels */
+int trx_sched_set_cipher(struct l1sched_trx *l1t, uint8_t chan_nr, int downlink,
+ int algo, uint8_t *key, int key_len);
+
+/* \brief close all logical channels and reset timeslots */
+void trx_sched_reset(struct l1sched_trx *l1t);
+
+#endif /* TRX_SCHEDULER_H */
diff --git a/include/osmo-bts/scheduler_backend.h b/include/osmo-bts/scheduler_backend.h
new file mode 100644
index 00000000..9f663cdd
--- /dev/null
+++ b/include/osmo-bts/scheduler_backend.h
@@ -0,0 +1,82 @@
+#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);
+
+const ubit_t *_sched_dl_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn);
+int _sched_rts(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn);
+void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int activate);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index e577eba6..fea205c1 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -2,10 +2,12 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS)
-noinst_LIBRARIES = libbts.a
+noinst_LIBRARIES = libbts.a libl1sched.a
libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
rsl.c vty.c paging.c measurement.c amr.c lchan.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
load_indication.c pcu_sock.c handover.c msg_utils.c \
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
l1sap.c cbch.c power_control.c main.c
+
+libl1sched_a_SOURCES = scheduler.c
diff --git a/src/osmo-bts-trx/scheduler.c b/src/common/scheduler.c
index 1710b4e5..64d89ac4 100644
--- a/src/osmo-bts-trx/scheduler.c
+++ b/src/common/scheduler.c
@@ -2,6 +2,7 @@
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2015 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -30,87 +31,21 @@
#include <osmocom/core/bits.h>
#include <osmocom/gsm/a5.h>
-#include <osmocom/netif/rtp.h>
-
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/l1sap.h>
-#include <osmo-bts/amr.h>
-
-#include "l1_if.h"
-#include "scheduler.h"
-#include "gsm0503_coding.h"
-#include "trx_if.h"
-#include "loops.h"
-#include "amr.h"
-#include "loops.h"
-
-/* Enable this to multiply TOA of RACH by 10.
- * This is usefull to check tenth of timing advances with RSSI test tool.
- * Note that regular phones will not work when using this test! */
-//#define TA_TEST
-
-void *tall_bts_ctx;
-
-static struct gsm_bts *bts;
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
-/* clock states */
-static uint32_t transceiver_lost;
-uint32_t transceiver_last_fn;
-static struct timeval transceiver_clock_tv;
-static struct osmo_timer_list transceiver_clock_timer;
-
-/* clock advance for the transceiver */
-uint32_t trx_clock_advance = 20;
-
-/* advance RTS to give some time for data processing. (especially PCU) */
-uint32_t trx_rts_advance = 5; /* about 20ms */
-
-typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan);
-typedef ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn,
- uint32_t fn, enum trx_chan_type chan, uint8_t bid);
-typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
+extern void *tall_bts_ctx;
-static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+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 trx_l1h *l1h, uint8_t tn, uint32_t fn,
+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 trx_l1h *l1h, uint8_t tn, uint32_t fn,
+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 trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid);
-static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
-static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
-static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
-static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
-static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa);
-
/*! \brief Dummy Burst (TS 05.02 Chapter 5.2.6) */
static const ubit_t dummy_burst[148] = {
0,0,0,
@@ -123,7 +58,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,
@@ -132,7 +67,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, },
@@ -144,7 +79,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,
};
@@ -153,27 +88,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 },
@@ -219,23 +134,22 @@ static const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
* init / exit
*/
-int trx_sched_init(struct trx_l1h *l1h)
+int trx_sched_init(struct l1sched_trx *l1t)
{
uint8_t tn;
int i;
- struct trx_chan_state *chan_state;
- LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1h->trx->nr);
+ LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1t->trx->nr);
- /* hack to get bts */
- bts = l1h->trx->bts;
+ for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- for (tn = 0; tn < TRX_NR_TS; 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];
+ l1ts->mf_index = 0;
+ l1ts->mf_last_fn = 0;
+ INIT_LLIST_HEAD(&l1ts->dl_prims);
+ for (i = 0; i < ARRAY_SIZE(&l1ts->chan_state); i++) {
+ struct l1sched_chan_state *chan_state;
+ chan_state = &l1ts->chan_state[i];
chan_state->active = 0;
}
}
@@ -243,18 +157,20 @@ int trx_sched_init(struct trx_l1h *l1h)
return 0;
}
-void trx_sched_exit(struct trx_l1h *l1h)
+void trx_sched_exit(struct l1sched_trx *l1t)
{
+ struct gsm_bts_trx_ts *ts;
uint8_t tn;
int i;
- struct trx_chan_state *chan_state;
- LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1h->trx->nr);
+ LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1t->trx->nr);
- for (tn = 0; tn < TRX_NR_TS; tn++) {
- msgb_queue_flush(&l1h->dl_prims[tn]);
+ for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ msgb_queue_flush(&l1ts->dl_prims);
for (i = 0; i < _TRX_CHAN_MAX; i++) {
- chan_state = &l1h->chan_states[tn][i];
+ struct l1sched_chan_state *chan_state;
+ chan_state = &l1ts->chan_state[i];
if (chan_state->dl_bursts) {
talloc_free(chan_state->dl_bursts);
chan_state->dl_bursts = NULL;
@@ -265,259 +181,30 @@ void trx_sched_exit(struct trx_l1h *l1h)
}
}
/* clear lchan channel states */
- for (i = 0; i < TRX_NR_TS; i++)
- l1h->trx->ts[tn].lchan[i].state = LCHAN_S_NONE;
+ ts = &l1t->trx->ts[tn];
+ for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
+ lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
}
}
/* close all logical channels and reset timeslots */
-void trx_sched_reset(struct trx_l1h *l1h)
-{
- trx_sched_exit(l1h);
- trx_sched_init(l1h);
-}
-
-
-/*
- * data request (from upper layer)
- */
-
-int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
-{
- uint8_t tn = l1sap->u.data.chan_nr & 7;
-
- LOGP(DL1C, LOGL_INFO, "PH-DATA.req: chan_nr=0x%02x link_id=0x%02x "
- "fn=%u ts=%u trx=%u\n", l1sap->u.data.chan_nr,
- l1sap->u.data.link_id, l1sap->u.data.fn, tn, l1h->trx->nr);
-
- if (!l1sap->oph.msg)
- abort();
-
- /* ignore empty frame */
- if (!msgb_l2len(l1sap->oph.msg)) {
- msgb_free(l1sap->oph.msg);
- return 0;
- }
-
- msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
-
- return 0;
-}
-
-int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
+void trx_sched_reset(struct l1sched_trx *l1t)
{
- uint8_t tn = l1sap->u.tch.chan_nr & 7;
-
- LOGP(DL1C, LOGL_INFO, "TCH.req: chan_nr=0x%02x "
- "fn=%u ts=%u trx=%u\n", l1sap->u.tch.chan_nr,
- l1sap->u.tch.fn, tn, l1h->trx->nr);
-
- if (!l1sap->oph.msg)
- abort();
-
- /* ignore empty frame */
- if (!msgb_l2len(l1sap->oph.msg)) {
- msgb_free(l1sap->oph.msg);
- return 0;
- }
-
- msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
-
- return 0;
+ trx_sched_exit(l1t);
+ trx_sched_init(l1t);
}
-
-/*
- * ready-to-send indication (to upper layer)
- */
-
-/* RTS for data frame */
-static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan)
-{
- uint8_t chan_nr, link_id;
- struct msgb *msg;
- struct osmo_phsap_prim *l1sap;
-
- /* get data for RTS indication */
- chan_nr = trx_chan_desc[chan].chan_nr | tn;
- link_id = trx_chan_desc[chan].link_id;
-
- if (!chan_nr) {
- LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing "
- "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr);
- return -ENODEV;
- }
-
- LOGP(DL1C, LOGL_INFO, "PH-RTS.ind: chan=%s chan_nr=0x%02x "
- "link_id=0x%02x fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
- chan_nr, link_id, fn, tn, l1h->trx->nr);
-
- /* send clock information to loops process */
- if (L1SAP_IS_LINK_SACCH(link_id))
- trx_loop_sacch_clock(l1h, chan_nr, &l1h->chan_states[tn][chan]);
-
- /* generate prim */
- msg = l1sap_msgb_alloc(200);
- if (!msg)
- return -ENOMEM;
- l1sap = msgb_l1sap_prim(msg);
- osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
- PRIM_OP_INDICATION, msg);
- l1sap->u.data.chan_nr = chan_nr;
- l1sap->u.data.link_id = link_id;
- l1sap->u.data.fn = fn;
-
- return l1sap_up(l1h->trx, l1sap);
-}
-
-static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, int facch)
-{
- uint8_t chan_nr, link_id;
- struct msgb *msg;
- struct osmo_phsap_prim *l1sap;
- int rc = 0;
-
- /* get data for RTS indication */
- chan_nr = trx_chan_desc[chan].chan_nr | tn;
- link_id = trx_chan_desc[chan].link_id;
-
- if (!chan_nr) {
- LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing "
- "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr);
- return -ENODEV;
- }
-
- LOGP(DL1C, LOGL_INFO, "TCH RTS.ind: chan=%s chan_nr=0x%02x "
- "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
- chan_nr, fn, tn, l1h->trx->nr);
-
- /* only send, if FACCH is selected */
- if (facch) {
- /* generate prim */
- msg = l1sap_msgb_alloc(200);
- if (!msg)
- return -ENOMEM;
- l1sap = msgb_l1sap_prim(msg);
- osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS,
- PRIM_OP_INDICATION, msg);
- l1sap->u.data.chan_nr = chan_nr;
- l1sap->u.data.link_id = link_id;
- l1sap->u.data.fn = fn;
-
- rc = l1sap_up(l1h->trx, l1sap);
- }
-
- /* dont send, if TCH is in signalling only mode */
- if (l1h->chan_states[tn][chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
- /* generate prim */
- msg = l1sap_msgb_alloc(200);
- if (!msg)
- return -ENOMEM;
- l1sap = msgb_l1sap_prim(msg);
- osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS,
- PRIM_OP_INDICATION, msg);
- l1sap->u.tch.chan_nr = chan_nr;
- l1sap->u.tch.fn = fn;
-
- return l1sap_up(l1h->trx, l1sap);
- }
-
- return rc;
-}
-
-/* RTS for full rate traffic frame */
-static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan)
-{
- /* TCH/F may include FACCH on every 4th burst */
- return rts_tch_common(l1h, tn, fn, chan, 1);
-}
-
-
-/* RTS for half rate traffic frame */
-static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan)
-{
- /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */
- return rts_tch_common(l1h, tn, fn, chan, ((fn % 26) >> 2) & 1);
-}
-
-
-/*
- * TX on downlink
- */
-
-/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
-static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
-{
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
-
- return NULL;
-}
-
-static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
-{
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
-
- return (ubit_t *) fcch_burst;
-}
-
-static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
-{
- static ubit_t bits[148], burst[78];
- uint8_t sb_info[4];
- struct gsm_time t;
- uint8_t t3p, bsic;
-
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
-
- /* create SB info from GSM time and BSIC */
- gsm_fn2gsmtime(&t, fn);
- t3p = t.t3 / 10;
- bsic = l1h->trx->bts->bsic;
- sb_info[0] =
- ((bsic & 0x3f) << 2) |
- ((t.t1 & 0x600) >> 9);
- sb_info[1] =
- ((t.t1 & 0x1fe) >> 1);
- sb_info[2] =
- ((t.t1 & 0x001) << 7) |
- ((t.t2 & 0x1f) << 2) |
- ((t3p & 0x6) >> 1);
- sb_info[3] =
- (t3p & 0x1);
-
- /* encode bursts */
- sch_encode(burst, sb_info);
-
- /* compose burst */
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 39);
- memcpy(bits + 42, sch_train, 64);
- memcpy(bits + 106, burst + 39, 39);
- memset(bits + 145, 0, 3);
-
- return bits;
-}
-
-static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn,
- enum trx_chan_type chan)
+struct msgb *_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;
uint32_t prim_fn;
uint8_t chan_nr, link_id;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
/* get prim of current fn from queue */
- llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) {
+ llist_for_each_entry_safe(msg, msg2, &l1ts->dl_prims, list) {
l1sap = msgb_l1sap_prim(msg);
if (l1sap->oph.operation != PRIM_OP_REQUEST) {
wrong_type:
@@ -548,7 +235,7 @@ free_msg:
"is out of range, or channel already disabled. "
"If this happens in conjunction with PCU, "
"increase 'rts-advance' by 5. (current fn=%u)\n",
- l1h->trx->nr, tn, l1sap->u.data.fn, fn);
+ l1t->trx->nr, tn, l1sap->u.data.fn, fn);
/* unlink and free message */
llist_del(&msg->list);
msgb_free(msg);
@@ -578,12 +265,13 @@ found_msg:
return msg;
}
-static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi)
+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;
uint8_t chan_nr = trx_chan_desc[chan].chan_nr | tn;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
/* compose primitive */
msg = l1sap_msgb_alloc(l2_len);
@@ -599,19 +287,20 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
memcpy(msg->l2h, l2, l2_len);
if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id))
- l1h->chan_states[tn][chan].lost = 0;
+ l1ts->chan_state[chan].lost = 0;
/* forward primitive */
- l1sap_up(l1h->trx, l1sap);
+ l1sap_up(l1t->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)
+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;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
/* compose primitive */
msg = l1sap_msgb_alloc(tch_len);
@@ -624,1106 +313,182 @@ static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
if (tch_len)
memcpy(msg->l2h, tch, tch_len);
- if (l1h->chan_states[tn][chan].lost)
- l1h->chan_states[tn][chan].lost--;
+ if (l1ts->chan_state[chan].lost)
+ l1ts->chan_state[chan].lost--;
/* forward primitive */
- l1sap_up(l1h->trx, l1sap);
+ l1sap_up(l1t->trx, l1sap);
return 0;
}
-static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
-{
- struct msgb *msg = NULL; /* make GCC happy */
- ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
- static ubit_t bits[148];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- /* get mac block from queue */
- msg = dequeue_prim(l1h, tn, fn, chan);
- if (msg)
- goto got_msg;
-
- LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
-
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
- }
- return NULL;
-
-got_msg:
- /* check validity of message */
- if (msgb_l2len(msg) != 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 (++(l1h->chan_states[tn][chan].lost) > 1) {
- /* TODO: Should we pass old TOA here? Otherwise we risk
- * unnecessary decreasing TA */
-
- /* Send uplnk measurement information to L2 */
- l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
- 456, 456, -110, 0);
-
- compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -110);
- }
- }
-
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
- return NULL;
- }
-
- /* encode bursts */
- xcch_encode(*bursts_p, msg->l2h);
-
- /* free message */
- msgb_free(msg);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, tsc[l1h->config.tsc], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- return bits;
-}
-
-static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
-{
- struct msgb *msg = NULL; /* make GCC happy */
- ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
- static ubit_t bits[148];
- int rc;
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
-
- /* get mac block from queue */
- msg = dequeue_prim(l1h, tn, fn, chan);
- if (msg)
- goto got_msg;
-
- LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
- }
- return NULL;
-got_msg:
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
- return NULL;
- }
-
- /* encode bursts */
- rc = pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h);
-
- /* check validity of message */
- if (rc) {
- LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! "
- "(len=%d)\n", rc);
- /* free message */
- msgb_free(msg);
- goto no_msg;
- }
-
- /* free message */
- msgb_free(msg);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, tsc[l1h->config.tsc], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- return bits;
-}
-
-static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
- struct msgb **_msg_facch, int codec_mode_request)
-{
- struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- struct osmo_phsap_prim *l1sap;
-
- /* handle loss detection of received TCH frames */
- if (rsl_cmode == RSL_CMOD_SPD_SPEECH
- && ++(l1h->chan_states[tn][chan].lost) > 5) {
- uint8_t tch_data[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(l1h, tn, 0, chan, tch_data, len);
- break;
- default:
-inval_mode1:
- LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
- len = 0;
- }
- if (len)
- compose_tch_ind(l1h, tn, 0, chan, tch_data, len);
- }
-
- /* get frame and unlink from queue */
- msg1 = dequeue_prim(l1h, tn, fn, chan);
- msg2 = dequeue_prim(l1h, tn, fn, chan);
- if (msg1) {
- l1sap = msgb_l1sap_prim(msg1);
- if (l1sap->oph.primitive == PRIM_TCH) {
- msg_tch = msg1;
- if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH) {
- LOGP(DL1C, LOGL_FATAL, "TCH twice, "
- "please FIX! ");
- msgb_free(msg2);
- } else
- msg_facch = msg2;
- }
- } else {
- msg_facch = msg1;
- if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive != PRIM_TCH) {
- LOGP(DL1C, LOGL_FATAL, "FACCH twice, "
- "please FIX! ");
- msgb_free(msg2);
- } else
- msg_tch = msg2;
- }
- }
- } else if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH)
- msg_tch = msg2;
- else
- msg_facch = msg2;
- }
-
- /* check validity of message */
- if (msg_facch && msgb_l2len(msg_facch) != 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,
- l1h->trx->nr, tn, fn);
- goto free_bad_msg;
- }
-
- switch (tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR / HR */
- if (chan != TRXC_TCHF) { /* HR */
- len = 15;
- if (msgb_l2len(msg_tch) >= 1
- && (msg_tch->l2h[0] & 0xf0) != 0x00) {
- LOGP(DL1C, LOGL_NOTICE, "%s "
- "Transmitting 'bad "
- "HR frame' trx=%u ts=%u at "
- "fn=%u.\n",
- trx_chan_desc[chan].name,
- l1h->trx->nr, tn, fn);
- goto free_bad_msg;
- }
- break;
- }
- len = 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,
- l1h->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,
- l1h->trx->nr, tn, fn);
- goto free_bad_msg;
- }
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- len = amr_decompose_payload(msg_tch->l2h,
- msgb_l2len(msg_tch), &cmr_codec, &ft_codec,
- &bfi);
- cmr = -1;
- ft = -1;
- for (i = 0; i < chan_state->codecs; i++) {
- if (chan_state->codec[i] == cmr_codec)
- cmr = i;
- if (chan_state->codec[i] == ft_codec)
- ft = i;
- }
- if (cmr >= 0) { /* new request */
- chan_state->dl_cmr = cmr;
- /* disable AMR loop */
- trx_loop_amr_set(chan_state, 0);
- } else {
- /* enable AMR loop */
- trx_loop_amr_set(chan_state, 1);
- }
- if (ft < 0) {
- LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) "
- " of RTP frame not in list. "
- "trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1h->trx->nr, tn);
- goto free_bad_msg;
- }
- if (codec_mode_request && chan_state->dl_ft != ft) {
- LOGP(DL1C, LOGL_NOTICE, "%s Codec (FT = %d) "
- " of RTP cannot be changed now, but in "
- "next frame. trx=%u ts=%u\n",
- trx_chan_desc[chan].name, ft_codec,
- l1h->trx->nr, tn);
- goto free_bad_msg;
- }
- chan_state->dl_ft = ft;
- if (bfi) {
- LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
- "AMR frame' trx=%u ts=%u at fn=%u.\n",
- trx_chan_desc[chan].name,
- l1h->trx->nr, tn, fn);
- goto free_bad_msg;
- }
- break;
- default:
-inval_mode2:
- LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please "
- "fix!\n");
- goto free_bad_msg;
- }
- if (len < 0) {
- LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR "
- "payload\n");
- goto free_bad_msg;
- }
- if (msgb_l2len(msg_tch) != len) {
- LOGP(DL1C, LOGL_ERROR, "Cannot send payload with "
- "invalid length! (expecing %d, received %d)\n",
- len, msgb_l2len(msg_tch));
-free_bad_msg:
- /* free message */
- msgb_free(msg_tch);
- msg_tch = NULL;
- goto send_frame;
- }
- }
-
-send_frame:
- *_msg_tch = msg_tch;
- *_msg_facch = msg_facch;
-}
+/*
+ * data request (from upper layer)
+ */
-static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, 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)
{
- struct msgb *msg_tch = NULL, *msg_facch = NULL;
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- uint8_t tch_mode = chan_state->tch_mode;
- ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
- static ubit_t bits[148];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
+ uint8_t tn = l1sap->u.data.chan_nr & 7;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch,
- (((fn + 4) % 26) >> 2) & 1);
+ 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);
- /* 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);
- }
+ if (!l1sap->oph.msg)
+ abort();
- /* no message at all */
- if (!msg_tch && !msg_facch) {
- LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
- goto send_burst;
+ /* ignore empty frame */
+ if (!msgb_l2len(l1sap->oph.msg)) {
+ msgb_free(l1sap->oph.msg);
+ return 0;
}
- /* encode bursts (priorize FACCH) */
- if (msg_facch)
- tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
- 1);
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
- /* the first FN 4,13,21 defines that CMI is included in frame,
- * the first FN 0,8,17 defines that CMR is included in frame.
- */
- tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1,
- chan_state->codec, chan_state->codecs,
- chan_state->dl_ft,
- chan_state->dl_cmr);
- else
- tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1);
-
- /* free message */
- if (msg_tch)
- msgb_free(msg_tch);
- if (msg_facch)
- msgb_free(msg_facch);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, tsc[l1h->config.tsc], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+ msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
- return bits;
+ return 0;
}
-static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid)
+int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
{
- struct msgb *msg_tch = NULL, *msg_facch = NULL;
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- uint8_t tch_mode = chan_state->tch_mode;
- ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
- static ubit_t bits[148];
-
- /* send burst, if we already got a frame */
- if (bid > 0) {
- if (!*bursts_p)
- return NULL;
- goto send_burst;
- }
+ uint8_t tn = l1sap->u.tch.chan_nr & 7;
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- /* 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;
- }
+ 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);
- /* 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);
- }
- }
+ if (!l1sap->oph.msg)
+ abort();
- /* no message at all */
- if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
- LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
- "trx=%u ts=%u at fn=%u to transmit.\n",
- trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
- goto send_burst;
+ /* ignore empty frame */
+ if (!msgb_l2len(l1sap->oph.msg)) {
+ msgb_free(l1sap->oph.msg);
+ return 0;
}
- /* encode bursts (priorize FACCH) */
- if (msg_facch) {
- tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
- chan_state->dl_ongoing_facch = 1; /* first of two tch frames */
- } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */
- chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
- /* the first FN 4,13,21 or 5,14,22 defines that CMI is included
- * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
- * included in frame. */
- tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1,
- chan_state->codec, chan_state->codecs,
- chan_state->dl_ft,
- chan_state->dl_cmr);
- else
- tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch));
-
- /* free message */
- if (msg_tch)
- msgb_free(msg_tch);
- if (msg_facch)
- msgb_free(msg_facch);
-
-send_burst:
- /* compose burst */
- burst = *bursts_p + bid * 116;
- memset(bits, 0, 3);
- memcpy(bits + 3, burst, 58);
- memcpy(bits + 61, tsc[l1h->config.tsc], 26);
- memcpy(bits + 87, burst + 58, 58);
- memset(bits + 145, 0, 3);
-
- LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+ msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
- return bits;
+ return 0;
}
-/*
- * RX on uplink (indication to upper layer)
+/*
+ * ready-to-send indication (to upper layer)
*/
-static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+/* 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;
- struct osmo_phsap_prim l1sap;
- uint8_t ra;
- int rc;
+ 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;
- LOGP(DL1C, LOGL_NOTICE, "Received Access Burst on %s fn=%u toa=%.2f\n",
- trx_chan_desc[chan].name, fn, toa);
-
- /* decode */
- rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic);
- if (rc) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u "
- "(%u/51)\n", fn, fn % 51);
- return 0;
+ 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;
}
- /* 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;
+ 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);
- /* forward primitive */
- 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.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 trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+static int rts_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
+ enum trx_chan_type chan, int facch)
{
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint32_t *first_fn = &chan_state->ul_first_fn;
- uint8_t *mask = &chan_state->ul_mask;
- float *rssi_sum = &chan_state->rssi_sum;
- uint8_t *rssi_num = &chan_state->rssi_num;
- float *toa_sum = &chan_state->toa_sum;
- uint8_t *toa_num = &chan_state->toa_num;
- uint8_t l2[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(l1h, tn, fn, chan, bid, bits, rssi, toa);
-
- LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
- return -ENOMEM;
- }
+ 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;
- /* 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(l1h, 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) % l1h->mf_period[tn], l1h->mf_period[tn],
- trx_chan_desc[chan].name);
-
- /* we require first burst to have correct FN */
- if (!(*mask & 0x1)) {
- *mask = 0x0;
- return 0;
- }
- }
- *mask = 0x0;
-
- /* decode */
- rc = xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total);
- if (rc) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u "
- "(%u/%u) for %s\n", *first_fn,
- (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn],
- trx_chan_desc[chan].name);
- l2_len = 0;
- } else
- l2_len = GSM_MACBLOCK_LEN;
-
- /* 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);
-}
+ 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 trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
-{
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint8_t *mask = &chan_state->ul_mask;
- float *rssi_sum = &chan_state->rssi_sum;
- uint8_t *rssi_num = &chan_state->rssi_num;
- float *toa_sum = &chan_state->toa_sum;
- uint8_t *toa_num = &chan_state->toa_num;
- uint8_t l2[54+1];
- int n_errors, n_bits_total;
- int rc;
-
- LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 464);
- if (!*bursts_p)
+ /* 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 % l1h->mf_period[tn], l1h->mf_period[tn],
- trx_chan_desc[chan].name);
- }
- *mask = 0x0;
-
- /* decode */
- rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total);
-
- /* Send uplnk measurement information to L2 */
- l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
- n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num);
+ 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 % l1h->mf_period[tn],
- l1h->mf_period[tn], trx_chan_desc[chan].name);
- return 0;
+ rc = l1sap_up(l1t->trx, l1sap);
}
- l2[0] = 7; /* valid frame */
-
- return compose_ph_data_ind(l1h, tn, (fn + GSM_HYPERFRAME - 3) % GSM_HYPERFRAME, chan,
- l2, rc + 1, *rssi_sum / *rssi_num);
-}
-
-static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
-{
- struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint8_t *mask = &chan_state->ul_mask;
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- uint8_t tch_data[128]; /* just to be safe */
- int rc, amr = 0;
- int n_errors, n_bits_total;
-
- /* handle rach, if handover rach detection is turned on */
- if (chan_state->ho_rach_detect == 1)
- return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
-
- LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 928);
- if (!*bursts_p)
+ /* 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 % l1h->mf_period[tn], l1h->mf_period[tn],
- trx_chan_desc[chan].name);
- }
- *mask = 0x0;
-
- /* decode
- * also shift buffer by 4 bursts for interleaving */
- switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
- : tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* FR */
- rc = tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- rc = tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /* the first FN 0,8,17 defines that CMI is included in frame,
- * the first FN 4,13,21 defines that CMR is included in frame.
- * NOTE: A frame ends 7 FN after start.
- */
- rc = tch_afs_decode(tch_data + 2, *bursts_p,
- (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
- &chan_state->ul_cmr, &n_errors, &n_bits_total);
- if (rc)
- trx_loop_amr_input(l1h,
- trx_chan_desc[chan].chan_nr | tn, chan_state,
- (float)n_errors/(float)n_bits_total);
- amr = 2; /* we store tch_data + 2 header bytes */
- /* only good speech frames get rtp header */
- if (rc != 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(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
- n_errors, n_bits_total, rssi, toa);
-
- /* Check if the frame is bad */
- if (rc < 0) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
- "fn=%u for %s\n", fn, trx_chan_desc[chan].name);
- goto bfi;
- }
- if (rc < 4) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
- "fn=%u for %s with codec mode %d (out of range)\n",
- fn, trx_chan_desc[chan].name, rc);
- goto bfi;
- }
+ 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(l1h, 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(l1h, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
- tch_data, rc);
+ return rc;
}
-static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
- float toa)
+/* 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 trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
- sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
- uint8_t *mask = &chan_state->ul_mask;
- uint8_t rsl_cmode = chan_state->rsl_cmode;
- uint8_t tch_mode = chan_state->tch_mode;
- uint8_t tch_data[128]; /* just to be safe */
- int rc, amr = 0;
- int n_errors, n_bits_total;
-
- /* handle rach, if handover rach detection is turned on */
- if (chan_state->ho_rach_detect == 1)
- return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
-
- LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n",
- trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
-
- /* alloc burst memory, if not already */
- if (!*bursts_p) {
- *bursts_p = talloc_zero_size(tall_bts_ctx, 696);
- if (!*bursts_p)
- return -ENOMEM;
- }
-
- /* clear burst */
- if (bid == 0) {
- memset(*bursts_p + 464, 0, 232);
- *mask = 0x0;
- }
-
- /* update mask */
- *mask |= (1 << bid);
-
- /* copy burst to end of buffer of 6 bursts */
- burst = *bursts_p + bid * 116 + 464;
- memcpy(burst, bits + 3, 58);
- memcpy(burst + 58, bits + 87, 58);
-
- /* wait until complete set of bursts */
- if (bid != 1)
- return 0;
-
- /* check for complete set of bursts */
- if ((*mask & 0x3) != 0x3) {
- LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending "
- "at fn=%u (%u/%u) for %s\n", fn,
- fn % l1h->mf_period[tn], l1h->mf_period[tn],
- trx_chan_desc[chan].name);
- }
- *mask = 0x0;
-
- /* skip second of two TCH frames of FACCH was received */
- if (chan_state->ul_ongoing_facch) {
- chan_state->ul_ongoing_facch = 0;
- memcpy(*bursts_p, *bursts_p + 232, 232);
- memcpy(*bursts_p + 232, *bursts_p + 464, 232);
- goto bfi;
- }
-
- /* decode
- * also shift buffer by 4 bursts for interleaving */
- switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1
- : tch_mode) {
- case GSM48_CMODE_SPEECH_V1: /* HR or signalling */
- /* Note on FN-10: If we are at FN 10, we decoded an even aligned
- * TCH/FACCH frame, because our burst buffer carries 6 bursts.
- * Even FN ending at: 10,11,19,20,2,3
- */
- rc = tch_hr_decode(tch_data, *bursts_p,
- (((fn + 26 - 10) % 26) >> 2) & 1,
- &n_errors, &n_bits_total);
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /* the first FN 0,8,17 or 1,9,18 defines that CMI is included
- * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR
- * is included in frame.
- */
- rc = tch_ahs_decode(tch_data + 2, *bursts_p,
- (((fn + 26 - 10) % 26) >> 2) & 1,
- (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
- &chan_state->ul_cmr, &n_errors, &n_bits_total);
- if (rc)
- trx_loop_amr_input(l1h,
- trx_chan_desc[chan].chan_nr | tn, chan_state,
- (float)n_errors/(float)n_bits_total);
- amr = 2; /* we store tch_data + 2 two */
- /* only good speech frames get rtp header */
- if (rc != 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(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
- n_errors, n_bits_total, rssi, toa);
-
- /* Check if the frame is bad */
- if (rc < 0) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
- "fn=%u for %s\n", fn, trx_chan_desc[chan].name);
- goto bfi;
- }
- if (rc < 4) {
- LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at "
- "fn=%u for %s with codec mode %d (out of range)\n",
- fn, trx_chan_desc[chan].name, rc);
- goto bfi;
- }
-
- /* FACCH */
- if (rc == GSM_MACBLOCK_LEN) {
- chan_state->ul_ongoing_facch = 1;
- compose_ph_data_ind(l1h, 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(l1h, 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
*/
@@ -2487,61 +1252,59 @@ static const struct trx_sched_multiframe trx_sched_multiframes[] = {
*/
/* set multiframe scheduler to given pchan */
-int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
+int trx_sched_set_pchan(struct l1sched_trx *l1t, uint8_t tn,
enum gsm_phys_chan_config pchan)
{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
int i;
- /* ignore disabled slots */
- if (!(l1h->config.slotmask & (1 << tn)))
- return -ENOTSUP;
-
for (i = 0; i < ARRAY_SIZE(trx_sched_multiframes); i++) {
if (trx_sched_multiframes[i].pchan == pchan
&& (trx_sched_multiframes[i].slotmask & (1 << tn))) {
- l1h->mf_index[tn] = i;
- l1h->mf_period[tn] = trx_sched_multiframes[i].period;
- l1h->mf_frames[tn] = trx_sched_multiframes[i].frames;
+ l1ts->mf_index = i;
+ l1ts->mf_period = trx_sched_multiframes[i].period;
+ l1ts->mf_frames = trx_sched_multiframes[i].frames;
LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with "
"%s trx=%d ts=%d\n",
trx_sched_multiframes[i].name,
- l1h->trx->nr, tn);
+ l1t->trx->nr, tn);
return 0;
}
}
LOGP(DL1C, LOGL_NOTICE, "Failed to configuring multiframe "
- "trx=%d ts=%d\n", l1h->trx->nr, tn);
+ "trx=%d ts=%d\n", l1t->trx->nr, tn);
return -ENOTSUP;
}
/* setting all logical channels given attributes to active/inactive */
-int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
+int trx_sched_set_lchan(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t link_id,
int active)
{
uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
uint8_t ss = l1sap_chan2ss(chan_nr);
int i;
int rc = -EINVAL;
- struct trx_chan_state *chan_state;
/* look for all matching chan_nr/link_id */
for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ struct l1sched_chan_state *chan_state;
+ chan_state = &l1ts->chan_state[i];
/* skip if pchan type does not match pdch flag */
- if ((trx_sched_multiframes[l1h->mf_index[tn]].pchan
+ if ((trx_sched_multiframes[l1ts->mf_index].pchan
== GSM_PCHAN_PDCH)
!= trx_chan_desc[i].pdch)
continue;
if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
&& trx_chan_desc[i].link_id == link_id) {
- chan_state = &l1h->chan_states[tn][i];
rc = 0;
if (chan_state->active == active)
continue;
LOGP(DL1C, LOGL_NOTICE, "%s %s on trx=%d ts=%d\n",
(active) ? "Activating" : "Deactivating",
- trx_chan_desc[i].name, l1h->trx->nr, tn);
+ trx_chan_desc[i].name, l1t->trx->nr, tn);
if (active)
memset(chan_state, 0, sizeof(*chan_state));
chan_state->active = active;
@@ -2554,41 +1317,42 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
talloc_free(chan_state->ul_bursts);
chan_state->ul_bursts = NULL;
}
+ if (!active)
+ chan_state->ho_rach_detect = 0;
}
}
/* 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);
- }
+ if (!active)
+ _sched_act_rach_det(l1t, tn, ss, 0);
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,
+int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmode,
uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
uint8_t codec2, uint8_t codec3, uint8_t initial_id, uint8_t handover)
{
uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
uint8_t ss = l1sap_chan2ss(chan_nr);
int i;
int rc = -EINVAL;
- struct trx_chan_state *chan_state;
+ struct l1sched_chan_state *chan_state;
/* no mode for PDCH */
- if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+ if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
return 0;
/* look for all matching chan_nr/link_id */
for (i = 0; i < _TRX_CHAN_MAX; i++) {
if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
&& trx_chan_desc[i].link_id == 0x00) {
- chan_state = &l1h->chan_states[tn][i];
+ chan_state = &l1ts->chan_state[i];
LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u, handover %u "
"on %s of trx=%d ts=%d\n", rsl_cmode, tch_mode,
- handover, trx_chan_desc[i].name, l1h->trx->nr,
+ handover, trx_chan_desc[i].name, l1t->trx->nr,
tn);
chan_state->rsl_cmode = rsl_cmode;
chan_state->tch_mode = tch_mode;
@@ -2616,28 +1380,23 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
* 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);
- }
+ _sched_act_rach_det(l1t, tn, ss, handover);
return rc;
}
/* setting cipher on logical channels */
-int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
+int trx_sched_set_cipher(struct l1sched_trx *l1t, uint8_t chan_nr, int downlink,
int algo, uint8_t *key, int key_len)
{
uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
int i;
int rc = -EINVAL;
- struct trx_chan_state *chan_state;
+ struct l1sched_chan_state *chan_state;
/* no cipher for PDCH */
- if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+ if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
return 0;
/* no algorithm given means a5/0 */
@@ -2655,11 +1414,11 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
if (trx_chan_desc[i].pdch)
continue;
if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)) {
- chan_state = &l1h->chan_states[tn][i];
+ chan_state = &l1ts->chan_state[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);
+ trx_chan_desc[i].name, l1t->trx->nr, tn);
if (downlink) {
chan_state->dl_encr_algo = algo;
memcpy(chan_state->dl_encr_key, key, key_len);
@@ -2677,21 +1436,22 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
}
/* process ready-to-send */
-static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn)
+int _sched_rts(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn)
{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
const 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])
+ if (!l1ts->mf_index)
return 0;
/* get frame from multiframe */
- period = l1h->mf_period[tn];
+ period = l1ts->mf_period;
offset = fn % period;
- frame = l1h->mf_frames[tn] + offset;
+ frame = l1ts->mf_frames + offset;
chan = frame->dl_chan;
bid = frame->dl_bid;
@@ -2707,49 +1467,50 @@ 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].active)
+ && !l1ts->chan_state[chan].active)
return -EINVAL;
- return func(l1h, tn, fn, frame->dl_chan);
+ return func(l1t, 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)
+const ubit_t *_sched_dl_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn)
{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct l1sched_chan_state *l1cs;
const struct trx_sched_frame *frame;
uint8_t offset, period, bid;
trx_sched_dl_func *func;
enum trx_chan_type chan;
ubit_t *bits = NULL;
- if (!l1h->mf_index[tn])
+ if (!l1ts->mf_index)
goto no_data;
/* get frame from multiframe */
- period = l1h->mf_period[tn];
+ period = l1ts->mf_period;
offset = fn % period;
- frame = l1h->mf_frames[tn] + offset;
+ frame = l1ts->mf_frames + offset;
chan = frame->dl_chan;
bid = frame->dl_bid;
func = trx_chan_desc[chan].dl_fn;
+ l1cs = &l1ts->chan_state[chan];
+
/* check if channel is active */
- if (!trx_chan_desc[chan].auto_active
- && !l1h->chan_states[tn][chan].active)
+ if (!trx_chan_desc[chan].auto_active && !l1cs->active)
goto no_data;
/* get burst from function */
- bits = func(l1h, tn, fn, chan, bid);
+ bits = func(l1t, tn, fn, chan, bid);
/* encrypt */
- if (bits && l1h->chan_states[tn][chan].dl_encr_algo) {
+ if (bits && l1cs->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);
+ osmo_a5(l1cs->dl_encr_algo, l1cs->dl_encr_key, fn, ks, NULL);
for (i = 0; i < 57; i++) {
bits[i + 3] ^= ks[i];
bits[i + 88] ^= ks[i + 57];
@@ -2758,7 +1519,7 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn,
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 (bits == NULL && l1t->trx == l1t->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",
@@ -2770,41 +1531,44 @@ if (0) if (chan != TRXC_IDLE) // hack
}
/* process uplink burst */
-int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
+int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t current_fn,
sbit_t *bits, int8_t rssi, float toa)
{
+ struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ struct l1sched_chan_state *l1cs;
const struct trx_sched_frame *frame;
uint8_t offset, period, bid;
trx_sched_ul_func *func;
enum trx_chan_type chan;
uint32_t fn, elapsed;
- if (!l1h->mf_index[tn])
+ if (!l1ts->mf_index)
return -EINVAL;
/* calculate how many frames have been elapsed */
- elapsed = (current_fn + GSM_HYPERFRAME - l1h->mf_last_fn[tn]) % GSM_HYPERFRAME;
+ elapsed = (current_fn + GSM_HYPERFRAME - l1ts->mf_last_fn) % GSM_HYPERFRAME;
/* 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) % GSM_HYPERFRAME;
+ fn = (l1ts->mf_last_fn + 1) % GSM_HYPERFRAME;
else
fn = current_fn;
while (42) {
/* get frame from multiframe */
- period = l1h->mf_period[tn];
+ period = l1ts->mf_period;
offset = fn % period;
- frame = l1h->mf_frames[tn] + offset;
+ frame = l1ts->mf_frames + offset;
chan = frame->ul_chan;
bid = frame->ul_bid;
func = trx_chan_desc[chan].ul_fn;
+ l1cs = &l1ts->chan_state[chan];
+
/* check if channel is active */
- if (!trx_chan_desc[chan].auto_active
- && !l1h->chan_states[tn][chan].active)
+ if (!trx_chan_desc[chan].auto_active && !l1cs->active)
goto next_frame;
/* omit bursts which have no handler, like IDLE bursts */
@@ -2814,12 +1578,12 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
/* put burst to function */
if (fn == current_fn) {
/* decrypt */
- if (bits && l1h->chan_states[tn][chan].ul_encr_algo) {
+ if (bits && l1cs->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,
+ osmo_a5(l1cs->ul_encr_algo,
+ l1cs->ul_encr_key,
fn, NULL, ks);
for (i = 0; i < 57; i++) {
if (ks[i])
@@ -2829,13 +1593,12 @@ 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
- && !l1h->chan_states[tn][chan].ho_rach_detect) {
+ func(l1t, tn, fn, chan, bid, bits, rssi, toa);
+ } else if (chan != TRXC_RACH && !l1cs->ho_rach_detect) {
sbit_t spare[148];
memset(spare, 0, 148);
- func(l1h, tn, fn, chan, bid, spare, -128, 0);
+ func(l1t, tn, fn, chan, bid, spare, -128, 0);
}
next_frame:
@@ -2846,218 +1609,13 @@ next_frame:
fn = (fn + 1) % GSM_HYPERFRAME;
}
- l1h->mf_last_fn[tn] = fn;
-
- return 0;
-}
-
-/* schedule all frames of all TRX for given FN */
-static int trx_sched_fn(uint32_t fn)
-{
- struct gsm_bts_trx *trx;
- struct trx_l1h *l1h;
- uint8_t tn;
- const ubit_t *bits;
- uint8_t gain;
-
- /* send time indication */
- l1if_mph_time_ind(bts, fn);
-
- /* advance frame number, so the transceiver has more time until
- * it must be transmitted. */
- fn = (fn + trx_clock_advance) % GSM_HYPERFRAME;
-
- /* 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 < TRX_NR_TS; tn++) {
- /* ignore disabled slots */
- if (!(l1h->config.slotmask & (1 << tn)))
- continue;
- /* ready-to-send */
- trx_sched_rts(l1h, tn,
- (fn + trx_rts_advance) % GSM_HYPERFRAME);
- /* get burst for FN */
- bits = trx_sched_dl_burst(l1h, tn, fn);
- if (!bits) {
- /* if no bits, send no burst */
- continue;
- } else
- gain = 0;
- trx_if_data(l1h, tn, fn, gain, bits);
- }
- }
+ l1ts->mf_last_fn = fn;
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 l1sched_ts *l1sched_trx_get_ts(struct l1sched_trx *l1t, uint8_t tn)
{
- struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
- int32_t elapsed;
-
- /* check if transceiver is still alive */
- if (transceiver_lost++ == TRX_LOSS_FRAMES) {
- struct gsm_bts_trx *trx;
-
- LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n");
-
-no_clock:
- transceiver_available = 0;
-
- /* flush pending messages of transceiver */
- /* close all logical channels and reset timeslots */
- llist_for_each_entry(trx, &bts->trx_list, list) {
- trx_if_flush(trx_l1h_hdl(trx));
- trx_sched_reset(trx_l1h_hdl(trx));
- if (trx->nr == 0)
- trx_if_cmd_poweroff(trx_l1h_hdl(trx));
- }
-
- /* tell BSC */
- check_transceiver_availability(bts, 0);
-
- return;
- }
-
- gettimeofday(&tv_now, NULL);
-
- elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
- + (tv_now.tv_usec - tv_clock->tv_usec);
-
- /* if someone played with clock, or if the process stalled */
- if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) {
- LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n",
- elapsed);
- goto no_clock;
- }
-
- /* schedule next FN clock */
- while (elapsed > FRAME_DURATION_uS / 2) {
- tv_clock->tv_usec += FRAME_DURATION_uS;
- if (tv_clock->tv_usec >= 1000000) {
- tv_clock->tv_sec++;
- tv_clock->tv_usec -= 1000000;
- }
- transceiver_last_fn = (transceiver_last_fn + 1) % GSM_HYPERFRAME;
- trx_sched_fn(transceiver_last_fn);
- elapsed -= FRAME_DURATION_uS;
- }
- osmo_timer_schedule(&transceiver_clock_timer, 0,
- FRAME_DURATION_uS - elapsed);
+ OSMO_ASSERT(tn < ARRAY_SIZE(l1t->ts));
+ return &l1t->ts[tn];
}
-
-
-/* receive clock from transceiver */
-int trx_sched_clock(uint32_t fn)
-{
- struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
- int32_t elapsed;
- int32_t elapsed_fn;
-
- if (quit)
- return 0;
-
- /* reset lost counter */
- transceiver_lost = 0;
-
- gettimeofday(&tv_now, NULL);
-
- /* clock becomes valid */
- if (!transceiver_available) {
- LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n",
- fn);
-
- transceiver_available = 1;
-
- /* start provisioning transceiver */
- l1if_provision_transceiver(bts);
-
- /* tell BSC */
- check_transceiver_availability(bts, 1);
-
-new_clock:
- transceiver_last_fn = fn;
- trx_sched_fn(transceiver_last_fn);
-
- /* schedule first FN clock */
- memcpy(tv_clock, &tv_now, sizeof(struct timeval));
- memset(&transceiver_clock_timer, 0,
- sizeof(transceiver_clock_timer));
- transceiver_clock_timer.cb = trx_ctrl_timer_cb;
- transceiver_clock_timer.data = bts;
- osmo_timer_schedule(&transceiver_clock_timer, 0,
- FRAME_DURATION_uS);
-
- return 0;
- }
-
- osmo_timer_del(&transceiver_clock_timer);
-
- /* calculate elapsed time since last_fn */
- elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
- + (tv_now.tv_usec - tv_clock->tv_usec);
-
- /* how much frames have been elapsed since last fn processed */
- elapsed_fn = (fn + GSM_HYPERFRAME - transceiver_last_fn) % GSM_HYPERFRAME;
- if (elapsed_fn >= 135774)
- elapsed_fn -= GSM_HYPERFRAME;
-
- /* check for max clock skew */
- if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
- LOGP(DL1C, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
- "new fn=%u\n", transceiver_last_fn, fn);
- goto new_clock;
- }
-
- LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n",
- elapsed_fn * FRAME_DURATION_uS - elapsed);
-
- /* too many frames have been processed already */
- if (elapsed_fn < 0) {
- /* set clock to the time or last FN should have been
- * transmitted. */
- tv_clock->tv_sec = tv_now.tv_sec;
- tv_clock->tv_usec = tv_now.tv_usec +
- (0 - elapsed_fn) * FRAME_DURATION_uS;
- if (tv_clock->tv_usec >= 1000000) {
- tv_clock->tv_sec++;
- tv_clock->tv_usec -= 1000000;
- }
- /* set time to the time our next FN has to be transmitted */
- osmo_timer_schedule(&transceiver_clock_timer, 0,
- FRAME_DURATION_uS * (1 - elapsed_fn));
-
- return 0;
- }
-
- /* transmit what we still need to transmit */
- while (fn != transceiver_last_fn) {
- transceiver_last_fn = (transceiver_last_fn + 1) % GSM_HYPERFRAME;
- trx_sched_fn(transceiver_last_fn);
- }
-
- /* schedule next FN to be transmitted */
- memcpy(tv_clock, &tv_now, sizeof(struct timeval));
- osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS);
-
- return 0;
-}
-
diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am
index 26bc8e2e..0c0d2818 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 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_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
+osmobts_trx_SOURCES = main.c trx_if.c l1_if.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 $(top_builddir)/src/common/libl1sched.a $(LDADD)
diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index a45ba05b..edd4b7bd 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -36,10 +36,10 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/amr.h>
#include <osmo-bts/abis.h>
+#include <osmo-bts/scheduler.h>
#include "l1_if.h"
#include "trx_if.h"
-#include "scheduler.h"
static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = {
@@ -68,9 +68,10 @@ struct trx_l1h *l1if_open(struct gsm_bts_trx *trx)
if (!l1h)
return NULL;
l1h->trx = trx;
+ l1h->l1s.trx = trx;
trx->role_bts.l1h = l1h;
- trx_sched_init(l1h);
+ trx_sched_init(&l1h->l1s);
rc = trx_if_open(l1h);
if (rc < 0) {
@@ -89,7 +90,7 @@ err:
void l1if_close(struct trx_l1h *l1h)
{
trx_if_close(l1h);
- trx_sched_exit(l1h);
+ trx_sched_exit(&l1h->l1s);
talloc_free(l1h);
}
@@ -267,7 +268,7 @@ int bts_model_trx_close(struct gsm_bts_trx *trx)
enum gsm_phys_chan_config pchan = trx->ts[0].pchan;
/* close all logical channels and reset timeslots */
- trx_sched_reset(l1h);
+ trx_sched_reset(&l1h->l1s);
/* deactivate lchan for CCCH */
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
@@ -374,8 +375,12 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts)
l1if_provision_transceiver_trx(l1h);
}
+ /* ignore disabled slots */
+ if (!(l1h->config.slotmask & (1 << tn)))
+ return NM_NACK_RES_NOTAVAIL;
+
/* set physical channel */
- rc = trx_sched_set_pchan(l1h, tn, pchan);
+ rc = trx_sched_set_pchan(&l1h->l1s, tn, pchan);
if (rc)
return NM_NACK_RES_NOTAVAIL;
@@ -413,17 +418,17 @@ static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan,
if (!downlink) {
/* set uplink */
- trx_sched_set_cipher(l1h, chan_nr, 0, lchan->encr.alg_id - 1,
+ trx_sched_set_cipher(&l1h->l1s, chan_nr, 0, lchan->encr.alg_id - 1,
lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
} else {
/* set downlink and also set uplink, if not already */
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
- trx_sched_set_cipher(l1h, chan_nr, 0,
+ trx_sched_set_cipher(&l1h->l1s, chan_nr, 0,
lchan->encr.alg_id - 1, lchan->encr.key,
lchan->encr.key_len);
}
- trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1,
+ trx_sched_set_cipher(&l1h->l1s, chan_nr, 1, lchan->encr.alg_id - 1,
lchan->encr.key, lchan->encr.key_len);
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
}
@@ -510,12 +515,12 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
if (!msg)
break;
/* put data into scheduler's queue */
- return trx_sched_ph_data_req(l1h, l1sap);
+ return trx_sched_ph_data_req(&l1h->l1s, l1sap);
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
if (!msg)
break;
/* put data into scheduler's queue */
- return trx_sched_tch_req(l1h, l1sap);
+ return trx_sched_tch_req(&l1h->l1s, l1sap);
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
switch (l1sap->u.info.type) {
case PRIM_INFO_ACT_CIPH:
@@ -542,11 +547,11 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
break;
}
/* activate dedicated channel */
- trx_sched_set_lchan(l1h, chan_nr, 0x00, 1);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 1);
/* activate associated channel */
- trx_sched_set_lchan(l1h, chan_nr, 0x40, 1);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 1);
/* set mode */
- trx_sched_set_mode(l1h, chan_nr,
+ trx_sched_set_mode(&l1h->l1s, chan_nr,
lchan->rsl_cmode, lchan->tch_mode,
lchan->tch.amr_mr.num_modes,
lchan->tch.amr_mr.bts_mode[0].mode,
@@ -574,7 +579,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
}
if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
/* change mode */
- trx_sched_set_mode(l1h, chan_nr,
+ trx_sched_set_mode(&l1h->l1s, chan_nr,
lchan->rsl_cmode, lchan->tch_mode,
lchan->tch.amr_mr.num_modes,
lchan->tch.amr_mr.bts_mode[0].mode,
@@ -591,12 +596,12 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
break;
}
/* deactivate associated channel */
- trx_sched_set_lchan(l1h, chan_nr, 0x40, 0);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 0);
if (!l1sap->u.info.u.act_req.sacch_only) {
/* set lchan inactive */
lchan_set_state(lchan, LCHAN_S_NONE);
/* deactivate dedicated channel */
- trx_sched_set_lchan(l1h, chan_nr, 0x00, 0);
+ trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 0);
/* confirm only on dedicated channel */
mph_info_chan_confirm(l1h, chan_nr,
PRIM_INFO_DEACTIVATE, 0);
diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h
index 2c672cb9..f4926876 100644
--- a/src/osmo-bts-trx/l1_if.h
+++ b/src/osmo-bts-trx/l1_if.h
@@ -1,109 +1,7 @@
#ifndef L1_IF_H_TRX
#define L1_IF_H_TRX
-/* These types define the different channels on a multiframe.
- * Each channel has queues and can be activated individually.
- */
-enum trx_chan_type {
- TRXC_IDLE = 0,
- TRXC_FCCH,
- TRXC_SCH,
- TRXC_BCCH,
- TRXC_RACH,
- TRXC_CCCH,
- TRXC_TCHF,
- TRXC_TCHH_0,
- TRXC_TCHH_1,
- TRXC_SDCCH4_0,
- TRXC_SDCCH4_1,
- TRXC_SDCCH4_2,
- TRXC_SDCCH4_3,
- TRXC_SDCCH8_0,
- TRXC_SDCCH8_1,
- TRXC_SDCCH8_2,
- TRXC_SDCCH8_3,
- TRXC_SDCCH8_4,
- TRXC_SDCCH8_5,
- TRXC_SDCCH8_6,
- TRXC_SDCCH8_7,
- TRXC_SACCHTF,
- TRXC_SACCHTH_0,
- TRXC_SACCHTH_1,
- TRXC_SACCH4_0,
- TRXC_SACCH4_1,
- TRXC_SACCH4_2,
- TRXC_SACCH4_3,
- TRXC_SACCH8_0,
- TRXC_SACCH8_1,
- TRXC_SACCH8_2,
- TRXC_SACCH8_3,
- TRXC_SACCH8_4,
- TRXC_SACCH8_5,
- TRXC_SACCH8_6,
- TRXC_SACCH8_7,
- TRXC_PDTCH,
- TRXC_PTCCH,
- _TRX_CHAN_MAX
-};
-
-/* States each channel on a multiframe */
-struct trx_chan_state {
- /* scheduler */
- uint8_t active; /* Channel is active */
- ubit_t *dl_bursts; /* burst buffer for TX */
- sbit_t *ul_bursts; /* burst buffer for RX */
- uint32_t ul_first_fn; /* fn of first burst */
- uint8_t ul_mask; /* mask of received bursts */
-
- /* RSSI / TOA */
- uint8_t rssi_num; /* number of RSSI values */
- float rssi_sum; /* sum of RSSI values */
- uint8_t toa_num; /* number of TOA values */
- float toa_sum; /* sum of TOA values */
-
- /* loss detection */
- uint8_t lost; /* (SACCH) loss detection */
-
- /* mode */
- uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */
-
- /* AMR */
- uint8_t codec[4]; /* 4 possible codecs for amr */
- int codecs; /* number of possible codecs */
- float ber_sum; /* sum of bit error rates */
- int ber_num; /* number of bit error rates */
- uint8_t ul_ft; /* current uplink FT index */
- uint8_t dl_ft; /* current downlink FT index */
- uint8_t ul_cmr; /* current uplink CMR index */
- uint8_t dl_cmr; /* current downlink CMR index */
- uint8_t amr_loop; /* if AMR loop is enabled */
-
- /* TCH/H */
- uint8_t dl_ongoing_facch; /* FACCH/H on downlink */
- uint8_t ul_ongoing_facch; /* FACCH/H on uplink */
-
- /* encryption */
- int ul_encr_algo; /* A5/x encry algo downlink */
- int dl_encr_algo; /* A5/x encry algo uplink */
- int ul_encr_key_len;
- int dl_encr_key_len;
- uint8_t ul_encr_key[MAX_A5_KEY_LEN];
- uint8_t dl_encr_key[MAX_A5_KEY_LEN];
-
- /* measurements */
- struct {
- uint8_t clock; /* cyclic clock counter */
- int8_t rssi[32]; /* last RSSI values */
- int rssi_count; /* received RSSI values */
- int rssi_valid_count; /* number of stored value */
- int rssi_got_burst; /* any burst received so far */
- float toa_sum; /* sum of TOA values */
- int toa_num; /* number of TOA value */
- } meas;
-
- /* handover */
- uint8_t ho_rach_detect; /* if rach detection is on */
-};
+#include <osmo-bts/scheduler.h>
struct trx_config {
uint8_t poweron; /* poweron(1) or poweroff(0) */
@@ -152,16 +50,9 @@ struct trx_l1h {
/* transceiver config */
struct trx_config config;
-
- uint8_t mf_index[TRX_NR_TS]; /* selected multiframe index */
- uint32_t mf_last_fn[TRX_NR_TS]; /* last received frame */
- uint8_t mf_period[TRX_NR_TS]; /* period of multiframe */
- const struct trx_sched_frame *mf_frames[TRX_NR_TS]; /* pointer to frame layout */
-
- /* Channel states for all channels on all timeslots */
- struct trx_chan_state chan_states[TRX_NR_TS][_TRX_CHAN_MAX];
- struct llist_head dl_prims[TRX_NR_TS]; /* Queue primitves for TX */
uint8_t ho_rach_detect[TRX_NR_TS][TS_MAX_LCHAN];
+
+ struct l1sched_trx l1s;
};
struct trx_l1h *l1if_open(struct gsm_bts_trx *trx);
@@ -176,4 +67,10 @@ void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta
int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
int n_errors, int n_bits_total, float rssi, float toa);
+static inline struct l1sched_trx *trx_l1sched_hdl(struct gsm_bts_trx *trx)
+{
+ struct trx_l1h *l1h = trx_l1h_hdl(trx);
+ return &l1h->l1s;
+}
+
#endif /* L1_IF_H_TRX */
diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c
index 52ac170a..8070e802 100644
--- a/src/osmo-bts-trx/loops.c
+++ b/src/osmo-bts-trx/loops.c
@@ -33,7 +33,7 @@
#include "l1_if.h"
#include "loops.h"
-#define MS_PWR_DBM(lvl) ms_pwr_dbm(gsm_arfcn2band(l1h->config.arfcn), lvl)
+#define MS_PWR_DBM(arfcn, lvl) ms_pwr_dbm(gsm_arfcn2band(arfcn), lvl)
/*
* MS Power loop
@@ -42,11 +42,12 @@
int trx_ms_power_loop = 0;
int8_t trx_target_rssi = -10;
-static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
- uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff)
+static int ms_power_diff(struct gsm_lchan *lchan, uint8_t chan_nr, int8_t diff)
{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+ uint16_t arfcn = trx->arfcn;
int8_t new_power;
-
+
new_power = lchan->ms_power - (diff >> 1);
if (diff == 0)
@@ -56,7 +57,7 @@ static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
new_power = 0;
// FIXME: to go above 1W, we need to know classmark of MS
- if (l1h->config.arfcn >= 512 && l1h->config.arfcn <= 885) {
+ if (arfcn >= 512 && arfcn <= 885) {
if (new_power > 15)
new_power = 15;
} else {
@@ -73,8 +74,8 @@ static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
if (lchan->ms_power == new_power) {
LOGP(DLOOP, LOGL_INFO, "Keeping MS new_power of trx=%u "
"chan_nr=0x%02x at control level %d (%d dBm)\n",
- l1h->trx->nr, chan_nr, new_power,
- MS_PWR_DBM(new_power));
+ trx->nr, chan_nr, new_power,
+ MS_PWR_DBM(arfcn, new_power));
return 0;
}
@@ -82,15 +83,16 @@ static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
LOGP(DLOOP, LOGL_INFO, "%s MS new_power of trx=%u chan_nr=0x%02x from "
"control level %d (%d dBm) to %d (%d dBm)\n",
(diff > 0) ? "Raising" : "Lowering",
- l1h->trx->nr, chan_nr, lchan->ms_power,
- MS_PWR_DBM(lchan->ms_power), new_power, MS_PWR_DBM(new_power));
+ trx->nr, chan_nr, lchan->ms_power,
+ MS_PWR_DBM(arfcn, lchan->ms_power), new_power,
+ MS_PWR_DBM(arfcn, new_power));
lchan->ms_power = new_power;
return 0;
}
-static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
+static int ms_power_val(struct l1sched_chan_state *chan_state, int8_t rssi)
{
/* ignore inserted dummy frames, treat as lost frames */
if (rssi < -127)
@@ -112,9 +114,10 @@ static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
return 0;
}
-static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
- uint8_t chan_nr, struct trx_chan_state *chan_state)
+static int ms_power_clock(struct gsm_lchan *lchan,
+ uint8_t chan_nr, struct l1sched_chan_state *chan_state)
{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
int rssi;
int i;
@@ -134,9 +137,8 @@ static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
if (chan_state->meas.rssi_count == 0) {
LOGP(DLOOP, LOGL_NOTICE, "LOST SACCH frame of trx=%u "
"chan_nr=0x%02x, so we raise MS power\n",
- l1h->trx->nr, chan_nr);
- return ms_power_diff(l1h, lchan, chan_nr, chan_state,
- MS_RAISE_MAX);
+ trx->nr, chan_nr);
+ return ms_power_diff(lchan, chan_nr, MS_RAISE_MAX);
}
/* reset total counter */
@@ -157,9 +159,10 @@ static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
/* change RSSI */
LOGP(DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
"MS power: %d (%d dBm) of trx=%u chan_nr=0x%02x\n", rssi,
- trx_target_rssi, lchan->ms_power, MS_PWR_DBM(lchan->ms_power),
- l1h->trx->nr, chan_nr);
- ms_power_diff(l1h, lchan, chan_nr, chan_state, trx_target_rssi - rssi);
+ trx_target_rssi, lchan->ms_power,
+ MS_PWR_DBM(trx->arfcn, lchan->ms_power),
+ trx->nr, chan_nr);
+ ms_power_diff(lchan, chan_nr, trx_target_rssi - rssi);
return 0;
}
@@ -171,9 +174,11 @@ static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
int trx_ta_loop = 1;
-int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
- struct trx_chan_state *chan_state, float toa)
+int ta_val(struct gsm_lchan *lchan, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state, float toa)
{
+ struct gsm_bts_trx *trx = lchan->ts->trx;
+
/* check if the current L1 header acks to the current ordered TA */
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
return 0;
@@ -190,19 +195,19 @@ int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
if (toa < -0.9F && lchan->rqd_ta > 0) {
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
"early (%.2f), now lowering TA from %d to %d\n",
- l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
+ trx->nr, chan_nr, toa, lchan->rqd_ta,
lchan->rqd_ta - 1);
lchan->rqd_ta--;
} else if (toa > 0.9F && lchan->rqd_ta < 63) {
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
"late (%.2f), now raising TA from %d to %d\n",
- l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
+ trx->nr, chan_nr, toa, lchan->rqd_ta,
lchan->rqd_ta + 1);
lchan->rqd_ta++;
} else
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is "
"correct (%.2f), keeping current TA of %d\n",
- l1h->trx->nr, chan_nr, toa, lchan->rqd_ta);
+ trx->nr, chan_nr, toa, lchan->rqd_ta);
chan_state->meas.toa_num = 0;
chan_state->meas.toa_sum = 0;
@@ -210,29 +215,29 @@ int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
return 0;
}
-int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state, int8_t rssi, float toa)
+int trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state, int8_t rssi, float toa)
{
- struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+ struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
if (trx_ms_power_loop)
ms_power_val(chan_state, rssi);
if (trx_ta_loop)
- ta_val(l1h, lchan, chan_nr, chan_state, toa);
+ ta_val(lchan, chan_nr, chan_state, toa);
return 0;
}
-int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state)
+int trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state)
{
- struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+ struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
if (trx_ms_power_loop)
- ms_power_clock(l1h, lchan, chan_nr, chan_state);
+ ms_power_clock(lchan, chan_nr, chan_state);
/* count the number of SACCH clocks */
chan_state->meas.clock++;
@@ -240,10 +245,11 @@ int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
return 0;
}
-int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state, float ber)
+int trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state, float ber)
{
- struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+ struct gsm_bts_trx *trx = l1t->trx;
+ struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
.lchan[l1sap_chan2ss(chan_nr)];
int c_i;
@@ -280,7 +286,7 @@ int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
"codec id %d of trx=%u chan_nr=0x%02x\n", ber,
- chan_state->ul_ft, l1h->trx->nr, chan_nr);
+ chan_state->ul_ft, trx->nr, chan_nr);
/* degrade */
if (chan_state->dl_cmr > 0) {
@@ -290,7 +296,7 @@ int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
"from codec id %d to %d of trx=%u "
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
- chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr);
+ chan_state->dl_cmr - 1, trx->nr, chan_nr);
chan_state->dl_cmr--;
}
@@ -306,7 +312,7 @@ int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
"from codec id %d to %d of trx=%u "
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
- chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr);
+ chan_state->dl_cmr + 1, trx->nr, chan_nr);
chan_state->dl_cmr++;
}
@@ -316,7 +322,7 @@ int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
return 0;
}
-int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
+int trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
{
if (chan_state->amr_loop && !loop) {
chan_state->amr_loop = 0;
@@ -336,4 +342,3 @@ int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
return 0;
}
-
diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h
index 27b0ef23..613d2d01 100644
--- a/src/osmo-bts-trx/loops.h
+++ b/src/osmo-bts-trx/loops.h
@@ -17,15 +17,15 @@ extern int trx_ms_power_loop;
extern int8_t trx_target_rssi;
extern int trx_ta_loop;
-int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state, int8_t rssi, float toa);
+int trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state, int8_t rssi, float toa);
-int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state);
+int trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state);
-int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
- struct trx_chan_state *chan_state, float ber);
+int trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+ struct l1sched_chan_state *chan_state, float ber);
-int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop);
+int trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop);
#endif /* _TRX_LOOPS_H */
diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c
index 6c2dabf7..aa6987c5 100644
--- a/src/osmo-bts-trx/main.c
+++ b/src/osmo-bts-trx/main.c
@@ -53,10 +53,10 @@
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/control_if.h>
+#include <osmo-bts/scheduler.h>
#include "l1_if.h"
#include "trx_if.h"
-#include "scheduler.h"
int bts_model_init(struct gsm_bts *bts)
{
diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h
deleted file mode 100644
index 3e306937..00000000
--- a/src/osmo-bts-trx/scheduler.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef TRX_SCHEDULER_H
-#define TRX_SCHEDULER_H
-
-/*! \brief how many frame numbers in advance we should send bursts to PHY */
-extern uint32_t trx_clock_advance;
-/*! \brief advance RTS.ind to L2 by that many clocks */
-extern uint32_t trx_rts_advance;
-/*! \brief last frame number as received from PHY */
-extern uint32_t transceiver_last_fn;
-
-
-/*! \brief Initialize the scheudler data structures */
-int trx_sched_init(struct trx_l1h *l1h);
-
-/*! \brief De-initialize the scheudler data structures */
-void trx_sched_exit(struct trx_l1h *l1h);
-
-/*! \brief Handle a PH-DATA.req from L2 down to L1 */
-int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
-
-/*! \brief Handle a PH-TCH.req from L2 down to L1 */
-int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
-
-/*! \brief PHY informs us of new (current) GSM freme nunmber */
-int trx_sched_clock(uint32_t fn);
-
-/*! \brief handle an UL burst received by PHY */
-int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
- sbit_t *bits, int8_t rssi, float toa);
-
-/*! \brief set multiframe scheduler to given physical channel config */
-int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
- enum gsm_phys_chan_config pchan);
-
-/*! \brief set all matching logical channels active/inactive */
-int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
- int active);
-
-/*! \brief set mode of all matching logical channels to given mode(s) */
-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_codec,
- uint8_t handover);
-
-/*! \brief set ciphering on given 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);
-
-/* \brief close all logical channels and reset timeslots */
-void trx_sched_reset(struct trx_l1h *l1h);
-
-#endif /* TRX_SCHEDULER_H */
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
new file mode 100644
index 00000000..ac9212ba
--- /dev/null
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -0,0 +1,1470 @@
+/* Scheduler worker functiosn for OsmoBTS-TRX */
+
+/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2015 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/a5.h>
+
+#include <osmocom/netif/rtp.h>
+
+#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/logging.h>
+#include <osmo-bts/rsl.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/amr.h>
+#include <osmo-bts/scheduler.h>
+#include <osmo-bts/scheduler_backend.h>
+
+#include "l1_if.h"
+#include "gsm0503_coding.h"
+#include "trx_if.h"
+#include "loops.h"
+#include "amr.h"
+
+extern void *tall_bts_ctx;
+
+/* clock states */
+static uint32_t transceiver_lost;
+uint32_t transceiver_last_fn;
+static struct timeval transceiver_clock_tv;
+static struct osmo_timer_list transceiver_clock_timer;
+
+/* clock advance for the transceiver */
+uint32_t trx_clock_advance = 20;
+
+/* advance RTS to give some time for data processing. (especially PCU) */
+uint32_t trx_rts_advance = 5; /* about 20ms */
+
+/* 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];
+ uint8_t link_id = trx_chan_desc[chan].link_id;
+ uint8_t chan_nr = trx_chan_desc[chan].chan_nr | 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;
+ }
+
+ /* send clock information to loops process */
+ if (L1SAP_IS_LINK_SACCH(link_id))
+ trx_loop_sacch_clock(l1t, chan_nr, &l1ts->chan_state[chan]);
+
+ /* 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);
+}
+
+/* schedule all frames of all TRX for given FN */
+static int trx_sched_fn(struct gsm_bts *bts, uint32_t fn)
+{
+ struct gsm_bts_trx *trx;
+ uint8_t tn;
+ const ubit_t *bits;
+ uint8_t gain;
+
+ /* send time indication */
+ l1if_mph_time_ind(bts, fn);
+
+ /* advance frame number, so the transceiver has more time until
+ * it must be transmitted. */
+ fn = (fn + trx_clock_advance) % GSM_HYPERFRAME;
+
+ /* process every TRX */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ struct trx_l1h *l1h = trx_l1h_hdl(trx);
+ struct l1sched_trx *l1t = trx_l1sched_hdl(trx);
+
+ /* we don't schedule, if power is off */
+ if (!trx_if_powered(l1h))
+ continue;
+
+ /* process every TS of TRX */
+ for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
+ /* ready-to-send */
+ _sched_rts(l1t, tn,
+ (fn + trx_rts_advance) % GSM_HYPERFRAME);
+ /* get burst for FN */
+ bits = _sched_dl_burst(l1t, tn, fn);
+ if (!bits) {
+ /* if no bits, send no burst */
+ continue;
+ } 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 gsm_bts *bts = data;
+ struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
+ int32_t elapsed;
+
+ /* check if transceiver is still alive */
+ if (transceiver_lost++ == TRX_LOSS_FRAMES) {
+ struct gsm_bts_trx *trx;
+
+ LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n");
+
+no_clock:
+ transceiver_available = 0;
+
+ /* flush pending messages of transceiver */
+ /* close all logical channels and reset timeslots */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ trx_if_flush(trx_l1h_hdl(trx));
+ trx_sched_reset(trx_l1sched_hdl(trx));
+ if (trx->nr == 0)
+ trx_if_cmd_poweroff(trx_l1h_hdl(trx));
+ }
+
+ /* tell BSC */
+ check_transceiver_availability(bts, 0);
+
+ return;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
+
+ /* if someone played with clock, or if the process stalled */
+ if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) {
+ LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n",
+ elapsed);
+ goto no_clock;
+ }
+
+ /* schedule next FN clock */
+ while (elapsed > FRAME_DURATION_uS / 2) {
+ tv_clock->tv_usec += FRAME_DURATION_uS;
+ if (tv_clock->tv_usec >= 1000000) {
+ tv_clock->tv_sec++;
+ tv_clock->tv_usec -= 1000000;
+ }
+ transceiver_last_fn = (transceiver_last_fn + 1) % GSM_HYPERFRAME;
+ trx_sched_fn(bts, transceiver_last_fn);
+ elapsed -= FRAME_DURATION_uS;
+ }
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS - elapsed);
+}
+
+
+/* receive clock from transceiver */
+int trx_sched_clock(struct gsm_bts *bts, uint32_t fn)
+{
+ struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
+ int32_t elapsed;
+ int32_t elapsed_fn;
+
+ if (quit)
+ return 0;
+
+ /* reset lost counter */
+ transceiver_lost = 0;
+
+ gettimeofday(&tv_now, NULL);
+
+ /* clock becomes valid */
+ if (!transceiver_available) {
+ LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n",
+ fn);
+
+ transceiver_available = 1;
+
+ /* start provisioning transceiver */
+ l1if_provision_transceiver(bts);
+
+ /* tell BSC */
+ check_transceiver_availability(bts, 1);
+
+new_clock:
+ transceiver_last_fn = fn;
+ trx_sched_fn(bts, transceiver_last_fn);
+
+ /* schedule first FN clock */
+ memcpy(tv_clock, &tv_now, sizeof(struct timeval));
+ memset(&transceiver_clock_timer, 0,
+ sizeof(transceiver_clock_timer));
+ transceiver_clock_timer.cb = trx_ctrl_timer_cb;
+ transceiver_clock_timer.data = bts;
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS);
+
+ return 0;
+ }
+
+ osmo_timer_del(&transceiver_clock_timer);
+
+ /* calculate elapsed time since last_fn */
+ elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000
+ + (tv_now.tv_usec - tv_clock->tv_usec);
+
+ /* how much frames have been elapsed since last fn processed */
+ elapsed_fn = (fn + GSM_HYPERFRAME - transceiver_last_fn) % GSM_HYPERFRAME;
+ if (elapsed_fn >= 135774)
+ elapsed_fn -= GSM_HYPERFRAME;
+
+ /* check for max clock skew */
+ if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) {
+ LOGP(DL1C, LOGL_NOTICE, "GSM clock skew: old fn=%u, "
+ "new fn=%u\n", transceiver_last_fn, fn);
+ goto new_clock;
+ }
+
+ LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n",
+ elapsed_fn * FRAME_DURATION_uS - elapsed);
+
+ /* too many frames have been processed already */
+ if (elapsed_fn < 0) {
+ /* set clock to the time or last FN should have been
+ * transmitted. */
+ tv_clock->tv_sec = tv_now.tv_sec;
+ tv_clock->tv_usec = tv_now.tv_usec +
+ (0 - elapsed_fn) * FRAME_DURATION_uS;
+ if (tv_clock->tv_usec >= 1000000) {
+ tv_clock->tv_sec++;
+ tv_clock->tv_usec -= 1000000;
+ }
+ /* set time to the time our next FN has to be transmitted */
+ osmo_timer_schedule(&transceiver_clock_timer, 0,
+ FRAME_DURATION_uS * (1 - elapsed_fn));
+
+ return 0;
+ }
+
+ /* transmit what we still need to transmit */
+ while (fn != transceiver_last_fn) {
+ transceiver_last_fn = (transceiver_last_fn + 1) % GSM_HYPERFRAME;
+ trx_sched_fn(bts, transceiver_last_fn);
+ }
+
+ /* schedule next FN to be transmitted */
+ memcpy(tv_clock, &tv_now, sizeof(struct timeval));
+ osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS);
+
+ return 0;
+}
+
+void _sched_act_rach_det(struct l1sched_trx *l1t, uint8_t tn, uint8_t ss, int activate)
+{
+ struct trx_l1h *l1h = trx_l1h_hdl(l1t->trx);
+
+ if (activate)
+ trx_if_cmd_handover(l1h, tn, ss);
+ else
+ trx_if_cmd_nohandover(l1h, tn, ss);
+}
diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c
index 419fea64..dbe6f68c 100644
--- a/src/osmo-bts-trx/trx_if.c
+++ b/src/osmo-bts-trx/trx_if.c
@@ -36,10 +36,10 @@
#include <osmo-bts/logging.h>
#include <osmo-bts/bts.h>
+#include <osmo-bts/scheduler.h>
#include "l1_if.h"
#include "trx_if.h"
-#include "scheduler.h"
/* enable to print RSSI level graph */
//#define TOA_RSSI_DEBUG
@@ -121,6 +121,7 @@ static struct osmo_fd trx_ofd_clk;
/* get clock from clock socket */
static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
{
+ struct trx_l1h *l1h = ofd->data;
char buf[1500];
int len;
uint32_t fn;
@@ -145,7 +146,7 @@ static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
"correctly, correcting to fn=%u\n", fn);
}
- trx_sched_clock(fn);
+ trx_sched_clock(l1h->trx->bts, fn);
return 0;
}
@@ -454,7 +455,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
fprintf(stderr, "%s\n", deb);
#endif
- trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa);
+ trx_sched_ul_burst(&l1h->l1s, tn, fn, bits, rssi, toa);
return 0;
}
@@ -503,7 +504,7 @@ int trx_if_open(struct trx_l1h *l1h)
/* open sockets */
if (l1h->trx->nr == 0) {
- rc = trx_udp_open(NULL, &trx_ofd_clk, base_port_local,
+ rc = trx_udp_open(l1h, &trx_ofd_clk, base_port_local,
trx_clk_read_cb);
if (rc < 0)
return rc;
@@ -558,3 +559,7 @@ void trx_if_close(struct trx_l1h *l1h)
trx_udp_close(&l1h->trx_ofd_data);
}
+int trx_if_powered(struct trx_l1h *l1h)
+{
+ return l1h->config.poweron;
+}
diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h
index ac0ee42c..3862e2be 100644
--- a/src/osmo-bts-trx/trx_if.h
+++ b/src/osmo-bts-trx/trx_if.h
@@ -31,5 +31,6 @@ int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr,
int trx_if_open(struct trx_l1h *l1h);
void trx_if_flush(struct trx_l1h *l1h);
void trx_if_close(struct trx_l1h *l1h);
+int trx_if_powered(struct trx_l1h *l1h);
#endif /* TRX_IF_H */
diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c
index 5da0c21f..a4a79091 100644
--- a/src/osmo-bts-trx/trx_vty.c
+++ b/src/osmo-bts-trx/trx_vty.c
@@ -39,9 +39,9 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/vty.h>
+#include <osmo-bts/scheduler.h>
#include "l1_if.h"
-#include "scheduler.h"
#include "trx_if.h"
#include "loops.h"