aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/scheduler.c')
-rw-r--r--src/common/scheduler.c1031
1 files changed, 635 insertions, 396 deletions
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index 95a1b00e..a449d167 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -3,6 +3,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>
+ * Contributions by sysmocom - s.f.m.c. GmbH
*
* All Rights Reserved
*
@@ -14,7 +15,7 @@
* 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.
+ * GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -29,8 +30,12 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/stats.h>
#include <osmocom/gsm/protocol/gsm_08_58.h>
+#include <osmocom/gsm/gsm0502.h>
#include <osmocom/gsm/a5.h>
#include <osmo-bts/gsm_data.h>
@@ -39,17 +44,16 @@
#include <osmo-bts/l1sap.h>
#include <osmo-bts/scheduler.h>
#include <osmo-bts/scheduler_backend.h>
+#include <osmo-bts/bts.h>
extern void *tall_bts_ctx;
-static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan);
-static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan);
-static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan);
+static int rts_data_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br);
+static int rts_tchf_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br);
+static int rts_tchh_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br);
+
/*! \brief Dummy Burst (TS 05.02 Chapter 5.2.6) */
-static const ubit_t dummy_burst[GSM_BURST_LEN] = {
+const ubit_t _sched_dummy_burst[GSM_BURST_LEN] = {
0,0,0,
1,1,1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,1,0,
0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,
@@ -59,28 +63,51 @@ static const ubit_t dummy_burst[GSM_BURST_LEN] = {
0,0,0,
};
-/*! \brief FCCH Burst (TS 05.02 Chapter 5.2.4) */
-const ubit_t _sched_fcch_burst[GSM_BURST_LEN] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-};
-
-/*! \brief Training Sequences (TS 05.02 Chapter 5.2.3) */
-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, },
- { 0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0, },
- { 0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1, },
- { 0,1,0,0,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,1,0, },
- { 1,0,1,0,0,1,1,1,1,1,0,1,1,0,0,0,1,0,1,0,0,1,1,1,1,1, },
- { 1,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,1,1,0,0, },
+/*! Training Sequences for Normal Burst (see 3GPP TS 45.002, section 5.2.3) */
+const ubit_t _sched_train_seq_gmsk_nb[4][8][26] = {
+ { /* TSC set 1, table 5.2.3a */
+ { 0,0,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,1 },
+ { 0,0,1,0,1,1,0,1,1,1,0,1,1,1,1,0,0,0,1,0,1,1,0,1,1,1 },
+ { 0,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,0,1,0,0,0,0,1,1,1,0 },
+ { 0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0 },
+ { 0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1 },
+ { 0,1,0,0,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,1,0 },
+ { 1,0,1,0,0,1,1,1,1,1,0,1,1,0,0,0,1,0,1,0,0,1,1,1,1,1 },
+ { 1,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,1,1,0,0 },
+ },
+ { /* TSC set 2, table 5.2.3b */
+ { 0,1,1,0,0,0,1,0,0,0,1,0,0,1,0,0,1,1,1,1,0,1,0,1,1,1 },
+ { 0,1,0,1,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,1 },
+ { 0,1,0,0,0,0,0,1,0,1,1,0,0,0,1,1,1,0,1,1,1,0,1,1,0,0 },
+ { 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0 },
+ { 0,1,1,1,0,1,0,0,1,1,1,1,0,1,0,0,1,1,1,0,1,1,1,1,1,0 },
+ { 0,1,0,0,0,0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,1,1,0,0,1,1 },
+ { 0,0,0,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,1,1,1,0,1,0,1 },
+ { 0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,0,0,1,0,1,0,0,1 },
+ },
+ { /* TSC set 3, table 5.2.3c */
+ { 1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1,1,0,1,0,1,0,0,0,1,0 },
+ { 0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,1,0,0,0 },
+ { 1,1,0,0,1,0,0,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,0,1,1,0 },
+ { 0,0,1,1,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,0 },
+ { 0,0,0,1,1,1,1,0,1,0,1,1,1,0,1,0,0,0,0,1,0,0,0,1,1,0 },
+ { 1,1,0,0,1,1,1,1,0,1,0,1,0,1,1,1,1,0,0,1,0,0,0,0,0,0 },
+ { 1,0,1,1,1,0,0,1,1,0,1,0,1,1,1,1,1,1,0,0,0,1,0,0,0,0 },
+ { 1,1,1,0,0,1,0,1,1,1,1,0,1,1,1,0,0,0,0,0,1,0,0,1,0,0 },
+ },
+ { /* TSC set 4, table 5.2.3d */
+ { 1,1,0,0,1,1,1,0,1,0,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0 },
+ { 0,1,1,0,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,1,1,1,0,0,0,0 },
+ { 1,1,1,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,0,0,0,0,0 },
+ { 0,1,1,0,1,1,0,0,1,1,1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0 },
+ { 1,1,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0 },
+ { 1,1,0,1,0,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0 },
+ { 0,0,1,0,0,1,1,1,1,1,1,1,0,0,1,0,1,0,1,0,1,1,0,0,0,0 },
+ { 0,1,0,1,1,1,0,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,1,1,0 },
+ },
};
-const ubit_t _sched_egprs_tsc[8][78] = {
+const ubit_t _sched_train_seq_8psk_nb[8][78] = {
{ 1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,0,0,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,0,0,1,0,0,1, },
@@ -108,7 +135,7 @@ const ubit_t _sched_egprs_tsc[8][78] = {
};
/*! \brief SCH training sequence (TS 05.02 Chapter 5.2.5) */
-const ubit_t _sched_sch_train[64] = {
+const ubit_t _sched_train_seq_gmsk_sb[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,
};
@@ -118,18 +145,12 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
[TRXC_IDLE] = {
.name = "IDLE",
.desc = "Idle channel",
-
- /* On C0, BTS needs to ensure discontinuous burst transmission.
- * Therefore we need to send dummy bursts on IDLE slots. */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
- .dl_fn = tx_idle_fn,
},
[TRXC_FCCH] = {
.name = "FCCH", /* 3GPP TS 05.02, section 3.3.2.1 */
.desc = "Frequency correction channel",
/* Tx only, frequency correction bursts */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.dl_fn = tx_fcch_fn,
},
[TRXC_SCH] = {
@@ -137,7 +158,6 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
.desc = "Synchronization channel",
/* Tx only, synchronization bursts */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.dl_fn = tx_sch_fn,
},
[TRXC_BCCH] = {
@@ -148,7 +168,6 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
/* Tx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.rts_fn = rts_data_fn,
.dl_fn = tx_data_fn,
},
@@ -158,7 +177,6 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
.chan_nr = RSL_CHAN_RACH,
/* Rx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.ul_fn = rx_rach_fn,
},
[TRXC_CCCH] = {
@@ -169,7 +187,6 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
/* Tx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4),
* regular interleaving (3GPP TS 05.02, clause 7, table 3):
* a L2 frame is interleaved over 4 consecutive bursts. */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.rts_fn = rts_data_fn,
.dl_fn = tx_data_fn,
},
@@ -200,13 +217,14 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
*
- * - a traffic frame is interleaved over 6 consecutive bursts
+ * - a traffic frame is interleaved over 4 consecutive bursts
* using the even numbered bits of the first 2 bursts,
- * all bits of the middle two 2 bursts,
* and odd numbered bits of the last 2 bursts;
* - a FACCH/H frame 'steals' (replaces) two traffic frames,
- * interleaving is done over 4 consecutive bursts,
- * the same as given for a TCH/FS. */
+ * interleaving is done over 6 consecutive bursts,
+ * using the even numbered bits of the first 2 bursts,
+ * all bits of the middle two 2 bursts,
+ * and odd numbered bits of the last 2 bursts. */
.rts_fn = rts_tchh_fn,
.dl_fn = tx_tchh_fn,
.ul_fn = rx_tchh_fn,
@@ -525,10 +543,7 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
.chan_nr = RSL_CHAN_OSMO_PDCH,
/* Rx and Tx, multiple coding schemes: CS-2..4 and MCS-1..9 (3GPP TS
- * 05.03, chapter 5), regular interleaving as specified for xCCH.
- * NOTE: the burst buffer is three times bigger because the
- * payload of EDGE bursts is three times longer. */
- .flags = TRX_CHAN_FLAG_PDCH,
+ * 05.03, chapter 5), regular interleaving as specified for xCCH. */
.rts_fn = rts_data_fn,
.dl_fn = tx_pdtch_fn,
.ul_fn = rx_pdtch_fn,
@@ -538,11 +553,14 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
.desc = "Packet Timing advance control channel",
.chan_nr = RSL_CHAN_OSMO_PDCH,
- /* Same as for TRXC_BCCH (xCCH), see above. */
- .flags = TRX_CHAN_FLAG_PDCH,
+ /* On the Uplink, mobile stations transmit random Access Bursts
+ * to allow estimation of the timing advance for one MS in packet
+ * transfer mode. On Downlink, the network sends timing advance
+ * updates for several mobile stations. The coding scheme used
+ * for PTCCH/D messages is the same as for PDTCH CS-1. */
.rts_fn = rts_data_fn,
- .dl_fn = tx_data_fn,
- .ul_fn = rx_data_fn,
+ .dl_fn = tx_pdtch_fn,
+ .ul_fn = rx_rach_fn,
},
[TRXC_CBCH] = {
/* TODO: distinguish CBCH on SDCCH/4 and SDCCH/8 */
@@ -551,163 +569,203 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
.chan_nr = RSL_CHAN_OSMO_CBCH4,
/* Tx only, same as for TRXC_BCCH (xCCH), see above. */
- .flags = TRX_CHAN_FLAG_AUTO_ACTIVE,
.rts_fn = rts_data_fn,
.dl_fn = tx_data_fn,
},
};
+enum {
+ L1SCHED_TS_CTR_DL_LATE,
+ L1SCHED_TS_CTR_DL_NOT_FOUND,
+};
+
+static const struct rate_ctr_desc l1sched_ts_ctr_desc[] = {
+ [L1SCHED_TS_CTR_DL_LATE] = {"l1sched_ts:dl_late", "Downlink frames arrived too late to submit to lower layers"},
+ [L1SCHED_TS_CTR_DL_NOT_FOUND] = {"l1sched_ts:dl_not_found", "Downlink frames not found while scheduling"},
+};
+static const struct rate_ctr_group_desc l1sched_ts_ctrg_desc = {
+ "l1sched_ts",
+ "L1 scheduler timeslot",
+ OSMO_STATS_CLASS_GLOBAL,
+ ARRAY_SIZE(l1sched_ts_ctr_desc),
+ l1sched_ts_ctr_desc
+};
+
/*
* init / exit
*/
-int trx_sched_init(struct l1sched_trx *l1t, struct gsm_bts_trx *trx)
+static void trx_sched_init_ts(struct gsm_bts_trx_ts *ts,
+ const unsigned int rate_ctr_idx)
{
- uint8_t tn;
+ struct l1sched_ts *l1ts;
unsigned int i;
+ char name[128];
- if (!trx)
- return -EINVAL;
+ l1ts = talloc_zero(ts->trx, struct l1sched_ts);
+ OSMO_ASSERT(l1ts != NULL);
- l1t->trx = trx;
+ /* Link both structures */
+ ts->priv = l1ts;
+ l1ts->ts = ts;
- LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1t->trx->nr);
+ l1ts->ctrs = rate_ctr_group_alloc(ts->trx,
+ &l1sched_ts_ctrg_desc,
+ rate_ctr_idx);
+ snprintf(name, sizeof(name), "bts%u-trx%u-ts%u%s",
+ ts->trx->bts->nr, ts->trx->nr, ts->nr,
+ ts->vamos.is_shadow ? "-shadow" : "");
+ rate_ctr_group_set_name(l1ts->ctrs, name);
- for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ INIT_LLIST_HEAD(&l1ts->dl_prims);
- l1ts->mf_index = 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;
- }
+ 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 = false;
}
-
- return 0;
}
-void trx_sched_exit(struct l1sched_trx *l1t)
+void trx_sched_init(struct gsm_bts_trx *trx)
{
- struct gsm_bts_trx_ts *ts;
- uint8_t tn;
- int i;
+ unsigned int tn;
- LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1t->trx->nr);
-
- 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++) {
- 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;
- }
- if (chan_state->ul_bursts) {
- talloc_free(chan_state->ul_bursts);
- chan_state->ul_bursts = NULL;
- }
- }
- /* clear lchan channel states */
- ts = &l1t->trx->ts[tn];
- for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
- lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+ OSMO_ASSERT(trx != NULL);
+
+ LOGPTRX(trx, DL1C, LOGL_DEBUG, "Init scheduler structures\n");
+
+ /* Allocate shadow timeslots */
+ gsm_bts_trx_init_shadow_ts(trx);
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ unsigned int rate_ctr_idx = trx->nr * 100 + tn;
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ /* Init primary and shadow timeslots */
+ trx_sched_init_ts(ts, rate_ctr_idx);
+ trx_sched_init_ts(ts->vamos.peer, rate_ctr_idx + 10);
}
}
-/* close all logical channels and reset timeslots */
-void trx_sched_reset(struct l1sched_trx *l1t)
+static void trx_sched_clean_ts(struct gsm_bts_trx_ts *ts)
{
- trx_sched_exit(l1t);
- trx_sched_init(l1t, l1t->trx);
+ struct l1sched_ts *l1ts = ts->priv;
+ unsigned int i;
+
+ msgb_queue_free(&l1ts->dl_prims);
+ rate_ctr_group_free(l1ts->ctrs);
+ l1ts->ctrs = NULL;
+
+ /* clear lchan channel states */
+ for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
+ lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+
+ talloc_free(l1ts);
+ ts->priv = NULL;
+}
+
+void trx_sched_clean(struct gsm_bts_trx *trx)
+{
+ unsigned int tn;
+
+ LOGPTRX(trx, DL1C, LOGL_DEBUG, "Clean scheduler structures\n");
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ /* Clean primary and shadow timeslots */
+ trx_sched_clean_ts(ts);
+ trx_sched_clean_ts(ts->vamos.peer);
+ }
+
+ /* Free previously allocated shadow timeslots */
+ gsm_bts_trx_free_shadow_ts(trx);
}
-struct msgb *_sched_dequeue_prim(struct l1sched_trx *l1t, int8_t tn, uint32_t fn,
- enum trx_chan_type chan)
+struct msgb *_sched_dequeue_prim(struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
{
struct msgb *msg, *msg2;
- struct osmo_phsap_prim *l1sap;
- uint32_t prim_fn;
+ uint32_t prim_fn, l1sap_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, &l1ts->dl_prims, list) {
- l1sap = msgb_l1sap_prim(msg);
- if (l1sap->oph.operation != PRIM_OP_REQUEST) {
-wrong_type:
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Prim has wrong type.\n");
-free_msg:
- /* unlink and free message */
- llist_del(&msg->list);
- msgb_free(msg);
- return NULL;
- }
+ struct osmo_phsap_prim *l1sap = msgb_l1sap_prim(msg);
switch (l1sap->oph.primitive) {
case PRIM_PH_DATA:
chan_nr = l1sap->u.data.chan_nr;
link_id = l1sap->u.data.link_id;
- prim_fn = ((l1sap->u.data.fn + GSM_HYPERFRAME - fn) % GSM_HYPERFRAME);
+ l1sap_fn = l1sap->u.data.fn;
break;
case PRIM_TCH:
chan_nr = l1sap->u.tch.chan_nr;
link_id = 0;
- prim_fn = ((l1sap->u.tch.fn + GSM_HYPERFRAME - fn) % GSM_HYPERFRAME);
+ l1sap_fn = l1sap->u.tch.fn;
break;
default:
- goto wrong_type;
+ LOGL1SB(DL1P, LOGL_ERROR, l1ts, br, "Prim has wrong type.\n");
+ goto free_msg;
}
- if (prim_fn > 100) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, chan, fn,
- "Prim %u is out of range (100), or channel %s with "
+ prim_fn = GSM_TDMA_FN_SUB(l1sap_fn, br->fn);
+ if (prim_fn > 100) { /* l1sap_fn < fn */
+ LOGL1SB(DL1P, LOGL_NOTICE, l1ts, br,
+ "Prim %u is out of range (%u vs exp %u), or channel %s with "
"type %s is already disabled. If this happens in "
"conjunction with PCU, increase 'rts-advance' by 5.\n",
- prim_fn, get_lchan_by_chan_nr(l1t->trx, chan_nr)->name,
- trx_chan_desc[chan].name);
+ prim_fn, l1sap_fn, br->fn,
+ get_lchan_by_chan_nr(l1ts->ts->trx, chan_nr)->name,
+ trx_chan_desc[br->chan].name);
+ rate_ctr_inc2(l1ts->ctrs, L1SCHED_TS_CTR_DL_LATE);
/* unlink and free message */
llist_del(&msg->list);
msgb_free(msg);
continue;
}
- if (prim_fn > 0)
- continue;
+ if (prim_fn > 0) /* l1sap_fn > fn */
+ break;
- goto found_msg;
+ /* l1sap_fn == fn */
+ if ((chan_nr ^ (trx_chan_desc[br->chan].chan_nr | br->tn))
+ || ((link_id & 0xc0) ^ trx_chan_desc[br->chan].link_id)) {
+ LOGL1SB(DL1P, LOGL_ERROR, l1ts, br, "Prim has wrong chan_nr=0x%02x link_id=%02x, "
+ "expecting chan_nr=0x%02x link_id=%02x.\n", chan_nr, link_id,
+ trx_chan_desc[br->chan].chan_nr | br->tn, trx_chan_desc[br->chan].link_id);
+ goto free_msg;
+ }
+
+ /* unlink and return message */
+ llist_del(&msg->list);
+ return msg;
}
+ /* Queue was traversed with no candidate, no prim is available for current FN: */
+ rate_ctr_inc2(l1ts->ctrs, L1SCHED_TS_CTR_DL_NOT_FOUND);
return NULL;
-found_msg:
- if ((chan_nr ^ (trx_chan_desc[chan].chan_nr | tn))
- || ((link_id & 0xc0) ^ trx_chan_desc[chan].link_id)) {
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, chan, fn, "Prim has wrong chan_nr=0x%02x link_id=%02x, "
- "expecting chan_nr=0x%02x link_id=%02x.\n", chan_nr, link_id,
- trx_chan_desc[chan].chan_nr | tn, trx_chan_desc[chan].link_id);
- goto free_msg;
- }
-
- /* unlink and return message */
+free_msg:
+ /* unlink and free message */
llist_del(&msg->list);
- return msg;
+ msgb_free(msg);
+ return NULL;
}
-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_ph_data_ind(struct l1sched_ts *l1ts, uint32_t fn,
+ enum trx_chan_type chan,
+ const uint8_t *data, size_t data_len,
+ uint16_t ber10k, float rssi,
int16_t ta_offs_256bits, int16_t link_qual_cb,
- uint16_t ber10k,
enum osmo_ph_pres_info_type presence_info)
{
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);
+ uint8_t chan_nr = trx_chan_desc[chan].chan_nr | l1ts->ts->nr;
+
+ /* VAMOS: use Osmocom specific channel number */
+ if (l1ts->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
/* compose primitive */
- msg = l1sap_msgb_alloc(l2_len);
+ msg = l1sap_msgb_alloc(data_len);
l1sap = msgb_l1sap_prim(msg);
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA,
PRIM_OP_INDICATION, msg);
@@ -719,48 +777,53 @@ int _sched_compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
l1sap->u.data.ta_offs_256bits = ta_offs_256bits;
l1sap->u.data.lqual_cb = link_qual_cb;
l1sap->u.data.pdch_presence_info = presence_info;
- msg->l2h = msgb_put(msg, l2_len);
- if (l2_len)
- memcpy(msg->l2h, l2, l2_len);
-
- if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id))
- l1ts->chan_state[chan].lost_frames = 0;
+ msg->l2h = msgb_put(msg, data_len);
+ if (data_len)
+ memcpy(msg->l2h, data, data_len);
/* forward primitive */
- l1sap_up(l1t->trx, l1sap);
+ l1sap_up(l1ts->ts->trx, l1sap);
return 0;
}
-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)
+int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
+ enum trx_chan_type chan,
+ const uint8_t *data, size_t data_len,
+ uint16_t ber10k, float rssi,
+ int16_t ta_offs_256bits, int16_t link_qual_cb,
+ uint8_t is_sub)
{
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
- struct gsm_bts_trx *trx = l1t->trx;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- uint8_t chan_nr = trx_chan_desc[chan].chan_nr | tn;
- struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
+ uint8_t chan_nr = trx_chan_desc[chan].chan_nr | l1ts->ts->nr;
+ struct gsm_lchan *lchan = &l1ts->ts->lchan[l1sap_chan2ss(chan_nr)];
+
+ /* VAMOS: use Osmocom specific channel number */
+ if (l1ts->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
/* compose primitive */
- msg = l1sap_msgb_alloc(tch_len);
+ msg = l1sap_msgb_alloc(data_len);
l1sap = msgb_l1sap_prim(msg);
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH,
PRIM_OP_INDICATION, msg);
l1sap->u.tch.chan_nr = chan_nr;
l1sap->u.tch.fn = fn;
- msg->l2h = msgb_put(msg, tch_len);
- if (tch_len)
- memcpy(msg->l2h, tch, tch_len);
-
- if (l1ts->chan_state[chan].lost_frames)
- l1ts->chan_state[chan].lost_frames--;
-
- LOGL1S(DL1P, LOGL_DEBUG, l1t, tn, -1, l1sap->u.data.fn,
- "%s Rx -> RTP: %s\n",
- gsm_lchan_name(lchan), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
+ l1sap->u.tch.rssi = (int8_t) (rssi);
+ l1sap->u.tch.ber10k = ber10k;
+ l1sap->u.tch.ta_offs_256bits = ta_offs_256bits;
+ l1sap->u.tch.lqual_cb = link_qual_cb;
+ l1sap->u.tch.is_sub = is_sub & 1;
+
+ msg->l2h = msgb_put(msg, data_len);
+ if (data_len)
+ memcpy(msg->l2h, data, data_len);
+
+ LOGL1S(DL1P, LOGL_DEBUG, l1ts, chan, l1sap->u.tch.fn, "%s Rx -> RTP: %s\n",
+ gsm_lchan_name(lchan), msgb_hexdump_l2(msg));
/* forward primitive */
- l1sap_up(l1t->trx, l1sap);
+ l1sap_up(l1ts->ts->trx, l1sap);
return 0;
}
@@ -771,39 +834,37 @@ int _sched_compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
* data request (from upper layer)
*/
-int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
+int trx_sched_ph_data_req(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
- uint8_t tn = l1sap->u.data.chan_nr & 7;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ uint8_t tn = L1SAP_CHAN2TS(l1sap->u.data.chan_nr);
+ struct l1sched_ts *l1ts = trx->ts[tn].priv;
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, -1, l1sap->u.data.fn,
+ LOGL1S(DL1P, LOGL_DEBUG, l1ts, -1, l1sap->u.data.fn,
"PH-DATA.req: chan_nr=0x%02x link_id=0x%02x\n",
l1sap->u.data.chan_nr, l1sap->u.data.link_id);
- if (!l1sap->oph.msg)
- abort();
-
/* ignore empty frame */
- if (!msgb_l2len(l1sap->oph.msg)) {
+ if (!l1sap->oph.msg->l2h || msgb_l2len(l1sap->oph.msg) == 0) {
msgb_free(l1sap->oph.msg);
return 0;
}
+ /* VAMOS: convert Osmocom specific channel number to a generic one */
+ if (trx->ts[tn].vamos.is_shadow)
+ l1sap->u.data.chan_nr &= ~RSL_CHAN_OSMO_VAMOS_MASK;
+
msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
return 0;
}
-int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
+int trx_sched_tch_req(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
{
- uint8_t tn = l1sap->u.tch.chan_nr & 7;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+ uint8_t tn = L1SAP_CHAN2TS(l1sap->u.tch.chan_nr);
+ struct l1sched_ts *l1ts = trx->ts[tn].priv;
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, -1, l1sap->u.tch.fn, "TCH.req: chan_nr=0x%02x\n",
- l1sap->u.tch.chan_nr);
-
- if (!l1sap->oph.msg)
- abort();
+ LOGL1S(DL1P, LOGL_DEBUG, l1ts, -1, l1sap->u.tch.fn,
+ "TCH.req: chan_nr=0x%02x\n", l1sap->u.tch.chan_nr);
/* ignore empty frame */
if (!msgb_l2len(l1sap->oph.msg)) {
@@ -811,36 +872,43 @@ int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
return 0;
}
+ /* VAMOS: convert Osmocom specific channel number to a generic one */
+ if (trx->ts[tn].vamos.is_shadow)
+ l1sap->u.tch.chan_nr &= ~RSL_CHAN_OSMO_VAMOS_MASK;
+
msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
return 0;
}
-/*
+/*
* ready-to-send indication (to upper layer)
*/
/* RTS for data frame */
-static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan)
+static int rts_data_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
{
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;
+ chan_nr = trx_chan_desc[br->chan].chan_nr | br->tn;
+ link_id = trx_chan_desc[br->chan].link_id;
- if (!chan_nr) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
- "RTS func with non-existing chan_nr %d\n", chan_nr);
- return -ENODEV;
- }
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn,
- "PH-RTS.ind: chan_nr=0x%02x link_id=0x%02x\n", chan_nr, link_id);
+ /* VAMOS: use Osmocom specific channel number */
+ if (l1ts->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
+
+ /* For handover detection, there are cases where the SACCH should remain inactive until the first RACH
+ * indicating the TA is received. */
+ if (L1SAP_IS_LINK_SACCH(link_id)
+ && !l1ts->chan_state[br->chan].lchan->want_dl_sacch_active)
+ return 0;
+
+ LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "PH-RTS.ind: chan_nr=0x%02x link_id=0x%02x\n", chan_nr, link_id);
/* generate prim */
msg = l1sap_msgb_alloc(200);
@@ -851,31 +919,30 @@ static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
PRIM_OP_INDICATION, msg);
l1sap->u.data.chan_nr = chan_nr;
l1sap->u.data.link_id = link_id;
- l1sap->u.data.fn = fn;
+ l1sap->u.data.fn = br->fn;
- return l1sap_up(l1t->trx, l1sap);
+ return l1sap_up(l1ts->ts->trx, l1sap);
}
-static int rts_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan, int facch)
+static int rts_tch_common(const struct l1sched_ts *l1ts,
+ const struct trx_dl_burst_req *br,
+ bool facch)
{
uint8_t chan_nr, link_id;
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
int rc = 0;
/* get data for RTS indication */
- chan_nr = trx_chan_desc[chan].chan_nr | tn;
- link_id = trx_chan_desc[chan].link_id;
+ chan_nr = trx_chan_desc[br->chan].chan_nr | br->tn;
+ link_id = trx_chan_desc[br->chan].link_id;
- if (!chan_nr) {
- LOGL1S(DL1P, LOGL_FATAL, l1t, tn, chan, fn,
- "RTS func with non-existing chan_nr %d\n", chan_nr);
- return -ENODEV;
- }
- LOGL1S(DL1P, LOGL_INFO, l1t, tn, chan, fn, "TCH RTS.ind: chan_nr=0x%02x\n", chan_nr);
+ /* VAMOS: use Osmocom specific channel number */
+ if (l1ts->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
+
+ LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "TCH RTS.ind: chan_nr=0x%02x\n", chan_nr);
/* only send, if FACCH is selected */
if (facch) {
@@ -888,13 +955,13 @@ static int rts_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
PRIM_OP_INDICATION, msg);
l1sap->u.data.chan_nr = chan_nr;
l1sap->u.data.link_id = link_id;
- l1sap->u.data.fn = fn;
+ l1sap->u.data.fn = br->fn;
- rc = l1sap_up(l1t->trx, l1sap);
+ rc = l1sap_up(l1ts->ts->trx, l1sap);
}
- /* dont send, if TCH is in signalling only mode */
- if (l1ts->chan_state[chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
+ /* don't send, if TCH is in signalling only mode */
+ if (l1ts->chan_state[br->chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
/* generate prim */
msg = l1sap_msgb_alloc(200);
if (!msg)
@@ -903,128 +970,259 @@ static int rts_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
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;
+ l1sap->u.tch.fn = br->fn;
- return l1sap_up(l1t->trx, l1sap);
+ return l1sap_up(l1ts->ts->trx, l1sap);
}
return rc;
}
/* RTS for full rate traffic frame */
-static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
- enum trx_chan_type chan)
+static int rts_tchf_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
{
/* TCH/F may include FACCH on every 4th burst */
- return rts_tch_common(l1t, tn, fn, chan, 1);
+ return rts_tch_common(l1ts, br, true);
}
+/* FACCH/H channel mapping for Downlink (see 3GPP TS 45.002, table 1).
+ * This mapping is valid for both FACCH/H(0) and FACCH/H(1). */
+const uint8_t sched_tchh_dl_facch_map[26] = {
+ [4] = 1, /* FACCH/H(0): B0(4,6,8,10,13,15) */
+ [5] = 1, /* FACCH/H(1): B0(5,7,9,11,14,16) */
+ [13] = 1, /* FACCH/H(0): B1(13,15,17,19,21,23) */
+ [14] = 1, /* FACCH/H(1): B1(14,16,18,20,22,24) */
+ [21] = 1, /* FACCH/H(0): B2(21,23,0,2,4,6) */
+ [22] = 1, /* FACCH/H(1): B2(22,24,1,3,5,7) */
+};
/* 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)
+static int rts_tchh_fn(const struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
{
- /* 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);
+ return rts_tch_common(l1ts, br, sched_tchh_dl_facch_map[br->fn % 26]);
}
/* set multiframe scheduler to given pchan */
-int trx_sched_set_pchan(struct l1sched_trx *l1t, uint8_t tn,
- enum gsm_phys_chan_config pchan)
+int trx_sched_set_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan)
{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- int i;
-
- i = find_sched_mframe_idx(pchan, tn);
+ struct l1sched_ts *l1ts = ts->priv;
+ int i = find_sched_mframe_idx(pchan, ts->nr);
if (i < 0) {
- LOGP(DL1C, LOGL_NOTICE, "Failed to configure multiframe "
- "trx=%d ts=%d\n", l1t->trx->nr, tn);
+ LOGP(DL1C, LOGL_NOTICE, "%s Failed to configure multiframe (pchan=0x%02x)\n",
+ gsm_ts_name(ts), pchan);
return -ENOTSUP;
}
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, l1t->trx->nr, tn);
+ if (ts->vamos.peer != NULL) {
+ l1ts = ts->vamos.peer->priv;
+ l1ts->mf_index = i;
+ l1ts->mf_period = trx_sched_multiframes[i].period;
+ l1ts->mf_frames = trx_sched_multiframes[i].frames;
+ }
+ LOGP(DL1C, LOGL_NOTICE, "%s Configured multiframe with '%s'\n",
+ gsm_ts_name(ts), trx_sched_multiframes[i].name);
return 0;
}
+/* Remove all matching (by chan_nr & link_id) primitives from the given queue */
+static void trx_sched_queue_filter(struct llist_head *q, uint8_t chan_nr, uint8_t link_id)
+{
+ struct msgb *msg, *_msg;
+
+ llist_for_each_entry_safe(msg, _msg, q, list) {
+ struct osmo_phsap_prim *l1sap = msgb_l1sap_prim(msg);
+ switch (l1sap->oph.primitive) {
+ case PRIM_PH_DATA:
+ if (l1sap->u.data.chan_nr != chan_nr)
+ continue;
+ if (l1sap->u.data.link_id != link_id)
+ continue;
+ break;
+ case PRIM_TCH:
+ if (l1sap->u.tch.chan_nr != chan_nr)
+ continue;
+ if (link_id != 0x00)
+ continue;
+ break;
+ default:
+ /* Shall not happen */
+ OSMO_ASSERT(0);
+ }
+
+ /* Unlink and free() */
+ llist_del(&msg->list);
+ talloc_free(msg);
+ }
+}
+
+static void _trx_sched_set_lchan(struct gsm_lchan *lchan,
+ enum trx_chan_type chan,
+ bool active)
+{
+ struct l1sched_ts *l1ts = lchan->ts->priv;
+ struct l1sched_chan_state *chan_state;
+
+ OSMO_ASSERT(l1ts != NULL);
+ chan_state = &l1ts->chan_state[chan];
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "%s %s\n",
+ (active) ? "Activating" : "Deactivating",
+ trx_chan_desc[chan].name);
+
+ if (active) {
+ /* Clean up everything */
+ memset(chan_state, 0, sizeof(*chan_state));
+
+ /* Bind to generic 'struct gsm_lchan' */
+ chan_state->lchan = lchan;
+
+ /* Allocate memory for Rx/Tx burst buffers. Use the maximim size
+ * of 24 * (2 * 58) bytes, which is sufficient to store up to 24 GMSK
+ * modulated bursts for CSD or up to 8 8PSK modulated bursts for EGPRS. */
+ const size_t buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD;
+ if (trx_chan_desc[chan].dl_fn != NULL)
+ chan_state->dl_bursts = talloc_zero_size(l1ts, buf_size);
+ if (trx_chan_desc[chan].ul_fn != NULL)
+ chan_state->ul_bursts = talloc_zero_size(l1ts, buf_size);
+ } else {
+ chan_state->ho_rach_detect = 0;
+
+ /* Remove pending Tx prims belonging to this lchan */
+ trx_sched_queue_filter(&l1ts->dl_prims,
+ trx_chan_desc[chan].chan_nr,
+ trx_chan_desc[chan].link_id);
+
+ /* Release memory used by Rx/Tx burst buffers */
+ TALLOC_FREE(chan_state->dl_bursts);
+ TALLOC_FREE(chan_state->ul_bursts);
+ }
+
+ chan_state->active = active;
+}
+
/* setting all logical channels given attributes to active/inactive */
-int trx_sched_set_lchan(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t link_id,
- int active)
+int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_id, bool active)
{
+ struct l1sched_ts *l1ts = lchan->ts->priv;
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;
+ bool found = false;
+
+ if (!l1ts) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "%s lchan with uninitialized scheduler structure\n",
+ (active) ? "Activating" : "Deactivating");
+ return -EINVAL;
+ }
+
+ /* VAMOS: convert Osmocom specific channel number to a generic one,
+ * otherwise we won't match anything in trx_chan_desc[]. */
+ if (lchan->ts->vamos.is_shadow)
+ chan_nr &= ~RSL_CHAN_OSMO_VAMOS_MASK;
/* 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.
- * FIXME: Is it possible at all? Clarify if so. */
- if ((trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
- && !(trx_chan_desc[i].flags & TRX_CHAN_FLAG_PDCH))
+ for (enum trx_chan_type chan = 0; chan < _TRX_CHAN_MAX; chan++) {
+ if (trx_chan_desc[chan].chan_nr != (chan_nr & RSL_CHAN_NR_MASK))
continue;
- if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
- && trx_chan_desc[i].link_id == link_id) {
- 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, l1t->trx->nr, tn);
- if (active)
- memset(chan_state, 0, sizeof(*chan_state));
- chan_state->active = active;
- /* free burst memory, to cleanly start with burst 0 */
- if (chan_state->dl_bursts) {
- talloc_free(chan_state->dl_bursts);
- chan_state->dl_bursts = NULL;
- }
- if (chan_state->ul_bursts) {
- talloc_free(chan_state->ul_bursts);
- chan_state->ul_bursts = NULL;
- }
- if (!active)
- chan_state->ho_rach_detect = 0;
- }
+ if (trx_chan_desc[chan].link_id != link_id)
+ continue;
+ if (l1ts->chan_state[chan].active == active)
+ continue;
+ found = true;
+ _trx_sched_set_lchan(lchan, chan, active);
}
/* disable handover detection (on deactivation) */
if (!active)
- _sched_act_rach_det(l1t, tn, ss, 0);
+ _sched_act_rach_det(lchan->ts->trx, tn, ss, 0);
- return rc;
+ return found ? 0 : -EINVAL;
+}
+
+int trx_sched_set_ul_access(struct gsm_lchan *lchan, uint8_t chan_nr, bool active)
+{
+ struct l1sched_ts *l1ts = lchan->ts->priv;
+ uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+ uint8_t ss = l1sap_chan2ss(chan_nr);
+ int i;
+
+ if (!l1ts) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "%s UL access on lchan with uninitialized scheduler structure.\n",
+ (active) ? "Activating" : "Deactivating");
+ return -EINVAL;
+ }
+
+ /* look for all matching chan_nr */
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ if (trx_chan_desc[i].chan_nr == (chan_nr & RSL_CHAN_NR_MASK)) {
+ struct l1sched_chan_state *l1cs = &l1ts->chan_state[i];
+
+ l1cs->ho_rach_detect = active;
+ }
+ }
+
+ _sched_act_rach_det(lchan->ts->trx, tn, ss, active);
+
+ return 0;
+}
+
+int trx_sched_set_bcch_ccch(struct gsm_lchan *lchan, bool active)
+{
+ struct l1sched_ts *l1ts = lchan->ts->priv;
+ static const enum trx_chan_type chans[] = {
+ TRXC_FCCH,
+ TRXC_SCH,
+ TRXC_BCCH,
+ TRXC_RACH,
+ TRXC_CCCH,
+ };
+
+ if (!l1ts)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(chans); i++) {
+ enum trx_chan_type chan = chans[i];
+
+ if (l1ts->chan_state[chan].active == active)
+ continue;
+ _trx_sched_set_lchan(lchan, chan, active);
+ }
+
+ return 0;
}
/* setting all logical channels given attributes to active/inactive */
-int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmode,
+int trx_sched_set_mode(struct gsm_bts_trx_ts *ts, 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)
{
+ struct l1sched_ts *l1ts = ts->priv;
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 l1sched_chan_state *chan_state;
/* no mode for PDCH */
- if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
+ if (ts->pchan == GSM_PCHAN_PDCH)
return 0;
+ /* VAMOS: convert Osmocom specific channel number to a generic one,
+ * otherwise we won't match anything in trx_chan_desc[]. */
+ if (ts->vamos.is_shadow)
+ chan_nr &= ~RSL_CHAN_OSMO_VAMOS_MASK;
+
/* 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 = &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, l1t->trx->nr,
- tn);
+ struct l1sched_chan_state *chan_state = &l1ts->chan_state[i];
+
+ LOGP(DL1C, LOGL_INFO,
+ "%s Set mode for %s (rsl_cmode=%u, tch_mode=%u, handover=%u)\n",
+ gsm_ts_name(ts), trx_chan_desc[i].name,
+ rsl_cmode, tch_mode, handover);
+
chan_state->rsl_cmode = rsl_cmode;
chan_state->tch_mode = tch_mode;
chan_state->ho_rach_detect = handover;
@@ -1039,8 +1237,8 @@ int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmo
chan_state->dl_ft = initial_id;
chan_state->ul_cmr = initial_id;
chan_state->dl_cmr = initial_id;
- chan_state->ber_sum = 0;
- chan_state->ber_num = 0;
+ chan_state->lqual_cb_sum = 0;
+ chan_state->lqual_cb_num = 0;
}
rc = 0;
}
@@ -1051,53 +1249,54 @@ int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmo
* 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) */
- _sched_act_rach_det(l1t, tn, ss, handover);
+ _sched_act_rach_det(ts->trx, tn, ss, handover);
return rc;
}
/* setting cipher on 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)
+int trx_sched_set_cipher(struct gsm_lchan *lchan, uint8_t chan_nr, bool downlink)
{
- uint8_t tn = L1SAP_CHAN2TS(chan_nr);
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- int i;
- int rc = -EINVAL;
- struct l1sched_chan_state *chan_state;
+ int algo = lchan->encr.alg_id - 1;
+ int i, rc = -EINVAL;
/* no cipher for PDCH */
- if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
+ if (ts_pchan(lchan->ts) == GSM_PCHAN_PDCH)
return 0;
+ /* VAMOS: convert Osmocom specific channel number to a generic one,
+ * otherwise we won't match anything in trx_chan_desc[]. */
+ if (lchan->ts->vamos.is_shadow)
+ chan_nr &= ~RSL_CHAN_OSMO_VAMOS_MASK;
+
/* no algorithm given means a5/0 */
if (algo <= 0)
algo = 0;
- else if (key_len != 8) {
- LOGP(DL1C, LOGL_ERROR, "Algo A5/%d not supported with given "
- "key len=%d\n", algo, key_len);
+ else if (lchan->encr.key_len != 8 && lchan->encr.key_len != 16) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR,
+ "Algo A5/%d not supported with given key_len=%u\n",
+ algo, lchan->encr.key_len);
return -ENOTSUP;
}
/* look for all matching chan_nr */
for (i = 0; i < _TRX_CHAN_MAX; i++) {
- /* skip if pchan type */
- if (trx_chan_desc[i].flags & TRX_CHAN_FLAG_PDCH)
- continue;
- if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)) {
- 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, l1t->trx->nr, tn);
+ if (trx_chan_desc[i].chan_nr == (chan_nr & RSL_CHAN_NR_MASK)) {
+ struct l1sched_ts *l1ts = lchan->ts->priv;
+ struct l1sched_chan_state *l1cs = &l1ts->chan_state[i];
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Set A5/%d %s for %s\n",
+ algo, (downlink) ? "downlink" : "uplink",
+ trx_chan_desc[i].name);
+
if (downlink) {
- chan_state->dl_encr_algo = algo;
- memcpy(chan_state->dl_encr_key, key, key_len);
- chan_state->dl_encr_key_len = key_len;
+ l1cs->dl_encr_algo = algo;
+ memcpy(l1cs->dl_encr_key, lchan->encr.key, lchan->encr.key_len);
+ l1cs->dl_encr_key_len = lchan->encr.key_len;
} else {
- chan_state->ul_encr_algo = algo;
- memcpy(chan_state->ul_encr_key, key, key_len);
- chan_state->ul_encr_key_len = key_len;
+ l1cs->ul_encr_algo = algo;
+ memcpy(l1cs->ul_encr_key, lchan->encr.key, lchan->encr.key_len);
+ l1cs->ul_encr_key_len = lchan->encr.key_len;
}
rc = 0;
}
@@ -1107,9 +1306,8 @@ int trx_sched_set_cipher(struct l1sched_trx *l1t, uint8_t chan_nr, int downlink,
}
/* process ready-to-send */
-int _sched_rts(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn)
+int _sched_rts(const struct l1sched_ts *l1ts, 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;
@@ -1137,87 +1335,99 @@ int _sched_rts(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn)
return 0;
/* check if channel is active */
- if (!TRX_CHAN_IS_ACTIVE(&l1ts->chan_state[chan], chan))
+ if (!l1ts->chan_state[chan].active)
return -EINVAL;
- return func(l1t, tn, fn, frame->dl_chan);
+ /* There is no burst, just for logging */
+ struct trx_dl_burst_req dbr = {
+ .fn = fn,
+ .tn = l1ts->ts->nr,
+ .bid = bid,
+ .chan = chan,
+ };
+
+ return func(l1ts, &dbr);
+}
+
+static void trx_sched_apply_att(const struct gsm_lchan *lchan,
+ struct trx_dl_burst_req *br)
+{
+ const struct trx_chan_desc *desc = &trx_chan_desc[br->chan];
+
+ /* Current BS power reduction value in dB */
+ br->att = lchan->bs_power_ctrl.current;
+
+ /* Temporary Overpower for SACCH/FACCH bursts */
+ if (!lchan->top_acch_active)
+ return;
+ if ((lchan->top_acch_cap.sacch_enable && desc->link_id == LID_SACCH) ||
+ (lchan->top_acch_cap.facch_enable && br->flags & TRX_BR_F_FACCH)) {
+ if (br->att > lchan->top_acch_cap.overpower_db)
+ br->att -= lchan->top_acch_cap.overpower_db;
+ else
+ br->att = 0;
+ }
}
/* process downlink burst */
-const ubit_t *_sched_dl_burst(struct l1sched_trx *l1t, uint8_t tn,
- uint32_t fn, uint16_t *nbits)
+void _sched_dl_burst(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
- struct l1sched_chan_state *l1cs;
+ const struct l1sched_chan_state *l1cs;
const struct trx_sched_frame *frame;
- uint8_t offset, period, bid;
+ uint8_t offset, period;
trx_sched_dl_func *func;
- enum trx_chan_type chan;
- ubit_t *bits = NULL;
if (!l1ts->mf_index)
- goto no_data;
+ return;
/* get frame from multiframe */
period = l1ts->mf_period;
- offset = fn % period;
+ offset = br->fn % period;
frame = l1ts->mf_frames + offset;
- chan = frame->dl_chan;
- bid = frame->dl_bid;
- func = trx_chan_desc[chan].dl_fn;
+ br->chan = frame->dl_chan;
+ br->bid = frame->dl_bid;
+ func = trx_chan_desc[br->chan].dl_fn;
- l1cs = &l1ts->chan_state[chan];
+ l1cs = &l1ts->chan_state[br->chan];
/* check if channel is active */
- if (!TRX_CHAN_IS_ACTIVE(l1cs, chan)) {
- if (nbits)
- *nbits = GSM_BURST_LEN;
- goto no_data;
- }
+ if (!l1cs->active)
+ return;
+
+ /* Training Sequence Code and Set */
+ br->tsc_set = l1ts->ts->tsc_set;
+ br->tsc = l1ts->ts->tsc;
/* get burst from function */
- bits = func(l1t, tn, fn, chan, bid, nbits);
+ if (func(l1ts, br) != 0)
+ return;
+
+ /* Modulation is indicated by func() */
+ br->mod = l1cs->dl_mod_type;
+
+ /* BS Power reduction (in dB) per logical channel */
+ if (l1cs->lchan != NULL)
+ trx_sched_apply_att(l1cs->lchan, br);
/* encrypt */
- if (bits && l1cs->dl_encr_algo) {
+ if (br->burst_len && l1cs->dl_encr_algo) {
ubit_t ks[114];
int i;
- osmo_a5(l1cs->dl_encr_algo, l1cs->dl_encr_key, fn, ks, NULL);
+ osmo_a5(l1cs->dl_encr_algo, l1cs->dl_encr_key, br->fn, ks, NULL);
for (i = 0; i < 57; i++) {
- bits[i + 3] ^= ks[i];
- bits[i + 88] ^= ks[i + 57];
+ br->burst[i + 3] ^= ks[i];
+ br->burst[i + 88] ^= ks[i + 57];
}
}
-
-no_data:
- /* in case of C0, we need a dummy burst to maintain RF power */
- 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",
- trx_chan_desc[chan].name, fn, tn, bid);
-#endif
- bits = (ubit_t *) dummy_burst;
- }
-
- return bits;
}
-#define TDMA_FN_SUM(a, b) \
- ((a + GSM_HYPERFRAME + b) % GSM_HYPERFRAME)
-
-#define TDMA_FN_SUB(a, b) \
- ((a + GSM_HYPERFRAME - b) % GSM_HYPERFRAME)
-
-static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
- struct l1sched_chan_state *l1cs, uint8_t tn, uint32_t fn)
+static int trx_sched_calc_frame_loss(struct l1sched_ts *l1ts,
+ struct l1sched_chan_state *l1cs,
+ const struct trx_ul_burst_ind *bi)
{
- const struct trx_sched_frame *frame_head;
const struct trx_sched_frame *frame;
- struct l1sched_ts *l1ts;
uint32_t elapsed_fs;
uint8_t offset, i;
uint32_t fn_i;
@@ -1230,13 +1440,8 @@ static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
if (l1cs->proc_tdma_fs == 0)
return 0;
- /* Get current TDMA frame info */
- l1ts = l1sched_trx_get_ts(l1t, tn);
- offset = fn % l1ts->mf_period;
- frame_head = l1ts->mf_frames + offset;
-
/* Not applicable for some logical channels */
- switch (frame_head->ul_chan) {
+ switch (bi->chan) {
case TRXC_IDLE:
case TRXC_RACH:
case TRXC_PDTCH:
@@ -1249,9 +1454,9 @@ static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
}
/* How many frames elapsed since the last one? */
- elapsed_fs = TDMA_FN_SUB(fn, l1cs->last_tdma_fn);
+ elapsed_fs = GSM_TDMA_FN_SUB(bi->fn, l1cs->last_tdma_fn);
if (elapsed_fs > l1ts->mf_period) { /* Too many! */
- LOGL1S(DL1P, LOGL_ERROR, l1t, tn, frame_head->ul_chan, fn,
+ LOGL1SB(DL1P, LOGL_ERROR, l1ts, bi,
"Too many (>%u) contiguous TDMA frames=%u elapsed "
"since the last processed fn=%u\n", l1ts->mf_period,
elapsed_fs, l1cs->last_tdma_fn);
@@ -1263,21 +1468,21 @@ static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
* There are several TDMA frames between the last processed
* frame and currently received one. Let's walk through this
* path and count potentially lost frames, i.e. for which
- * we didn't receive the corresponsing UL bursts.
+ * we didn't receive the corresponding UL bursts.
*
* Start counting from the last_fn + 1.
*/
for (i = 1; i < elapsed_fs; i++) {
- fn_i = TDMA_FN_SUM(l1cs->last_tdma_fn, i);
+ fn_i = GSM_TDMA_FN_SUM(l1cs->last_tdma_fn, i);
offset = fn_i % l1ts->mf_period;
frame = l1ts->mf_frames + offset;
- if (frame->ul_chan == frame_head->ul_chan)
+ if (frame->ul_chan == bi->chan)
l1cs->lost_tdma_fs++;
}
if (l1cs->lost_tdma_fs > 0) {
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, frame_head->ul_chan, fn,
+ LOGL1SB(DL1P, LOGL_NOTICE, l1ts, bi,
"At least %u TDMA frames were lost since the last "
"processed fn=%u\n", l1cs->lost_tdma_fs, l1cs->last_tdma_fn);
@@ -1290,31 +1495,33 @@ static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
trx_sched_ul_func *func;
/* Prepare dummy burst indication */
- struct trx_ul_burst_ind bi = {
+ struct trx_ul_burst_ind dbi = {
.flags = TRX_BI_F_NOPE_IND,
.burst_len = GSM_BURST_LEN,
.burst = { 0 },
.rssi = -128,
.toa256 = 0,
+ .chan = bi->chan,
/* TDMA FN is set below */
- .tn = tn,
+ .tn = bi->tn,
};
for (i = 1; i < elapsed_fs; i++) {
- fn_i = TDMA_FN_SUM(l1cs->last_tdma_fn, i);
+ fn_i = GSM_TDMA_FN_SUM(l1cs->last_tdma_fn, i);
offset = fn_i % l1ts->mf_period;
frame = l1ts->mf_frames + offset;
func = trx_chan_desc[frame->ul_chan].ul_fn;
- if (frame->ul_chan != frame_head->ul_chan)
+ if (frame->ul_chan != bi->chan)
continue;
- LOGL1S(DL1P, LOGL_NOTICE, l1t, tn, frame->ul_chan, fn,
- "Substituting lost TDMA frame=%u by all-zero "
- "dummy burst\n", fn_i);
+ dbi.bid = frame->ul_bid;
+ dbi.fn = fn_i;
- bi.fn = fn_i;
- func(l1t, frame->ul_chan, frame->ul_bid, &bi);
+ LOGL1SB(DL1P, LOGL_NOTICE, l1ts, &dbi,
+ "Substituting lost burst with NOPE.ind\n");
+
+ func(l1ts, &dbi);
l1cs->lost_tdma_fs--;
}
@@ -1323,15 +1530,42 @@ static int trx_sched_calc_frame_loss(struct l1sched_trx *l1t,
return 0;
}
+/* Process a single noise measurement for an inactive timeslot. */
+static void trx_sched_noise_meas(struct l1sched_chan_state *l1cs,
+ const struct trx_ul_burst_ind *bi)
+{
+ int *Avg = &l1cs->meas.interf_avg;
+
+ /* EWMA (Exponentially Weighted Moving Average):
+ *
+ * Avg[n] = a * Val[n] + (1 - a) * Avg[n - 1]
+ *
+ * Implemented using the '+=' operator:
+ *
+ * Avg += a * Val - a * Avg
+ * Avg += a * (Val - Avg)
+ *
+ * We use constant 'a' = 0.5, what is equal to:
+ *
+ * Avg += (Val - Avg) / 2
+ *
+ * We don't really need precisity here, so no scaling.
+ */
+
+ *Avg += (bi->rssi - *Avg) / 2;
+}
+
/* Process an Uplink burst indication */
-int trx_sched_ul_burst(struct l1sched_trx *l1t, struct trx_ul_burst_ind *bi)
+int trx_sched_ul_burst(struct l1sched_ts *l1ts, struct trx_ul_burst_ind *bi)
{
- struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, bi->tn);
struct l1sched_chan_state *l1cs;
const struct trx_sched_frame *frame;
- uint8_t offset, period, bid;
+ uint8_t offset, period;
trx_sched_ul_func *func;
- enum trx_chan_type chan;
+
+ /* VAMOS: redirect to the shadow timeslot */
+ if (bi->flags & TRX_BI_F_SHADOW_IND)
+ l1ts = l1ts->ts->vamos.peer->priv;
if (!l1ts->mf_index)
return -EINVAL;
@@ -1341,26 +1575,37 @@ int trx_sched_ul_burst(struct l1sched_trx *l1t, struct trx_ul_burst_ind *bi)
offset = bi->fn % period;
frame = l1ts->mf_frames + offset;
- chan = frame->ul_chan;
- bid = frame->ul_bid;
- l1cs = &l1ts->chan_state[chan];
- func = trx_chan_desc[chan].ul_fn;
+ bi->chan = frame->ul_chan;
+ bi->bid = frame->ul_bid;
+ l1cs = &l1ts->chan_state[bi->chan];
+ func = trx_chan_desc[bi->chan].ul_fn;
/* check if channel is active */
- if (!TRX_CHAN_IS_ACTIVE(l1cs, chan))
- return -EINVAL;
+ if (!l1cs->active) {
+ /* handle noise measurements on dedicated and idle channels */
+ if (TRX_CHAN_IS_DEDIC(bi->chan) || bi->chan == TRXC_IDLE)
+ trx_sched_noise_meas(l1cs, bi);
+ return 0;
+ }
/* omit bursts which have no handler, like IDLE bursts */
if (!func)
return -EINVAL;
/* calculate how many TDMA frames were potentially lost */
- trx_sched_calc_frame_loss(l1t, l1cs, bi->tn, bi->fn);
+ trx_sched_calc_frame_loss(l1ts, l1cs, bi);
/* update TDMA frame counters */
l1cs->last_tdma_fn = bi->fn;
l1cs->proc_tdma_fs++;
+ /* handle NOPE indications */
+ if (bi->flags & TRX_BI_F_NOPE_IND) {
+ /* NOTE: Uplink burst handler must check bi->burst_len before
+ * accessing bi->burst to avoid uninitialized memory access. */
+ return func(l1ts, bi);
+ }
+
/* decrypt */
if (bi->burst_len && l1cs->ul_encr_algo) {
ubit_t ks[114];
@@ -1376,13 +1621,7 @@ int trx_sched_ul_burst(struct l1sched_trx *l1t, struct trx_ul_burst_ind *bi)
}
/* Invoke the logical channel handler */
- func(l1t, chan, bid, bi);
+ func(l1ts, bi);
return 0;
}
-
-struct l1sched_ts *l1sched_trx_get_ts(struct l1sched_trx *l1t, uint8_t tn)
-{
- OSMO_ASSERT(tn < ARRAY_SIZE(l1t->ts));
- return &l1t->ts[tn];
-}